Пример #1
0
 def test_encodeOptimally_with_ascii_text(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = 'I went to the cafe today.\n\r'
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part, exact=False)
     self.assertEqual(part.get_payload(), part.get_payload(decode=True))
     self.assertIs(None, part['Content-Transfer-Encoding'])
Пример #2
0
 def test_encodeOptimally_with_ascii_text(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = 'I went to the cafe today.\n\r'
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part, exact=False)
     self.assertEqual(part.get_payload(), part.get_payload(decode=True))
     self.assertIs(None, part['Content-Transfer-Encoding'])
Пример #3
0
 def test_encodeOptimally_with_7_bit_binary(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = 'I went to the cafe today.\n\r'
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part)
     self.assertEqual(text, part.get_payload(decode=True))
     self.assertEqual('I went to the cafe today.=0A=0D', part.get_payload())
     self.assertEqual('quoted-printable', part['Content-Transfer-Encoding'])
Пример #4
0
 def test_encodeOptimally_with_7_bit_binary(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = 'I went to the cafe today.\n\r'
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part)
     self.assertEqual(text, part.get_payload(decode=True))
     self.assertEqual('I went to the cafe today.=0A=0D',
                      part.get_payload())
     self.assertEqual('quoted-printable',
                      part['Content-Transfer-Encoding'])
Пример #5
0
 def test_payload_encoding(self):
     jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa'
     jcode  = 'euc-jp'
     msg = Message()
     msg.set_payload(jhello, jcode)
     ustr = unicode(msg.get_payload(), msg.get_content_charset())
     self.assertEqual(jhello, ustr.encode(jcode))
Пример #6
0
 def test_encodeOptimally_with_binary(self):
     """Significantly non-ascii attachments should be base64-encoded."""
     bytes = '\x00\xff\x44\x55\xaa\x99'
     part = Message()
     part.set_payload(bytes)
     MailController.encodeOptimally(part)
     self.assertEqual(bytes, part.get_payload(decode=True))
     self.assertEqual('base64', part['Content-Transfer-Encoding'])
Пример #7
0
 def test_encodeOptimally_with_binary(self):
     """Significantly non-ascii attachments should be base64-encoded."""
     bytes = '\x00\xff\x44\x55\xaa\x99'
     part = Message()
     part.set_payload(bytes)
     MailController.encodeOptimally(part)
     self.assertEqual(bytes, part.get_payload(decode=True))
     self.assertEqual('base64', part['Content-Transfer-Encoding'])
Пример #8
0
 def test_encodeOptimally_with_text(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = u'I went to the caf\u00e9 today.'.encode('utf-8')
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part)
     self.assertEqual(text, part.get_payload(decode=True))
     self.assertEqual('quoted-printable', part['Content-Transfer-Encoding'])
Пример #9
0
 def test_encodeOptimally_with_text(self):
     """Mostly-ascii attachments should be encoded as quoted-printable."""
     text = u'I went to the caf\u00e9 today.'.encode('utf-8')
     part = Message()
     part.set_payload(text)
     MailController.encodeOptimally(part)
     self.assertEqual(text, part.get_payload(decode=True))
     self.assertEqual('quoted-printable',
                      part['Content-Transfer-Encoding'])
Пример #10
0
def construct_message(imap, msg_format, msg_to, msg_from, msg_date, msg_subject, msg_body):
	msg = Message()
	msg.add_header('Date', formatdate(time.mktime(msg_date.timetuple())))
	msg.add_header('Message-Id', create_id(msg_date, msg_from))
	msg.add_header('To', msg_to)
	msg.add_header('From', msg_from)
	msg.add_header('MIME-Version', '1.0')
	msg.add_header('Subject', msg_subject)
	payload = Message()
	payload.add_header('Content-Type', msg_format)
	if msg_format in ('text/html', 'text/plain'):
		payload.add_header('Content-Transfer-Encoding', '8bit')
		payload.set_payload(''.join(msg_body))
	else:
		payload.add_header('Content-Transfer-Encoding', 'base64')
		payload.add_header('Content-Disposition', 'attachment; filename="%s"' % msg_subject)
		payload.set_payload(encodestring(''.join(msg_body)).decode())
	for item in payload.items():
		msg.add_header(item[0], item[1])
		msg.set_payload(payload.get_payload())
	try:
		msg.as_string()
	except Exception, e:
		print e
