def getPlainBodyFromMail(self, mailString):
     # get content-type and body from mail given as string
     return MailBoxerTools.getPlainBodyFromMail(mailString)
 def unpackMail(self, mailString):
     # returns plainbody, content-type, htmlbody and attachments
     return MailBoxerTools.unpackMail(mailString)
 def lowerList(self, stringlist):
     # lowers all items of a list
     return MailBoxerTools.lowerList(stringlist)
 def HtmlToText(self, html):
     # converts html to text
     return MailBoxerTools.convertHTML2Text(html)
 def parseaddr(self, header):
     # wrapper for rfc822.parseaddr, returns (name, addr)
     return MailBoxerTools.parseaddr(header)
 def parseaddrList(self, header):
     # wrapper for rfc822.AddressList, returns list of (name, addr)
     return MailBoxerTools.parseaddrList(header)
 def splitMail(self, mailString):
     # return (header,body) of a mail given as string
     return MailBoxerTools.splitMail(mailString)
 def mime_decode_header(self, header):
     # Returns the unfolded and undecoded header
     return MailBoxerTools.mime_decode_header(header)
Пример #9
0
def send(callURL, mailString, maxBytes=None):


    # If you wish to use HTTP Basic Authentication, set a user id and password here.
    # Alternatively you can call the URL like:
    # http://username:password@yourHost/MailBoxer/manage_mailboxer
    # Note that this is not necessary in the default MailBoxer configuration, but
    # may be used to add some extra security.
    # Format: username:password
    AUTHORIZATION=''
    
    # If you want to strip out all attachments, leaving only plain text, set this.
    # If you have a email size limit set, it will apply on what is left _after_
    # attachment stripping. This will also trigger an 'error' message to be
    # (optionally) generated by MailBoxer, which provides an opportunity to
    # notify the sender of the attachment being stripped.
    #
    # If attachments are to be stripped, MailBoxerTools.py must also be available
    # in your PYTHONPATH. If MailBoxerTools.py is not available, this will be set
    # back to 0.
    STRIP_ATTACHMENTS = 0
    
    # Notify Zope-side MailBoxer of events (such as attachments that have been
    # stripped). Messages will still be logged to the syslog (if available).
    EVENT_NOTIFICATION = 0
    
    # If you have a special setup which don't allow locking / serialization,
    # set USE_LOCKS = 0
    USE_LOCKS = 1
    
    # This should work with Unix & Windows & MacOS,
    # if not, set it on your own (e.g. '/tmp/smtp2zope.lock').
    
    LOCKFILE_LOCATION = os.path.join(os.path.split(tempfile.mktemp())[0],
                                     'smtp2zope.lock')
    
    # The amount of time to wait to be serialised
    LOCK_TIMEOUT = 15 #seconds
        
    # Meaningful exit-codes for a smtp-server
    EXIT_USAGE = 64
    EXIT_NOUSER = 67
    EXIT_NOPERM = 77
    EXIT_TEMPFAIL = 75


    if callURL.find('http://') == -1:
        raise Exception('URL is specified (%s) is not a valid URL' % callURL)

    urlParts = urllib2.urlparse.urlparse(callURL)
    urlPath = '/'.join(filter(None, list(urlParts)[2].split('/'))[:-1])
    baseURL = urllib2.urlparse.urlunparse(urlParts[:2]+(urlPath,)+urlParts[3:])+'/'

    # Check for authentication-string (username:passwd) in URL
    # Url looks like: http://username:passwd@host/...
    auth_mark = callURL.find('@')
    if auth_mark<>-1:
        AUTHORIZATION = callURL[7:auth_mark]
        callURL = callURL.replace(AUTHORIZATION+'@','')

    # Check for optional maxBytes
    if maxBytes is not None:
        try:
            maxBytes  = long(maxBytes)
        except ValueError:
            raise Exception('the specified value of maxBytes (%s) was not an integer'
                            % maxBytes)
    else:
        maxBytes  = 0 # means: unlimited!!!

    if USE_LOCKS:
        # Create temporary lockfile
        lock = LockFile(LOCKFILE_LOCATION)
        try:
            lock.lock(LOCK_TIMEOUT)
        except TimeOutError:
            raise Exception('Serialisation timeout occurred, will request message to be requeued')

    event_codes = []
    if STRIP_ATTACHMENTS:
        # check to see if we have attachments
        text_body, content_type, html_body, attachments = MailBoxerTools.unpackMail(mailString)
        
        num_attachments = len(attachments)
        if num_attachments or html_body:
            content_type, text_body = MailBoxerTools.getPlainBodyFromMail(mailString)
            headers = MailBoxerTools.headersAsString(mailString, {'Content-Type': content_type})
            mailString = '%s\r\n\r\n%s' % (headers, text_body)
        
            if html_body:
                event_codes.append(100) # stripped HTML
                if num_attachments > 1: # we had a HTML _and_ attachments
                    event_codes.append(101) # stripped attachments
            elif num_attachments:
                event_codes.append(101) # stripped attachments
            
    # Check its size
    mailLen = len(mailString)
    if maxBytes>0 and mailLen>maxBytes:
        log_warning('Rejecting email, due to size (%s bytes, limit %s bytes)' %
                    (mailLen, maxBytes))
        event_codes.append(200) # email too long
        if not EVENT_NOTIFICATION:    
            sys.exit(EXIT_NOPERM)
        else:
            eventNotification(baseURL, event_codes, mailString)
            sys.exit(0)
    

    # Transfer mail to http-server.
    # urllib2 handles server-responses (errors) much better than urllib.
            
    # urllib2 is, in fact, too much better. Its built-in redirect handler
    # can mask authorization problems when a cookie-based authenticator is in
    # use -- as with Plone. Also, I can't think of any reason why we would
    # want to allow redirection of these requests!
    #
    # So, let's create and install a disfunctional redirect handler.
            
    # Install the broken redirect handler
    opener = urllib2.build_opener(BrokenHTTPRedirectHandler())
    urllib2.install_opener(opener)

    try:
        req = urllib2.Request(callURL)
        if AUTHORIZATION:
            auth = base64.encodestring(AUTHORIZATION).strip()
            req.add_header('Authorization', 'Basic %s' % auth)
        response = urllib2.urlopen(req, data='Mail='+urllib.quote(mailString)) 
    except Exception, e:
        # If MailBoxer doesn't exist, bounce message with EXIT_NOUSER,
        # so the sender will receive a "user-doesn't-exist"-mail from MTA.
        if hasattr(e, 'code'):
            if e.code == 404:
                raise Exception("URL at %s doesn't exist (%s)" % (callURL, e))
            else:
                # Server down? EXIT_TEMPFAIL causes the MTA to try again later.
                raise Exception('A problem, "%s", occurred uploading email to URL %s (error code was %s)' % (e, callURL, e.code))
        else:
            # Server down? EXIT_TEMPFAIL causes the MTA to try again later.
            raise Exception('A problem, "%s", occurred uploading email to server %s' % (e, callURL))
