[Pymilter] RE: PEmulating another MimeDefang function

Larry G. Wapnitsky LWapnitsky at ph.wrtdesign.com
Fri Nov 25 11:15:37 EST 2011


Solved the HTML issue...now working on the attachment issue.

Larry G. Wapnitsky MBA, MCSE, MCP+I
IT SUPPORT & ADMINISTRATION COORDINATOR
WRT  |  Wallace Roberts & Todd, LLC
1700 Market Street, 28th Fl
Philadelphia, PA 19103
T  215.430.5068
C  215.713.8635
E  lwapnitsky at WRTdesign.com


-----Original Message-----
From: Larry G. Wapnitsky 
Sent: Friday, November 25, 2011 8:12 AM
To: 'pymilter at bmsi.com'
Subject: RE: PEmulating another MimeDefang function

Update - I've managed two separate things over the past few days, but
neither is accomplishing exactly what I need.

1)  I can get the body back along with the attachments, but no matter
what I define as the attachment name, Outlook (or any other apps) sees
them as ATT#####.txt

2)  I can't seem to keep the formatting of the original message.


Larry G. Wapnitsky MBA, MCSE, MCP+I
IT SUPPORT & ADMINISTRATION COORDINATOR
WRT  |  Wallace Roberts & Todd, LLC
1700 Market Street, 28th Fl
Philadelphia, PA 19103
T  215.430.5068
C  215.713.8635
E  lwapnitsky at WRTdesign.com


-----Original Message-----
From: Larry G. Wapnitsky 
Sent: Wednesday, November 23, 2011 12:04 PM
To: 'pymilter at bmsi.com'
Subject: RE: Pymilter Digest, Vol 12, Issue 7

Daniele-

I think that's what I'm missing (btw - I've moved a little further since
our last conversation...thanks for the tips :))

Larry G. Wapnitsky MBA, MCSE, MCP+I
IT SUPPORT & ADMINISTRATION COORDINATOR
WRT  |  Wallace Roberts & Todd, LLC
1700 Market Street, 28th Fl
Philadelphia, PA 19103
T  215.430.5068
C  215.713.8635
E  lwapnitsky at WRTdesign.com


-----Original Message-----
From: pymilter-bounces at bmsi.com [mailto:pymilter-bounces at bmsi.com] On
Behalf Of pymilter-request at bmsi.com
Sent: Wednesday, November 23, 2011 12:00 PM
To: pymilter at bmsi.com
Subject: Pymilter Digest, Vol 12, Issue 7

Send Pymilter mailing list submissions to
	pymilter at bmsi.com

To subscribe or unsubscribe via the World Wide Web, visit
	http://www.bmsi.com/mailman/listinfo/pymilter
or, via email, send a message with subject or body 'help' to
	pymilter-request at bmsi.com

You can reach the person managing the list at
	pymilter-owner at bmsi.com

When replying, please edit your Subject line so it is more specific than
"Re: Contents of Pymilter digest..."


Today's Topics:

   1. RE: Emulating another MimeDefang function (Larry G. Wapnitsky)
   2. RE: Emulating another MimeDefang function (Larry G. Wapnitsky)
   3. Re: Emulating another MimeDefang function (Daniele Nicolodi)


----------------------------------------------------------------------

Message: 1
Date: Wed, 23 Nov 2011 10:38:22 -0500
From: "Larry G. Wapnitsky" <LWapnitsky at ph.wrtdesign.com>
Subject: RE: [Pymilter] Emulating another MimeDefang function
To: "Stuart D. Gathman" <stuart at bmsi.com>
Cc: pymilter at bmsi.com
Message-ID:
	
<FAE5CA7BEE39AF438CE87BF8DBE1375106AC9843 at ph-svr-exch1.wrtdesign.com>
Content-Type: text/plain;	charset="us-ascii"

So, I was able to remove the attachments in a function I've written, but
when the message gets sent, the attachments are still there rather than
my modified message.  Not sure what I'm missing.

Code below:


#! /usr/bin/env python

