예제 #1
0
    def decode_payload(self, encoding, payload):
        """
        Decode attachment payload data.

        :param encoding:
            The current encoding of the payload data.

        :param payload:
            the payload data
        """
        cte = encoding.lower()
        if cte == 'quoted-printable':
            return utils._qdecode(payload)
        elif cte == 'base64':
            try:
                return utils._bdecode(payload)
            except binascii.Error:
                # Incorrect padding
                return payload
        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
            sfp = StringIO()
            try:
                uu.decode(StringIO(payload + '\n'), sfp, quiet=True)
                payload = sfp.getvalue()
            except uu.Error:
                # Some decoding problem
                return payload
예제 #2
0
    def get_payload(self, i=None, decode=False):
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if decode:
            if self.is_multipart():
                return
            cte = self.get('content-transfer-encoding', '').lower()
            if cte == 'quoted-printable':
                return utils._qdecode(payload)
            if cte == 'base64':
                try:
                    return utils._bdecode(payload)
                except binascii.Error:
                    return payload

            elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
                sfp = StringIO()
                try:
                    uu.decode(StringIO(payload + '\n'), sfp, quiet=True)
                    payload = sfp.getvalue()
                except uu.Error:
                    return payload

        return payload
예제 #3
0
    def get_payload(self, i = None, decode = False):
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if decode:
            if self.is_multipart():
                return
            cte = self.get('content-transfer-encoding', '').lower()
            if cte == 'quoted-printable':
                return utils._qdecode(payload)
            if cte == 'base64':
                try:
                    return utils._bdecode(payload)
                except binascii.Error:
                    return payload

            elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
                sfp = StringIO()
                try:
                    uu.decode(StringIO(payload + '\n'), sfp, quiet=True)
                    payload = sfp.getvalue()
                except uu.Error:
                    return payload

        return payload
예제 #4
0
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if not decode:
            return payload
        # Decoded payloads always return bytes.  XXX split this part out into
        # a new method called .get_decoded_payload().
        if self.is_multipart():
            return None
        cte = self.get('content-transfer-encoding', '').lower()
        if cte == 'quoted-printable':
            return utils._qdecode(payload)
        elif cte == 'base64':
            try:
                if isinstance(payload, str):
                    payload = payload.encode('raw-unicode-escape')
                return base64.b64decode(payload)
                #return utils._bdecode(payload)
            except binascii.Error:
                # Incorrect padding
                pass
        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
            in_file = BytesIO(payload.encode('raw-unicode-escape'))
            out_file = BytesIO()
            try:
                uu.decode(in_file, out_file, quiet=True)
                return out_file.getvalue()
            except uu.Error:
                # Some decoding problem
                pass
        # Is there a better way to do this?  We can't use the bytes
        # constructor.
        if isinstance(payload, str):
            return payload.encode('raw-unicode-escape')
        return payload
예제 #5
0
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if not decode:
            return payload
        # Decoded payloads always return bytes.  XXX split this part out into
        # a new method called .get_decoded_payload().
        if self.is_multipart():
            return None
        cte = self.get('content-transfer-encoding', '').lower()
        if cte == 'quoted-printable':
            return utils._qdecode(payload)
        elif cte == 'base64':
            try:
                if isinstance(payload, str):
                    payload = payload.encode('raw-unicode-escape')
                return base64.b64decode(payload)
                #return utils._bdecode(payload)
            except binascii.Error:
                # Incorrect padding
                pass
        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
            in_file = BytesIO(payload.encode('raw-unicode-escape'))
            out_file = BytesIO()
            try:
                uu.decode(in_file, out_file, quiet=True)
                return out_file.getvalue()
            except uu.Error:
                # Some decoding problem
                pass
        # Is there a better way to do this?  We can't use the bytes
        # constructor.
        if isinstance(payload, str):
            return payload.encode('raw-unicode-escape')
        return payload
