Recipe 13.10. Inspecting a POP3 Mailbox Interactively
Credit: Xavier Defrang
Problem
You have a
POP3 mailbox somewhere, perhaps on a slow connection, and need to
examine messages and possibly mark them for deletion interactively.
Solution
The poplib module
of the Python Standard Library lets you write a script to solve this
task quite easily:
# Interactive script to clean POP3 mailboxes
from malformed or too-large mails
#
# Iterates over nonretrieved mails,
prints selected elements from the headers,
# prompts interactively about whether each message should be deleted
import sys, getpass, poplib, re
# Change according to your needs: POP host, userid, and password
POPHOST = "pop.domain.com"
POPUSER = "jdoe"
POPPASS = "
# How many lines to retrieve from body, and which headers to retrieve
MAXLINES = 10
HEADERS = "From To Subject".split( )
args = len(sys.argv)
if args>1: POPHOST = sys.argv[1]
if args>2: POPUSER = sys.argv[2]
if args>3: POPPASS = sys.argv[3]
if args>4: MAXLINES= int(sys.argv[4])
if args>5: HEADERS = sys.argv[5:]
# An RE to identify the headers you're actually interested in
rx_headers = re.compile('|'.join(headers), re.IGNORECASE)
try:
# Connect to the POP server and identify the user
pop = poplib.POP3(POPHOST)
pop.user(POPUSER)
# Authenticate user
if not POPPASS or POPPASS=='=':
# If no password was supplied, ask for the password
POPPASS = getpass.getpass("Password for %s@%s:" % (POPUSER, POPHOST))
pop.pass_(POPPASS)
# Get and print some general information (msg_count, box_size)
stat = pop.stat( )
print "Logged in as %s@%s" % (POPUSER, POPHOST)
print "Status: %d message(s), %d bytes" % stat
bye = False
count_del = 0
for msgnum in range(1, 1+stat[0]):
# 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 True:
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 = True
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
Discussion
Sometimes your POP3 mailbox is behind
a slow Internet link, and you don't want to wait for
that funny 10MB MPEG movie that you already received twice yesterday
to be fully downloaded before you can read your mail. Or maybe a
peculiar malformed message is hanging your MUA. Issues of this kind
are best tackled interactively, but you need a helpful script to let
you examine data about each message and determine which messages
should be removed.I used to deal with this kind of thing by telneting to the POP (Post
Office Protocol) server and trying to remember the POP3 protocol
commands (while hoping that the server implements the
help command in particular). Nowadays, I use the
script presented in this recipe to inspect my mailbox and do some
cleaning. Basically, the Python Standard Library POP3 module,
poplib, remembers the protocol commands on my
behalf, and this script helps me use those commands appropriately.The script in this recipe 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
the message on the server, or mark the message 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 you how poplib works. The
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 user and
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 often 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 call the
quit method. If you're familiar
with the POP3 protocol, you'll notice the close
correspondence between these methods and the POP3 commands.
See Also
Documentation for the standard library modules
poplib and getpass in the
Library Reference and Python in a
Nutshell; the POP protocol is described in RFC 1939
(http://www.ietf.org/rfc/rfc1939.txt).