debian/0000755000000000000000000000000012100001666007156 5ustar debian/copyright0000644000000000000000000000315512044254507011130 0ustar This package was put together by Joey Hess , using sources from the following location: http://rss2email.infogami.com/ rss2email is Copyright (C) 2004 Aaron Swartz, and licensed under the terms of version 2 or 3 of the GNU GPL. These licenses may be found on Debian systems in the files /usr/share/common-licenses/GPL-2 and /usr/share/common-licenses/GPL-3. feedparser is Copyright (c) 2002-2008 Mark Pilgrim and under the following license : * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. html2text is Copyright (C) 2004-2008 Aaron Swartz and licensed under the GNU GPL 3. debian/patches/0000755000000000000000000000000012100001643010600 5ustar debian/patches/0007-Fix-email-header-injection.patch0000644000000000000000000000215212100001535017226 0ustar From: Etienne Millon Date: Fri, 25 May 2012 18:04:08 +0200 Subject: Fix email header injection Bug: http://bugs.python.org/issue5871 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=526064 --- rss2email.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rss2email.py b/rss2email.py index 69998db..a6c3cbe 100755 --- a/rss2email.py +++ b/rss2email.py @@ -111,9 +111,16 @@ PROXY="" CHARSET_LIST='US-ASCII', 'ISO-8859-1', 'UTF-8', 'BIG5', 'ISO-2022-JP' from email.MIMEText import MIMEText -from email.Header import Header +from email.Header import Header as _Header from email.Utils import parseaddr, formataddr - + +class Header(_Header): + # Work-around for + def append(self, s=None, *args, **kwargs): + if s is not None: + s = s.replace('\n', ' ').replace('\r', ' ') + _Header.append(self, s, *args, **kwargs) + # Note: You can also override the send function. def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtpserver=None): debian/patches/0004-convert-tabs-to-spaces.patch0000644000000000000000000020234012100001535016506 0ustar From: Etienne Millon Date: Thu, 13 Oct 2011 12:55:25 +0200 Subject: convert tabs to spaces --- rss2email.py | 1402 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 701 insertions(+), 701 deletions(-) diff --git a/rss2email.py b/rss2email.py index 7696d99..4651f56 100755 --- a/rss2email.py +++ b/rss2email.py @@ -59,7 +59,7 @@ DATE_HEADER_ORDER = ('modified', 'issued', 'created') #DEPRECATED QP_REQUIRED = 0 #DEPRECATED - + # 1: Name feeds as they're being processed. # 0: Keep quiet. VERBOSE = 0 @@ -113,120 +113,120 @@ CHARSET_LIST='US-ASCII', 'BIG5', 'ISO-2022-JP', 'ISO-8859-1', 'UTF-8' from email.MIMEText import MIMEText from email.Header import Header from email.Utils import parseaddr, formataddr - + # Note: You can also override the send function. def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtpserver=None): - """Send an email. - - All arguments should be Unicode strings (plain ASCII works as well). - - Only the real name part of sender and recipient addresses may contain - non-ASCII characters. - - The email will be properly MIME encoded and delivered though SMTP to - localhost port 25. This is easy to change if you want something different. - - The charset of the email will be the first one out of the list - that can represent all the characters occurring in the email. - """ - - # Header class is smart enough to try US-ASCII, then the charset we - # provide, then fall back to UTF-8. - header_charset = 'ISO-8859-1' - - # We must choose the body charset manually - for body_charset in CHARSET_LIST: - try: - body.encode(body_charset) - except (UnicodeError, LookupError): - pass - else: - break - - # Split real name (which is optional) and email address parts - sender_name, sender_addr = parseaddr(sender) - recipient_name, recipient_addr = parseaddr(recipient) - - # We must always pass Unicode strings to Header, otherwise it will - # use RFC 2047 encoding even on plain ASCII strings. - sender_name = str(Header(unicode(sender_name), header_charset)) - recipient_name = str(Header(unicode(recipient_name), header_charset)) - - # Make sure email addresses do not contain non-ASCII characters - sender_addr = sender_addr.encode('ascii') - recipient_addr = recipient_addr.encode('ascii') - - # Create the message ('plain' stands for Content-Type: text/plain) - msg = MIMEText(body.encode(body_charset), contenttype, body_charset) - msg['To'] = formataddr((recipient_name, recipient_addr)) - msg['Subject'] = Header(unicode(subject), header_charset) - for hdr in extraheaders.keys(): - try: - msg[hdr] = Header(unicode(extraheaders[hdr], header_charset)) - except: - msg[hdr] = Header(extraheaders[hdr]) - - fromhdr = formataddr((sender_name, sender_addr)) - msg['From'] = fromhdr - - msg_as_string = msg.as_string() -#DEPRECATED if QP_REQUIRED: -#DEPRECATED ins, outs = SIO(msg_as_string), SIO() -#DEPRECATED mimify.mimify(ins, outs) -#DEPRECATED msg_as_string = outs.getvalue() - - if SMTP_SEND: - if not smtpserver: - import smtplib - - try: - if SMTP_SSL: - smtpserver = smtplib.SMTP_SSL() - else: - smtpserver = smtplib.SMTP() - smtpserver.connect(SMTP_SERVER) - except KeyboardInterrupt: - raise - except Exception, e: - print >>warn, "" - print >>warn, ('Fatal error: could not connect to mail server "%s"' % SMTP_SERVER) - print >>warn, ('Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly') - if hasattr(e, 'reason'): - print >>warn, "Reason:", e.reason - sys.exit(1) - - if AUTHREQUIRED: - try: - smtpserver.ehlo() - if not SMTP_SSL: smtpserver.starttls() - smtpserver.ehlo() - smtpserver.login(SMTP_USER, SMTP_PASS) - except KeyboardInterrupt: - raise - except Exception, e: - print >>warn, "" - print >>warn, ('Fatal error: could not authenticate with mail server "%s" as user "%s"' % (SMTP_SERVER, SMTP_USER)) - print >>warn, ('Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly') - if hasattr(e, 'reason'): - print >>warn, "Reason:", e.reason - sys.exit(1) - - smtpserver.sendmail(sender, recipient, msg_as_string) - return smtpserver - - else: - try: - p = subprocess.Popen(["/usr/sbin/sendmail", recipient], stdin=subprocess.PIPE, stdout=subprocess.PIPE) - p.communicate(msg_as_string) - status = p.returncode - assert status != None, "just a sanity check" - if status != 0: - print >>warn, "" - print >>warn, ('Fatal error: sendmail exited with code %s' % status) - sys.exit(1) - except: - print '''Error attempting to send email via sendmail. Possibly you need to configure your config.py to use a SMTP server? Please refer to the rss2email documentation or website (http://rss2email.infogami.com) for complete documentation of config.py. The options below may suffice for configuring email: + """Send an email. + + All arguments should be Unicode strings (plain ASCII works as well). + + Only the real name part of sender and recipient addresses may contain + non-ASCII characters. + + The email will be properly MIME encoded and delivered though SMTP to + localhost port 25. This is easy to change if you want something different. + + The charset of the email will be the first one out of the list + that can represent all the characters occurring in the email. + """ + + # Header class is smart enough to try US-ASCII, then the charset we + # provide, then fall back to UTF-8. + header_charset = 'ISO-8859-1' + + # We must choose the body charset manually + for body_charset in CHARSET_LIST: + try: + body.encode(body_charset) + except (UnicodeError, LookupError): + pass + else: + break + + # Split real name (which is optional) and email address parts + sender_name, sender_addr = parseaddr(sender) + recipient_name, recipient_addr = parseaddr(recipient) + + # We must always pass Unicode strings to Header, otherwise it will + # use RFC 2047 encoding even on plain ASCII strings. + sender_name = str(Header(unicode(sender_name), header_charset)) + recipient_name = str(Header(unicode(recipient_name), header_charset)) + + # Make sure email addresses do not contain non-ASCII characters + sender_addr = sender_addr.encode('ascii') + recipient_addr = recipient_addr.encode('ascii') + + # Create the message ('plain' stands for Content-Type: text/plain) + msg = MIMEText(body.encode(body_charset), contenttype, body_charset) + msg['To'] = formataddr((recipient_name, recipient_addr)) + msg['Subject'] = Header(unicode(subject), header_charset) + for hdr in extraheaders.keys(): + try: + msg[hdr] = Header(unicode(extraheaders[hdr], header_charset)) + except: + msg[hdr] = Header(extraheaders[hdr]) + + fromhdr = formataddr((sender_name, sender_addr)) + msg['From'] = fromhdr + + msg_as_string = msg.as_string() +#DEPRECATED if QP_REQUIRED: +#DEPRECATED ins, outs = SIO(msg_as_string), SIO() +#DEPRECATED mimify.mimify(ins, outs) +#DEPRECATED msg_as_string = outs.getvalue() + + if SMTP_SEND: + if not smtpserver: + import smtplib + + try: + if SMTP_SSL: + smtpserver = smtplib.SMTP_SSL() + else: + smtpserver = smtplib.SMTP() + smtpserver.connect(SMTP_SERVER) + except KeyboardInterrupt: + raise + except Exception, e: + print >>warn, "" + print >>warn, ('Fatal error: could not connect to mail server "%s"' % SMTP_SERVER) + print >>warn, ('Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly') + if hasattr(e, 'reason'): + print >>warn, "Reason:", e.reason + sys.exit(1) + + if AUTHREQUIRED: + try: + smtpserver.ehlo() + if not SMTP_SSL: smtpserver.starttls() + smtpserver.ehlo() + smtpserver.login(SMTP_USER, SMTP_PASS) + except KeyboardInterrupt: + raise + except Exception, e: + print >>warn, "" + print >>warn, ('Fatal error: could not authenticate with mail server "%s" as user "%s"' % (SMTP_SERVER, SMTP_USER)) + print >>warn, ('Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly') + if hasattr(e, 'reason'): + print >>warn, "Reason:", e.reason + sys.exit(1) + + smtpserver.sendmail(sender, recipient, msg_as_string) + return smtpserver + + else: + try: + p = subprocess.Popen(["/usr/sbin/sendmail", recipient], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p.communicate(msg_as_string) + status = p.returncode + assert status != None, "just a sanity check" + if status != 0: + print >>warn, "" + print >>warn, ('Fatal error: sendmail exited with code %s' % status) + sys.exit(1) + except: + print '''Error attempting to send email via sendmail. Possibly you need to configure your config.py to use a SMTP server? Please refer to the rss2email documentation or website (http://rss2email.infogami.com) for complete documentation of config.py. The options below may suffice for configuring email: # 1: Use SMTP_SERVER to send mail. # 0: Call /usr/sbin/sendmail to send mail. SMTP_SEND = 0 @@ -236,8 +236,8 @@ AUTHREQUIRED = 0 # if you need to use SMTP AUTH set to 1 SMTP_USER = 'username' # for SMTP AUTH, set SMTP username here SMTP_PASS = 'password' # for SMTP AUTH, set SMTP password here ''' - sys.exit(1) - return None + sys.exit(1) + return None ## html2text options ## @@ -256,38 +256,38 @@ BODY_WIDTH = 0 import sys sys.path.insert(0,".") try: - from config import * + from config import * except: - pass + pass warn = sys.stderr - + if QP_REQUIRED: - print >>warn, "QP_REQUIRED has been deprecated in rss2email." + print >>warn, "QP_REQUIRED has been deprecated in rss2email." ### Import Modules ### import cPickle as pickle, time, os, traceback, sys, types, subprocess hash = () try: - import hashlib - hash = hashlib.md5 + import hashlib + hash = hashlib.md5 except ImportError: - import md5 - hash = md5.new + import md5 + hash = md5.new unix = 0 try: - import fcntl -# A pox on SunOS file locking methods - if (sys.platform.find('sunos') == -1): - unix = 1 + import fcntl +# A pox on SunOS file locking methods + if (sys.platform.find('sunos') == -1): + unix = 1 except: - pass - + pass + import socket; socket_errors = [] for e in ['error', 'gaierror']: - if hasattr(socket, e): socket_errors.append(getattr(socket, e)) + if hasattr(socket, e): socket_errors.append(getattr(socket, e)) #DEPRECATED import mimify #DEPRECATED from StringIO import StringIO as SIO @@ -347,617 +347,617 @@ def isstr(f): return isinstance(f, type('')) or isinstance(f, type(u'')) def ishtml(t): return type(t) is type(()) def contains(a,b): return a.find(b) != -1 def unu(s): # I / freakin' hate / that unicode - if type(s) is types.UnicodeType: return s.encode('utf-8') - else: return s + if type(s) is types.UnicodeType: return s.encode('utf-8') + else: return s ### Parsing Utilities ### def getContent(entry, HTMLOK=0): - """Select the best content from an entry, deHTMLizing if necessary. - If raw HTML is best, an ('HTML', best) tuple is returned. """ - - # How this works: - # * We have a bunch of potential contents. - # * We go thru looking for our first choice. - # (HTML or text, depending on HTMLOK) - # * If that doesn't work, we go thru looking for our second choice. - # * If that still doesn't work, we just take the first one. - # - # Possible future improvement: - # * Instead of just taking the first one - # pick the one in the "best" language. - # * HACK: hardcoded HTMLOK, should take a tuple of media types - - conts = entry.get('content', []) - - if entry.get('summary_detail', {}): - conts += [entry.summary_detail] - - if conts: - if HTMLOK: - for c in conts: - if contains(c.type, 'html'): return ('HTML', c.value) - - if not HTMLOK: # Only need to convert to text if HTML isn't OK - for c in conts: - if contains(c.type, 'html'): - return html2text(c.value) - - for c in conts: - if c.type == 'text/plain': return c.value - - return conts[0].value - - return "" + """Select the best content from an entry, deHTMLizing if necessary. + If raw HTML is best, an ('HTML', best) tuple is returned. """ + + # How this works: + # * We have a bunch of potential contents. + # * We go thru looking for our first choice. + # (HTML or text, depending on HTMLOK) + # * If that doesn't work, we go thru looking for our second choice. + # * If that still doesn't work, we just take the first one. + # + # Possible future improvement: + # * Instead of just taking the first one + # pick the one in the "best" language. + # * HACK: hardcoded HTMLOK, should take a tuple of media types + + conts = entry.get('content', []) + + if entry.get('summary_detail', {}): + conts += [entry.summary_detail] + + if conts: + if HTMLOK: + for c in conts: + if contains(c.type, 'html'): return ('HTML', c.value) + + if not HTMLOK: # Only need to convert to text if HTML isn't OK + for c in conts: + if contains(c.type, 'html'): + return html2text(c.value) + + for c in conts: + if c.type == 'text/plain': return c.value + + return conts[0].value + + return "" def getID(entry): - """Get best ID from an entry.""" - if TRUST_GUID: - if 'id' in entry and entry.id: - # Newer versions of feedparser could return a dictionary - if type(entry.id) is DictType: - return entry.id.values()[0] + """Get best ID from an entry.""" + if TRUST_GUID: + if 'id' in entry and entry.id: + # Newer versions of feedparser could return a dictionary + if type(entry.id) is DictType: + return entry.id.values()[0] - return entry.id + return entry.id - content = getContent(entry) - if content and content != "\n": return hash(unu(content)).hexdigest() - if 'link' in entry: return entry.link - if 'title' in entry: return hash(unu(entry.title)).hexdigest() + content = getContent(entry) + if content and content != "\n": return hash(unu(content)).hexdigest() + if 'link' in entry: return entry.link + if 'title' in entry: return hash(unu(entry.title)).hexdigest() def getName(r, entry): - """Get the best name.""" - - if NO_FRIENDLY_NAME: return '' - - feed = r.feed - if hasattr(r, "url") and r.url in OVERRIDE_FROM.keys(): - return OVERRIDE_FROM[r.url] - - name = feed.get('title', '') - - if 'name' in entry.get('author_detail', []): # normally {} but py2.1 - if entry.author_detail.name: - if name: name += ": " - det=entry.author_detail.name - try: - name += entry.author_detail.name - except UnicodeDecodeError: - name += unicode(entry.author_detail.name, 'utf-8') - - elif 'name' in feed.get('author_detail', []): - if feed.author_detail.name: - if name: name += ", " - name += feed.author_detail.name - - return name + """Get the best name.""" + + if NO_FRIENDLY_NAME: return '' + + feed = r.feed + if hasattr(r, "url") and r.url in OVERRIDE_FROM.keys(): + return OVERRIDE_FROM[r.url] + + name = feed.get('title', '') + + if 'name' in entry.get('author_detail', []): # normally {} but py2.1 + if entry.author_detail.name: + if name: name += ": " + det=entry.author_detail.name + try: + name += entry.author_detail.name + except UnicodeDecodeError: + name += unicode(entry.author_detail.name, 'utf-8') + + elif 'name' in feed.get('author_detail', []): + if feed.author_detail.name: + if name: name += ", " + name += feed.author_detail.name + + return name def validateEmail(email, planb): - """Do a basic quality check on email address, but return planb if email doesn't appear to be well-formed""" - email_parts = email.split('@') - if len(email_parts) != 2: - return planb - return email - + """Do a basic quality check on email address, but return planb if email doesn't appear to be well-formed""" + email_parts = email.split('@') + if len(email_parts) != 2: + return planb + return email + def getEmail(r, entry): - """Get the best email_address. If the best guess isn't well-formed (something@somthing.com), use DEFAULT_FROM instead""" - - feed = r.feed - - if FORCE_FROM: return DEFAULT_FROM - - if hasattr(r, "url") and r.url in OVERRIDE_EMAIL.keys(): - return validateEmail(OVERRIDE_EMAIL[r.url], DEFAULT_FROM) - - if 'email' in entry.get('author_detail', []): - return validateEmail(entry.author_detail.email, DEFAULT_FROM) - - if 'email' in feed.get('author_detail', []): - return validateEmail(feed.author_detail.email, DEFAULT_FROM) - - if USE_PUBLISHER_EMAIL: - if 'email' in feed.get('publisher_detail', []): - return validateEmail(feed.publisher_detail.email, DEFAULT_FROM) - - if feed.get("errorreportsto", ''): - return validateEmail(feed.errorreportsto, DEFAULT_FROM) - - if hasattr(r, "url") and r.url in DEFAULT_EMAIL.keys(): - return DEFAULT_EMAIL[r.url] - return DEFAULT_FROM + """Get the best email_address. If the best guess isn't well-formed (something@somthing.com), use DEFAULT_FROM instead""" + + feed = r.feed + + if FORCE_FROM: return DEFAULT_FROM + + if hasattr(r, "url") and r.url in OVERRIDE_EMAIL.keys(): + return validateEmail(OVERRIDE_EMAIL[r.url], DEFAULT_FROM) + + if 'email' in entry.get('author_detail', []): + return validateEmail(entry.author_detail.email, DEFAULT_FROM) + + if 'email' in feed.get('author_detail', []): + return validateEmail(feed.author_detail.email, DEFAULT_FROM) + + if USE_PUBLISHER_EMAIL: + if 'email' in feed.get('publisher_detail', []): + return validateEmail(feed.publisher_detail.email, DEFAULT_FROM) + + if feed.get("errorreportsto", ''): + return validateEmail(feed.errorreportsto, DEFAULT_FROM) + + if hasattr(r, "url") and r.url in DEFAULT_EMAIL.keys(): + return DEFAULT_EMAIL[r.url] + return DEFAULT_FROM ### Simple Database of Feeds ### class Feed: - def __init__(self, url, to): - self.url, self.etag, self.modified, self.seen = url, None, None, {} - self.active = True - self.to = to + def __init__(self, url, to): + self.url, self.etag, self.modified, self.seen = url, None, None, {} + self.active = True + self.to = to def load(lock=1): - if not os.path.exists(feedfile): - print 'Feedfile "%s" does not exist. If you\'re using r2e for the first time, you' % feedfile - print "have to run 'r2e new' first." - sys.exit(1) - try: - feedfileObject = open(feedfile, 'r') - except IOError, e: - print "Feedfile could not be opened: %s" % e - sys.exit(1) - feeds = pickle.load(feedfileObject) - - if lock: - locktype = 0 - if unix: - locktype = fcntl.LOCK_EX - fcntl.flock(feedfileObject.fileno(), locktype) - #HACK: to deal with lock caching - feedfileObject = open(feedfile, 'r') - feeds = pickle.load(feedfileObject) - if unix: - fcntl.flock(feedfileObject.fileno(), locktype) - if feeds: - for feed in feeds[1:]: - if not hasattr(feed, 'active'): - feed.active = True - - return feeds, feedfileObject + if not os.path.exists(feedfile): + print 'Feedfile "%s" does not exist. If you\'re using r2e for the first time, you' % feedfile + print "have to run 'r2e new' first." + sys.exit(1) + try: + feedfileObject = open(feedfile, 'r') + except IOError, e: + print "Feedfile could not be opened: %s" % e + sys.exit(1) + feeds = pickle.load(feedfileObject) + + if lock: + locktype = 0 + if unix: + locktype = fcntl.LOCK_EX + fcntl.flock(feedfileObject.fileno(), locktype) + #HACK: to deal with lock caching + feedfileObject = open(feedfile, 'r') + feeds = pickle.load(feedfileObject) + if unix: + fcntl.flock(feedfileObject.fileno(), locktype) + if feeds: + for feed in feeds[1:]: + if not hasattr(feed, 'active'): + feed.active = True + + return feeds, feedfileObject def unlock(feeds, feedfileObject): - if not unix: - pickle.dump(feeds, open(feedfile, 'w')) - else: - fd = open(feedfile+'.tmp', 'w') - pickle.dump(feeds, fd) - fd.flush() - os.fsync(fd.fileno()) - fd.close() - os.rename(feedfile+'.tmp', feedfile) - fcntl.flock(feedfileObject.fileno(), fcntl.LOCK_UN) - -#@timelimit(FEED_TIMEOUT) + if not unix: + pickle.dump(feeds, open(feedfile, 'w')) + else: + fd = open(feedfile+'.tmp', 'w') + pickle.dump(feeds, fd) + fd.flush() + os.fsync(fd.fileno()) + fd.close() + os.rename(feedfile+'.tmp', feedfile) + fcntl.flock(feedfileObject.fileno(), fcntl.LOCK_UN) + +#@timelimit(FEED_TIMEOUT) def parse(url, etag, modified): - if PROXY == '': - return feedparser.parse(url, etag, modified) - else: - proxy = urllib2.ProxyHandler( {"http":PROXY} ) - return feedparser.parse(url, etag, modified, handlers = [proxy]) - - + if PROXY == '': + return feedparser.parse(url, etag, modified) + else: + proxy = urllib2.ProxyHandler( {"http":PROXY} ) + return feedparser.parse(url, etag, modified, handlers = [proxy]) + + ### Program Functions ### def add(*args): - if len(args) == 2 and contains(args[1], '@') and not contains(args[1], '://'): - urls, to = [args[0]], args[1] - else: - urls, to = args, None - - feeds, feedfileObject = load() - if (feeds and not isstr(feeds[0]) and to is None) or (not len(feeds) and to is None): - print "No email address has been defined. Please run 'r2e email emailaddress' or" - print "'r2e add url emailaddress'." - sys.exit(1) - for url in urls: feeds.append(Feed(url, to)) - unlock(feeds, feedfileObject) + if len(args) == 2 and contains(args[1], '@') and not contains(args[1], '://'): + urls, to = [args[0]], args[1] + else: + urls, to = args, None + + feeds, feedfileObject = load() + if (feeds and not isstr(feeds[0]) and to is None) or (not len(feeds) and to is None): + print "No email address has been defined. Please run 'r2e email emailaddress' or" + print "'r2e add url emailaddress'." + sys.exit(1) + for url in urls: feeds.append(Feed(url, to)) + unlock(feeds, feedfileObject) def run(num=None): - feeds, feedfileObject = load() - smtpserver = None - try: - # We store the default to address as the first item in the feeds list. - # Here we take it out and save it for later. - default_to = "" - if feeds and isstr(feeds[0]): default_to = feeds[0]; ifeeds = feeds[1:] - else: ifeeds = feeds - - if num: ifeeds = [feeds[num]] - feednum = 0 - - for f in ifeeds: - try: - feednum += 1 - if not f.active: continue - - if VERBOSE: print >>warn, 'I: Processing [%d] "%s"' % (feednum, f.url) - r = {} - try: - r = timelimit(FEED_TIMEOUT, parse)(f.url, f.etag, f.modified) - except TimeoutError: - print >>warn, 'W: feed [%d] "%s" timed out' % (feednum, f.url) - continue - - # Handle various status conditions, as required - if 'status' in r: - if r.status == 301: f.url = r['url'] - elif r.status == 410: - print >>warn, "W: feed gone; deleting", f.url - feeds.remove(f) - continue - - http_status = r.get('status', 200) - if VERBOSE > 1: print >>warn, "I: http status", http_status - http_headers = r.get('headers', { - 'content-type': 'application/rss+xml', - 'content-length':'1'}) - exc_type = r.get("bozo_exception", Exception()).__class__ - if http_status != 304 and not r.entries and not r.get('version', ''): - if http_status not in [200, 302]: - print >>warn, "W: error %d [%d] %s" % (http_status, feednum, f.url) - - elif contains(http_headers.get('content-type', 'rss'), 'html'): - print >>warn, "W: looks like HTML [%d] %s" % (feednum, f.url) - - elif http_headers.get('content-length', '1') == '0': - print >>warn, "W: empty page [%d] %s" % (feednum, f.url) - - elif hasattr(socket, 'timeout') and exc_type == socket.timeout: - print >>warn, "W: timed out on [%d] %s" % (feednum, f.url) - - elif exc_type == IOError: - print >>warn, 'W: "%s" [%d] %s' % (r.bozo_exception, feednum, f.url) - - elif hasattr(feedparser, 'zlib') and exc_type == feedparser.zlib.error: - print >>warn, "W: broken compression [%d] %s" % (feednum, f.url) - - elif exc_type in socket_errors: - exc_reason = r.bozo_exception.args[1] - print >>warn, "W: %s [%d] %s" % (exc_reason, feednum, f.url) - - elif exc_type == urllib2.URLError: - if r.bozo_exception.reason.__class__ in socket_errors: - exc_reason = r.bozo_exception.reason.args[1] - else: - exc_reason = r.bozo_exception.reason - print >>warn, "W: %s [%d] %s" % (exc_reason, feednum, f.url) - - elif exc_type == AttributeError: - print >>warn, "W: %s [%d] %s" % (r.bozo_exception, feednum, f.url) - - elif exc_type == KeyboardInterrupt: - raise r.bozo_exception - - elif r.bozo: - print >>warn, 'E: error in [%d] "%s" feed (%s)' % (feednum, f.url, r.get("bozo_exception", "can't process")) - - else: - print >>warn, "=== rss2email encountered a problem with this feed ===" - print >>warn, "=== See the rss2email FAQ at http://www.allthingsrss.com/rss2email/ for assistance ===" - print >>warn, "=== If this occurs repeatedly, send this to lindsey@allthingsrss.com ===" - print >>warn, "E:", r.get("bozo_exception", "can't process"), f.url - print >>warn, r - print >>warn, "rss2email", __version__ - print >>warn, "feedparser", feedparser.__version__ - print >>warn, "html2text", h2t.__version__ - print >>warn, "Python", sys.version - print >>warn, "=== END HERE ===" - continue - - r.entries.reverse() - - for entry in r.entries: - id = getID(entry) - - # If TRUST_GUID isn't set, we get back hashes of the content. - # Instead of letting these run wild, we put them in context - # by associating them with the actual ID (if it exists). - - frameid = entry.get('id') - if not(frameid): frameid = id - if type(frameid) is DictType: - frameid = frameid.values()[0] - - # If this item's ID is in our database - # then it's already been sent - # and we don't need to do anything more. - - if frameid in f.seen: - if f.seen[frameid] == id: continue - - if not (f.to or default_to): - print "No default email address defined. Please run 'r2e email emailaddress'" - print "Ignoring feed %s" % f.url - break - - if 'title_detail' in entry and entry.title_detail: - title = entry.title_detail.value - if contains(entry.title_detail.type, 'html'): - title = html2text(title) - else: - title = getContent(entry)[:70] - - title = title.replace("\n", " ").strip() - - datetime = time.gmtime() - - if DATE_HEADER: - for datetype in DATE_HEADER_ORDER: - kind = datetype+"_parsed" - if kind in entry and entry[kind]: datetime = entry[kind] - - link = entry.get('link', "") - - from_addr = getEmail(r, entry) - - name = h2t.unescape(getName(r, entry)) - fromhdr = formataddr((name, from_addr,)) - tohdr = (f.to or default_to) - subjecthdr = title - datehdr = time.strftime("%a, %d %b %Y %H:%M:%S -0000", datetime) - useragenthdr = "rss2email" - - # Add post tags, if available - tagline = "" - if 'tags' in entry: - tags = entry.get('tags') - taglist = [] - if tags: - for tag in tags: - taglist.append(tag['term']) - if taglist: - tagline = ",".join(taglist) - - extraheaders = {'Date': datehdr, 'User-Agent': useragenthdr, 'X-RSS-Feed': f.url, 'X-RSS-ID': id, 'X-RSS-URL': link, 'X-RSS-TAGS' : tagline} - if BONUS_HEADER != '': - for hdr in BONUS_HEADER.strip().splitlines(): - pos = hdr.strip().find(':') - if pos > 0: - extraheaders[hdr[:pos]] = hdr[pos+1:].strip() - else: - print >>warn, "W: malformed BONUS HEADER", BONUS_HEADER - - entrycontent = getContent(entry, HTMLOK=HTML_MAIL) - contenttype = 'plain' - content = '' - if USE_CSS_STYLING and HTML_MAIL: - contenttype = 'html' - content = "\n" - content += '\n' - content += '\n' - content += '
\n' - content += ''+subjecthdr+'\n' - if ishtml(entrycontent): - body = entrycontent[1].strip() - else: - body = entrycontent.strip() - if body != '': - content += '
\n' + body + '
\n' - content += '\n
\n' - content += "\n\n" - else: - if ishtml(entrycontent): - contenttype = 'html' - content = "\n" - content = ("\n\n" + - '