Пример #11
0
def verpdeliver(mlist, msg, msgdata, envsender, failures, conn):
    for recip in msgdata['recips']:
        # We now need to stitch together the message with its header and
        # footer.  If we're VERPIng, we have to calculate the envelope sender
        # for each recipient.  Note that the list of recipients must be of
        # length 1.
        #
        # BAW: ezmlm includes the message number in the envelope, used when
        # sending a notification to the user telling her how many messages
        # they missed due to bouncing.  Neat idea.
        msgdata['recips'] = [recip]
        # Make a copy of the message and decorate + delivery that
        msgcopy = copy.deepcopy(msg)
        Decorate.process(mlist, msgcopy, msgdata)
        # Calculate the envelope sender, which we may be VERPing
        if msgdata.get('verp'):
            bmailbox, bdomain = Utils.ParseEmail(envsender)
            rmailbox, rdomain = Utils.ParseEmail(recip)
            if rdomain is None:
                # The recipient address is not fully-qualified.  We can't
                # deliver it to this person, nor can we craft a valid verp
                # header.  I don't think there's much we can do except ignore
                # this recipient.
                syslog('smtp', 'Skipping VERP delivery to unqual recip: %s',
                       recip)
                continue
            d = {'bounces': bmailbox,
                 'mailbox': rmailbox,
                 'host'   : DOT.join(rdomain),
                 }
            envsender = '%s@%s' % ((mm_cfg.VERP_FORMAT % d), DOT.join(bdomain))
        if mlist.personalize == 2:
            # When fully personalizing, we want the To address to point to the
            # recipient, not to the mailing list
            del msgcopy['to']
            name = None
            if mlist.isMember(recip):
                name = mlist.getMemberName(recip)
            if name:
                # Convert the name to an email-safe representation.  If the
                # name is a byte string, convert it first to Unicode, given
                # the character set of the member's language, replacing bad
                # characters for which we can do nothing about.  Once we have
                # the name as Unicode, we can create a Header instance for it
                # so that it's properly encoded for email transport.
                charset = Utils.GetCharSet(mlist.getMemberLanguage(recip))
                if charset == 'us-ascii':
                    # Since Header already tries both us-ascii and utf-8,
                    # let's add something a bit more useful.
                    charset = 'iso-8859-1'
                charset = Charset(charset)
                codec = charset.input_codec or 'ascii'
                if not isinstance(name, UnicodeType):
                    name = unicode(name, codec, 'replace')
                name = Header(name, charset).encode()
                msgcopy['To'] = formataddr((name, recip))
            else:
                msgcopy['To'] = recip
        # We can flag the mail as a duplicate for each member, if they've
        # already received this message, as calculated by Message-ID.  See
        # AvoidDuplicates.py for details.
        del msgcopy['x-mailman-copy']
        if msgdata.get('add-dup-header', {}).has_key(recip):
            msgcopy['X-Mailman-Copy'] = 'yes'
        # GPG encryption
        if 'encrypted_gpg' in msgdata and msgdata['encrypted_gpg'] and mlist.encrypt_policy!=0:
            # Encryption is not forbidden in config
            try:
                keyids=mlist.getGPGKeyIDs(recip)
            except:
                keyids=None
            if enforceEncryptPolicy(mlist,msg,msgdata) and keyids==None:
                syslog('gpg','Encryption mandatory, but no keys found for %s: '\
                        'Discarding message',recip)
                failures[recip]=(550,'Encryption mandatory, but no keys found')
                return
            gh = GPGUtils.GPGHelper(mlist)
            # Extract / generate plaintext
            gpg_use_inlineformat = False # TODO: Create config setting
            if not msgcopy.is_multipart() and gpg_use_inlineformat:
                plaintext=msgcopy.get_payload()
            else:
                if not msgcopy.is_multipart():
                    plaintext = 'Content-Type: %s\n' \
                        'Content-Disposition: inline\n' \
                        % msgcopy.get('Content-Type')
                    if not msgcopy.get('Content-Transfer-Encoding') is None:
                        plaintext += 'Content-Transfer-Encoding: %s\n' \
                                % msgcopy.get('Content-Transfer-Encoding')
                    plaintext += '\n%s' % msgcopy.get_payload()
                else:
                    hp = HeaderParser()
                    tmp = msgcopy.as_string()
                    tmpmsg = hp.parsestr(tmp)
                    plaintext = 'Content-Type: %s\n' \
                        'Content-Disposition: inline\n\n%s' \
                        % (msgcopy.get('Content-Type'),tmpmsg.get_payload())
            # Do encryption, report errors
            ciphertext = None
            if not keyids is None:
                # Can encrypt.
                # No signing policy, or voluntary and original wasn't signed: just encrypt
                if mlist.sign_policy == 0 or \
                    (mlist.sign_policy==1 and not msgdata['signed_gpg']):
                    ciphertext = gh.encryptMessage(plaintext,keyids)
                else:
                    ciphertext = gh.encryptSignMessage(plaintext,keyids)
                if ciphertext==None:
                    # Must always encrypt, since if we arrived here encrypt_policy
                    # is either Mantatory or (Voluntary and incoming msg was encrypted).
                    syslog('gpg',"Can't encrypt message to %s: " \
                            "Discarding message",keyids)
                    failures[recip]=(550,'Unable to encrypt message')
                    return
            # Compile encrypted message
            if not ciphertext is None:
                if msgcopy.has_key('Content-Transfer-Encoding'):
                    msgcopy.replace_header('Content-Transfer-Encoding','7bit')
                else:
                    msgcopy.add_header('Content-Transfer-Encoding','7bit')
                if not msgcopy.is_multipart() and gpg_use_inlineformat:
                    msgcopy.set_payload(ciphertext)
                    msgcopy.set_param('x-action','pgp-encrypted')
                else:
                    msgcopy.replace_header('Content-Type','multipart/encrypted')
                    msgcopy.set_param('protocol','application/pgp-encrypted')
                    msgcopy.set_payload(None)
                    submsg = Message()
                    submsg.add_header('Content-Type','application/pgp-encrypted')
                    submsg.set_payload('Version: 1\n')
                    msgcopy.attach(submsg)
                    submsg = Message()
                    submsg.add_header('Content-Type','application/octet-stream; name="encrypted.asc"')
                    submsg.add_header('Content-Disposition','inline; filename="encrypted.asc"')
                    submsg.set_payload(ciphertext)
                    msgcopy.attach(submsg)
                syslog('gpg','Sending encrypted message to %s',recip)
            else:
                syslog('gpg','Sending unencrypted message to %s',recip)

        if 'encrypted_smime' in msgdata and msgdata['encrypted_smime'] and mlist.encrypt_policy != 0:
            # FIXME: this is as crude as can be
            sm = SMIMEUtils.SMIMEHelper(mlist)
            recipfile = sm.getSMIMEMemberCertFile(recip)

            if not recipfile:
                failures[recip]=(550,'No S/MIME key found')
                return
            else:
                plaintext=msgcopy.get_payload()
                if not msgcopy.is_multipart():
                    plaintext = msgcopy.get_payload()
                    syslog('gpg', "About to S/MIME encrypt plaintext from singlepart")
                else:
                    # message contains e.g. signature?
                    # FIXME we fetch only the first attachment.  We search for
                    # attachments only 2 levels deep.  That's suboptimal...
                    # perhaps the PGP way (invoking
                    # hp = HeaderParser()
                    # ) is better.
                    submsgs = msgcopy.get_payload()
                    submsg = submsgs[0]
                    if not submsg.is_multipart():
                        plaintext = submsg.get_payload()
                    else:
                        subsubmsgs = submsg.get_payload()
                        subsubmsg = subsubmsgs[0]
                        plaintext = subsubmsg.get_payload()

                    syslog('gpg', "About to S/MIME encrypt plaintext from multipart")

                if mlist.sign_policy == 0 or \
                    (mlist.sign_policy==1 and not msgdata['signed_smime']):
                    ciphertext = sm.encryptMessage(plaintext,recipfile)
                else:
                    ciphertext = sm.encryptSignMessage(plaintext,recipfile)

                # deal with both header and body-part of ciphertext
                (header, body) = ciphertext.split("\n\n", 1)
                for l in header.split("\n"):
                    (k, v) = l.split(": ", 1)

                    # behave sane with borken openssl like 0.9.7e (e.g. Debian's 0.9.7e-3sarge1)
                    # openssl 0.9.8a-4a0.sarge.1 is known to work OK.
                    # A borken openssl (and therefore sm.encryptMessage) returns
                    #  Content-Type: application/x-pkcs7-mime; name="smime.p7m"
                    # while we need a
                    #  Content-Type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
                    if v == 'application/x-pkcs7-mime; name="smime.p7m"':
                        v = 'application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"'

                    try:
                        msgcopy.replace_header(k, v)
                    except KeyError:
                        msgcopy.add_header(k, v)

                msgcopy.set_payload(body)

        # For the final delivery stage, we can just bulk deliver to a party of
        # one. ;)
        bulkdeliver(mlist, msgcopy, msgdata, envsender, failures, conn)