Пример #10
0
def eventNotification(url, event_codes, mailString):
    event_codes = tuple(event_codes)
    if EVENT_NOTIFICATION and event_codes:
        server = xmlrpclib.ServerProxy(url)
        headers, body = MailBoxerTools.splitMail(mailString)
        server.manage_event(event_codes, headers)
Пример #11
0
if USE_LOCKS:
    # Create temporary lockfile
    lock = LockFile(LOCKFILE_LOCATION)
    try:
        lock.lock(LOCK_TIMEOUT)
    except TimeOutError:
        log_info('Serialisation timeout occurred, will request message to be requeued')
        sys.exit(EXIT_TEMPFAIL)

# Get the raw mail
mailString = sys.stdin.read()

event_codes = []
if STRIP_ATTACHMENTS:
    # check to see if we have attachments
    text_body, content_type, html_body, attachments = MailBoxerTools.unpackMail(mailString)
    
    num_attachments = len(attachments)
    if num_attachments or html_body:
        content_type, text_body = MailBoxerTools.getPlainBodyFromMail(mailString)
        headers = MailBoxerTools.headersAsString(mailString, {'Content-Type': content_type})
        mailString = '%s\r\n\r\n%s' % (headers, text_body)
        
        if html_body:
            event_codes.append(100) # stripped HTML
            if num_attachments > 1: # we had a HTML _and_ attachments
                event_codes.append(101) # stripped attachments
        elif num_attachments:
            event_codes.append(101) # stripped attachments
            
