| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 1 | # Copyright (C) 2001,2002 Python Software Foundation | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 2 | # Author: barry@zope.com (Barry Warsaw) | 
 | 3 |  | 
 | 4 | """Miscellaneous utilities. | 
 | 5 | """ | 
 | 6 |  | 
| Barry Warsaw | 9aa6435 | 2001-11-09 17:45:48 +0000 | [diff] [blame] | 7 | import time | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 8 | import socket | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 9 | import re | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 10 | import random | 
 | 11 | import os | 
 | 12 | import warnings | 
 | 13 | from cStringIO import StringIO | 
 | 14 | from types import ListType | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 15 |  | 
| Barry Warsaw | 030ddf7 | 2002-11-05 19:54:52 +0000 | [diff] [blame] | 16 | from email._parseaddr import quote | 
 | 17 | from email._parseaddr import AddressList as _AddressList | 
 | 18 | from email._parseaddr import mktime_tz | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 19 |  | 
 | 20 | # We need wormarounds for bugs in these methods in older Pythons (see below) | 
| Barry Warsaw | 030ddf7 | 2002-11-05 19:54:52 +0000 | [diff] [blame] | 21 | from email._parseaddr import parsedate as _parsedate | 
 | 22 | from email._parseaddr import parsedate_tz as _parsedate_tz | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 23 |  | 
| Barry Warsaw | 8c1aac2 | 2002-05-19 23:44:19 +0000 | [diff] [blame] | 24 | try: | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 25 |     True, False | 
 | 26 | except NameError: | 
 | 27 |     True = 1 | 
 | 28 |     False = 0 | 
 | 29 |  | 
 | 30 | try: | 
| Barry Warsaw | 8c1aac2 | 2002-05-19 23:44:19 +0000 | [diff] [blame] | 31 |     from quopri import decodestring as _qdecode | 
 | 32 | except ImportError: | 
 | 33 |     # Python 2.1 doesn't have quopri.decodestring() | 
 | 34 |     def _qdecode(s): | 
 | 35 |         import quopri as _quopri | 
 | 36 |  | 
 | 37 |         if not s: | 
 | 38 |             return s | 
| Barry Warsaw | 8c1aac2 | 2002-05-19 23:44:19 +0000 | [diff] [blame] | 39 |         infp = StringIO(s) | 
 | 40 |         outfp = StringIO() | 
 | 41 |         _quopri.decode(infp, outfp) | 
 | 42 |         value = outfp.getvalue() | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 43 |         if not s.endswith('\n') and value.endswith('\n'): | 
| Barry Warsaw | 8c1aac2 | 2002-05-19 23:44:19 +0000 | [diff] [blame] | 44 |             return value[:-1] | 
 | 45 |         return value | 
 | 46 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 47 | import base64 | 
 | 48 |  | 
 | 49 | # Intrapackage imports | 
| Barry Warsaw | 21f77ac | 2002-06-02 19:07:16 +0000 | [diff] [blame] | 50 | from email.Encoders import _bencode, _qencode | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 51 |  | 
 | 52 | COMMASPACE = ', ' | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 53 | EMPTYSTRING = '' | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 54 | UEMPTYSTRING = u'' | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 55 | CRLF = '\r\n' | 
 | 56 |  | 
 | 57 | specialsre = re.compile(r'[][\()<>@,:;".]') | 
 | 58 | escapesre = re.compile(r'[][\()"]') | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 59 |  | 
 | 60 |  | 
| Barry Warsaw | e968ead | 2001-10-04 17:05:11 +0000 | [diff] [blame] | 61 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 62 | # Helpers | 
 | 63 |  | 
 | 64 | def _identity(s): | 
 | 65 |     return s | 
 | 66 |  | 
 | 67 |  | 
 | 68 | def _bdecode(s): | 
 | 69 |     if not s: | 
 | 70 |         return s | 
 | 71 |     # We can't quite use base64.encodestring() since it tacks on a "courtesy | 
 | 72 |     # newline".  Blech! | 
 | 73 |     if not s: | 
 | 74 |         return s | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 75 |     value = base64.decodestring(s) | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 76 |     if not s.endswith('\n') and value.endswith('\n'): | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 77 |         return value[:-1] | 
 | 78 |     return value | 
 | 79 |  | 
 | 80 |  | 