'+subjecthdr+'

\n\n' + - entrycontent[1].strip() + # drop type tag (HACK: bad abstraction) - '

URL: '+link+'

' ) - - if hasattr(entry,'enclosures'): - for enclosure in entry.enclosures: - if enclosure.url != "": - content += ('Enclosure: '+enclosure.url+"
\n") - if 'links' in entry: - for extralink in entry.links: - if ('rel' in extralink) and extralink['rel'] == u'via': - content += 'Via: '+extralink['title']+'
\n' + feeds, feedfileObject = load() + smtpserver = None + try: + # We store the default to address as the first item in the feeds list. + # Here we take it out and save it for later. + default_to = "" + if feeds and isstr(feeds[0]): default_to = feeds[0]; ifeeds = feeds[1:] + else: ifeeds = feeds + + if num: ifeeds = [feeds[num]] + feednum = 0 + + for f in ifeeds: + try: + feednum += 1 + if not f.active: continue + + if VERBOSE: print >>warn, 'I: Processing [%d] "%s"' % (feednum, f.url) + r = {} + try: + r = timelimit(FEED_TIMEOUT, parse)(f.url, f.etag, f.modified) + except TimeoutError: + print >>warn, 'W: feed [%d] "%s" timed out' % (feednum, f.url) + continue + + # Handle various status conditions, as required + if 'status' in r: + if r.status == 301: f.url = r['url'] + elif r.status == 410: + print >>warn, "W: feed gone; deleting", f.url + feeds.remove(f) + continue + + http_status = r.get('status', 200) + if VERBOSE > 1: print >>warn, "I: http status", http_status + http_headers = r.get('headers', { + 'content-type': 'application/rss+xml', + 'content-length':'1'}) + exc_type = r.get("bozo_exception", Exception()).__class__ + if http_status != 304 and not r.entries and not r.get('version', ''): + if http_status not in [200, 302]: + print >>warn, "W: error %d [%d] %s" % (http_status, feednum, f.url) + + elif contains(http_headers.get('content-type', 'rss'), 'html'): + print >>warn, "W: looks like HTML [%d] %s" % (feednum, f.url) + + elif http_headers.get('content-length', '1') == '0': + print >>warn, "W: empty page [%d] %s" % (feednum, f.url) + + elif hasattr(socket, 'timeout') and exc_type == socket.timeout: + print >>warn, "W: timed out on [%d] %s" % (feednum, f.url) + + elif exc_type == IOError: + print >>warn, 'W: "%s" [%d] %s' % (r.bozo_exception, feednum, f.url) + + elif hasattr(feedparser, 'zlib') and exc_type == feedparser.zlib.error: + print >>warn, "W: broken compression [%d] %s" % (feednum, f.url) + + elif exc_type in socket_errors: + exc_reason = r.bozo_exception.args[1] + print >>warn, "W: %s [%d] %s" % (exc_reason, feednum, f.url) + + elif exc_type == urllib2.URLError: + if r.bozo_exception.reason.__class__ in socket_errors: + exc_reason = r.bozo_exception.reason.args[1] + else: + exc_reason = r.bozo_exception.reason + print >>warn, "W: %s [%d] %s" % (exc_reason, feednum, f.url) + + elif exc_type == AttributeError: + print >>warn, "W: %s [%d] %s" % (r.bozo_exception, feednum, f.url) + + elif exc_type == KeyboardInterrupt: + raise r.bozo_exception + + elif r.bozo: + print >>warn, 'E: error in [%d] "%s" feed (%s)' % (feednum, f.url, r.get("bozo_exception", "can't process")) + + else: + print >>warn, "=== rss2email encountered a problem with this feed ===" + print >>warn, "=== See the rss2email FAQ at http://www.allthingsrss.com/rss2email/ for assistance ===" + print >>warn, "=== If this occurs repeatedly, send this to lindsey@allthingsrss.com ===" + print >>warn, "E:", r.get("bozo_exception", "can't process"), f.url + print >>warn, r + print >>warn, "rss2email", __version__ + print >>warn, "feedparser", feedparser.__version__ + print >>warn, "html2text", h2t.__version__ + print >>warn, "Python", sys.version + print >>warn, "=== END HERE ===" + continue + + r.entries.reverse() + + for entry in r.entries: + id = getID(entry) + + # If TRUST_GUID isn't set, we get back hashes of the content. + # Instead of letting these run wild, we put them in context + # by associating them with the actual ID (if it exists). + + frameid = entry.get('id') + if not(frameid): frameid = id + if type(frameid) is DictType: + frameid = frameid.values()[0] + + # If this item's ID is in our database + # then it's already been sent + # and we don't need to do anything more. + + if frameid in f.seen: + if f.seen[frameid] == id: continue + + if not (f.to or default_to): + print "No default email address defined. Please run 'r2e email emailaddress'" + print "Ignoring feed %s" % f.url + break + + if 'title_detail' in entry and entry.title_detail: + title = entry.title_detail.value + if contains(entry.title_detail.type, 'html'): + title = html2text(title) + else: + title = getContent(entry)[:70] + + title = title.replace("\n", " ").strip() + + datetime = time.gmtime() + + if DATE_HEADER: + for datetype in DATE_HEADER_ORDER: + kind = datetype+"_parsed" + if kind in entry and entry[kind]: datetime = entry[kind] + + link = entry.get('link', "") + + from_addr = getEmail(r, entry) + + name = h2t.unescape(getName(r, entry)) + fromhdr = formataddr((name, from_addr,)) + tohdr = (f.to or default_to) + subjecthdr = title + datehdr = time.strftime("%a, %d %b %Y %H:%M:%S -0000", datetime) + useragenthdr = "rss2email" + + # Add post tags, if available + tagline = "" + if 'tags' in entry: + tags = entry.get('tags') + taglist = [] + if tags: + for tag in tags: + taglist.append(tag['term']) + if taglist: + tagline = ",".join(taglist) + + extraheaders = {'Date': datehdr, 'User-Agent': useragenthdr, 'X-RSS-Feed': f.url, 'X-RSS-ID': id, 'X-RSS-URL': link, 'X-RSS-TAGS' : tagline} + if BONUS_HEADER != '': + for hdr in BONUS_HEADER.strip().splitlines(): + pos = hdr.strip().find(':') + if pos > 0: + extraheaders[hdr[:pos]] = hdr[pos+1:].strip() + else: + print >>warn, "W: malformed BONUS HEADER", BONUS_HEADER + + entrycontent = getContent(entry, HTMLOK=HTML_MAIL) + contenttype = 'plain' + content = '' + if USE_CSS_STYLING and HTML_MAIL: + contenttype = 'html' + content = "\n" + content += '\n' + content += '\n' + content += '
\n' + content += ''+subjecthdr+'\n' + if ishtml(entrycontent): + body = entrycontent[1].strip() + else: + body = entrycontent.strip() + if body != '': + content += '
\n' + body + '
\n' + content += '\n
\n' + content += "\n\n" + else: + if ishtml(entrycontent): + contenttype = 'html' + content = "\n" + content = ("\n\n" + + '