# Check its size
Пример #12
0
def send(callURL, mailString, maxBytes=None):

    # If you wish to use HTTP Basic Authentication, set a user id and password here.
    # Alternatively you can call the URL like:
    # http://username:password@yourHost/MailBoxer/manage_mailboxer
    # Note that this is not necessary in the default MailBoxer configuration, but
    # may be used to add some extra security.
    # Format: username:password
    AUTHORIZATION = ''

    # If you want to strip out all attachments, leaving only plain text, set this.
    # If you have a email size limit set, it will apply on what is left _after_
    # attachment stripping. This will also trigger an 'error' message to be
    # (optionally) generated by MailBoxer, which provides an opportunity to
    # notify the sender of the attachment being stripped.
    #
    # If attachments are to be stripped, MailBoxerTools.py must also be available
    # in your PYTHONPATH. If MailBoxerTools.py is not available, this will be set
    # back to 0.
    STRIP_ATTACHMENTS = 0

    # Notify Zope-side MailBoxer of events (such as attachments that have been
    # stripped). Messages will still be logged to the syslog (if available).
    EVENT_NOTIFICATION = 0

    # If you have a special setup which don't allow locking / serialization,
    # set USE_LOCKS = 0
    USE_LOCKS = 1

    # This should work with Unix & Windows & MacOS,
    # if not, set it on your own (e.g. '/tmp/smtp2zope.lock').

    LOCKFILE_LOCATION = os.path.join(
        os.path.split(tempfile.mktemp())[0], 'smtp2zope.lock')

    # The amount of time to wait to be serialised
    LOCK_TIMEOUT = 15  #seconds

    # Meaningful exit-codes for a smtp-server
    EXIT_USAGE = 64
    EXIT_NOUSER = 67
    EXIT_NOPERM = 77
    EXIT_TEMPFAIL = 75

    if callURL.find('http://') == -1:
        raise Exception('URL is specified (%s) is not a valid URL' % callURL)

    urlParts = urllib2.urlparse.urlparse(callURL)
    urlPath = '/'.join(filter(None, list(urlParts)[2].split('/'))[:-1])
    baseURL = urllib2.urlparse.urlunparse(urlParts[:2] +
                                          (urlPath, ) + urlParts[3:]) + '/'

    # Check for authentication-string (username:passwd) in URL
    # Url looks like: http://username:passwd@host/...
    auth_mark = callURL.find('@')
    if auth_mark <> -1:
        AUTHORIZATION = callURL[7:auth_mark]
        callURL = callURL.replace(AUTHORIZATION + '@', '')

    # Check for optional maxBytes
    if maxBytes is not None:
        try:
            maxBytes = long(maxBytes)
        except ValueError:
            raise Exception(
                'the specified value of maxBytes (%s) was not an integer' %
                maxBytes)
    else:
        maxBytes = 0  # means: unlimited!!!

    if USE_LOCKS:
        # Create temporary lockfile
        lock = LockFile(LOCKFILE_LOCATION)
        try:
            lock.lock(LOCK_TIMEOUT)
        except TimeOutError:
            raise Exception(
                'Serialisation timeout occurred, will request message to be requeued'
            )

    event_codes = []
    if STRIP_ATTACHMENTS:
        # check to see if we have attachments
        text_body, content_type, html_body, attachments = MailBoxerTools.unpackMail(
            mailString)

        num_attachments = len(attachments)
        if num_attachments or html_body:
            content_type, text_body = MailBoxerTools.getPlainBodyFromMail(
                mailString)
            headers = MailBoxerTools.headersAsString(
                mailString, {'Content-Type': content_type})
            mailString = '%s\r\n\r\n%s' % (headers, text_body)

            if html_body:
                event_codes.append(100)  # stripped HTML
                if num_attachments > 1:  # we had a HTML _and_ attachments
                    event_codes.append(101)  # stripped attachments
            elif num_attachments:
                event_codes.append(101)  # stripped attachments

    # Check its size
    mailLen = len(mailString)
    if maxBytes > 0 and mailLen > maxBytes:
        log_warning('Rejecting email, due to size (%s bytes, limit %s bytes)' %
                    (mailLen, maxBytes))
        event_codes.append(200)  # email too long
        if not EVENT_NOTIFICATION:
            sys.exit(EXIT_NOPERM)
        else:
            eventNotification(baseURL, event_codes, mailString)
            sys.exit(0)

    # Transfer mail to http-server.
    # urllib2 handles server-responses (errors) much better than urllib.

    # urllib2 is, in fact, too much better. Its built-in redirect handler
    # can mask authorization problems when a cookie-based authenticator is in
    # use -- as with Plone. Also, I can't think of any reason why we would
    # want to allow redirection of these requests!
    #
    # So, let's create and install a disfunctional redirect handler.

    # Install the broken redirect handler
    opener = urllib2.build_opener(BrokenHTTPRedirectHandler())
    urllib2.install_opener(opener)

    try:
        req = urllib2.Request(callURL)
        if AUTHORIZATION:
            auth = base64.encodestring(AUTHORIZATION).strip()
            req.add_header('Authorization', 'Basic %s' % auth)
        response = urllib2.urlopen(req,
                                   data='Mail=' + urllib.quote(mailString))
    except Exception, e:
        # If MailBoxer doesn't exist, bounce message with EXIT_NOUSER,
        # so the sender will receive a "user-doesn't-exist"-mail from MTA.
        if hasattr(e, 'code'):
            if e.code == 404:
                raise Exception("URL at %s doesn't exist (%s)" % (callURL, e))
            else:
                # Server down? EXIT_TEMPFAIL causes the MTA to try again later.
                raise Exception(
                    'A problem, "%s", occurred uploading email to URL %s (error code was %s)'
                    % (e, callURL, e.code))
        else:
            # Server down? EXIT_TEMPFAIL causes the MTA to try again later.
            raise Exception(
                'A problem, "%s", occurred uploading email to server %s' %
                (e, callURL))
Пример #13
0
def eventNotification(url, event_codes, mailString):
    event_codes = tuple(event_codes)
    if EVENT_NOTIFICATION and event_codes:
        server = xmlrpclib.ServerProxy(url)
        headers, body = MailBoxerTools.splitMail(mailString)
        server.manage_event(event_codes, headers)