File: class/Workbook/Examples/Lecture14/pymail.py

#!/usr/local/bin/python
######################################################
# use the Python POP3 mail interface module to view
# your pop email account messages; runs on top of 
# sockets (the mail server may be a remote machine),
# and uses the rfc822 module to extract mail header 
# information; this is a very simple command-line 
# browsing interface, but is a start at a scriptable
# replacement for things like MS-Outlook (I wrote
# this right after my ISP took away telnet access);
# also see the library manual for modules multifile
# and mimetools to handle multi-part messages and 
# decoding, and the imaplib module for IMAP4 servers; 
# uses StringIO to make msg text a file-like object;
# retrieving messages does not delete them--keeps a
# list of messages to delete and reconnects on exit;
# it would be easy to throw the index in a Tkinter
# ilstbox and display messages in a scrolled text 
# box, as a GUI mail interface--see PyMailGui.py.    
######################################################

import poplib, rfc822, string, StringIO

def connect(servername, user, passwd):
    print 'Connecting...'
    server = poplib.POP3(servername)
    server.user(user)                    # connect, login to mail server
    server.pass_(passwd)                 # pass is a reserved word
    print server.getwelcome()            # print returned greeting message 
    return server

def loadmessages(servername, user, passwd, loadfrom=1):
    server = connect(servername, user, passwd)
    try:
        print server.list()
        (msgCount, msgBytes) = server.stat()
        print 'There are', msgCount, 'mail messages in', msgBytes, 'bytes'
        print 'Retrieving:',
        msgList = []
        for i in range(loadfrom, msgCount+1):            # empty if low >= high
            print i,                                     # fetch mail now
            (hdr, message, octets) = server.retr(i)      # save text on list
            msgList.append(string.join(message, '\n'))   # leave mail on server 
        print
    finally:
        server.quit()                                    # unlock the mail box
    assert len(msgList) == (msgCount - loadfrom) + 1     # msg nums start at 1
    return msgList

def deletemessages(servername, user, passwd, toDelete, verify=1):
    print 'To be deleted:', toDelete
    if verify and raw_input('Delete?')[:1] not in ['y', 'Y']:
        print 'Delete cancelled.'
    else:
        server = connect(servername, user, passwd)
        try:
            print 'Deleting messages from server.'
            for msgnum in toDelete:                 # reconnect to delete mail
                server.dele(msgnum)                 # mbox locked until quit()
        finally:
            server.quit()

def showindex(msgList):
    count = 0   
    for msg in msgList:                      # strip,show some mail headers
        strfile = StringIO.StringIO(msg)     # make string look like a file
        msghdrs = rfc822.Message(strfile)    # parse mail headers into a dict
        count   = count + 1
        print '%d:\t%d bytes' % (count, len(msg))
        for hdr in ('From', 'Date', 'Subject'):
            try:
                print '\t%s=>%s' % (hdr, msghdrs[hdr])
            except KeyError:
                print '\t%s=>(unknown)' % hdr
            #print '\n\t%s=>%s' % (hdr, msghdrs.get(hdr, '(unknown)')
        if count % 5 == 0:
            raw_input('[Press Enter key]')  # pause after each 5 

def showmessage(i, msgList):
    if 1 <= i <= len(msgList):
        print '-'*80
        print msgList[i-1]              # this prints entire mail--hdrs+text
        print '-'*80                    # to get text only, call file.read()
    else:                               # after rfc822.Message reads hdr lines
        print 'Bad message number'

def savemessage(i, mailfile, msgList):
    if 1 <= i <= len(msgList):
        open(mailfile, 'a').write('\n' + msgList[i-1] + '-'*80 + '\n')
    else:
        print 'Bad message number'

def msgnum(command):
    try:
        return string.atoi(string.split(command)[1])
    except:
        return -1   # assume this is bad

helptext = """
Available commands:
i     - index display
l n?  - list all messages (or just message n)
d n?  - mark all messages for deletion (or just message n)
s n?  - save all messages to a file (or just message n)
m     - compose and send a new mail message
q     - quit pymail
?     - display this help text
"""

def interact(msgList, mailfile):
    showindex(msgList)
    toDelete = []
    while 1:
        try:
            command = raw_input('[Pymail] Action? (i, l, d, s, m, q, ?) ')
        except EOFError:
            command = 'q'

        # quit
        if not command or command == 'q': 
            break

        # index
        elif command[0] == 'i':          
            showindex(msgList)

        # list
        elif command[0] == 'l':         
            if len(command) == 1:
                for i in range(1, len(msgList)+1): 
                    showmessage(i, msgList)
            else:
                showmessage(msgnum(command), msgList)

        # save
        elif command[0] == 's':        
            if len(command) == 1:
                for i in range(1, len(msgList)+1): 
                    savemessage(i, mailfile, msgList)
            else:
                savemessage(msgnum(command), mailfile, msgList)

        # delete 
        elif command[0] == 'd':               
            if len(command) == 1:
                toDelete = range(1, len(msgList)+1)     # delete all later
            else:
                delnum = msgnum(command)
                if (1 <= delnum <= len(msgList)) and (delnum not in toDelete):
                    toDelete.append(delnum)
                else:
                    print 'Bad message number'

        # mail
        elif command[0] == 'm':                # send a new mail via smtp
            try:                               # reuse existing script
                execfile('smtpmail.py', {})    # run file in own namespace
            except:
                print 'Error - mail not sent'  # don't die if script dies

        elif command[0] == '?':
            print helptext
        else:
            print 'What? -- type "?" for commands help'
    return toDelete

if __name__ == '__main__':
    import sys, getpass, mailconfig
    mailserver = mailconfig.popservername        # ex: 'starship.python.net'
    mailuser   = mailconfig.popusername          # ex: 'lutz'
    mailfile   = mailconfig.savemailfile         # ex:  r'c:\stuff\savemail'
    mailpswd   = getpass.getpass('Password for %s?' % mailserver)

    if sys.platform[:3] == 'win': raw_input()
    print '[Pymail email client]'
    msgList    = loadmessages(mailserver, mailuser, mailpswd)     # load all
    toDelete   = interact(msgList, mailfile)
    if toDelete: deletemessages(mailserver, mailuser, mailpswd, toDelete)
    print 'Bye.'



[Home page] Books Code Blog Python Author Train Find ©M.Lutz