| Barry Warsaw | e968ead | 2001-10-04 17:05:11 +0000 | [diff] [blame] | 81 |  | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 82 | def fix_eols(s): | 
 | 83 |     """Replace all line-ending characters with \r\n.""" | 
 | 84 |     # Fix newlines with no preceding carriage return | 
 | 85 |     s = re.sub(r'(?<!\r)\n', CRLF, s) | 
 | 86 |     # Fix carriage returns with no following newline | 
 | 87 |     s = re.sub(r'\r(?!\n)', CRLF, s) | 
 | 88 |     return s | 
 | 89 |  | 
 | 90 |  | 
 | 91 |  | 
 | 92 | def formataddr(pair): | 
 | 93 |     """The inverse of parseaddr(), this takes a 2-tuple of the form | 
 | 94 |     (realname, email_address) and returns the string value suitable | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 95 |     for an RFC 2822 From, To or Cc header. | 
| Tim Peters | 8ac1495 | 2002-05-23 15:15:30 +0000 | [diff] [blame] | 96 |  | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 97 |     If the first element of pair is false, then the second element is | 
 | 98 |     returned unmodified. | 
 | 99 |     """ | 
 | 100 |     name, address = pair | 
 | 101 |     if name: | 
 | 102 |         quotes = '' | 
 | 103 |         if specialsre.search(name): | 
 | 104 |             quotes = '"' | 
 | 105 |         name = escapesre.sub(r'\\\g<0>', name) | 
 | 106 |         return '%s%s%s <%s>' % (quotes, name, quotes, address) | 
 | 107 |     return address | 
 | 108 |  | 
 | 109 | # For backwards compatibility | 
 | 110 | def dump_address_pair(pair): | 
 | 111 |     warnings.warn('Use email.Utils.formataddr() instead', | 
 | 112 |                   DeprecationWarning, 2) | 
 | 113 |     return formataddr(pair) | 
 | 114 |  | 
 | 115 |  | 
 | 116 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 117 | def getaddresses(fieldvalues): | 
 | 118 |     """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" | 
 | 119 |     all = COMMASPACE.join(fieldvalues) | 
| Barry Warsaw | e1df15c | 2002-04-12 20:50:05 +0000 | [diff] [blame] | 120 |     a = _AddressList(all) | 
| Barry Warsaw | 4be9ecc | 2002-05-22 01:52:10 +0000 | [diff] [blame] | 121 |     return a.addresslist | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 122 |  | 
 | 123 |  | 
| Barry Warsaw | e968ead | 2001-10-04 17:05:11 +0000 | [diff] [blame] | 124 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 125 | ecre = re.compile(r''' | 
 | 126 |   =\?                   # literal =? | 
 | 127 |   (?P<charset>[^?]*?)   # non-greedy up to the next ? is the charset | 
 | 128 |   \?                    # literal ? | 
 | 129 |   (?P<encoding>[qb])    # either a "q" or a "b", case insensitive | 
 | 130 |   \?                    # literal ? | 
 | 131 |   (?P<atom>.*?)         # non-greedy up to the next ?= is the atom | 
 | 132 |   \?=                   # literal ?= | 
 | 133 |   ''', re.VERBOSE | re.IGNORECASE) | 
 | 134 |  | 
 | 135 |  | 
 | 136 | def decode(s): | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 137 |     """Return a decoded string according to RFC 2047, as a unicode string. | 
 | 138 |  | 
 | 139 |     NOTE: This function is deprecated.  Use Header.decode_header() instead. | 
 | 140 |     """ | 
 | 141 |     warnings.warn('Use Header.decode_header() instead.', DeprecationWarning, 2) | 
 | 142 |     # Intra-package import here to avoid circular import problems. | 
| Barry Warsaw | 21f77ac | 2002-06-02 19:07:16 +0000 | [diff] [blame] | 143 |     from email.Header import decode_header | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 144 |     L = decode_header(s) | 
 | 145 |     if not isinstance(L, ListType): | 
 | 146 |         # s wasn't decoded | 
 | 147 |         return s | 
 | 148 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 149 |     rtn = [] | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 150 |     for atom, charset in L: | 
 | 151 |         if charset is None: | 
 | 152 |             rtn.append(atom) | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 153 |         else: | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 154 |             # Convert the string to Unicode using the given encoding.  Leave | 
 | 155 |             # Unicode conversion errors to strict. | 
 | 156 |             rtn.append(unicode(atom, charset)) | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 157 |     # Now that we've decoded everything, we just need to join all the parts | 
 | 158 |     # together into the final string. | 
 | 159 |     return UEMPTYSTRING.join(rtn) | 
 | 160 |  | 
 | 161 |  | 
| Barry Warsaw | e968ead | 2001-10-04 17:05:11 +0000 | [diff] [blame] | 162 |  | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 163 | def encode(s, charset='iso-8859-1', encoding='q'): | 
 | 164 |     """Encode a string according to RFC 2047.""" | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 165 |     warnings.warn('Use Header.Header.encode() instead.', DeprecationWarning, 2) | 
| Barry Warsaw | c44d2c5 | 2001-12-03 19:26:40 +0000 | [diff] [blame] | 166 |     encoding = encoding.lower() | 
 | 167 |     if encoding == 'q': | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 168 |         estr = _qencode(s) | 
| Barry Warsaw | c44d2c5 | 2001-12-03 19:26:40 +0000 | [diff] [blame] | 169 |     elif encoding == 'b': | 
| Barry Warsaw | ba92580 | 2001-09-23 03:17:28 +0000 | [diff] [blame] | 170 |         estr = _bencode(s) | 
 | 171 |     else: | 
 | 172 |         raise ValueError, 'Illegal encoding code: ' + encoding | 
| Barry Warsaw | c44d2c5 | 2001-12-03 19:26:40 +0000 | [diff] [blame] | 173 |     return '=?%s?%s?%s?=' % (charset.lower(), encoding, estr) | 
| Barry Warsaw | aa79f4d | 2001-11-09 16:59:56 +0000 | [diff] [blame] | 174 |  | 
 | 175 |  | 
 | 176 |  | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 177 | def formatdate(timeval=None, localtime=False): | 
| Barry Warsaw | 9cff0e6 | 2001-11-09 17:07:28 +0000 | [diff] [blame] | 178 |     """Returns a date string as specified by RFC 2822, e.g.: | 
| Barry Warsaw | aa79f4d | 2001-11-09 16:59:56 +0000 | [diff] [blame] | 179 |  | 
 | 180 |     Fri, 09 Nov 2001 01:08:47 -0000 | 
 | 181 |  | 