import Milter
import StringIO
import email
import email.Message
import hashlib
import mime
import os
import sys
import tempfile
import time
import rfc822


from email import Errors
from email.Message import Message

## ==  IP Information
from socket import AF_INET, AF_INET6
## ==

## === Define multiprocessing == ##

if True:
    from multiprocessing import Process as Thread, Queue
else:
    from threading import Thread
    from Queue import Queue

logq = Queue(maxsize=4)


def background():
    while True:
        t = logq.get()
        if not t: break
        msg,id,ts = t
        print "%s [%d]" % (time.strftime('%Y%b%d
%H:%M:%S',time.localtime(ts)),id),
        # 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
        for i in msg: print i,
        print

## === End Define Multiprocesing === ##

class mltr_SaveAttachments(Milter.Base):

    def __init__(self):
        self.id = Milter.uniqueID()

    def log(self,*msg):
        logq.put((msg,self.id,time.time()))

    def connect(self, IPname, family, hostaddr):
        self.IP = hostaddr[0]
        self.port = hostaddr[1]
        if family == AF_INET6:
            self.flow = hostaddr[2]
            self.scope = hostaddr[3]
        else:
            self.flow = None
            self.scope = None
        self.IPname = IPname # Name from a reverse IP lookup
        self.H = None
        self.fp = None
        self.receiver = self.getsymval('j')
        self.log("connect from %s at %s" % (IPname, hostaddr) )
        return Milter.CONTINUE

    @Milter.noreply
    def header(self, name, hval):
        self.fp.write("%s: %s\n" % (name,hval)) # add header to buffer
        return Milter.CONTINUE

    @Milter.noreply
    def eoh(self):
        self.fp.write("\n")                             # terminate
headers
        return Milter.CONTINUE



    def envfrom(self,mailfrom,*str):
#        self.log("envfrom")
        # self.F = mailfrom
        # self.R = []
        # self.fromparms = Milter.dictfromlist(str)
        # self.user = self.getsymval('{auth_authen}')
        # self.log("mail from:", mailfrom, *str)
        self.fp = StringIO.StringIO()
        # self.canon_from = '@'.join(parse_addr(mailfrom))
        # self.fp.write('From %s %s\n' % (self.canon_from,time,ctime()))
        return Milter.CONTINUE


    def attachment(self):
        msg = self._msg
        attachDir = attach_dir(msg)
        removedParts = []

        for part in msg.walk():
            fname = ""

            if part.is_multipart():
                continue

            dtypes = part.get_params(None, 'Content-Disposition')

            if not dtypes:
                if part.get_content_type() == 'text/plain':
                    continue
                ctypes = part.getparams()
                if not ctypes:
                    continue
                for key,val in ctypes:
                    if key.lower() == 'name':
                        fname = val
            else:
                for key,val in dtypes:
                    if key.lower() == 'filename':
                        fname = val

            if fname:
                removedParts.append(fname)
                data = part.get_payload(decode=1)
                extract_attachment(data, attachDir, fname)
                part = delete_attachments(part)

        self.log(removedParts)
#        return Milter.CONTINUE
        return msg


    def eom(self):
        self.fp.seek(0)
        self._msg = mime.message_from_file(self.fp)
        self._msg = self.attachment()

        self.log(self._msg)
        return Milter.ACCEPT
#        return Milter.TEMPFAIL
## ===


def delete_attachments(part):
    for key,value in part.get_params():
        part.del_param(key)

    part.set_payload('[DELETED]\n')
    part.set_type('text/plain')
    del part['Content-Disposition']

    return part



def attach_dir(msg):
    tempname = fname = tempfile.mktemp(".tmp")
    out = tempfile.TemporaryFile()
    msg.dump(out)
    out.seek(0)
    buf = out.read()
    hashDir = hashit(buf)
    attachDir = dropDir + hashDir

    if not os.path.isdir(hashDir):
        os.mkdir(attachDir)

    return attachDir


def extract_attachment(data, attachDir, fname):
    exdir_file = attachDir + "/" + fname
    extracted = open(exdir_file, "wb")
    extracted.write(data)
    extracted.close()



def hashit(data):
    sha1 = hashlib.sha1()
    # f = open(filepath, 'rb')
    # try:
    #     sha1.update(f.read())
    # finally:
    #     f.close()
    sha1.update(data)

    return sha1.hexdigest()

dropDir = "/dropdir/"

def main():
    bt = Thread(target=background)
    bt.start()
    socketname = "/tmp/py_testmilter.sock"
    timeout = 600
    Milter.factory = mltr_SaveAttachments
    flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS
    flags += Milter.ADDRCPT
    flags += Milter.DELRCPT
    Milter.set_flags(flags)     # tell Sendmail/Postfix which features
we use
    print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
    sys.stdout.flush()
    Milter.runmilter("py_testmilter",socketname,timeout)
    logq.put(None)
    bt.join()
    print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')

if __name__ == "__main__":
    main()



-----Original Message-----
From: Stuart D. Gathman [mailto:stuart at bmsi.com]
Sent: Monday, November 21, 2011 3:43 PM
To: Larry G. Wapnitsky
Cc: pymilter at bmsi.com
Subject: RE: [Pymilter] Emulating another MimeDefang function

On Mon, 21 Nov 2011, Larry G. Wapnitsky wrote:

> Will try this.

If you were curious about the standard place for filenames (as opposed
the many places LookOut checks), look at the get_filename() method in
email.Message (python comes with source).  It looks first in the
Content-Disposition header field for the filename attribute (the
"correct"
place), and secondly in the name attribute of Content-Type.

--
 	      Stuart D. Gathman <stuart at bmsi.com>
     Business Management Systems Inc.  Phone: 703 591-0911 Fax: 703
591-6154 "Confutatis maledictis, flammis acribus addictis" - background
song for a Microsoft sponsored "Where do you want to go from here?"
commercial.




----wrtmail--%3423wrt%----

<!DSPAM:133D113DA422445417198307>



------------------------------

Message: 2
Date: Wed, 23 Nov 2011 11:37:48 -0500
From: "Larry G. Wapnitsky" <LWapnitsky at ph.wrtdesign.com>
Subject: RE: [Pymilter] Emulating another MimeDefang function
To: "Stuart D. Gathman" <stuart at bmsi.com>
Cc: pymilter at bmsi.com
Message-ID:
	
<FAE5CA7BEE39AF438CE87BF8DBE1375106AC9844 at ph-svr-exch1.wrtdesign.com>
Content-Type: text/plain;	charset="us-ascii"

Updated code, still not working properly:

#! /usr/bin/env python

import Milter
import StringIO
import email
import email.Message
import hashlib
import mime
import os
import sys
import tempfile
import time
import rfc822


from email import Errors
from email.Message import Message

## ==  IP Information
from socket import AF_INET, AF_INET6
## ==

## === Define multiprocessing == ##

if True:
    from multiprocessing import Process as Thread, Queue
else:
    from threading import Thread
    from Queue import Queue

logq = Queue(maxsize=4)


def background():
    while True:
        t = logq.get()
        if not t: break
        msg,id,ts = t
        print "%s [%d]" % (time.strftime('%Y%b%d
%H:%M:%S',time.localtime(ts)),id),
        # 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
        for i in msg: print i,
        print

## === End Define Multiprocesing === ##

class mltr_SaveAttachments(Milter.Base):
    
    def __init__(self):
        self.id = Milter.uniqueID()

    def close(self):
        # always called, even when abort is called.  Clean up
        # any external resources here.
        return Milter.CONTINUE

    def abort(self):
        # client disconnected prematurely
        return Milter.CONTINUE

    def log(self,*msg):
        logq.put((msg,self.id,time.time()))
    
    @Milter.noreply
    def connect(self, IPname, family, hostaddr):
        self.IP = hostaddr[0]
        self.port = hostaddr[1]
        if family == AF_INET6:
            self.flow = hostaddr[2]
            self.scope = hostaddr[3]
        else:
            self.flow = None
            self.scope = None
        self.IPname = IPname # Name from a reverse IP lookup
        self.H = None
        self.fp = None
        self.receiver = self.getsymval('j')
        self.log("connect from %s at %s" % (IPname, hostaddr) )
        return Milter.CONTINUE

    @Milter.noreply
    def header(self, name, hval):
        self.fp.write("%s: %s\n" % (name,hval))	# add header to buffer
        return Milter.CONTINUE
    
    @Milter.noreply
    def eoh(self):
        self.fp.write("\n")				# terminate
headers
        return Milter.CONTINUE
    
    @Milter.noreply
    def body(self,chunk):
        self.fp.write(chunk)
        return Milter.CONTINUE
    

    def envfrom(self,mailfrom,*str):
#        self.log("envfrom")
        # self.F = mailfrom
        # self.R = []
        # self.fromparms = Milter.dictfromlist(str)
        # self.user = self.getsymval('{auth_authen}')
        # self.log("mail from:", mailfrom, *str)
        self.fp = StringIO.StringIO()
        # self.canon_from = '@'.join(parse_addr(mailfrom))
        # self.fp.write('From %s %s\n' % (self.canon_from,time,ctime()))
        return Milter.CONTINUE


    def attachment(self):
        msg = self._msg
        attachDir = attach_dir(msg)
        removedParts = []
        payload = []
        
        
        for part in msg.walk():
            fname = ""
            
            if part.is_multipart():
                continue
            
            dtypes = part.get_params(None, 'Content-Disposition')

            if not dtypes:
                if part.get_content_type() == 'text/plain':
                    payload.append(part)
                    continue
                ctypes = part.getparams()
                if not ctypes:
                    continue
                for key,val in ctypes:
                    if key.lower() == 'name':
                        fname = val
            else:
                for key,val in dtypes:
                    if key.lower() == 'filename':
                        fname = val
                        
            if fname:
                removedParts.append(fname)
                data = part.get_payload(decode=1)
                extract_attachment(data, attachDir, fname)
                part = self.delete_attachments(part, fname)
                payload.append(part)

#        self.log(payload)
        del msg["content-type"]
        del msg["content-disposition"]
        del msg["content-transfer-encoding"]

        msg.set_type('plain/txt')
        msg.set_payload(payload)

        self._msg = msg

        return Milter.CONTINUE
        
    def delete_attachments(self, part,fname):
        for key,value in part.get_params():
            part.del_param(key)
            
        part.set_payload('[DELETED]\n')
        del part["content-type"]
        del part["content-disposition"]
        del part["content-transfer-encoding"]
        part["Content-Type"] = "text/plain, name="+fname+".TXT"
        return part


    def eom(self):
        self.fp.seek(0)
        self._msg = mime.message_from_file(self.fp)
        self.attachment()
        
        self.log("### MESSAGE ###")
        self.log(self._msg)

        return Milter.ACCEPT
#        return Milter.TEMPFAIL
## ===





def attach_dir(msg):
    tempname = fname = tempfile.mktemp(".tmp")
    out = tempfile.TemporaryFile()
    msg.dump(out)
    out.seek(0)
    buf = out.read()
    hashDir = hashit(buf)
    attachDir = dropDir + hashDir
    
    if not os.path.isdir(hashDir):
        os.mkdir(attachDir)

    return attachDir


def extract_attachment(data, attachDir, fname):
    exdir_file = attachDir + "/" + fname
    extracted = open(exdir_file, "wb")
    extracted.write(data)
    extracted.close()
    


def hashit(data):
    sha1 = hashlib.sha1()
    # f = open(filepath, 'rb')
    # try:
    #     sha1.update(f.read())
    # finally:
    #     f.close()
    sha1.update(data)
       
    return sha1.hexdigest()

dropDir = "/dropdir/"

def main():
    bt = Thread(target=background)
    bt.start()
    socketname = "/tmp/py_testmilter.sock"
    timeout = 600
    Milter.factory = mltr_SaveAttachments
    flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS
    flags += Milter.ADDRCPT
    flags += Milter.DELRCPT
    Milter.set_flags(flags)     # tell Sendmail/Postfix which features
we use
    print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
    sys.stdout.flush()
    Milter.runmilter("py_testmilter",socketname,timeout)
    logq.put(None)
    bt.join()
    print "%s milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')

if __name__ == "__main__":
    main()

Larry G. Wapnitsky MBA, MCSE, MCP+I
IT SUPPORT & ADMINISTRATION COORDINATOR
WRT  |  Wallace Roberts & Todd, LLC
1700 Market Street, 28th Fl
Philadelphia, PA 19103
T  215.430.5068
C  215.713.8635
E  lwapnitsky at WRTdesign.com


-----Original Message-----
From: Larry G. Wapnitsky
Sent: Wednesday, November 23, 2011 10:38 AM
To: 'Stuart D. Gathman'
Cc: pymilter at bmsi.com
Subject: RE: [Pymilter] Emulating another MimeDefang function

So, I was able to remove the attachments in a function I've written, but
when the message gets sent, the attachments are still there rather than
my modified message.  Not sure what I'm missing.

Code below:


#! /usr/bin/env python

import Milter
import StringIO
import email
import email.Message
import hashlib
import mime
import os
import sys
import tempfile
import time
import rfc822


from email import Errors
from email.Message import Message

## ==  IP Information
from socket import AF_INET, AF_INET6
## ==

## === Define multiprocessing == ##

if True:
    from multiprocessing import Process as Thread, Queue
else:
    from threading import Thread
    from Queue import Queue

logq = Queue(maxsize=4)


def background():
    while True:
        t = logq.get()
        if not t: break
        msg,id,ts = t
        print "%s [%d]" % (time.strftime('%Y%b%d
%H:%M:%S',time.localtime(ts)),id),
        # 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
        for i in msg: print i,
        print

## === End Define Multiprocesing === ##

class mltr_SaveAttachments(Milter.Base):

    def __init__(self):
        self.id = Milter.uniqueID()

    def log(self,*msg):
        logq.put((msg,self.id,time.time()))

    def connect(self, IPname, family, hostaddr):
        self.IP = hostaddr[0]
        self.port = hostaddr[1]
        if family == AF_INET6:
            self.flow = hostaddr[2]
            self.scope = hostaddr[3]
        else:
            self.flow = None
            self.scope = None
        self.IPname = IPname # Name from a reverse IP lookup
        self.H = None
        self.fp = None
        self.receiver = self.getsymval('j')
        self.log("connect from %s at %s" % (IPname, hostaddr) )
        return Milter.CONTINUE

    @Milter.noreply
    def header(self, name, hval):
        self.fp.write("%s: %s\n" % (name,hval)) # add header to buffer
        return Milter.CONTINUE

    @Milter.noreply
    def eoh(self):
        self.fp.write("\n")                             # terminate
headers
        return Milter.CONTINUE



    def envfrom(self,mailfrom,*str):
#        self.log("envfrom")
        # self.F = mailfrom
        # self.R = []
        # self.fromparms = Milter.dictfromlist(str)
        # self.user = self.getsymval('{auth_authen}')
        # self.log("mail from:", mailfrom, *str)
        self.fp = StringIO.StringIO()
        # self.canon_from = '@'.join(parse_addr(mailfrom))
        # self.fp.write('From %s %s\n' % (self.canon_from,time,ctime()))
        return Milter.CONTINUE


    def attachment(self):
        msg = self._msg
        attachDir = attach_dir(msg)
        removedParts = []

        for part in msg.walk():
            fname = ""

            if part.is_multipart():
                continue

            dtypes = part.get_params(None, 'Content-Disposition')

            if not dtypes:
                if part.get_content_type() == 'text/plain':
                    continue
                ctypes = part.getparams()
                if not ctypes:
                    continue
                for key,val in ctypes:
                    if key.lower() == 'name':
                        fname = val
            else:
                for key,val in dtypes:
                    if key.lower() == 'filename':
                        fname = val

            if fname:
                removedParts.append(fname)
                data = part.get_payload(decode=1)
                extract_attachment(data, attachDir, fname)
                part = delete_attachments(part)

        self.log(removedParts)
#        return Milter.CONTINUE
        return msg


    def eom(self):
        self.fp.seek(0)
        self._msg = mime.message_from_file(self.fp)
        self._msg = self.attachment()

        self.log(self._msg)
        return Milter.ACCEPT
#        return Milter.TEMPFAIL
## ===


def delete_attachments(part):
    for key,value in part.get_params():
        part.del_param(key)

    part.set_payload('[DELETED]\n')
    part.set_type('text/plain')
    del part['Content-Disposition']

    return part



def attach_dir(msg):
    tempname = fname = tempfile.mktemp(".tmp")
    out = tempfile.TemporaryFile()
    msg.dump(out)
    out.seek(0)
    buf = out.read()
    hashDir = hashit(buf)
    attachDir = dropDir + hashDir

    if not os.path.isdir(hashDir):
        os.mkdir(attachDir)

    return attachDir


def extract_attachment(data, attachDir, fname):
    exdir_file = attachDir + "/" + fname
    extracted = open(exdir_file, "wb")
    extracted.write(data)
    extracted.close()



def hashit(data):
    sha1 = hashlib.sha1()
    # f = open(filepath, 'rb')
    # try:
    #     sha1.update(f.read())
    # finally:
    #     f.close()
    sha1.update(data)

    return sha1.hexdigest()

dropDir = "/dropdir/"

def main():
    bt = Thread(target=background)
    bt.start()
    socketname = "/tmp/py_testmilter.sock"
    timeout = 600
    Milter.factory = mltr_SaveAttachments
    flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS
    flags += Milter.ADDRCPT
    flags += Milter.DELRCPT
    Milter.set_flags(flags)     # tell Sendmail/Postfix which features
we use
    print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
    sys.stdout.flush()
    Milter.runmilter("py_testmilter",socketname,timeout)
    logq.put(None)
    bt.join()
    print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')

if __name__ == "__main__":
    main()



-----Original Message-----
From: Stuart D. Gathman [mailto:stuart at bmsi.com]
Sent: Monday, November 21, 2011 3:43 PM
To: Larry G. Wapnitsky
Cc: pymilter at bmsi.com
Subject: RE: [Pymilter] Emulating another MimeDefang function

On Mon, 21 Nov 2011, Larry G. Wapnitsky wrote:

> Will try this.

If you were curious about the standard place for filenames (as opposed
the many places LookOut checks), look at the get_filename() method in
email.Message (python comes with source).  It looks first in the
Content-Disposition header field for the filename attribute (the
"correct"
place), and secondly in the name attribute of Content-Type.

--
 	      Stuart D. Gathman <stuart at bmsi.com>
     Business Management Systems Inc.  Phone: 703 591-0911 Fax: 703
591-6154 "Confutatis maledictis, flammis acribus addictis" - background
song for a Microsoft sponsored "Where do you want to go from here?"
commercial.




----wrtmail--%3423wrt%----

<!DSPAM:133D14A27F324454233173089>



------------------------------

Message: 3
Date: Wed, 23 Nov 2011 17:59:29 +0100
From: Daniele Nicolodi <daniele at grinta.net>
Subject: Re: [Pymilter] Emulating another MimeDefang function
To: pymilter at bmsi.com
Message-ID: <4ECD2671.1060805 at grinta.net>
Content-Type: text/plain; charset=ISO-8859-1

On 23/11/11 17:37, Larry G. Wapnitsky wrote:
> Updated code, still not working properly:

Where are you telling your SMTP to replace the body of the message?

Cheers,
--
Daniele


------------------------------

_______________________________________________
Pymilter mailing list
Pymilter at bmsi.com
http://www.bmsi.com/mailman/listinfo/pymilter


End of Pymilter Digest, Vol 12, Issue 7
***************************************




----wrtmail--%3423wrt%----



More information about the Pymilter mailing list