예제 #6
0
 def get_payload(self, i=None, decode=False):
     if self.is_multipart():
         if decode:
             return
         if i is None:
             return self._payload
         return self._payload[i]
     if i is not None and not isinstance(self._payload, list):
         raise TypeError('Expected list, got %s' % type(self._payload))
     payload = self._payload
     cte = str(self.get('content-transfer-encoding', '')).lower()
     if isinstance(payload, str):
         if utils._has_surrogates(payload):
             bpayload = payload.encode('ascii', 'surrogateescape')
             if not decode:
                 try:
                     payload = bpayload.decode(
                         self.get_param('charset', 'ascii'), 'replace')
                 except LookupError:
                     payload = bpayload.decode('ascii', 'replace')
                 if decode:
                     try:
                         bpayload = payload.encode('ascii')
                     except UnicodeError:
                         bpayload = payload.encode('raw-unicode-escape')
         elif decode:
             try:
                 bpayload = payload.encode('ascii')
             except UnicodeError:
                 bpayload = payload.encode('raw-unicode-escape')
     if not decode:
         return payload
     if cte == 'quoted-printable':
         return utils._qdecode(bpayload)
     if cte == 'base64':
         (value, defects) = decode_b(b''.join(bpayload.splitlines()))
         for defect in defects:
             self.policy.handle_defect(self, defect)
         return value
     if cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
         in_file = BytesIO(bpayload)
         out_file = BytesIO()
         try:
             uu.decode(in_file, out_file, quiet=True)
             return out_file.getvalue()
         except uu.Error:
             return bpayload
     if isinstance(payload, str):
         return bpayload
     return payload
예제 #7
0
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if decode:
            if self.is_multipart():
                return None
            cte = self.get('content-transfer-encoding', '').lower()
            if cte == 'quoted-printable':
                return utils._qdecode(payload)
            elif cte == 'base64':
                try:
                    return utils._bdecode(payload)
                except binascii.Error:
                    # Incorrect padding
                    return payload
            elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
                sfp = StringIO()
                try:
                    uu.decode(StringIO(payload+'\n'), sfp, quiet=True)
                    payload = sfp.getvalue()
                except uu.Error:
                    # Some decoding problem
                    return payload
        # Everything else, including encodings with 8bit or 7bit are returned
        # unchanged.
        return payload
예제 #8
0
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError("Expected list, got %s" % type(self._payload))
        else:
            payload = self._payload[i]
        if decode:
            if self.is_multipart():
                return None
            cte = self.get("content-transfer-encoding", "").lower()
            if cte == "quoted-printable":
                return utils._qdecode(payload)
            elif cte == "base64":
                try:
                    return utils._bdecode(payload)
                except binascii.Error:
                    # Incorrect padding
                    return payload
            elif cte in ("x-uuencode", "uuencode", "uue", "x-uue"):
                sfp = StringIO()
                try:
                    uu.decode(StringIO(payload + "\n"), sfp, quiet=True)
                    payload = sfp.getvalue()
                except uu.Error:
                    # Some decoding problem
                    return payload
        # Everything else, including encodings with 8bit or 7bit are returned
        # unchanged.
        return payload
예제 #9
0
 def get_payload(self, i=None, decode=False):
     if self.is_multipart():
         if decode:
             return
         if i is None:
             return self._payload
         return self._payload[i]
     if i is not None and not isinstance(self._payload, list):
         raise TypeError('Expected list, got %s' % type(self._payload))
     payload = self._payload
     cte = str(self.get('content-transfer-encoding', '')).lower()
     if isinstance(payload, str):
         if utils._has_surrogates(payload):
             bpayload = payload.encode('ascii', 'surrogateescape')
             if not decode:
                 try:
                     payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace')
                 except LookupError:
                     payload = bpayload.decode('ascii', 'replace')
                 if decode:
                     try:
                         bpayload = payload.encode('ascii')
                     except UnicodeError:
                         bpayload = payload.encode('raw-unicode-escape')
         elif decode:
             try:
                 bpayload = payload.encode('ascii')
             except UnicodeError:
                 bpayload = payload.encode('raw-unicode-escape')
     if not decode:
         return payload
     if cte == 'quoted-printable':
         return utils._qdecode(bpayload)
     if cte == 'base64':
         (value, defects) = decode_b(b''.join(bpayload.splitlines()))
         for defect in defects:
             self.policy.handle_defect(self, defect)
         return value
     if cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
         in_file = BytesIO(bpayload)
         out_file = BytesIO()
         try:
             uu.decode(in_file, out_file, quiet=True)
             return out_file.getvalue()
         except uu.Error:
             return bpayload
     if isinstance(payload, str):
         return bpayload
     return payload