| Barry Warsaw | 9cff0e6 | 2001-11-09 17:07:28 +0000 | [diff] [blame] | 182 |     Optional timeval if given is a floating point time value as accepted by | 
 | 183 |     gmtime() and localtime(), otherwise the current time is used. | 
 | 184 |  | 
| Barry Warsaw | 5bdb2be | 2002-09-28 20:49:57 +0000 | [diff] [blame] | 185 |     Optional localtime is a flag that when True, interprets timeval, and | 
| Barry Warsaw | 9cff0e6 | 2001-11-09 17:07:28 +0000 | [diff] [blame] | 186 |     returns a date relative to the local timezone instead of UTC, properly | 
 | 187 |     taking daylight savings time into account. | 
| Barry Warsaw | aa79f4d | 2001-11-09 16:59:56 +0000 | [diff] [blame] | 188 |     """ | 
 | 189 |     # Note: we cannot use strftime() because that honors the locale and RFC | 
 | 190 |     # 2822 requires that day and month names be the English abbreviations. | 
 | 191 |     if timeval is None: | 
 | 192 |         timeval = time.time() | 
 | 193 |     if localtime: | 
 | 194 |         now = time.localtime(timeval) | 
 | 195 |         # Calculate timezone offset, based on whether the local zone has | 
 | 196 |         # daylight savings time, and whether DST is in effect. | 
 | 197 |         if time.daylight and now[-1]: | 
 | 198 |             offset = time.altzone | 
 | 199 |         else: | 
 | 200 |             offset = time.timezone | 
| Barry Warsaw | e5739a6 | 2001-11-19 18:36:43 +0000 | [diff] [blame] | 201 |         hours, minutes = divmod(abs(offset), 3600) | 
 | 202 |         # Remember offset is in seconds west of UTC, but the timezone is in | 
 | 203 |         # minutes east of UTC, so the signs differ. | 
 | 204 |         if offset > 0: | 
 | 205 |             sign = '-' | 
 | 206 |         else: | 
 | 207 |             sign = '+' | 
 | 208 |         zone = '%s%02d%02d' % (sign, hours, minutes / 60) | 
| Barry Warsaw | aa79f4d | 2001-11-09 16:59:56 +0000 | [diff] [blame] | 209 |     else: | 
 | 210 |         now = time.gmtime(timeval) | 
 | 211 |         # Timezone offset is always -0000 | 
 | 212 |         zone = '-0000' | 
 | 213 |     return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( | 
 | 214 |         ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]], | 
 | 215 |         now[2], | 
 | 216 |         ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | 
 | 217 |          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1], | 
 | 218 |         now[0], now[3], now[4], now[5], | 
 | 219 |         zone) | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 220 |  | 
 | 221 |  | 
 | 222 |  | 
 | 223 | def make_msgid(idstring=None): | 
| Barry Warsaw | 0ebc5c9 | 2002-10-01 00:44:13 +0000 | [diff] [blame] | 224 |     """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 225 |  | 
 | 226 |     <20020201195627.33539.96671@nightshade.la.mastaler.com> | 
 | 227 |  | 
 | 228 |     Optional idstring if given is a string used to strengthen the | 
