#! /usr/bin/python

# Written by Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk>.
# For new versions, look at http://melkor.dnp.fmph.uniba.sk/~garabik/pycmail.html

import sys, os, os.path, pwd, string, StringIO, re, time
import rfc822, socket


class MailDestiny:

    def __init__(self, destinyname=""):
        self.destinyname = destinyname


class DevNull(MailDestiny):
    "send mail to /dev/null"

    def __init__(self):
        pass

    def getfd(self):
        self.fd = open("/dev/null", "a")
        return self.fd

    def closefd(self):
        self.fd.close


class Pipe(MailDestiny):
    "send mail to another program - headers included"

    def getfd(self):
        self.fd = os.popen(self.destinyname, "w")
        return self.fd

    def closefd(self):
        self.fd.close


class Forward(MailDestiny):
    "forward mail to another address"

    def getfd(self):
        self.fd = os.popen(sendmailbin+" "+self.destinyname, "w")
        return self.fd

    def closefd(self):
        self.fd.close


class MailDir(MailDestiny):
    "deliver mail to a maildir directory"

    def filename(self):
        return `long(time.time())`[:-1]+"."+`os.getpid()`+"."+socket.gethostname()

    def getfd(self):
        self.name = self.filename()
        while os.path.isfile(self.name):
            time.sleep(2)
        try:
            os.makedirs (self.destinyname+"/tmp", 0700)
            os.makedirs (self.destinyname+"/cur", 0700)
            os.makedirs (self.destinyname+"/new", 0700)
        except:
            pass
        os.umask(077)
        self.fd=open(self.destinyname+"/tmp/"+self.name,"a")
        return self.fd

    def closefd(self):
        self.fd.close()
        os.link(self.destinyname+"/tmp/"+self.name, self.destinyname+"/new/"+self.name)
        os.unlink(self.destinyname+"/tmp/"+self.name)


class MailBox(MailDestiny):
    "deliver mail to BSD mail folder - needs lockfile (from procmail package) for locking to work!"

    def lock(self):
        if self.destinyname == defaultbox:
            os.system(lockfilebin+" -ml")
        else:
            os.system(lockfilebin+" "+self.destinyname+".lock")

    def unlock(self):
        if self.destinyname == defaultbox:
            os.system(lockfilebin+" -mu")
        else:
            os.unlink(self.destinyname+".lock")

    def locked(self):
        return os.path.isfile(self.destinyname+".lock")

    def filename(self):
        return self.destinyname

    def getfd(self):
        self.lock()
        self.name = self.filename()
        os.umask(077)
        self.fd=open(self.name,"a")
        return self.fd

    def closefd(self):
        self.fd.close
        self.unlock()



def Append(*dest):
    "add to the mail destination"
    global DESTINATION
    for i in dest:
        DESTINATION.append(i)

def Set(*dest):
    "set the mail destination"
    global DESTINATION
    DESTINATION = []
    for i in dest:
        DESTINATION.append(i)

def Junk():
    Set(DevNull())



def SendMail(recipient, sender=None, subject=None, text="pycmail test mail"):
    "send mail"
    "to do: specify eventual additional headers as parameters"
    if sender == None:
        sender = USERNAME
    sm=os.popen(sendmailbin+" -t", "w")
    sm.write("From: %s\n" % sender)
    sm.write("To: %s\n" % recipient)
    if subject:
        sm.write("Subject: %s\n" % subject)
    sm.write("\n")
    sm.write(text)
    sm.close()


def Reply(recipient=None, sender=None, subject=None, text="pycmail test reply"):
    "reply to the current mail"
    "to do: specify eventual additional headers as parameters"
    if subject == None:
        subject = "Re: "+SUBJECT
    if recipient == None:
        recipient = mailmsg.getaddr('Reply-To:')[1] or ADDR_FROM[1]
    if recipient <> None:
        SendMail(recipient, sender = sender, subject=subject, text=text)
    

def Stop():
    raise "stop_of_pycmailrc"


def Matches(s, regexp, flags=''):
    return Contains(s, regexp, reg=1, flags=flags)

def Contains(s, sub, case=1, rex=0, flags=None):
    "return true if sub occurs in s"
    "if rex is 1, do it as regular expression, else just substring"
    if rex:
        if flags == None:
            flags = re.M
        else:
            flags = flags+re.M
        if case:
            f = flags
        else:
            f = flags+re.I
        return re.search(sub, s or "", re.M+flags)
    else:    
        if case:
            return string.count(s or "", sub)
        else:
            return string.count(string.lower(s or ""), string.lower(sub))

def InHeader(hname, sub, case=1, rex=0, flags=None):
    "return true if sub is in header with name hname"
    return Contains(mailmsg.getheader(hname), sub, case, rex, flags)

def untuple(list):
    "change list of tuples into tuple of lists"
    return map(lambda x: x[0], list), map(lambda x: x[1], list)


USERNAME = pwd.getpwuid(os.getuid())[0]
USERHOME = pwd.getpwuid(os.getuid())[5]
defaultbox = "/var/spool/mail/"+USERNAME
default = MailBox(defaultbox)
bufsize = 4096
bodysize = 1000

sendmailbin = "/usr/sbin/sendmail"
lockfilebin = "/usr/bin/lockfile"

DESTINATION = []


try:
    if os.path.isfile("/etc/pycmailrc"):
        execfile("/etc/pycmailrc")


    stdin = sys.stdin
    msg = ""

    while 1:
        newline = stdin.readline()
        msg = msg+newline
        if newline == '\n' or newline == "":
            start_of_body = len(msg)
            break

    to_read = bufsize*(1+(len(msg)+bodysize)/bufsize)-(len(msg)+bodysize)
    msg = msg + stdin.read(to_read)

    msgIO = StringIO.StringIO(msg)
    mailmsg = rfc822.Message(msgIO)
    FROM = mailmsg.getheader('From') or ""
    TO = mailmsg.getheader('To') or ""
    CC = mailmsg.getheader('Cc') or ""
    SUBJECT = mailmsg.getheader('Subject') or ""
    ADDR_FROM = mailmsg.getaddr('From')
    ADDR_TO = mailmsg.getaddr('To')
    ADDR_CC = mailmsg.getaddr('Cc')
    NAMELIST_TO, ADDRLIST_TO = untuple(mailmsg.getaddrlist('To'))
    NAMELIST_CC, ADDRLIST_CC = untuple(mailmsg.getaddrlist('Cc'))
    BODY = msg[start_of_body:]
    msgIO.close()

    if os.path.isfile(USERHOME+"/.pycmailrc"):
        try:
            execfile(USERHOME+"/.pycmailrc")
        except "stop_of_pycmailrc":  # used to simulate a goto command
            pass  # we need some better error handling in the future...

    if DESTINATION == []:
        DESTINATION = [default]

    destfd = []
    for i in DESTINATION:
        fd = i.getfd()
        fd.write(msg)
        destfd.append(fd)

    while 1:
        msg = stdin.read(bufsize)
        if msg == "": break
        for i in destfd:
            i.write(msg)

finally:
    for i in DESTINATION:
        try:
            i.closefd()
        except:
            pass

