Credit: Xavier Defrang
Suppose you have a POP3 mailbox somewhere, perhaps on a slow connection, and need to examine messages, and perhaps mark them for deletion, in an interactive way. Perhaps you’re behind a slow Internet link and don’t want to wait for that funny 10-MB MPEG movie that you already received twice yesterday to be fully downloaded before you can read your mail. Or maybe there’s a peculiar malformed message that is hanging your MUA. This issue is best tackled interactively, but you need a helping script to let you examine some data about each message and determine which messages should be removed.
Instead of telneting to your POP server and trying to remember the
POP3 protocol commands (or hoping that the server implements
help), you can use the small script shown in Example 10-3 to inspect your mailbox and do some cleaning.
Basically, Python’s standard POP3 module,
poplib, remembers the protocol commands on your
behalf, and this script helps you use them appropriately.
Example 10-3 uses the
poplib module to connect to your mailbox. It
then prompts you about what to do with each undelivered message. You
can view the top of the message, leave it on the server, or mark it
for deletion. No particular tricks or hacks are used in this piece of
code: it’s a simple example of
poplib usage. In addition to being practically
useful in emergencies, it can show how
poplib.POP3 call returns an object that
is ready for connection to a POP3 server specified as its argument.
We complete the connection by calling the
pass_ methods to specify a user ID and
password. Note the trailing underscore in
pass_: this method could not be called
pass because that is a Python keyword (the
do-nothing statement), and by convention, such issues are always
solved by appending an underscore to the identifier.
After connection, we keep working with methods of the
pop object. The
stat method returns the number of messages
and the total size of the mailbox in bytes. The
top method takes a message-number argument and
returns information about that message, as well as the message itself
as a list of lines (you can specify a second argument
N to ensure that no more than
N lines are returned). The
dele method also takes a message-number
argument and deletes that message from the mailbox (without
renumbering all other messages). When we’re done, we
quit method. If you’re
familiar with the POP3 protocol, you’ll notice the
close correspondence between these methods and the POP3 commands.
Example 10-3. Interactive POP3 mailbox inspector
# Helper interactive script to clean POP3 mailboxes from malformed mails that # hangs MUA's, messages that are too large, etc. # # Iterates over nonretrieved mails, prints selected elements from the headers, # and prompts interactively about whether each message should be deleted import sys, getpass, poplib, re # Change according to your needs POPHOST = "pop.domain.com" POPUSER = "jdoe" POPPASS = "" # The number of message body lines to retrieve MAXLINES = 10 HEADERS = "From To Subject".split( ) args = len(sys.argv) if args>1: POPHOST = sys.argv if args>2: POPUSER = sys.argv if args>3: POPPASS = sys.argv if args>4: MAXLINES= int(sys.argv) if args>5: HEADERS = sys.argv[5:] # Headers you're actually interested in rx_headers = re.compile('|'.join(headers), re.IGNORECASE) try: # Connect to the POPer and identify user pop = poplib.POP3(POPHOST) pop.user(POPUSER) if not POPPASS or POPPASS=='=': # If no password was supplied, ask for it POPPASS = getpass.getpass("Password for %s@%s:" % (POPUSER, POPHOST)) # Authenticate user pop.pass_(POPPASS) # Get some general information (msg_count, box_size) stat = pop.stat( ) # Print some useless information print "Logged in as %s@%s" % (POPUSER, POPHOST) print "Status: %d message(s), %d bytes" % stat bye = 0 count_del = 0 for n in range(stat): msgnum = n+1 # Retrieve headers response, lines, bytes = pop.top(msgnum, MAXLINES) # Print message info and headers you're interested in print "Message %d (%d bytes)" % (msgnum, bytes) print "-" * 30 print "\n".join(filter(rx_headers.match, lines)) print "-" * 30 # Input loop while 1: k = raw_input("(d=delete, s=skip, v=view, q=quit) What?") k = k[:1].lower( ) if k == 'd': # Mark message for deletion k = raw_input("Delete message %d? (y/n)" % msgnum) if k in "yY": pop.dele(msgnum) print "Message %d marked for deletion" % msgnum count_del += 1 break elif k == 's': print "Message %d left on server" % msgnum break elif k == 'v': print "-" * 30 print "\n".join(lines) print "-" * 30 elif k == 'q': bye = 1 break # Time to say goodbye? if bye: print "Bye" break # Summary print "Deleting %d message(s) in mailbox %s@%s" % ( count_del, POPUSER, POPHOST) # Commit operations and disconnect from server print "Closing POP3 session" pop.quit( ) except poplib.error_proto, detail: # Fancy error handling print "POP3 Protocol Error:", detail
Documentation for the standard library modules
getpass in the
Library Reference; the POP protocol is described
in RFC 1939 (http://www.ietf.org/rfc/rfc1939.txt).