Пример #12
0
class SmartMessage:
    """Uproszczony interfejs dla bibliotek Pythona, który potrafi tworzyæ
    wiadomoœci tekstowe i z za³¹cznikami MIME."""
    def __init__(self, fromAddr, toAddrs, subject, body, enc='iso-8859-2'):
        """Zacznij od za³o¿enia, i¿ bêdzie to prosta wiadomoœæ tekstowa
        zgodna z RFC 2822 i bez MIME."""
        self.msg = Message()
        self.msg.set_payload(body)
        self['Subject'] = subject
        self.setFrom(fromAddr)
        self.setTo(toAddrs)
        self.hasAttachments = False
        self.enc = enc

    def setFrom(self, fromAddr):
        "Ustawia adres nadawcy wiadomoœci."
        if not fromAddr or not type(fromAddr) == type(''):
            raise Exception, 'Wiadomoœæ musi mieæ jednego i tylko jednego nadawcê.'
        self['From'] = fromAddr

    def setTo(self, to):
        "Ustawia adresy osób, które maj¹ otrzymaæ wiadomoœæ."
        if not to:
            raise Exception, 'Wiadomoœæ musi mieæ co najmniej jednego odbiorcê.'
        self._addresses(to, 'To')

        #Dodatkowo przechowuj adresy jako listê. Byæ mo¿e
        #skorzysta z niej kod, który zajmie siê wysy³aniem wiadomoœci.
        self.to = to

    def setCc(self, cc):
        """Ustawia adresy osób, które maj¹ otrzymaæ kopiê wiadomoœc. choæ
        nie jest ona adresowana do nich w sposób bezpoœredni."""
        self._addresses(cc, 'Cc')

    def addAttachment(self, attachment, filename, mimetype=None):
        "Do³¹cza do wiadomoœci wskazany plik."

        #Odgadnij g³ówny i dodatkowy typ MIME na podstawie nazwy pliku.
        if not mimetype:
            mimetype = mimetypes.guess_type(filename)[0]
        if not mimetype:
            raise Exception, "Nie uda³o siê okreœliæ typu MIME dla", filename
        if '/' in mimetype:
            major, minor = mimetype.split('/')
        else:
            major = mimetype
            minor = None

        #Wiadomoœæ by³a konstruowana z za³o¿eniem, i¿ bêdzie zawieraæ
        #tylko i wy³¹cznie tekst. Poniewa¿ wiem, ¿e bêdzie zawieraæ
        #co najmniej jeden za³¹cznik, musimy zmieniæ j¹ na wiadomoœæ
        #wieloczêœciow¹ i wkleiæ tekst jako pierwsz¹ czêœæ.
        if not self.hasAttachments:
            body = self.msg.get_payload()
            newMsg = MIMEMultipart()
            newMsg.attach(MIMEText(body, 'plain', self.enc))
            #Skopiuj stare nag³ówki do nowego obiektu.
            for header, value in self.msg.items():
                newMsg[header] = value
            self.msg = newMsg
            self.hasAttachments = True
        subMessage = MIMENonMultipart(major, minor, name=filename)
        subMessage.set_payload(attachment)

        #Zakoduj teksty jako quoted printable natomiast wszystkie
        #inne typy jako base64.
        if major == 'text':
            encoder = Encoders.encode_quopri
        else:
            encoder = Encoders.encode_base64
        encoder(subMessage)

        #Powi¹¿ fragment MIME z g³ówn¹ wiadomoœci¹.
        self.msg.attach(subMessage)

    def _addresses(self, addresses, key):
        """Ustawia zawartoœæ nag³ówka na podstawie listy przekazanych adresów."""
        if hasattr(addresses, '__iter__'):
            addresses = ', '.join(addresses)
        self[key] = addresses

    #Kilka metod dodatkowych umo¿liwiaj¹cych traktowanie klasy w podobny
    #sposób, jak klasy Message lub MultipartMessage, stosuj¹c odpowiedni¹
    #delegacjê poleceñ do tych klas.
    def __getitem__(self, key):
        "Zwróæ nag³ówek o podanym kluczu."
        return self.msg[key]

    def __setitem__(self, key, value):
        "Ustaw nag³ówek o wskazanej nazwie."
        self.msg[key] = value

    def __getattr__(self, key):
        return getattr(self.msg, key)

    def __str__(self):
        "Zwróæ tekstow¹ reprezentacjê wiadomoœci."
        return self.msg.as_string()
