blob: 71d10c4af15a360f90ec0efdd2e6b626716379fa [file] [log] [blame]
Barry Warsaw409a4c02002-04-10 21:01:31 +00001# Copyright (C) 2001,2002 Python Software Foundation
Barry Warsawba925802001-09-23 03:17:28 +00002# Author: barry@zope.com (Barry Warsaw)
3
4"""Basic message object for the email package object model.
5"""
6
Barry Warsawba925802001-09-23 03:17:28 +00007import re
Barry Warsaw409a4c02002-04-10 21:01:31 +00008import warnings
Barry Warsawba925802001-09-23 03:17:28 +00009from cStringIO import StringIO
Barry Warsaw409a4c02002-04-10 21:01:31 +000010from types import ListType, StringType
Barry Warsawba925802001-09-23 03:17:28 +000011
Barry Warsawba925802001-09-23 03:17:28 +000012# Intrapackage imports
13import Errors
14import Utils
Barry Warsaw409a4c02002-04-10 21:01:31 +000015import Charset
Barry Warsawba925802001-09-23 03:17:28 +000016
Barry Warsawbeb59452001-09-26 05:41:51 +000017SEMISPACE = '; '
Barry Warsaw409a4c02002-04-10 21:01:31 +000018
19# Regular expression used to split header parameters. BAW: this may be too
20# simple. It isn't strictly RFC 2045 (section 5.1) compliant, but it catches
21# most headers found in the wild. We may eventually need a full fledged
22# parser eventually.
Barry Warsaw2539cf52001-10-25 22:43:46 +000023paramre = re.compile(r'\s*;\s*')
Barry Warsaw409a4c02002-04-10 21:01:31 +000024# Regular expression that matches `special' characters in parameters, the
25# existance of which force quoting of the parameter value.
26tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
27
28
29
30# Helper function
31def _formatparam(param, value=None, quote=1):
32 """Convenience function to format and return a key=value pair.
33
34 Will quote the value if needed or if quote is true.
35 """
36 if value is not None and len(value) > 0:
37 # BAW: Please check this. I think that if quote is set it should
38 # force quoting even if not necessary.
39 if quote or tspecials.search(value):
40 return '%s="%s"' % (param, Utils.quote(value))
41 else:
42 return '%s=%s' % (param, value)
43 else:
44 return param
Barry Warsawbeb59452001-09-26 05:41:51 +000045
Barry Warsawba925802001-09-23 03:17:28 +000046
Barry Warsawe968ead2001-10-04 17:05:11 +000047
Barry Warsawba925802001-09-23 03:17:28 +000048class Message:
49 """Basic message object for use inside the object tree.
50
51 A message object is defined as something that has a bunch of RFC 2822
52 headers and a payload. If the body of the message is a multipart, then
53 the payload is a list of Messages, otherwise it is a string.
54
55 These objects implement part of the `mapping' interface, which assumes
56 there is exactly one occurrance of the header per message. Some headers
57 do in fact appear multiple times (e.g. Received:) and for those headers,
58 you must use the explicit API to set or get all the headers. Not all of
59 the mapping methods are implemented.
60
61 """
62 def __init__(self):
63 self._headers = []
64 self._unixfrom = None
65 self._payload = None
Barry Warsaw409a4c02002-04-10 21:01:31 +000066 self._charset = None
Barry Warsawba925802001-09-23 03:17:28 +000067 # Defaults for multipart messages
68 self.preamble = self.epilogue = None
69
70 def __str__(self):
71 """Return the entire formatted message as a string.
72 This includes the headers, body, and `unixfrom' line.
73 """
74 return self.as_string(unixfrom=1)
75
76 def as_string(self, unixfrom=0):
77 """Return the entire formatted message as a string.
78 Optional `unixfrom' when true, means include the Unix From_ envelope
79 header.
80 """
81 from Generator import Generator
82 fp = StringIO()
83 g = Generator(fp)
84 g(self, unixfrom=unixfrom)
85 return fp.getvalue()
86
87 def is_multipart(self):
88 """Return true if the message consists of multiple parts."""
89 if type(self._payload) is ListType:
90 return 1
91 return 0
92
93 #
94 # Unix From_ line
95 #
96 def set_unixfrom(self, unixfrom):
97 self._unixfrom = unixfrom
98
99 def get_unixfrom(self):
100 return self._unixfrom
101
102 #
103 # Payload manipulation.
104 #
105 def add_payload(self, payload):
106 """Add the given payload to the current payload.
107
108 If the current payload is empty, then the current payload will be made
109 a scalar, set to the given value.
110 """
Barry Warsaw409a4c02002-04-10 21:01:31 +0000111 warnings.warn('add_payload() is deprecated, use attach() instead.',
112 DeprecationWarning, 2)
Barry Warsawba925802001-09-23 03:17:28 +0000113 if self._payload is None:
114 self._payload = payload
115 elif type(self._payload) is ListType:
116 self._payload.append(payload)
117 elif self.get_main_type() not in (None, 'multipart'):
118 raise Errors.MultipartConversionError(
119 'Message main Content-Type: must be "multipart" or missing')
120 else:
121 self._payload = [self._payload, payload]
122
Barry Warsaw409a4c02002-04-10 21:01:31 +0000123 def attach(self, payload):
124 """Add the given payload to the current payload.
125
126 The current payload will always be a list of objects after this method
127 is called. If you want to set the payload to a scalar object
128 (e.g. because you're attaching a message/rfc822 subpart), use
129 set_payload() instead.
130 """
131 if self._payload is None:
132 self._payload = [payload]
133 else:
134 self._payload.append(payload)
Barry Warsawba925802001-09-23 03:17:28 +0000135
136 def get_payload(self, i=None, decode=0):
137 """Return the current payload exactly as is.
138
139 Optional i returns that index into the payload.
140
141 Optional decode is a flag indicating whether the payload should be
142 decoded or not, according to the Content-Transfer-Encoding: header.
143 When true and the message is not a multipart, the payload will be
144 decoded if this header's value is `quoted-printable' or `base64'. If
145 some other encoding is used, or the header is missing, the payload is
146 returned as-is (undecoded). If the message is a multipart and the
147 decode flag is true, then None is returned.
148 """
149 if i is None:
150 payload = self._payload
151 elif type(self._payload) is not ListType:
152 raise TypeError, i
153 else:
154 payload = self._payload[i]
155 if decode:
156 if self.is_multipart():
157 return None
158 cte = self.get('content-transfer-encoding', '')
159 if cte.lower() == 'quoted-printable':
160 return Utils._qdecode(payload)
161 elif cte.lower() == 'base64':
162 return Utils._bdecode(payload)
163 # Everything else, including encodings with 8bit or 7bit are returned
164 # unchanged.
165 return payload
166
167
Barry Warsaw409a4c02002-04-10 21:01:31 +0000168 def set_payload(self, payload, charset=None):
169 """Set the payload to the given value.
Barry Warsawba925802001-09-23 03:17:28 +0000170
Barry Warsaw409a4c02002-04-10 21:01:31 +0000171 Optionally set the charset, which must be a Charset instance."""
172 self._payload = payload
173 if charset is not None:
174 self.set_charset(charset)
175
176 def set_charset(self, charset):
177 """Set the charset of the payload to a given character set.
178
179 charset can be a string or a Charset object. If it is a string, it
180 will be converted to a Charset object by calling Charset's
181 constructor. If charset is None, the charset parameter will be
182 removed from the Content-Type: field. Anything else will generate a
183 TypeError.
184
185 The message will be assumed to be a text message encoded with
186 charset.input_charset. It will be converted to charset.output_charset
187 and encoded properly, if needed, when generating the plain text
188 representation of the message. MIME headers (MIME-Version,
189 Content-Type, Content-Transfer-Encoding) will be added as needed.
190 """
191 if charset is None:
192 self.del_param('charset')
193 self._charset = None
194 return
195 if isinstance(charset, StringType):
196 charset = Charset.Charset(charset)
197 if not isinstance(charset, Charset.Charset):
198 raise TypeError, charset
199 # BAW: should we accept strings that can serve as arguments to the
200 # Charset constructor?
201 self._charset = charset
202 if not self.has_key('MIME-Version'):
203 self.add_header('MIME-Version', '1.0')
204 if not self.has_key('Content-Type'):
205 self.add_header('Content-Type', 'text/plain',
206 charset=charset.get_output_charset())
207 else:
208 self.set_param('charset', charset.get_output_charset())
209 if not self.has_key('Content-Transfer-Encoding'):
210 cte = charset.get_body_encoding()
211 if callable(cte):
212 cte(self)
213 else:
214 self.add_header('Content-Transfer-Encoding', cte)
215
216 def get_charset(self):
217 """Return the Charset object associated with the message's payload."""
218 return self._charset
219
Barry Warsawba925802001-09-23 03:17:28 +0000220 #
221 # MAPPING INTERFACE (partial)
222 #
223 def __len__(self):
Barry Warsawbeb59452001-09-26 05:41:51 +0000224 """Return the total number of headers, including duplicates."""
Barry Warsawba925802001-09-23 03:17:28 +0000225 return len(self._headers)
226
227 def __getitem__(self, name):
228 """Get a header value.
229
230 Return None if the header is missing instead of raising an exception.
231
232 Note that if the header appeared multiple times, exactly which
233 occurrance gets returned is undefined. Use getall() to get all
234 the values matching a header field name.
235 """
236 return self.get(name)
237
238 def __setitem__(self, name, val):
239 """Set the value of a header.
240
241 Note: this does not overwrite an existing header with the same field
242 name. Use __delitem__() first to delete any existing headers.
243 """
244 self._headers.append((name, val))
245
246 def __delitem__(self, name):
247 """Delete all occurrences of a header, if present.
248
249 Does not raise an exception if the header is missing.
250 """
251 name = name.lower()
252 newheaders = []
253 for k, v in self._headers:
254 if k.lower() <> name:
255 newheaders.append((k, v))
256 self._headers = newheaders
257
258 def __contains__(self, key):
259 return key.lower() in [k.lower() for k, v in self._headers]
260
261 def has_key(self, name):
262 """Return true if the message contains the header."""
Barry Warsawbeb59452001-09-26 05:41:51 +0000263 missing = []
264 return self.get(name, missing) is not missing
Barry Warsawba925802001-09-23 03:17:28 +0000265
266 def keys(self):
267 """Return a list of all the message's header field names.
268
269 These will be sorted in the order they appeared in the original
270 message, and may contain duplicates. Any fields deleted and
271 re-inserted are always appended to the header list.
272 """
273 return [k for k, v in self._headers]
274
275 def values(self):
276 """Return a list of all the message's header values.
277
278 These will be sorted in the order they appeared in the original
279 message, and may contain duplicates. Any fields deleted and
Barry Warsawbf7c52c2001-11-24 16:56:56 +0000280 re-inserted are always appended to the header list.
Barry Warsawba925802001-09-23 03:17:28 +0000281 """
282 return [v for k, v in self._headers]
283
284 def items(self):
285 """Get all the message's header fields and values.
286
287 These will be sorted in the order they appeared in the original
288 message, and may contain duplicates. Any fields deleted and
Barry Warsawbf7c52c2001-11-24 16:56:56 +0000289 re-inserted are always appended to the header list.
Barry Warsawba925802001-09-23 03:17:28 +0000290 """
291 return self._headers[:]
292
293 def get(self, name, failobj=None):
294 """Get a header value.
295
296 Like __getitem__() but return failobj instead of None when the field
297 is missing.
298 """
299 name = name.lower()
300 for k, v in self._headers:
301 if k.lower() == name:
302 return v
303 return failobj
304
305 #
306 # Additional useful stuff
307 #
308
309 def get_all(self, name, failobj=None):
310 """Return a list of all the values for the named field.
311
312 These will be sorted in the order they appeared in the original
313 message, and may contain duplicates. Any fields deleted and
Greg Ward6253c2d2001-11-24 15:49:53 +0000314 re-inserted are always appended to the header list.
Barry Warsaw9300a752001-10-09 15:48:29 +0000315
316 If no such fields exist, failobj is returned (defaults to None).
Barry Warsawba925802001-09-23 03:17:28 +0000317 """
318 values = []
319 name = name.lower()
320 for k, v in self._headers:
321 if k.lower() == name:
322 values.append(v)
Barry Warsaw9300a752001-10-09 15:48:29 +0000323 if not values:
324 return failobj
Barry Warsawba925802001-09-23 03:17:28 +0000325 return values
326
327 def add_header(self, _name, _value, **_params):
328 """Extended header setting.
329
330 name is the header field to add. keyword arguments can be used to set
331 additional parameters for the header field, with underscores converted
332 to dashes. Normally the parameter will be added as key="value" unless
333 value is None, in which case only the key will be added.
334
335 Example:
336
337 msg.add_header('content-disposition', 'attachment', filename='bud.gif')
338
339 """
340 parts = []
341 for k, v in _params.items():
342 if v is None:
343 parts.append(k.replace('_', '-'))
344 else:
Barry Warsaw409a4c02002-04-10 21:01:31 +0000345 parts.append(_formatparam(k.replace('_', '-'), v))
Barry Warsawba925802001-09-23 03:17:28 +0000346 if _value is not None:
347 parts.insert(0, _value)
348 self._headers.append((_name, SEMISPACE.join(parts)))
349
350 def get_type(self, failobj=None):
351 """Returns the message's content type.
352
353 The returned string is coerced to lowercase and returned as a single
354 string of the form `maintype/subtype'. If there was no Content-Type:
355 header in the message, failobj is returned (defaults to None).
356 """
357 missing = []
358 value = self.get('content-type', missing)
359 if value is missing:
360 return failobj
Barry Warsawbeb59452001-09-26 05:41:51 +0000361 return paramre.split(value)[0].lower()
Barry Warsawba925802001-09-23 03:17:28 +0000362
363 def get_main_type(self, failobj=None):
364 """Return the message's main content type if present."""
365 missing = []
366 ctype = self.get_type(missing)
367 if ctype is missing:
368 return failobj
369 parts = ctype.split('/')
370 if len(parts) > 0:
371 return ctype.split('/')[0]
372 return failobj
373
374 def get_subtype(self, failobj=None):
375 """Return the message's content subtype if present."""
376 missing = []
377 ctype = self.get_type(missing)
378 if ctype is missing:
379 return failobj
380 parts = ctype.split('/')
381 if len(parts) > 1:
382 return ctype.split('/')[1]
383 return failobj
384
Barry Warsawbeb59452001-09-26 05:41:51 +0000385 def _get_params_preserve(self, failobj, header):
386 # Like get_params() but preserves the quoting of values. BAW:
387 # should this be part of the public interface?
388 missing = []
389 value = self.get(header, missing)
390 if value is missing:
391 return failobj
392 params = []
393 for p in paramre.split(value):
394 try:
395 name, val = p.split('=', 1)
Barry Warsaw409a4c02002-04-10 21:01:31 +0000396 name = name.rstrip()
397 val = val.lstrip()
Barry Warsawbeb59452001-09-26 05:41:51 +0000398 except ValueError:
399 # Must have been a bare attribute
400 name = p
401 val = ''
402 params.append((name, val))
403 return params
404
Barry Warsaw409a4c02002-04-10 21:01:31 +0000405 def get_params(self, failobj=None, header='content-type', unquote=1):
Barry Warsawba925802001-09-23 03:17:28 +0000406 """Return the message's Content-Type: parameters, as a list.
407
Barry Warsawbeb59452001-09-26 05:41:51 +0000408 The elements of the returned list are 2-tuples of key/value pairs, as
409 split on the `=' sign. The left hand side of the `=' is the key,
410 while the right hand side is the value. If there is no `=' sign in
411 the parameter the value is the empty string. The value is always
Barry Warsaw409a4c02002-04-10 21:01:31 +0000412 unquoted, unless unquote is set to a false value.
Barry Warsawbeb59452001-09-26 05:41:51 +0000413
Barry Warsawba925802001-09-23 03:17:28 +0000414 Optional failobj is the object to return if there is no Content-Type:
415 header. Optional header is the header to search instead of
Barry Warsaw409a4c02002-04-10 21:01:31 +0000416 Content-Type:.
Barry Warsawba925802001-09-23 03:17:28 +0000417 """
418 missing = []
Barry Warsawbeb59452001-09-26 05:41:51 +0000419 params = self._get_params_preserve(missing, header)
420 if params is missing:
Barry Warsawba925802001-09-23 03:17:28 +0000421 return failobj
Barry Warsaw409a4c02002-04-10 21:01:31 +0000422 if unquote:
423 return [(k, Utils.unquote(v)) for k, v in params]
424 else:
425 return params
Barry Warsawba925802001-09-23 03:17:28 +0000426
Barry Warsaw409a4c02002-04-10 21:01:31 +0000427 def get_param(self, param, failobj=None, header='content-type', unquote=1):
Barry Warsawba925802001-09-23 03:17:28 +0000428 """Return the parameter value if found in the Content-Type: header.
429
430 Optional failobj is the object to return if there is no Content-Type:
431 header. Optional header is the header to search instead of
432 Content-Type:
Barry Warsawbeb59452001-09-26 05:41:51 +0000433
434 Parameter keys are always compared case insensitively. Values are
Barry Warsaw409a4c02002-04-10 21:01:31 +0000435 always unquoted, unless unquote is set to a false value.
Barry Warsawba925802001-09-23 03:17:28 +0000436 """
Barry Warsawbeb59452001-09-26 05:41:51 +0000437 if not self.has_key(header):
Barry Warsawba925802001-09-23 03:17:28 +0000438 return failobj
Barry Warsawbeb59452001-09-26 05:41:51 +0000439 for k, v in self._get_params_preserve(failobj, header):
440 if k.lower() == param.lower():
Barry Warsaw409a4c02002-04-10 21:01:31 +0000441 if unquote:
442 return Utils.unquote(v)
443 else:
444 return v
Barry Warsawba925802001-09-23 03:17:28 +0000445 return failobj
446
Barry Warsaw409a4c02002-04-10 21:01:31 +0000447 def set_param(self, param, value, header='Content-Type', requote=1):
448 """Set a parameter in the Content-Type: header.
449
450 If the parameter already exists in the header, its value will be
451 replaced with the new value.
452
453 If header is Content-Type: and has not yet been defined in this
454 message, it will be set to "text/plain" and the new parameter and
455 value will be appended, as per RFC 2045.
456
457 An alternate header can specified in the header argument, and
458 all parameters will be quoted as appropriate unless requote is
459 set to a false value.
460 """
461 if not self.has_key(header) and header.lower() == 'content-type':
462 ctype = 'text/plain'
463 else:
464 ctype = self.get(header)
465 if not self.get_param(param, header=header):
466 if not ctype:
467 ctype = _formatparam(param, value, requote)
468 else:
469 ctype = SEMISPACE.join(
470 [ctype, _formatparam(param, value, requote)])
471 else:
472 ctype = ''
473 for old_param, old_value in self.get_params(header=header,
474 unquote=requote):
475 append_param = ''
476 if old_param.lower() == param.lower():
477 append_param = _formatparam(param, value, requote)
478 else:
479 append_param = _formatparam(old_param, old_value, requote)
480 if not ctype:
481 ctype = append_param
482 else:
483 ctype = SEMISPACE.join([ctype, append_param])
484 if ctype <> self.get(header):
485 del self[header]
486 self[header] = ctype
487
488 def del_param(self, param, header='content-type', requote=1):
489 """Remove the given parameter completely from the Content-Type header.
490
491 The header will be re-written in place without param or its value.
492 All values will be quoted as appropriate unless requote is set to a
493 false value.
494 """
495 if not self.has_key(header):
496 return
497 new_ctype = ''
498 for p, v in self.get_params(header, unquote=requote):
499 if p.lower() <> param.lower():
500 if not new_ctype:
501 new_ctype = _formatparam(p, v, requote)
502 else:
503 new_ctype = SEMISPACE.join([new_ctype,
504 _formatparam(p, v, requote)])
505 if new_ctype <> self.get(header):
506 del self[header]
507 self[header] = new_ctype
508
509 def set_type(self, type, header='Content-Type', requote=1):
510 """Set the main type and subtype for the Content-Type: header.
511
512 type must be a string in the form "maintype/subtype", otherwise a
513 ValueError is raised.
514
515 This method replaces the Content-Type: header, keeping all the
516 parameters in place. If requote is false, this leaves the existing
517 header's quoting as is. Otherwise, the parameters will be quoted (the
518 default).
519
520 An alternate header can be specified in the header argument. When the
521 Content-Type: header is set, we'll always also add a MIME-Version:
522 header.
523 """
524 # BAW: should we be strict?
525 if not type.count('/') == 1:
526 raise ValueError
527 # Set the Content-Type: you get a MIME-Version:
528 if header.lower() == 'content-type':
529 del self['mime-version']
530 self['MIME-Version'] = '1.0'
531 if not self.has_key(header):
532 self[header] = type
533 return
534 params = self.get_params(header, unquote=requote)
535 del self[header]
536 self[header] = type
537 # Skip the first param; it's the old type.
538 for p, v in params[1:]:
539 self.set_param(p, v, header, requote)
540
Barry Warsawba925802001-09-23 03:17:28 +0000541 def get_filename(self, failobj=None):
542 """Return the filename associated with the payload if present.
543
544 The filename is extracted from the Content-Disposition: header's
545 `filename' parameter, and it is unquoted.
546 """
547 missing = []
548 filename = self.get_param('filename', missing, 'content-disposition')
549 if filename is missing:
550 return failobj
551 return Utils.unquote(filename.strip())
552
553 def get_boundary(self, failobj=None):
554 """Return the boundary associated with the payload if present.
555
556 The boundary is extracted from the Content-Type: header's `boundary'
557 parameter, and it is unquoted.
558 """
559 missing = []
560 boundary = self.get_param('boundary', missing)
561 if boundary is missing:
562 return failobj
563 return Utils.unquote(boundary.strip())
564
565 def set_boundary(self, boundary):
566 """Set the boundary parameter in Content-Type: to 'boundary'.
567
568 This is subtly different than deleting the Content-Type: header and
569 adding a new one with a new boundary parameter via add_header(). The
570 main difference is that using the set_boundary() method preserves the
571 order of the Content-Type: header in the original message.
572
573 HeaderParseError is raised if the message has no Content-Type: header.
574 """
Barry Warsawbeb59452001-09-26 05:41:51 +0000575 missing = []
576 params = self._get_params_preserve(missing, 'content-type')
577 if params is missing:
Barry Warsawba925802001-09-23 03:17:28 +0000578 # There was no Content-Type: header, and we don't know what type
579 # to set it to, so raise an exception.
580 raise Errors.HeaderParseError, 'No Content-Type: header found'
581 newparams = []
582 foundp = 0
Barry Warsawbeb59452001-09-26 05:41:51 +0000583 for pk, pv in params:
584 if pk.lower() == 'boundary':
585 newparams.append(('boundary', '"%s"' % boundary))
Barry Warsawba925802001-09-23 03:17:28 +0000586 foundp = 1
587 else:
Barry Warsawbeb59452001-09-26 05:41:51 +0000588 newparams.append((pk, pv))
Barry Warsawba925802001-09-23 03:17:28 +0000589 if not foundp:
590 # The original Content-Type: header had no boundary attribute.
591 # Tack one one the end. BAW: should we raise an exception
592 # instead???
Barry Warsawbeb59452001-09-26 05:41:51 +0000593 newparams.append(('boundary', '"%s"' % boundary))
Barry Warsawba925802001-09-23 03:17:28 +0000594 # Replace the existing Content-Type: header with the new value
595 newheaders = []
596 for h, v in self._headers:
597 if h.lower() == 'content-type':
Barry Warsawbeb59452001-09-26 05:41:51 +0000598 parts = []
599 for k, v in newparams:
600 if v == '':
601 parts.append(k)
602 else:
603 parts.append('%s=%s' % (k, v))
604 newheaders.append((h, SEMISPACE.join(parts)))
605
Barry Warsawba925802001-09-23 03:17:28 +0000606 else:
607 newheaders.append((h, v))
608 self._headers = newheaders
609
610 def walk(self):
611 """Walk over the message tree, yielding each subpart.
612
Barry Warsaw2a9e3852001-11-05 19:19:55 +0000613 The walk is performed in depth-first order. This method is a
Barry Warsawba925802001-09-23 03:17:28 +0000614 generator.
615 """
Barry Warsawbeb59452001-09-26 05:41:51 +0000616 yield self
Barry Warsawba925802001-09-23 03:17:28 +0000617 if self.is_multipart():
618 for subpart in self.get_payload():
619 for subsubpart in subpart.walk():
620 yield subsubpart
Barry Warsawba925802001-09-23 03:17:28 +0000621
622 def get_charsets(self, failobj=None):
623 """Return a list containing the charset(s) used in this message.
Tim Peters527e64f2001-10-04 05:36:56 +0000624
Barry Warsawba925802001-09-23 03:17:28 +0000625 The returned list of items describes the Content-Type: headers'
626 charset parameter for this message and all the subparts in its
627 payload.
628
629 Each item will either be a string (the value of the charset parameter
630 in the Content-Type: header of that part) or the value of the
631 'failobj' parameter (defaults to None), if the part does not have a
632 main MIME type of "text", or the charset is not defined.
633
634 The list will contain one string for each part of the message, plus
635 one for the container message (i.e. self), so that a non-multipart
636 message will still return a list of length 1.
637 """
638 return [part.get_param('charset', failobj) for part in self.walk()]