| Barry Warsaw | 0ebc5c9 | 2002-10-01 00:44:13 +0000 | [diff] [blame] | 229 |     uniqueness of the message id. | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 230 |     """ | 
 | 231 |     timeval = time.time() | 
 | 232 |     utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) | 
 | 233 |     pid = os.getpid() | 
 | 234 |     randint = random.randrange(100000) | 
 | 235 |     if idstring is None: | 
 | 236 |         idstring = '' | 
 | 237 |     else: | 
 | 238 |         idstring = '.' + idstring | 
 | 239 |     idhost = socket.getfqdn() | 
 | 240 |     msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost) | 
 | 241 |     return msgid | 
 | 242 |  | 
 | 243 |  | 
 | 244 |  | 
 | 245 | # These functions are in the standalone mimelib version only because they've | 
 | 246 | # subsequently been fixed in the latest Python versions.  We use this to worm | 
 | 247 | # around broken older Pythons. | 
 | 248 | def parsedate(data): | 
 | 249 |     if not data: | 
 | 250 |         return None | 
 | 251 |     return _parsedate(data) | 
 | 252 |  | 
 | 253 |  | 
 | 254 | def parsedate_tz(data): | 
 | 255 |     if not data: | 
 | 256 |         return None | 
 | 257 |     return _parsedate_tz(data) | 
 | 258 |  | 
 | 259 |  | 
 | 260 | def parseaddr(addr): | 
| Barry Warsaw | 24fd025 | 2002-04-15 22:00:25 +0000 | [diff] [blame] | 261 |     addrs = _AddressList(addr).addresslist | 
 | 262 |     if not addrs: | 
| Barry Warsaw | 409a4c0 | 2002-04-10 21:01:31 +0000 | [diff] [blame] | 263 |         return '', '' | 
| Barry Warsaw | 24fd025 | 2002-04-15 22:00:25 +0000 | [diff] [blame] | 264 |     return addrs[0] | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 265 |  | 
 | 266 |  | 
| Barry Warsaw | 184d55a | 2002-09-11 02:22:48 +0000 | [diff] [blame] | 267 | # rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. | 
 | 268 | def unquote(str): | 
 | 269 |     """Remove quotes from a string.""" | 
 | 270 |     if len(str) > 1: | 
 | 271 |         if str.startswith('"') and str.endswith('"'): | 
 | 272 |             return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') | 
 | 273 |         if str.startswith('<') and str.endswith('>'): | 
 | 274 |             return str[1:-1] | 
 | 275 |     return str | 
 | 276 |  | 
 | 277 |  | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 278 |  | 
 | 279 | # RFC2231-related functions - parameter encoding and decoding | 
 | 280 | def decode_rfc2231(s): | 
 | 281 |     """Decode string according to RFC 2231""" | 
 | 282 |     import urllib | 
 | 283 |     charset, language, s = s.split("'", 2) | 
 | 284 |     s = urllib.unquote(s) | 
 | 285 |     return charset, language, s | 
 | 286 |  | 
 | 287 |  | 
 | 288 | def encode_rfc2231(s, charset=None, language=None): | 
| Barry Warsaw | 0ebc5c9 | 2002-10-01 00:44:13 +0000 | [diff] [blame] | 289 |     """Encode string according to RFC 2231. | 
 | 290 |  | 
 | 291 |     If neither charset nor language is given, then s is returned as-is.  If | 
 | 292 |     charset is given but not language, the string is encoded using the empty | 
 | 293 |     string for language. | 
 | 294 |     """ | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 295 |     import urllib | 
 | 296 |     s = urllib.quote(s, safe='') | 
 | 297 |     if charset is None and language is None: | 
 | 298 |         return s | 
| Barry Warsaw | 0ebc5c9 | 2002-10-01 00:44:13 +0000 | [diff] [blame] | 299 |     if language is None: | 
 | 300 |         language = '' | 
 | 301 |     return "%s'%s'%s" % (charset, language, s) | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 302 |  | 
 | 303 |  | 
 | 304 | rfc2231_continuation = re.compile(r'^(?P<name>\w+)\*((?P<num>[0-9]+)\*?)?$') | 
 | 305 |  | 
 | 306 | def decode_params(params): | 
| Barry Warsaw | 0ebc5c9 | 2002-10-01 00:44:13 +0000 | [diff] [blame] | 307 |     """Decode parameters list according to RFC 2231. | 
 | 308 |  | 
 | 309 |     params is a sequence of 2-tuples containing (content type, string value). | 
 | 310 |     """ | 
| Barry Warsaw | 12566a8 | 2002-06-29 05:58:04 +0000 | [diff] [blame] | 311 |     new_params = [] | 
 | 312 |     # maps parameter's name to a list of continuations | 
 | 313 |     rfc2231_params = {} | 
 | 314 |     # params is a sequence of 2-tuples containing (content_type, string value) | 
 | 315 |     name, value = params[0] | 
 | 316 |     new_params.append((name, value)) | 
 | 317 |     # Cycle through each of the rest of the parameters. | 
 | 318 |     for name, value in params[1:]: | 
 | 319 |         value = unquote(value) | 
 | 320 |         mo = rfc2231_continuation.match(name) | 
 | 321 |         if mo: | 
 | 322 |             name, num = mo.group('name', 'num') | 
 | 323 |             if num is not None: | 
 | 324 |                 num = int(num) | 
 | 325 |             rfc2231_param1 = rfc2231_params.setdefault(name, []) | 
 | 326 |             rfc2231_param1.append((num, value)) | 
 | 327 |         else: | 
 | 328 |             new_params.append((name, '"%s"' % quote(value))) | 
 | 329 |     if rfc2231_params: | 
 | 330 |         for name, continuations in rfc2231_params.items(): | 
 | 331 |             value = [] | 
 | 332 |             # Sort by number | 
 | 333 |             continuations.sort() | 
 | 334 |             # And now append all values in num order | 
 | 335 |             for num, continuation in continuations: | 
 | 336 |                 value.append(continuation) | 
 | 337 |             charset, language, value = decode_rfc2231(EMPTYSTRING.join(value)) | 
 | 338 |             new_params.append((name, | 
 | 339 |                                (charset, language, '"%s"' % quote(value)))) | 
 | 340 |     return new_params |