from twisted.internet.protocol import Factory
from twisted.protocols.pop3 import Mailbox, POP3, POP3Error
import md5
import os.path

DEBUG = 1
PORT = 5336

def getFactory(hepData):
    f = Factory()
    f.hepData = hepData
    f.protocol = POP3Server
    return f

class POP3Mailbox(Mailbox):

    def __init__(self, store):
        print "mailbox.__init__"
        self._store = store
        # this forces self._store.messageMap to be built
        self.messageIDs = self._store.list()
        self.messagesToDelete = []
        print "ok."
    
    def deleteMessage(self, messageNo):
        # just mark the message as deleted for now.
        print "marking message for deletion"
        self.messagesToDelete.append(self.getUidl(messageNo))

    def flushDeletedMessages(self):
        print "flushing messages marked for deletion"
        for messageID in self.messagesToDelete:
            self._store.delete(messageID)

    def getUidl(self, messageNo):
        print "getting UIDL"
        return self.messageIDs[messageNo]
    
    def listMessages(self, i=None):
        print "listing"
        mList = []
        for messageID in self.messageIDs:
            mList.append(os.path.getsize(self._store.messageMap[self.getUidl(self.messageIDs.index(messageID))]))
        return i and mList[i] or mList

    def getMessage(self, messageNo):
        print "getting", messageNo
        print open(self._store.messageMap[self.getUidl(messageNo)])
        return open(self._store.messageMap[self.getUidl(messageNo)])

class POP3Server(POP3):
    # for debugging only
    def __init__(*args):
        print "starting POP3Server"

    # for some reason these aren't in the Twisted POP3 class
    def do_STAT(self):
        size = 0
        messages = self.mbox.listMessages()
        for m in messages:
            size = size + m
        self.successResponse('%i %i' % (len(messages), size))

    def do_USER(self, username):
        if self.factory.hepData.users.has_key(username):
            self.username = username
            self.successResponse("Hello %s." % username)
        else:
            self.failResponse("No such user.")
        
    def do_PASS(self, password):
        user = self.factory.hepData.users[self.username]
        if user.password == password:
            self.mbox = POP3Mailbox(user.mailboxes['Inbox'])
            self.successResponse("Login successful.")
        else:
            self.failResponse("Login failed, bad password.")
            
    def do_QUIT(self):
        self.mbox.flushDeletedMessages()
        POP3.do_QUIT(self)
        
    # the do_TOP in the Twisted POP3 class is missing the last line that writes ".\r\n".
    # once this bug has been fixed this function can come out.
    def do_TOP(self, i, size):
        resp, fp = self.getMessageFile(i)
        if not fp:
            return
        size = max(int(size), resp)
        self.successResponse(size)
        while size:
            line = fp.readline()
            print line
            if not line:
                break
            if line[-1] == '\n':
                line = line[:-1]
            if line[:1] == '.':
                line = '.'+line
            self.transport.write(line[:size]+'\r\n')
            size = size-len(line[:size])
        self.transport.write('.\r\n')


    def authenticateUserAPOP(self, username, digest):
        try:
            correctDigest  = md5.new(self.magic + self.factory.hepData.users[username].password).hexdigest()  
            if digest == correctDigest:
                hepUser = self.factory.hepData.users[username]
                return POP3Mailbox(hepUser.mailboxes['Inbox'])
        except:
            pass
    
    def processCommand(self, command, *args):
        command = command.upper()
        print command, args
        if self.mbox is None and command not in ('APOP', 'USER', 'PASS'):
            raise POP3Error("not authenticated yet: cannot do %s" % command)
        return apply(getattr(self, 'do_'+command), args)