'+subjecthdr+'

\n\n' + + entrycontent[1].strip() + # drop type tag (HACK: bad abstraction) + '

URL: '+link+'

' ) + + if hasattr(entry,'enclosures'): + for enclosure in entry.enclosures: + if enclosure.url != "": + content += ('Enclosure: '+enclosure.url+"
\n") + if 'links' in entry: + for extralink in entry.links: + if ('rel' in extralink) and extralink['rel'] == u'via': + content += 'Via: '+extralink['title']+'
\n' - content += ("\n") - else: - content = entrycontent.strip() + "\n\nURL: "+link - if hasattr(entry,'enclosures'): - for enclosure in entry.enclosures: - if enclosure.url != "": - content += ('\nEnclosure: ' + enclosure.url + "\n") - if 'links' in entry: - for extralink in entry.links: - if ('rel' in extralink) and extralink['rel'] == u'via': - content += 'Via: '+extralink['title']+'\n' - - smtpserver = send(fromhdr, tohdr, subjecthdr, content, contenttype, extraheaders, smtpserver) - - f.seen[frameid] = id - - f.etag, f.modified = r.get('etag', None), r.get('modified', None) - except (KeyboardInterrupt, SystemExit): - raise - except: - print >>warn, "=== rss2email encountered a problem with this feed ===" - print >>warn, "=== See the rss2email FAQ at http://www.allthingsrss.com/rss2email/ for assistance ===" - print >>warn, "=== If this occurs repeatedly, send this to lindsey@allthingsrss.com ===" - print >>warn, "E: could not parse", f.url - traceback.print_exc(file=warn) - print >>warn, "rss2email", __version__ - print >>warn, "feedparser", feedparser.__version__ - print >>warn, "html2text", h2t.__version__ - print >>warn, "Python", sys.version - print >>warn, "=== END HERE ===" - continue - - finally: - unlock(feeds, feedfileObject) - if smtpserver: - smtpserver.quit() + content += ("\n") + else: + content = entrycontent.strip() + "\n\nURL: "+link + if hasattr(entry,'enclosures'): + for enclosure in entry.enclosures: + if enclosure.url != "": + content += ('\nEnclosure: ' + enclosure.url + "\n") + if 'links' in entry: + for extralink in entry.links: + if ('rel' in extralink) and extralink['rel'] == u'via': + content += 'Via: '+extralink['title']+'\n' + + smtpserver = send(fromhdr, tohdr, subjecthdr, content, contenttype, extraheaders, smtpserver) + + f.seen[frameid] = id + + f.etag, f.modified = r.get('etag', None), r.get('modified', None) + except (KeyboardInterrupt, SystemExit): + raise + except: + print >>warn, "=== rss2email encountered a problem with this feed ===" + print >>warn, "=== See the rss2email FAQ at http://www.allthingsrss.com/rss2email/ for assistance ===" + print >>warn, "=== If this occurs repeatedly, send this to lindsey@allthingsrss.com ===" + print >>warn, "E: could not parse", f.url + traceback.print_exc(file=warn) + print >>warn, "rss2email", __version__ + print >>warn, "feedparser", feedparser.__version__ + print >>warn, "html2text", h2t.__version__ + print >>warn, "Python", sys.version + print >>warn, "=== END HERE ===" + continue + + finally: + unlock(feeds, feedfileObject) + if smtpserver: + smtpserver.quit() def list(): - feeds, feedfileObject = load(lock=0) - default_to = "" - - if feeds and isstr(feeds[0]): - default_to = feeds[0]; ifeeds = feeds[1:]; i=1 - print "default email:", default_to - else: ifeeds = feeds; i = 0 - for f in ifeeds: - active = ('[ ]', '[*]')[f.active] - print `i`+':',active, f.url, '('+(f.to or ('default: '+default_to))+')' - if not (f.to or default_to): - print " W: Please define a default address with 'r2e email emailaddress'" - i+= 1 + feeds, feedfileObject = load(lock=0) + default_to = "" + + if feeds and isstr(feeds[0]): + default_to = feeds[0]; ifeeds = feeds[1:]; i=1 + print "default email:", default_to + else: ifeeds = feeds; i = 0 + for f in ifeeds: + active = ('[ ]', '[*]')[f.active] + print `i`+':',active, f.url, '('+(f.to or ('default: '+default_to))+')' + if not (f.to or default_to): + print " W: Please define a default address with 'r2e email emailaddress'" + i+= 1 def opmlexport(): - import xml.sax.saxutils - feeds, feedfileObject = load(lock=0) - - if feeds: - print '\n\n\nrss2email OPML export\n\n' - for f in feeds[1:]: - url = xml.sax.saxutils.escape(f.url) - print '' % (url, url) - print '\n' + import xml.sax.saxutils + feeds, feedfileObject = load(lock=0) + + if feeds: + print '\n\n\nrss2email OPML export\n\n' + for f in feeds[1:]: + url = xml.sax.saxutils.escape(f.url) + print '' % (url, url) + print '\n' def opmlimport(importfile): - importfileObject = None - print 'Importing feeds from', importfile - if not os.path.exists(importfile): - print 'OPML import file "%s" does not exist.' % feedfile - try: - importfileObject = open(importfile, 'r') - except IOError, e: - print "OPML import file could not be opened: %s" % e - sys.exit(1) - try: - import xml.dom.minidom - dom = xml.dom.minidom.parse(importfileObject) - newfeeds = dom.getElementsByTagName('outline') - except: - print 'E: Unable to parse OPML file' - sys.exit(1) - - feeds, feedfileObject = load(lock=1) - - import xml.sax.saxutils - - for f in newfeeds: - if f.hasAttribute('xmlUrl'): - feedurl = f.getAttribute('xmlUrl') - print 'Adding %s' % xml.sax.saxutils.unescape(feedurl) - feeds.append(Feed(feedurl, None)) - - unlock(feeds, feedfileObject) + importfileObject = None + print 'Importing feeds from', importfile + if not os.path.exists(importfile): + print 'OPML import file "%s" does not exist.' % feedfile + try: + importfileObject = open(importfile, 'r') + except IOError, e: + print "OPML import file could not be opened: %s" % e + sys.exit(1) + try: + import xml.dom.minidom + dom = xml.dom.minidom.parse(importfileObject) + newfeeds = dom.getElementsByTagName('outline') + except: + print 'E: Unable to parse OPML file' + sys.exit(1) + + feeds, feedfileObject = load(lock=1) + + import xml.sax.saxutils + + for f in newfeeds: + if f.hasAttribute('xmlUrl'): + feedurl = f.getAttribute('xmlUrl') + print 'Adding %s' % xml.sax.saxutils.unescape(feedurl) + feeds.append(Feed(feedurl, None)) + + unlock(feeds, feedfileObject) def delete(n): - feeds, feedfileObject = load() - if (n == 0) and (feeds and isstr(feeds[0])): - print >>warn, "W: ID has to be equal to or higher than 1" - elif n >= len(feeds): - print >>warn, "W: no such feed" - else: - print >>warn, "W: deleting feed %s" % feeds[n].url - feeds = feeds[:n] + feeds[n+1:] - if n != len(feeds): - print >>warn, "W: feed IDs have changed, list before deleting again" - unlock(feeds, feedfileObject) - + feeds, feedfileObject = load() + if (n == 0) and (feeds and isstr(feeds[0])): + print >>warn, "W: ID has to be equal to or higher than 1" + elif n >= len(feeds): + print >>warn, "W: no such feed" + else: + print >>warn, "W: deleting feed %s" % feeds[n].url + feeds = feeds[:n] + feeds[n+1:] + if n != len(feeds): + print >>warn, "W: feed IDs have changed, list before deleting again" + unlock(feeds, feedfileObject) + def toggleactive(n, active): - feeds, feedfileObject = load() - if (n == 0) and (feeds and isstr(feeds[0])): - print >>warn, "W: ID has to be equal to or higher than 1" - elif n >= len(feeds): - print >>warn, "W: no such feed" - else: - action = ('Pausing', 'Unpausing')[active] - print >>warn, "%s feed %s" % (action, feeds[n].url) - feeds[n].active = active - unlock(feeds, feedfileObject) - + feeds, feedfileObject = load() + if (n == 0) and (feeds and isstr(feeds[0])): + print >>warn, "W: ID has to be equal to or higher than 1" + elif n >= len(feeds): + print >>warn, "W: no such feed" + else: + action = ('Pausing', 'Unpausing')[active] + print >>warn, "%s feed %s" % (action, feeds[n].url) + feeds[n].active = active + unlock(feeds, feedfileObject) + def reset(): - feeds, feedfileObject = load() - if feeds and isstr(feeds[0]): - ifeeds = feeds[1:] - else: ifeeds = feeds - for f in ifeeds: - if VERBOSE: print "Resetting %d already seen items" % len(f.seen) - f.seen = {} - f.etag = None - f.modified = None - - unlock(feeds, feedfileObject) - + feeds, feedfileObject = load() + if feeds and isstr(feeds[0]): + ifeeds = feeds[1:] + else: ifeeds = feeds + for f in ifeeds: + if VERBOSE: print "Resetting %d already seen items" % len(f.seen) + f.seen = {} + f.etag = None + f.modified = None + + unlock(feeds, feedfileObject) + def email(addr): - feeds, feedfileObject = load() - if feeds and isstr(feeds[0]): feeds[0] = addr - else: feeds = [addr] + feeds - unlock(feeds, feedfileObject) + feeds, feedfileObject = load() + if feeds and isstr(feeds[0]): feeds[0] = addr + else: feeds = [addr] + feeds + unlock(feeds, feedfileObject) if __name__ == '__main__': - args = sys.argv - try: - if len(args) < 3: raise InputError, "insufficient args" - feedfile, action, args = args[1], args[2], args[3:] - - if action == "run": - if args and args[0] == "--no-send": - def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtpserver=None): - if VERBOSE: print 'Not sending:', unu(subject) - - if args and args[-1].isdigit(): run(int(args[-1])) - else: run() - - elif action == "email": - if not args: - raise InputError, "Action '%s' requires an argument" % action - else: - email(args[0]) - - elif action == "add": add(*args) - - elif action == "new": - if len(args) == 1: d = [args[0]] - else: d = [] - pickle.dump(d, open(feedfile, 'w')) - - elif action == "list": list() - - elif action in ("help", "--help", "-h"): print __doc__ - - elif action == "delete": - if not args: - raise InputError, "Action '%s' requires an argument" % action - elif args[0].isdigit(): - delete(int(args[0])) - else: - raise InputError, "Action '%s' requires a number as its argument" % action - - elif action in ("pause", "unpause"): - if not args: - raise InputError, "Action '%s' requires an argument" % action - elif args[0].isdigit(): - active = (action == "unpause") - toggleactive(int(args[0]), active) - else: - raise InputError, "Action '%s' requires a number as its argument" % action - - elif action == "reset": reset() - - elif action == "opmlexport": opmlexport() - - elif action == "opmlimport": - if not args: - raise InputError, "OPML import '%s' requires a filename argument" % action - opmlimport(args[0]) - - else: - raise InputError, "Invalid action" - - except InputError, e: - print "E:", e - print - print __doc__ + args = sys.argv + try: + if len(args) < 3: raise InputError, "insufficient args" + feedfile, action, args = args[1], args[2], args[3:] + + if action == "run": + if args and args[0] == "--no-send": + def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtpserver=None): + if VERBOSE: print 'Not sending:', unu(subject) + + if args and args[-1].isdigit(): run(int(args[-1])) + else: run() + + elif action == "email": + if not args: + raise InputError, "Action '%s' requires an argument" % action + else: + email(args[0]) + + elif action == "add": add(*args) + + elif action == "new": + if len(args) == 1: d = [args[0]] + else: d = [] + pickle.dump(d, open(feedfile, 'w')) + + elif action == "list": list() + + elif action in ("help", "--help", "-h"): print __doc__ + + elif action == "delete": + if not args: + raise InputError, "Action '%s' requires an argument" % action + elif args[0].isdigit(): + delete(int(args[0])) + else: + raise InputError, "Action '%s' requires a number as its argument" % action + + elif action in ("pause", "unpause"): + if not args: + raise InputError, "Action '%s' requires an argument" % action + elif args[0].isdigit(): + active = (action == "unpause") + toggleactive(int(args[0]), active) + else: + raise InputError, "Action '%s' requires a number as its argument" % action + + elif action == "reset": reset() + + elif action == "opmlexport": opmlexport() + + elif action == "opmlimport": + if not args: + raise InputError, "OPML import '%s' requires a filename argument" % action + opmlimport(args[0]) + + else: + raise InputError, "Invalid action" + + except InputError, e: + print "E:", e + print + print __doc__ debian/patches/series0000644000000000000000000000046712100001643012024 0ustar 0001-remove-newlines.patch 0002-wrapper.patch 0003-Setup-the-correct-version-number-in-rss2email.py.patch 0004-convert-tabs-to-spaces.patch 0005-Use-a-simpler-default-email-address.patch 0006-Prefer-utf8-in-CHARSET_LIST.patch 0007-Fix-email-header-injection.patch 0008-Fix-encoding-of-From-and-To-headers.patch debian/patches/0002-wrapper.patch0000644000000000000000000000111412100001535013655 0ustar From: David Watson Date: Mon, 18 Apr 2011 10:41:04 +0200 Subject: wrapper --- r2e | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/r2e b/r2e index e582fae..ed735cf 100755 --- a/r2e +++ b/r2e @@ -1,2 +1,16 @@ #!/bin/sh -python rss2email.py feeds.dat $* +set -e +dir=~/.rss2email +if [ "$1" = "-d" ]; then + if [ -z "$2" ]; then + echo "r2e: -d missing directory" >&2 + exit 1 + fi + dir="$2" + shift 2 +fi +if [ ! -d "$dir" ]; then + mkdir -p "$dir" +fi +cd "$dir" +exec python /usr/share/rss2email/rss2email.py feeds.dat $* debian/patches/0008-Fix-encoding-of-From-and-To-headers.patch0000644000000000000000000000335512100001643020562 0ustar From: Thorsten Glaser Date: Wed, 23 Jan 2013 15:25:35 +0100 Subject: Fix encoding of From and To headers Debian-Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=638994 --- rss2email.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rss2email.py b/rss2email.py index a6c3cbe..bdfb41a 100755 --- a/rss2email.py +++ b/rss2email.py @@ -114,6 +114,12 @@ from email.MIMEText import MIMEText from email.Header import Header as _Header from email.Utils import parseaddr, formataddr +def name_format(realname, mailaddress): + if not realname: + return mailaddress + foo = formataddr(("." + realname, mailaddress)) + return foo.replace(".", "", 1) + class Header(_Header): # Work-around for def append(self, s=None, *args, **kwargs): @@ -166,7 +172,7 @@ def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtps # Create the message ('plain' stands for Content-Type: text/plain) msg = MIMEText(body.encode(body_charset), contenttype, body_charset) - msg['To'] = formataddr((recipient_name, recipient_addr)) + msg['To'] = name_format(recipient_name, recipient_addr) msg['Subject'] = Header(unicode(subject), header_charset) for hdr in extraheaders.keys(): try: @@ -174,7 +180,7 @@ def send(sender, recipient, subject, body, contenttype, extraheaders=None, smtps except: msg[hdr] = Header(extraheaders[hdr]) - fromhdr = formataddr((sender_name, sender_addr)) + fromhdr = name_format(sender_name, sender_addr) msg['From'] = fromhdr msg_as_string = msg.as_string() debian/patches/0003-Setup-the-correct-version-number-in-rss2email.py.patch0000644000000000000000000000137512100001535023427 0ustar From: Etienne Millon Date: Fri, 26 Aug 2011 19:05:15 +0200 Subject: Setup the correct version number in rss2email.py This version is 2.71, but __version__ incorrectly states that it is 2.70. This number is used in the User-Agent string and in error messages. --- rss2email.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rss2email.py b/rss2email.py index 0dc2d04..7696d99 100755 --- a/rss2email.py +++ b/rss2email.py @@ -15,7 +15,7 @@ Usage: opmlexport opmlimport filename """ -__version__ = "2.70" +__version__ = "2.71" __author__ = "Lindsey Smith (lindsey@allthingsrss.com)" __copyright__ = "(C) 2004 Aaron Swartz. GNU GPL 2 or 3." ___contributors__ = ["Dean Jackson", "Brian Lalor", "Joey Hess", debian/patches/0001-remove-newlines.patch0000644000000000000000000000116612100001535015322 0ustar From: David Watson Date: Mon, 18 Apr 2011 10:41:04 +0200 Subject: remove-newlines --- html2text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/html2text.py b/html2text.py index 0ed4cec..1cd5194 100755 --- a/html2text.py +++ b/html2text.py @@ -305,6 +305,7 @@ class _html2text(HTMLParser.HTMLParser): if has_key(attrs, 'src'): attrs['href'] = attrs['src'] alt = attrs.get('alt', '') + alt = re.sub('\n', ' ', alt) i = self.previousIndex(attrs) if i is not None: attrs = self.a[i] debian/patches/0005-Use-a-simpler-default-email-address.patch0000644000000000000000000000225312100001535020762 0ustar From: Etienne Millon Date: Thu, 5 Jan 2012 17:12:02 +0100 Subject: Use a simpler default email address Fixes #464077. As explained by the submitter, user@rss2email.invalid is less verbose and simpler to understand. --- config.py.example | 2 +- rss2email.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py.example b/config.py.example index 586ac5d..cdd760b 100755 --- a/config.py.example +++ b/config.py.example @@ -1,7 +1,7 @@ ### Options for configuring rss2email ### # The email address messages are from by default: -DEFAULT_FROM = "bozo@dev.null.invalid" +DEFAULT_FROM = "user@rss2email.invalid" # 1: Send text/html messages when possible. # 0: Convert HTML to plain text. diff --git a/rss2email.py b/rss2email.py index 4651f56..9735b28 100755 --- a/rss2email.py +++ b/rss2email.py @@ -29,7 +29,7 @@ urllib2.install_opener(urllib2.build_opener()) ### Vaguely Customizable Options ### # The email address messages are from by default: -DEFAULT_FROM = "bozo@dev.null.invalid" +DEFAULT_FROM = "user@rss2email.invalid" # 1: Send text/html messages when possible. # 0: Convert HTML to plain text. debian/patches/0006-Prefer-utf8-in-CHARSET_LIST.patch0000644000000000000000000000255612100001535016651 0ustar From: Etienne Millon Date: Mon, 20 Feb 2012 15:28:52 +0100 Subject: Prefer utf8 in CHARSET_LIST Bug-Debian: http://bugs.debian.org/659920 --- config.py.example | 2 +- rss2email.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py.example b/config.py.example index cdd760b..ee2e004 100755 --- a/config.py.example +++ b/config.py.example @@ -91,4 +91,4 @@ PROXY="" # To most correctly encode emails with international characters, we iterate through the list below and use the first character set that works # Eventually (and theoretically) ISO-8859-1 and UTF-8 are our catch-all failsafes -CHARSET_LIST='US-ASCII', 'BIG5', 'ISO-2022-JP', 'ISO-8859-1', 'UTF-8' +CHARSET_LIST='US-ASCII', 'ISO-8859-1', 'UTF-8', 'BIG5', 'ISO-2022-JP' diff --git a/rss2email.py b/rss2email.py index 9735b28..69998db 100755 --- a/rss2email.py +++ b/rss2email.py @@ -108,7 +108,7 @@ PROXY="" # To most correctly encode emails with international characters, we iterate through the list below and use the first character set that works # Eventually (and theoretically) ISO-8859-1 and UTF-8 are our catch-all failsafes -CHARSET_LIST='US-ASCII', 'BIG5', 'ISO-2022-JP', 'ISO-8859-1', 'UTF-8' +CHARSET_LIST='US-ASCII', 'ISO-8859-1', 'UTF-8', 'BIG5', 'ISO-2022-JP' from email.MIMEText import MIMEText from email.Header import Header debian/changelog0000644000000000000000000004065112100001666011036 0ustar rss2email (1:2.71-2) unstable; urgency=low * Fix encoding of From and To headers. Thanks to Thorsten Glaser (Closes: #638994) -- Etienne Millon Wed, 23 Jan 2013 16:37:22 +0100 rss2email (1:2.71-1) unstable; urgency=low * New maintainer. * r2e.1 : - mention how to use authenticated feeds (Closes: #526124). - fix typo on opmlimport / opmlexport (thanks to Cristian Rigamonti, Closes: #659292) * New patches : - convert tabs to spaces (Closes: #520911). - change default email address (Closes: #464077). - change default CHARSET_LIST to prefer utf-8 (Closes: #659920). - avoid header injection in email headers (Closes: #526064, thanks to Jakub Wilk). * debian/control: - Bump Standards-Version to 3.9.3 (no changes needed). - Add VCS-* fields. -- Etienne Millon Tue, 12 Jun 2012 14:15:06 +0200 rss2email (1:2.71-0.1) unstable; urgency=low * Non-maintainer upload. * New upstream release. - don't ship html2text, depend on it instead (Closes: #555447, #570126, #484034). - Feed URL is included in a header (Closes: #447144, #518410). - OPML import/export is now possible (Closes: #248056). - pausing/resuming feed is now possible (Closes: #407822). - new patch setting __version__ to the correct number. * debian/copyright : - rss2email can be used under the GPL v2 or v3 (Closes: #567584). - add copyright information for feedparser. * Switched to 3.0 (quilt) format. * Remove Vcs information (dead links). * Update Homepage. * Add debian/source/format. * Bump Standards-Version to 3.9.2 (no changes needed). * Add working debian/watch file. * Add build-{arch,indep} targets. * Switch from python-support to dh_python2. -- Etienne Millon Thu, 08 Sep 2011 13:50:15 +0200 rss2email (1:2.65-1) unstable; urgency=low * New upstream release. Closes: #525489. * Tested broken previously broken feeds. Closes: #520910, #458964, #497142. * Bump Standards Version, no changes needed. * Update debhelper version. -- David Watson Fri, 24 Apr 2009 23:51:42 +0100 rss2email (1:2.62-3) unstable; urgency=low * Change of maintainer. -- David Watson Wed, 28 May 2008 22:41:53 +0100 rss2email (1:2.62-2) unstable; urgency=low * Fix man page section. Closes: #469831 -- Joey Hess Fri, 07 Mar 2008 11:33:34 -0500 rss2email (1:2.62-1) unstable; urgency=low * New upstream bugfix release. * Simplified SunOS fix * Local feeds (/home/user/file.xml) should work -- Joey Hess Mon, 14 Jan 2008 16:44:44 -0500 rss2email (1:2.61-1) unstable; urgency=low * New upstream release. - Now really compatible with SunOS - Don't wrap long subject headers - New parameter CHARSET_LIST to override or supplement the order in which charsets are tried against an entry - Don't use blank content to generate id - Using GMail as mail server should work * html2text also updated, to version 2.29 * Added Homepage field. * Moved to git. * Remove upstream's dos CR's at install time. -- Joey Hess Fri, 07 Dec 2007 20:44:06 -0500 rss2email (1:2.60-6) unstable; urgency=low * Patch from Martin Krafft to fix a case where the smtpserver variable could be defined before being used. Closes: #439930 * Patch from Rainer Koenig to stop line wrapping in very long subject lines. Closes: #383594 -- Joey Hess Tue, 28 Aug 2007 13:42:30 -0400 rss2email (1:2.60-5) unstable; urgency=low * Don't allow config.py elsewhere in the search path to be loaded instead of the user's config.py. Thanks, Thomas Fogwill. Closes: #428812 -- Joey Hess Thu, 14 Jun 2007 11:45:58 -0400 rss2email (1:2.60-4) unstable; urgency=low * Add a -d option to r2e that can be used to make it use a different directory for its config.dat and feeds. Closes: #425953 -- Joey Hess Fri, 25 May 2007 13:44:04 -0400 rss2email (1:2.60-3) unstable; urgency=low * Check exit status of sendmail, and die if it fails. Closes: #402725 -- Joey Hess Wed, 13 Dec 2006 14:34:42 -0500 rss2email (1:2.60-2) unstable; urgency=low * ACK aba's NMU. -- Joey Hess Wed, 13 Dec 2006 14:09:08 -0500 rss2email (1:2.60-1.1) unstable; urgency=high * Non-maintainer upload. * Use fcntl even on non-Sunos-Unix, i.e. Debian. Closes: #401077 -- Andreas Barth Sat, 9 Dec 2006 21:41:02 +0000 rss2email (1:2.60-1) unstable; urgency=low * New upstream release, including the change in -3 below and a few other small changes. -- Joey Hess Fri, 25 Aug 2006 23:19:20 -0400 rss2email (1:2.59-3) unstable; urgency=low * Patch from Tatsuya Kinoshita to make it not add quotes around a sender name, since if the name gets mime-encoded, the quotes will result in an invalid encoding. -- Joey Hess Thu, 24 Aug 2006 17:05:01 -0400 rss2email (1:2.59-2) unstable; urgency=low * Use python-support for python transition. Closes: #380937 -- Joey Hess Thu, 10 Aug 2006 18:26:01 -0400 rss2email (1:2.59-1) unstable; urgency=low * New upstream release: - Support for listing urls for podcast enclosures - Timeout for nonresponsive feeds (Closes: #362649) - Configurable CSS styling for HTML mail - Improved empty feed checking - Improved invalid feed messages • - Total rewrite of email code that should fix encoding problems (Closes: #361902) • - Added configurable timeout for nonresponsive feeds • - Fixed incorrectly using text summary_detail instead of html content - Fixed bug with deleting feed 0 if no default email was set - Print name of feed that is being deleted * New web site. * Document new FEED_TIMEOUT, USE_CSS_STYLING, STYLE_SHEET, and PROXY config variables in config.py. * Add missing build target to rules file. * Current standards version. -- Joey Hess Mon, 19 Jun 2006 16:39:28 -0400 rss2email (1:2.57-1) unstable; urgency=low * New upstream release, now including all the patches I've collected for this package. "fixes a slew of reliability and other bugs" -- Joey Hess Fri, 7 Apr 2006 18:26:47 -0400 rss2email (1:2.56-1) unstable; urgency=low * New upstream release "SMTP AUTH, Windows support, HTML in titles. First version by Lindsey Smith." -- Joey Hess Tue, 4 Apr 2006 19:57:55 -0400 rss2email (1:2.55.dfsg1-2) unstable; urgency=low * Update html2text to version 2.24. -- Joey Hess Thu, 16 Mar 2006 12:52:19 -0500 rss2email (1:2.55.dfsg1-1) unstable; urgency=low * Remove feedparser.py from this package and depend on the new python-feedparser package. Closes: #345352 * Repacked the upstream tarball since the feedparser included in it turns out to be invalidly licensed. * Version number munge so new repacked tarball will be accepted. -- Joey Hess Fri, 30 Dec 2005 14:16:02 -0500 rss2email (1:2.55-6) unstable; urgency=low * Current policy. * Fix perms of python libs -- Joey Hess Sun, 18 Dec 2005 17:12:03 -0500 rss2email (1:2.55-5) unstable; urgency=low * Document num parameter to run subcommand. Closes: #340425 -- Joey Hess Sun, 27 Nov 2005 15:58:27 -0500 rss2email (1:2.55-4) unstable; urgency=low * Remove the detailed and outdated description of the config file from the man page. Closes: #328231 -- Joey Hess Tue, 8 Nov 2005 15:26:25 -0500 rss2email (1:2.55-3) unstable; urgency=low * Man page patch from Florian Ernst. Closes: #334043 -- Joey Hess Sat, 15 Oct 2005 13:59:17 -0400 rss2email (1:2.55-2) unstable; urgency=low * Patch from Stephane Bortzmeyer to add support for the official namespace of Atom 1.0. Closes: #333490 -- Joey Hess Wed, 12 Oct 2005 19:56:29 -0400 rss2email (1:2.55-1) unstable; urgency=low * New upstream release of rss2email ("datetime parsing bug"). * Also updates html2text to version 2.23 and feedparser to pre-3.4-. -- Joey Hess Wed, 3 Aug 2005 14:06:19 -0400 rss2email (1:2.54-7) unstable; urgency=low * Patch from Tatsuya Kinoshita to workaround mimify behavior that adds a newline to subject line if it contains very long words. Closes: #320185 -- Joey Hess Wed, 27 Jul 2005 22:55:15 -0400 rss2email (1:2.54-6) unstable; urgency=low * Patch from tbm to fix crash in list if there is a feed with no address, and no default address is set. Closes: #310485 * Patch from tbm to fix crash when web serveracepts connection but thenimmediatly closes it. Closes: #312067 -- Joey Hess Sat, 18 Jun 2005 23:07:50 -0400 rss2email (1:2.54-5) unstable; urgency=low * Patch from tbm to fix unexpected code byte problem in utf8 codec by passing "replace" to utf_8_decode. Closes: #313097 * Patch from tbm to avoid backtraces when too few or wrong arguments are given, and instead display useful error messages. Closes: #313099 * Patch from tbm to make delete more reliable, so it no longer allows you to: remove the default email address ('feed' 0) and thereby hose your feed file, 'remove' entries that don't exist without warning; and so it only says IDs have changed when they really have. Closes: #313101 -- Joey Hess Fri, 17 Jun 2005 14:11:46 -0400 rss2email (1:2.54-4) unstable; urgency=low * Patch from tbm to support --help without an E: message. Closes: #310482 * Patch from tbm to avoid backtrace when adding a feed if no default email address is defined. Closes: #310487 * Patch from tbm to ignore or if no list is open, rather than crashing on such a buggy feed. Closes: #303298 -- Joey Hess Tue, 24 May 2005 13:10:03 -0400 rss2email (1:2.54-3) unstable; urgency=low * Add approximatly useless README.Debian. What's another inode? Closes: #305534 * Patch from tbm to fail with a useful error message if the feeds file does not exist. Closes: #309867 * Arch indep build depends fiddling. -- Joey Hess Fri, 20 May 2005 19:37:15 -0400 rss2email (1:2.54-2) unstable; urgency=low * Patched html2text to kill newline in alt tags. Closes: #299027 -- Joey Hess Sat, 12 Mar 2005 00:34:46 -0500 rss2email (1:2.54-1) unstable; urgency=low * New upstream release: "HTML descriptions, text wrapping, Python 2.1, and more!" - Adds back word wrapping support. Closes: #265514 - Incorporates liw's patch and several others. * Update config.py. * Update feed parser home page. -- Joey Hess Fri, 27 Aug 2004 16:42:25 -0400 rss2email (1:2.51-6) unstable; urgency=low * Patch from Lars Wirzenius to properly encode the From and Subject in 7-bit ascii. Closes: #265350 -- Joey Hess Thu, 12 Aug 2004 21:47:44 -0300 rss2email (1:2.51-5) unstable; urgency=low * sendmail comment typo fix. Closes: #259691 -- Joey Hess Mon, 19 Jul 2004 16:34:21 -0400 rss2email (1:2.51-4) unstable; urgency=low * Upgrade to feedparser 3.3. Closes: #259621 -- Joey Hess Mon, 19 Jul 2004 15:10:22 -0400 rss2email (1:2.51-3) unstable; urgency=low * Fix a typo that broke adding feeds with email addresses. Closes: #259360 -- Joey Hess Wed, 14 Jul 2004 10:59:32 -0400 rss2email (1:2.51-2) unstable; urgency=low * Updated to feedparser version "3.2-", fixes a crash wih feeds in some character encodings. -- Joey Hess Wed, 30 Jun 2004 18:38:34 -0400 rss2email (1:2.51-1) unstable; urgency=low * New upstream release: "Fixes a crash in older versions of Python on slow feeds" -- Joey Hess Mon, 28 Jun 2004 21:21:04 -0400 rss2email (1:2.5-1) unstable; urgency=low * New upstream release. - Fix various crashes. Closes: #235135, #242440, #241642 - The default from address uses the .invalid tld now. Closes: #253284 - Uses author email addresses from RSS/Atom files. Closes: #234630 (patch from LIW) - Expand ndash entity. Closes: #237693 - Added http basicauth support. Closes: #249648 (just include user and password in URL) - Fix problem with empty descriptions. Closes: #249385 - No more debian diff for rss2email.py, html2text.py. * Use an epoch to fix the version number. * Updated urls in copyright file. * Update config.py, including html2text options UNICODE_SNOB and LINKS_EACH_PARAGRAPH. -- Joey Hess Mon, 28 Jun 2004 13:54:08 -0400 rss2email (2.30-4) unstable; urgency=low * Minor manpage fix. * Document all the config.py options in the manpage. Closes: #247144 -- Joey Hess Mon, 26 Apr 2004 23:23:16 -0400 rss2email (2.30-3) unstable; urgency=low * Man page improvements from liw. Closes: #234634 -- Joey Hess Wed, 25 Feb 2004 13:13:10 -0500 rss2email (2.30-2) unstable; urgency=low * Make rss2email.py display usage help for r2e. Closes: #233554 * Remove mention of old channels.txt from man page. Closes: #233555 -- Joey Hess Wed, 18 Feb 2004 18:03:54 -0500 rss2email (2.30-1) unstable; urgency=low * New upstream release: "Far more robust (saves on crash, catches more errors, atomic saves). Fix for Unicode crash. Use guid instead of link for seen frame." Closes: #232257 * Add NEWS.Debian for the upgrade warning. * Version number munge; this is 2.3 upstream. -- Joey Hess Wed, 11 Feb 2004 18:26:10 -0500 rss2email (2.28-1) unstable; urgency=low * New upstream releases: 2.27 "DATE_HEADER option (makes emails look as if they were sent when the item was posted), better error reporting, `r2e list` doesn't lock, fix for no-download crash. Tx Alan D." 2.28 "--no-send option for run, fixed FORCE_FROM. Tx Alan D." * Update manpage and config.py. -- Joey Hess Mon, 2 Feb 2004 16:13:44 -0500 rss2email (2.26-1) unstable; urgency=low * New upstream releases: 2.24 "Possible fix for process leak. Tx Alan Danziger." 2.25 "Quote email address names. Tx Alan Danziger, qmail." 2.26 "Fix for typo, email replacement. Added VERBOSE, FORCE_FROM options and redirect support. Tx Alan D., Pete Prodoehi, Matej." * Add VERBOSE and FORCE_FROM to config.py. * Re-disabled the xml validation stuff in rssparser, lost when I renamed it. -- Joey Hess Fri, 30 Jan 2004 18:57:21 -0500 rss2email (2.23-1) unstable; urgency=low * New upstream release. ("Fix for empty link tags.") -- Joey Hess Fri, 30 Jan 2004 13:49:08 -0500 rss2email (2.22-2) unstable; urgency=low * Fix accidental dep on python2.2. * Modify feedparser to work with python 2.1 as well as 2.2 and 2.3, patch from Aaron. -- Joey Hess Thu, 29 Jan 2004 17:08:48 -0500 rss2email (2.22-1) unstable; urgency=low * New upstream release. ("Added QP support, fix for empty GUIDs, and fix for major default email bugs.") * Update config.py. -- Joey Hess Thu, 29 Jan 2004 14:08:35 -0500 rss2email (2.2-1) unstable; urgency=low * New upstream release. ("Added default email support, backwards-compatible. Tx Matej Cepl.") * Fix typo in docstring. * Update man page. -- Joey Hess Thu, 29 Jan 2004 13:32:05 -0500 rss2email (2.1-1) unstable; urgency=low * New upstream release, incorporating all my patches to rss2email.py. * Last release forgot to close the WNPP bug, so this Closes: #229286 -- Joey Hess Wed, 28 Jan 2004 22:59:39 -0500 rss2email (2.0-1) unstable; urgency=low * Gathered the package from various sources, as explained in the copyright file, and thus built the .orig.tar.gz. * Commented out the xml.sax validation stuff in the feedparser, after discovering that python's xml parser is buggy, and crashes on some rss feeds. * Wrote a man page. * Add a User-Agent header to the mails. * Read config.py if available. * Modified r2e to cd to ~/.rss2email (making the directory first if necessary) before running rss2email, so the data file and config.py can be in their own subdirectory. -- Joey Hess Fri, 23 Jan 2004 20:48:59 -0500 debian/README.source0000644000000000000000000000022412044254507011346 0ustar Version 2.71 was repacked from the upstream tarball because its permissions were bogus (all bits reset). Besides fixing them, repacking is a no-op. debian/NEWS0000644000000000000000000000045011475670501007672 0ustar rss2email (2.30-1) unstable; urgency=low WARNING: This update will cause a lot of resends the first time you run it. To protect against this, run the old version, upgrade, and immediately run it with the --no-send option. -- Joey Hess Wed, 11 Feb 2004 18:26:10 -0500 debian/compat0000644000000000000000000000000211475670501010372 0ustar 7 debian/rss2email.examples0000644000000000000000000000002212044254507012624 0ustar config.py.example debian/control0000644000000000000000000000133012044254507010571 0ustar Source: rss2email Section: mail Priority: optional Maintainer: Etienne Millon Build-Depends: debhelper (>= 7.2.8) Build-Depends-Indep: python (>= 2.6.6-3~) Standards-Version: 3.9.3 Homepage: http://www.allthingsrss.com/rss2email/ Vcs-Git: git://git.debian.org/users/emillon-guest/rss2email.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=users/emillon-guest/rss2email.git Package: rss2email Architecture: all Depends: ${python:Depends}, ${misc:Depends}, python-feedparser, python-html2text Description: receive RSS feeds by email rss2email is a simple program which you can run in your crontab. It watches RSS (or Atom) feeds and sends you a nicely formatted email message for each new item. debian/rss2email.manpages0000644000000000000000000000001512044254507012603 0ustar debian/r2e.1 debian/rss2email.install0000644000000000000000000000007612044254507012465 0ustar r2e usr/bin rss2email.py usr/share/rss2email debian/watch0000644000000000000000000000017112044254507010221 0ustar version=3 http://www.allthingsrss.com/rss2email/download \ http://www.allthingsrss.com/rss2email/rss2email-(.+).tar.gz debian/r2e.10000644000000000000000000000525412044254507007751 0ustar .TH R2E 1 .SH NAME r2e \- receive RSS feeds by email .SH SYNOPSIS .B r2e [\-d dir] action [options] .SH DESCRIPTION .BR r2e is a simple program which you can run in your crontab. It watches RSS feeds and sends you nicely formatted email message for each new item. .P The program is configured by ~/.rss2email/config.py by default. To use a different config file in a different directory, you can specify the \-d option before any actions. .P For a quick start with r2e, try these steps: .P .RS .nf .BI "r2e new " your@address .BI "r2e add " http://feed.url/somewhere.rss .BI "r2e run " .RE .P The last command should eventually be put into your crontab, if you want things be sent you automatically. .P It is possible to use authenticated feeds using the following syntax: .P .RS .nf .BI "r2e add " http://user:password@example.com/feed .RE .SH ACTIONS .TP .B new [youremail] Create a new feedfile. If the second option is specified, it sets the default email address that mails are sent to. .TP .B add url [youremail] Subscribe to a feed. The first option is the URL of the feed. The optional second option is the email address to send new items to. Repeat for each feed you want to subscribe to. .TP .B run [\-\-no\-send] [num] Scan the feeds and send emails for new items. This can be run in a cron job. .P The \-\-no\-send option stops r2e from sending any email. This can be useful the first time you run it, as otherwise it would send every available story. .P If a number is specified, r2e will only download that feed. The list command lists the feed numbers. .TP .B email yournewemail Change the default email address. .TP .B list List all your currently subscribed feeds. .TP .B delete n Delete a feed, using its number from the list command. .TP .B pause n Temporarily ignore a feed. A paused feed won't be updated at all. .TP .B unpause n Re-enable updates from a feed. .TP .B opmlimport url Import feeds from an OPML file. .TP .B opmlexport Export feeds to standard output, as an OPML file. .SH "CONFIGURATION" The program's behavior can be controlled via the ~/.rss2email/config.py config file. The file is a python file, so variables are set using a syntax like this: VARIABLE = "value" .P If the value is a number, the quotes may be omitted. Most configuration variables in the file are boolean values, where a 1 indicates the option is set, and a 0 disables it. .P See the example config.py file for a full list of available configuration variables. .SH FILES .TP .B ~/.rss2email/feeds.dat The database of feeds. Use r2e to add, remove, or modify feeds, do not edit it directly. .TP .B ~/.rss2email/config.py If this file exists, it it read to configure the program. .SH AUTHOR Aaron Swartz debian/rules0000755000000000000000000000160212044254507010250 0ustar #!/usr/bin/make -f %: dh --with python2 $@ override_dh_fixperms: dh_fixperms chmod 644 debian/rss2email/usr/share/rss2email/rss2email.py \ debian/rss2email/usr/share/doc/rss2email/examples/config.py.example DEB_UPSTREAM_VERSION=$(shell dpkg-parsechangelog | sed -rne 's,^Version: ([0-9]+:)?([^-]+).*,\2,p') get-orig-source: uscan --noconf --force-download --rename --download-current-version --destdir=. tar xzf rss2email_$(DEB_UPSTREAM_VERSION).orig.tar.gz rm rss2email_$(DEB_UPSTREAM_VERSION).orig.tar.gz chmod -R +rwx rss2email-$(DEB_UPSTREAM_VERSION) mv rss2email-$(DEB_UPSTREAM_VERSION) rss2email-$(DEB_UPSTREAM_VERSION).orig GZIP=--best tar -cz --owner root --group root --mode a+rX \ -f rss2email_$(DEB_UPSTREAM_VERSION).orig.tar.gz \ rss2email-$(DEB_UPSTREAM_VERSION).orig rm -r rss2email-$(DEB_UPSTREAM_VERSION).orig debian/README.Debian0000644000000000000000000000010011475670501011224 0ustar Please see the man page for r2e for details on using rss2email. debian/source/0000755000000000000000000000000012044254507010471 5ustar debian/source/format0000644000000000000000000000001412044254507011677 0ustar 3.0 (quilt)