Пример #13
0
    def comment(self, text='', username='', time='',
                note=None, use_heading=None,
                REQUEST=None, subject_heading='', message_id=None,
                in_reply_to=None, exclude_address=None, sendmail=1):
        """Add a comment to this page.

        We try to do this efficiently, avoiding re-rendering the full page
        if possible.  The comment will be mailed out to any subscribers.
        If auto-subscription is in effect, we subscribe the poster to this
        page.

        subject_heading is so named to avoid a clash with some existing
        zope subject attribute.  note and use_heading are not used and
        kept only for backwards compatibility.
        """
        if not self.checkSufficientId(REQUEST):
            return self.denied(
                _("Sorry, this wiki doesn't allow anonymous edits. Please configure a username in options first."))
        if self.isDavLocked(): return self.davLockDialog()
        # gather info
        oldtext         = self.read()
        text            = text and self.cleanupText(text)
        subject_heading = subject_heading and self.cleanupText(subject_heading)
        if not username:
            username = self.usernameFrom(REQUEST)
            if re.match(r'^(?:\d{1,3}\.){3}\d{1,3}$',username): username = ''
        username        = username and self.tounicode(username)
        firstcomment    = self.messageCount()==0
        # ensure the page comment and mail-out will have the same
        # message-id, and the same timestamp if possible (helps threading
        # & troubleshooting)
        if time: dtime = DateTime(time)
        else:
            dtime = self.ZopeTime()
            time = dtime.rfc822()
        if not message_id: message_id = self.messageIdFromTime(dtime)
        # format this comment as standard rfc2822
        m = Message()
        m.set_charset(self.encoding())
        m.set_payload(self.toencoded(text))
        m['From']       = self.toencoded(username)
        m['Date']       = time
        m['Subject']    = self.toencoded(subject_heading)
        m['Message-ID'] = message_id
        if in_reply_to: m['In-Reply-To'] = in_reply_to
        m.set_unixfrom(self.fromLineFrom(m['From'],m['Date'])[:-1])
        t = self.tounicode(str(m))
        # discard junk comments
        if not (m['Subject'] or m.get_payload()): return
        self.checkForSpam(t)

        # do it
        self.saveRevision()
        # append to the raw source
        t = '\n\n' + t
        self.raw += t
        # and to the _prerendered cache, carefully mimicking a full
        # prerender. This works with current page types at least.
        t = self.pageType().preRenderMessage(self,m)
        if firstcomment: t=self.pageType().discussionSeparator(self) + t
        t = self.pageType().preRender(self,t)
        self.setPreRendered(self.preRendered()+t)
        self.cookDtmlIfNeeded()
        # extras
        self.setLastEditor(REQUEST)
        self.setLastLog(subject_heading)
        if self.autoSubscriptionEnabled(): self.subscribeThisUser(REQUEST)
        self.index_object()
        if REQUEST: REQUEST.cookies['zwiki_username'] = m['From'] # use real from address
        if sendmail:
            self.sendMailToSubscribers(
                m.get_payload(), REQUEST, subject=m['Subject'],
                message_id=m['Message-ID'], in_reply_to=m['In-Reply-To'],
                exclude_address=exclude_address)
        if REQUEST: REQUEST.RESPONSE.redirect(REQUEST['URL1']+'#bottom')