convert tabs to spaces
authorEtienne Millon <etienne.millon@gmail.com>
Thu, 13 Oct 2011 10:55:25 +0000 (12:55 +0200)
committerMatěj Cepl <mcepl@redhat.com>
Mon, 18 Feb 2013 19:15:07 +0000 (20:15 +0100)
rss2email.py

index 7696d99023988c94ffc9a0172e51d867716b1314..4651f56ca12dd516b228e3f25dfbe8b3a05e78c0 100755 (executable)
@@ -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 = "<html>\n" 
-                                               content += '<head><style><!--' + STYLE_SHEET + '//--></style></head>\n'
-                                               content += '<body>\n'
-                                               content += '<div id="entry">\n'
-                                               content += '<h1'
-                                               content += ' class="header"'
-                                               content += '><a href="'+link+'">'+subjecthdr+'</a></h1>\n'
-                                               if ishtml(entrycontent):
-                                                       body = entrycontent[1].strip()
-                                               else:
-                                                       body = entrycontent.strip()
-                                               if body != '':  
-                                                       content += '<div id="body"><table><tr><td>\n' + body + '</td></tr></table></div>\n'
-                                               content += '\n<p class="footer">URL: <a href="'+link+'">'+link+'</a>'
-                                               if hasattr(entry,'enclosures'):
-                                                       for enclosure in entry.enclosures:
-                                                               if (hasattr(enclosure, 'url') and enclosure.url != ""):
-                                                                       content += ('<br/>Enclosure: <a href="'+enclosure.url+'">'+enclosure.url+"</a>\n")
-                                                               if (hasattr(enclosure, 'src') and enclosure.src != ""):
-                                                                       content += ('<br/>Enclosure: <a href="'+enclosure.src+'">'+enclosure.src+'</a><br/><img src="'+enclosure.src+'"\n')
-                                               if 'links' in entry:
-                                                       for extralink in entry.links:
-                                                               if ('rel' in extralink) and extralink['rel'] == u'via':
-                                                                       extraurl = extralink['href']
-                                                                       extraurl = extraurl.replace('http://www.google.com/reader/public/atom/', 'http://www.google.com/reader/view/')
-                                                                       viatitle = extraurl
-                                                                       if ('title' in extralink):
-                                                                           viatitle = extralink['title']
-                                                                       content += '<br/>Via: <a href="'+extraurl+'">'+viatitle+'</a>\n'
-                                               content += '</p></div>\n'
-                                               content += "\n\n</body></html>"
-                                       else:   
-                                               if ishtml(entrycontent):
-                                                       contenttype = 'html'
-                                                       content = "<html>\n" 
-                                                       content = ("<html><body>\n\n" + 
-                                                                  '<h1><a href="'+link+'">'+subjecthdr+'</a></h1>\n\n' +
-                                                                  entrycontent[1].strip() + # drop type tag (HACK: bad abstraction)
-                                                                  '<p>URL: <a href="'+link+'">'+link+'</a></p>' )
-                                                                  
-                                                       if hasattr(entry,'enclosures'):
-                                                               for enclosure in entry.enclosures:
-                                                                       if enclosure.url != "":
-                                                                               content += ('Enclosure: <a href="'+enclosure.url+'">'+enclosure.url+"</a><br/>\n")
-                                                       if 'links' in entry:
-                                                               for extralink in entry.links:
-                                                                       if ('rel' in extralink) and extralink['rel'] == u'via':
-                                                                               content += 'Via: <a href="'+extralink['href']+'">'+extralink['title']+'</a><br/>\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 = "<html>\n" 
+                                                content += '<head><style><!--' + STYLE_SHEET + '//--></style></head>\n'
+                                                content += '<body>\n'
+                                                content += '<div id="entry">\n'
+                                                content += '<h1'
+                                                content += ' class="header"'
+                                                content += '><a href="'+link+'">'+subjecthdr+'</a></h1>\n'
+                                                if ishtml(entrycontent):
+                                                        body = entrycontent[1].strip()
+                                                else:
+                                                        body = entrycontent.strip()
+                                                if body != '':  
+                                                        content += '<div id="body"><table><tr><td>\n' + body + '</td></tr></table></div>\n'
+                                                content += '\n<p class="footer">URL: <a href="'+link+'">'+link+'</a>'
+                                                if hasattr(entry,'enclosures'):
+                                                        for enclosure in entry.enclosures:
+                                                                if (hasattr(enclosure, 'url') and enclosure.url != ""):
+                                                                        content += ('<br/>Enclosure: <a href="'+enclosure.url+'">'+enclosure.url+"</a>\n")
+                                                                if (hasattr(enclosure, 'src') and enclosure.src != ""):
+                                                                        content += ('<br/>Enclosure: <a href="'+enclosure.src+'">'+enclosure.src+'</a><br/><img src="'+enclosure.src+'"\n')
+                                                if 'links' in entry:
+                                                        for extralink in entry.links:
+                                                                if ('rel' in extralink) and extralink['rel'] == u'via':
+                                                                        extraurl = extralink['href']
+                                                                        extraurl = extraurl.replace('http://www.google.com/reader/public/atom/', 'http://www.google.com/reader/view/')
+                                                                        viatitle = extraurl
+                                                                        if ('title' in extralink):
+                                                                            viatitle = extralink['title']
+                                                                        content += '<br/>Via: <a href="'+extraurl+'">'+viatitle+'</a>\n'
+                                                content += '</p></div>\n'
+                                                content += "\n\n</body></html>"
+                                        else:   
+                                                if ishtml(entrycontent):
+                                                        contenttype = 'html'
+                                                        content = "<html>\n" 
+                                                        content = ("<html><body>\n\n" + 
+                                                                   '<h1><a href="'+link+'">'+subjecthdr+'</a></h1>\n\n' +
+                                                                   entrycontent[1].strip() + # drop type tag (HACK: bad abstraction)
+                                                                   '<p>URL: <a href="'+link+'">'+link+'</a></p>' )
+                                                                   
+                                                        if hasattr(entry,'enclosures'):
+                                                                for enclosure in entry.enclosures:
+                                                                        if enclosure.url != "":
+                                                                                content += ('Enclosure: <a href="'+enclosure.url+'">'+enclosure.url+"</a><br/>\n")
+                                                        if 'links' in entry:
+                                                                for extralink in entry.links:
+                                                                        if ('rel' in extralink) and extralink['rel'] == u'via':
+                                                                                content += 'Via: <a href="'+extralink['href']+'">'+extralink['title']+'</a><br/>\n'
                                                                 
-                                                       content += ("\n</body></html>")
-                                               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 += '<a href="'+extralink['href']+'">Via: '+extralink['title']+'</a>\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</body></html>")
+                                                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 += '<a href="'+extralink['href']+'">Via: '+extralink['title']+'</a>\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 '<?xml version="1.0" encoding="UTF-8"?>\n<opml version="1.0">\n<head>\n<title>rss2email OPML export</title>\n</head>\n<body>'
-               for f in feeds[1:]:
-                       url = xml.sax.saxutils.escape(f.url)
-                       print '<outline type="rss" text="%s" xmlUrl="%s"/>' % (url, url)
-               print '</body>\n</opml>'
+        import xml.sax.saxutils
+        feeds, feedfileObject = load(lock=0)
+        
+        if feeds:
+                print '<?xml version="1.0" encoding="UTF-8"?>\n<opml version="1.0">\n<head>\n<title>rss2email OPML export</title>\n</head>\n<body>'
+                for f in feeds[1:]:
+                        url = xml.sax.saxutils.escape(f.url)
+                        print '<outline type="rss" text="%s" xmlUrl="%s"/>' % (url, url)
+                print '</body>\n</opml>'
 
 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__