<NME> u2u_decode.py
<BEF> #!/usr/bin/env python
# u2u_decode.py
# unstructured rfc2047 header to unicode
#
import re
from email.header import decode_header, make_header

# check spaces between encoded_words (and strip them)
sre = re.compile(r'\?=[ \t]+=\?')
# re pat for MIME encoded_word (without trailing spaces)
mre = re.compile(r'=\?[^?]*?\?[bq]\?[^? \t]*?\?=', re.I)

tre = re.compile('=\?[^?]*\?=')

def decode_mime(m):
    # substitute matching encoded_word with unicode equiv.
    h = decode_header(m.group(0))
    u = unicode(make_header(h))
    return u

def clean_spaces(m):
    return m.group(0).replace(" ", "=20")

def u2u_decode(s):
    # utility function for (final) decoding of mime header
    # note: resulting string is in one line (no \n within)
    # note2: spaces between enc_words are stripped (see RFC2047)
    s = ''.join(s.splitlines())
    s = sre.sub('?==?', s)
    s = tre.sub(clean_spaces, s)
    u = mre.sub(decode_mime, s)
    return u

if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        s = sys.argv[1]
    else:
        s = """[Apache-Users 7784] ServerAlias =?ISO-2022-JP?B?GyRAGyRCJEsbKEI=?=:(
 =?ISO-2022-JP?B?GyRCJTMlbSVzGyhC?=)=?ISO-2022-JP?B?GyRCJHI7SBsoQg==?=
 =?ISO-2022-JP?B?GyRAGyRCTVEkOSRrJEg1c0YwJCwkKiQrJDckJBsoQg==?="""
        s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord'
    u = u2u_decode(s)
    print u

    header = "".join(header.splitlines())
    header = sre.sub("?==?", header)
    return ascii_re.sub(clean_non_printable_char, header)


def u2u_decode(s):
    """utility function for (final) decoding of mime header

    note: resulting string is in one line (no \n within)
    note2: spaces between enc_words are stripped (see RFC2047)
    """
    return mre.sub(decode_mime, clean_header(s)).strip(" \r\t\n")


def decode_address(value):
    """Special function for address decoding.

    We need a dedicated processing because RFC1342 explicitely says
    address MUST NOT contain encoded-word:

      These are the ONLY locations where an encoded-word may appear.  In
      particular, an encoded-word MUST NOT appear in any portion of an
      "address".  In addition, an encoded-word MUST NOT be used in a
      Received header field.
    """
    phrase, address = parseaddr(clean_header(value))
    if phrase:
        phrase = mre.sub(decode_mime, phrase)
    return phrase, address

<MSG> Better support of misencoded headers.

<DFF> @@ -1,45 +1,50 @@
 #!/usr/bin/env python
-# u2u_decode.py
-# unstructured rfc2047 header to unicode
-#
+# coding: utf-8
+"""
+u2u_decode.py
+
+unstructured rfc2047 header to unicode
+"""
 import re
 from email.header import decode_header, make_header
 
 # check spaces between encoded_words (and strip them)
 sre = re.compile(r'\?=[ \t]+=\?')
 # re pat for MIME encoded_word (without trailing spaces)
-mre = re.compile(r'=\?[^?]*?\?[bq]\?[^? \t]*?\?=', re.I)
+mre = re.compile(r'=\?[^?]*?\?[bq]\?[^?\t]*?\?=', re.I)
+
+
+def clean_spaces(m):
+    """Replace unencoded spaces in string
+
+    :param str m: a match object
+    :return: the cleaned string
+    """
+    return m.group(0).replace(" ", "=20")
 
-tre = re.compile('=\?[^?]*\?=')
 
 def decode_mime(m):
-    # substitute matching encoded_word with unicode equiv.
-    h = decode_header(m.group(0))
+    """substitute matching encoded_word with unicode equiv.
+    """
+    h = decode_header(clean_spaces(m))
     u = unicode(make_header(h))
     return u
 
-def clean_spaces(m):
-    return m.group(0).replace(" ", "=20")
 
 def u2u_decode(s):
-    # utility function for (final) decoding of mime header
-    # note: resulting string is in one line (no \n within)
-    # note2: spaces between enc_words are stripped (see RFC2047)
+    ur"""utility function for (final) decoding of mime header
+
+    note: resulting string is in one line (no \n within)
+    note2: spaces between enc_words are stripped (see RFC2047)
+
+    >>> u2u_decode('=?ISO-8859-15?Q?=20Profitez de tous les services en ligne sur impots.gouv.fr?=')
+    u' Profitez de tous les services en ligne sur impots.gouv.fr'
+    >>> u2u_decode('=?ISO-8859-1?Q?Accus=E9?= de =?ISO-8859-1?Q?r=E9ception?= de votre annonce')
+    u'Accus\xe9 de r\xe9ception de votre annonce'
+    >>> u2u_decode('Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord')
+    u'Sm\xf6rg\xe5sbord'
+    """
     s = ''.join(s.splitlines())
     s = sre.sub('?==?', s)
-    s = tre.sub(clean_spaces, s)
     u = mre.sub(decode_mime, s)
     return u
-
-if __name__ == '__main__':
-    import sys
-    if len(sys.argv) > 1:
-        s = sys.argv[1]
-    else:
-        s = """[Apache-Users 7784] ServerAlias =?ISO-2022-JP?B?GyRAGyRCJEsbKEI=?=:(
- =?ISO-2022-JP?B?GyRCJTMlbSVzGyhC?=)=?ISO-2022-JP?B?GyRCJHI7SBsoQg==?=
- =?ISO-2022-JP?B?GyRAGyRCTVEkOSRrJEg1c0YwJCwkKiQrJDckJBsoQg==?="""
-        s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord'
-    u = u2u_decode(s)
-    print u
-

Better support of misencoded headers.