예제 #10
0
    def get_payload(self, i = None, decode = False):
        """Return a reference to the payload.
        
        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.
        
        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).
        
        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.
        
        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        if i is None:
            payload = self._payload
        elif not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        else:
            payload = self._payload[i]
        if decode:
            if self.is_multipart():
                return
            cte = self.get('content-transfer-encoding', '').lower()
            if cte == 'quoted-printable':
                return utils._qdecode(payload)
            if cte == 'base64':
                try:
                    return utils._bdecode(payload)
                except binascii.Error:
                    return payload

            elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
                sfp = StringIO()
                try:
                    uu.decode(StringIO(payload + '\n'), sfp, quiet=True)
                    payload = sfp.getvalue()
                except uu.Error:
                    return payload

        return payload
예제 #11
0
파일: message.py 프로젝트: luw630/ddzserver
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        # Here is the logic table for this code, based on the email5.0.0 code:
        #   i     decode  is_multipart  result
        # ------  ------  ------------  ------------------------------
        #  None   True    True          None
        #   i     True    True          None
        #  None   False   True          _payload (a list)
        #   i     False   True          _payload element i (a Message)
        #   i     False   False         error (not a list)
        #   i     True    False         error (not a list)
        #  None   False   False         _payload
        #  None   True    False         _payload decoded (bytes)
        # Note that Barry planned to factor out the 'decode' case, but that
        # isn't so easy now that we handle the 8 bit data, which needs to be
        # converted in both the decode and non-decode path.
        if self.is_multipart():
            if decode:
                return None
            if i is None:
                return self._payload
            else:
                return self._payload[i]
        # For backward compatibility, Use isinstance and this error message
        # instead of the more logical is_multipart test.
        if i is not None and not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        payload = self._payload
        # cte might be a Header, so for now stringify it.
        cte = str(self.get('content-transfer-encoding', '')).lower()
        # payload may be bytes here.
        if isinstance(payload, str):
            if utils._has_surrogates(payload):
                bpayload = payload.encode('ascii', 'surrogateescape')
                if not decode:
                    try:
                        payload = bpayload.decode(
                            self.get_param('charset', 'ascii'), 'replace')
                    except LookupError:
                        payload = bpayload.decode('ascii', 'replace')
            elif decode:
                try:
                    bpayload = payload.encode('ascii')
                except UnicodeError:
                    # This won't happen for RFC compliant messages (messages
                    # containing only ASCII codepoints in the unicode input).
                    # If it does happen, turn the string into bytes in a way
                    # guaranteed not to fail.
                    bpayload = payload.encode('raw-unicode-escape')
        if not decode:
            return payload
        if cte == 'quoted-printable':
            return utils._qdecode(bpayload)
        elif cte == 'base64':
            # XXX: this is a bit of a hack; decode_b should probably be factored
            # out somewhere, but I haven't figured out where yet.
            value, defects = decode_b(b''.join(bpayload.splitlines()))
            for defect in defects:
                self.policy.handle_defect(self, defect)
            return value
        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
            in_file = BytesIO(bpayload)
            out_file = BytesIO()
            try:
                uu.decode(in_file, out_file, quiet=True)
                return out_file.getvalue()
            except uu.Error:
                # Some decoding problem
                return bpayload
        if isinstance(payload, str):
            return bpayload
        return payload
예제 #12
0
    def get_payload(self, i=None, decode=False):
        """Return a reference to the payload.

        The payload will either be a list object or a string.  If you mutate
        the list object, you modify the message's payload in place.  Optional
        i returns that index into the payload.

        Optional decode is a flag indicating whether the payload should be
        decoded or not, according to the Content-Transfer-Encoding header
        (default is False).

        When True and the message is not a multipart, the payload will be
        decoded if this header's value is `quoted-printable' or `base64'.  If
        some other encoding is used, or the header is missing, or if the
        payload has bogus data (i.e. bogus base64 or uuencoded data), the
        payload is returned as-is.

        If the message is a multipart and the decode flag is True, then None
        is returned.
        """
        # Here is the logic table for this code, based on the email5.0.0 code:
        #   i     decode  is_multipart  result
        # ------  ------  ------------  ------------------------------
        #  None   True    True          None
        #   i     True    True          None
        #  None   False   True          _payload (a list)
        #   i     False   True          _payload element i (a Message)
        #   i     False   False         error (not a list)
        #   i     True    False         error (not a list)
        #  None   False   False         _payload
        #  None   True    False         _payload decoded (bytes)
        # Note that Barry planned to factor out the 'decode' case, but that
        # isn't so easy now that we handle the 8 bit data, which needs to be
        # converted in both the decode and non-decode path.
        if self.is_multipart():
            if decode:
                return None
            if i is None:
                return self._payload
            else:
                return self._payload[i]
        # For backward compatibility, Use isinstance and this error message
        # instead of the more logical is_multipart test.
        if i is not None and not isinstance(self._payload, list):
            raise TypeError('Expected list, got %s' % type(self._payload))
        payload = self._payload
        # cte might be a Header, so for now stringify it.
        cte = str(self.get('content-transfer-encoding', '')).lower()
        # payload may be bytes here.
        if isinstance(payload, str):
            if utils._has_surrogates(payload):
                bpayload = payload.encode('ascii', 'surrogateescape')
                if not decode:
                    try:
                        payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace')
                    except LookupError:
                        payload = bpayload.decode('ascii', 'replace')
            elif decode:
                try:
                    bpayload = payload.encode('ascii')
                except UnicodeError:
                    # This won't happen for RFC compliant messages (messages
                    # containing only ASCII codepoints in the unicode input).
                    # If it does happen, turn the string into bytes in a way
                    # guaranteed not to fail.
                    bpayload = payload.encode('raw-unicode-escape')
        if not decode:
            return payload
        if cte == 'quoted-printable':
            return utils._qdecode(bpayload)
        elif cte == 'base64':
            # XXX: this is a bit of a hack; decode_b should probably be factored
            # out somewhere, but I haven't figured out where yet.
            value, defects = decode_b(b''.join(bpayload.splitlines()))
            for defect in defects:
                self.policy.handle_defect(self, defect)
            return value
        elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
            in_file = BytesIO(bpayload)
            out_file = BytesIO()
            try:
                uu.decode(in_file, out_file, quiet=True)
                return out_file.getvalue()
            except uu.Error:
                # Some decoding problem
                return bpayload
        if isinstance(payload, str):
            return bpayload
        return payload
예제 #13
0
def rewrite(part):
    need_rewrite = False

    for hdr in part.values():
        if max([len(i) for i in hdr.split('\n')]) > MAX_LINE_LENGTH:
            need_rewrite = True # Force MIME rewrite if we have long headers
            sys.stderr.write("FIXUP NEXT: Rewrite forced by long header line\n")

    for hdr, max_lines, max_items, max_bytes in NUKE_HDRS:
        (hdr, val) = (hdr.lower(), msg.get(hdr))
        if (val and ((max_lines >= 0 and len(val.split('\n')) > max_lines) or
                     (max_items >= 0 and len(val.split()) > max_items) or
                     (max_bytes >= 0 and len(val) > max_bytes))):
            del msg[hdr]
            sys.stderr.write("FIXUP NEXT: Removed long header line: "+hdr+"\n")
            need_rewrite = True

    if part.is_multipart():
        for subpart in part.get_payload():
            if rewrite(subpart):
                need_rewrite = True
        return need_rewrite

    payload = part.get_payload()
    max_line_length = max([ len(i) for i in payload.split('\n') ])

    cte     = part.get('content-transfer-encoding', '').lower().strip()
    if cte in ['8bit', '7bit', 'binary', '']:
        # Encode unencoded forms which contain 8bit characters or long lines
        update_cte = part.replace_header if (cte != '') else part.add_header
        nonascii_count = [(ord(c) >= 128) for c in payload].count(True)
        if ((NUKE_8BIT and nonascii_count > 0) or
            max_line_length > MAX_LINE_LENGTH):
            if nonascii_count < 100: 
                part.set_payload(qp_encode(payload))
                update_cte('Content-Transfer-Encoding', "quoted-printable")
            else:
                part.set_payload(utils._bencode(payload))
                update_cte('Content-Transfer-Encoding', "base64")
            need_rewrite = True
    elif cte in ['quoted-printable', 'base64']:
        # Recode quoted-printable or base64 with long lines
        if max_line_length > MAX_LINE_LENGTH:
            if cte == 'quoted-printable':
                raw=utils._qdecode(payload)
                part.set_payload(qp_encode(raw))
                need_rewrite = True
            elif cte == 'base64':
                try:
                    raw=utils._bdecode(payload)
                    part.set_payload(utils._bencode(raw))
                    need_rewrite = True
                except binascii.Error:
                    pass

    newcte =  part.get('content-transfer-encoding', '').lower().strip()
    if (newcte != cte):
        part.add_header('X-Mime-Autoconverted',
                        "from " + (cte or "none") + " to " + newcte)
        sys.stderr.write("FIXUP NEXT: Attachment converted " +
                         "from " + (cte or "none") + " to " + newcte + "\n")
    return need_rewrite
예제 #14
0
def rewrite(part):
    need_rewrite = False

    for hdr in part.values():
        if max([len(i) for i in hdr.split('\n')]) > MAX_LINE_LENGTH:
            need_rewrite = True  # Force MIME rewrite if we have long headers
            sys.stderr.write(
                "FIXUP NEXT: Rewrite forced by long header line\n")

    for hdr, max_lines, max_items, max_bytes in NUKE_HDRS:
        (hdr, val) = (hdr.lower(), msg.get(hdr))
        if (val and ((max_lines >= 0 and len(val.split('\n')) > max_lines) or
                     (max_items >= 0 and len(val.split()) > max_items) or
                     (max_bytes >= 0 and len(val) > max_bytes))):
            del msg[hdr]
            sys.stderr.write("FIXUP NEXT: Removed long header line: " + hdr +
                             "\n")
            need_rewrite = True

    if part.is_multipart():
        for subpart in part.get_payload():
            if rewrite(subpart):
                need_rewrite = True
        return need_rewrite

    payload = part.get_payload()
    max_line_length = max([len(i) for i in payload.split('\n')])

    cte = part.get('content-transfer-encoding', '').lower().strip()
    if cte in ['8bit', '7bit', 'binary', '']:
        # Encode unencoded forms which contain 8bit characters or long lines
        update_cte = part.replace_header if (cte != '') else part.add_header
        nonascii_count = [(ord(c) >= 128) for c in payload].count(True)
        if ((NUKE_8BIT and nonascii_count > 0)
                or max_line_length > MAX_LINE_LENGTH):
            if nonascii_count < 100:
                part.set_payload(qp_encode(payload))
                update_cte('Content-Transfer-Encoding', "quoted-printable")
            else:
                part.set_payload(utils._bencode(payload))
                update_cte('Content-Transfer-Encoding', "base64")
            need_rewrite = True
    elif cte in ['quoted-printable', 'base64']:
        # Recode quoted-printable or base64 with long lines
        if max_line_length > MAX_LINE_LENGTH:
            if cte == 'quoted-printable':
                raw = utils._qdecode(payload)
                part.set_payload(qp_encode(raw))
                need_rewrite = True
            elif cte == 'base64':
                try:
                    raw = utils._bdecode(payload)
                    part.set_payload(utils._bencode(raw))
                    need_rewrite = True
                except binascii.Error:
                    pass

    newcte = part.get('content-transfer-encoding', '').lower().strip()
    if (newcte != cte):
        part.add_header('X-Mime-Autoconverted',
                        "from " + (cte or "none") + " to " + newcte)
        sys.stderr.write("FIXUP NEXT: Attachment converted " + "from " +
                         (cte or "none") + " to " + newcte + "\n")
    return need_rewrite
예제 #15
0
def rewrite(part, drop_all_multipart_err):
    need_rewrite = False

    if (part.preamble and max_line_len(part.preamble) > MAX_LINE_LENGTH):
        part.preamble = "\n"
        sys.stderr.write("FIXUP NEXT: Removed over-long MIME preamble\n")
        need_rewrite = True
            
    if (part.epilogue and max_line_len(part.epilogue) > MAX_LINE_LENGTH):
        part.epilogue = "\n"
        sys.stderr.write("FIXUP NEXT: Removed over-long MIME epilogue\n")
        need_rewrite = True
            
    for hdr in part.values():
        if max_line_len(hdr) > MAX_LINE_LENGTH:
            need_rewrite = True # Force MIME rewrite if we have long headers
            sys.stderr.write("FIXUP NEXT: Rewrite forced by long header line\n")

    for hdr, max_lines, max_items, max_bytes in NUKE_HDRS:
        (hdr, val) = (hdr.lower(), part.get(hdr))
        if (val and ((max_lines >= 0 and len(val.split('\n')) > max_lines) or
                     (max_items >= 0 and len(val.split()) > max_items) or
                     (max_bytes >= 0 and len(val) > max_bytes))):
            del part[hdr]
            sys.stderr.write("FIXUP NEXT: Removed long header line: "+hdr+"\n")
            need_rewrite = True

    # Exchange Online can't cope with very long component in address list    
    for hdr in ['To', 'Cc', 'Bcc']:
        val = part.get(hdr, "")
        for addr in val.split(','): # Need better parsing here!
            if len(addr) > 1950:
                part['X-Broken-' + hdr] = val
                del part[hdr]
                sys.stderr.write("FIXUP NEXT: Renamed broken " + hdr +
                                 " to X-Broken-" + hdr + "\n")
                need_rewrite = True

    ct = part.get_content_type()
    max_name_len = 0
    params = part.get_params()
    if params:
        for (key,value) in part.get_params():
            if key in ['name', 'filename']:
                if len(value) > max_name_len:
                    max_name_len = len(value)
                
    if max_name_len > MAX_FILENAME:
        need_rewrite = True
        part_count=len(part.get_payload())
        part_str = ('Removed ' + ct +
                    ' with long filename (' + str(max_name_len) +
                    ' characters) which chokes Exchange Online')
        nuke_part(part, 1, part_str)
        return need_rewrite
        
    if part.is_multipart():
        if (drop_all_multipart_err):
            need_rewrite = True
            part_count=len(part.get_payload())
            part_str = drop_all_multipart_err
            nuke_part(part, 0, part_str)
        elif (len(part.get_payload()) > MAX_SUBPARTS):
            need_rewrite = True
            part_count=len(part.get_payload())
            part_str = ('Removed ' + ct +
                        ' with ' + str(part_count) +
                        ' subparts/attachments which chokes Exchange Online')
            nuke_part(part, 1, part_str)
        elif ct in ['multipart/appledouble']:
            need_rewrite = True
            part_str = ('Removed ' + ct +
                        ' which chokes Exchange Online')
            nuke_part(part, 1, part_str)
        else:
            for subpart in part.get_payload():
                if rewrite(subpart, drop_all_multipart_err):
                    need_rewrite = True
        return need_rewrite

    payload = part.get_payload()
    max_line_length = max_line_len(payload)

    cte     = part.get('content-transfer-encoding', '').lower().strip()
    if cte in ['8bit', '7bit', 'binary', '']:
        # Encode unencoded forms which contain 8bit characters or long lines
        update_cte = part.replace_header if (cte != '') else part.add_header
        nonascii_count = [(ord(c) >= 128) for c in payload].count(True)
        if ((NUKE_8BIT and nonascii_count > 0) or
            max_line_length > MAX_LINE_LENGTH):
            if nonascii_count < 100: 
                part.set_payload(qp_encode(payload))
                update_cte('Content-Transfer-Encoding', "quoted-printable")
            else:
                part.set_payload(utils._bencode(payload))
                update_cte('Content-Transfer-Encoding', "base64")
            need_rewrite = True
    elif (cte in ['quoted-printable', 'base64']):
        decode_error = False
        try:
            if cte == 'quoted-printable':
                raw=utils._qdecode(payload)
            else:
                raw=utils._bdecode(payload)

            if (len(payload) > 100) and (len(raw) < len(payload)/10):
                raise binascii.Error
        except binascii.Error:
            decode_error = True

        if decode_error:
            # Discard broken attachment which would no decode
            need_rewrite = True
            part_str = ('Removed ' + ct +
                        ' with broken attachment which failed to decode')
            nuke_part(part, 1, part_str)
        elif max_line_length > MAX_LINE_LENGTH:
            sys.stderr.write("FIXUP NEXT: Recoded " +
                             (cte or "none") + " attachment [Long lines]\n")

            # Recode quoted-printable or base64 with long lines
            need_rewrite = True
            if cte == 'quoted-printable':
                part.set_payload(qp_encode(raw))
            else:
                part.set_payload(utils._bencode(raw))
                
    newcte =  part.get('content-transfer-encoding', '').lower().strip()
    if (newcte and (newcte != cte)):
        part.add_header('X-Mime-Autoconverted',
                        "from " + (cte or "none") + " to " + newcte)
        if max_line_length > MAX_LINE_LENGTH:
            sys.stderr.write("FIXUP NEXT: Attachment converted " +
                             "from " + (cte or "none") + " to " + newcte +
                             " [Long lines]\n")
        else:
            sys.stderr.write("FIXUP NEXT: Attachment converted " +
                             "from " + (cte or "none") + " to " + newcte +
                             " [Raw Binary data]\n")
            
    return need_rewrite