blob: d6f59f4cc47108ec557a867e5e1277d63bd35537 [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 Warsaw908dc4b2002-06-29 05:56:15 +000010from types import ListType, TupleType, StringType
Barry Warsawba925802001-09-23 03:17:28 +000011
Barry Warsawba925802001-09-23 03:17:28 +000012# Intrapackage imports
Barry Warsaw8ba76e82002-06-02 19:05:51 +000013from email import Errors
14from email import Utils
15from email import 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
Barry Warsaw908dc4b2002-06-29 05:56:15 +000030# Helper functions
Barry Warsaw409a4c02002-04-10 21:01:31 +000031def _formatparam(param, value=None, quote=1):
32 """Convenience function to format and return a key=value pair.
33
Barry Warsaw908dc4b2002-06-29 05:56:15 +000034 This will quote the value if needed or if quote is true.
Barry Warsaw409a4c02002-04-10 21:01:31 +000035 """
36 if value is not None and len(value) > 0:
Barry Warsaw908dc4b2002-06-29 05:56:15 +000037 # TupleType is used for RFC 2231 encoded parameter values where items
38 # are (charset, language, value). charset is a string, not a Charset
39 # instance.
40 if isinstance(value, TupleType):
41 # Convert to ascii, ignore language
42 value = unicode(value[2], value[0]).encode("ascii")
Barry Warsaw409a4c02002-04-10 21:01:31 +000043 # BAW: Please check this. I think that if quote is set it should
44 # force quoting even if not necessary.
45 if quote or tspecials.search(value):
46 return '%s="%s"' % (param, Utils.quote(value))
47 else:
48 return '%s=%s' % (param, value)
49 else:
50 return param
Barry Warsawbeb59452001-09-26 05:41:51 +000051
Barry Warsawba925802001-09-23 03:17:28 +000052
Barry Warsaw908dc4b2002-06-29 05:56:15 +000053def _unquotevalue(value):
54 if isinstance(value, TupleType):
55 return (value[0], value[1], Utils.unquote(value[2]))
56 else:
57 return Utils.unquote(value)
58
59
Barry Warsawe968ead2001-10-04 17:05:11 +000060
Barry Warsawba925802001-09-23 03:17:28 +000061class Message:
62 """Basic message object for use inside the object tree.
63
64 A message object is defined as something that has a bunch of RFC 2822
65 headers and a payload. If the body of the message is a multipart, then
66 the payload is a list of Messages, otherwise it is a string.
67
68 These objects implement part of the `mapping' interface, which assumes
69 there is exactly one occurrance of the header per message. Some headers
70 do in fact appear multiple times (e.g. Received:) and for those headers,
71 you must use the explicit API to set or get all the headers. Not all of
72 the mapping methods are implemented.
73
74 """
75 def __init__(self):
76 self._headers = []
77 self._unixfrom = None
78 self._payload = None
Barry Warsaw409a4c02002-04-10 21:01:31 +000079 self._charset = None
Barry Warsawba925802001-09-23 03:17:28 +000080 # Defaults for multipart messages
81 self.preamble = self.epilogue = None
82
83 def __str__(self):
84 """Return the entire formatted message as a string.
85 This includes the headers, body, and `unixfrom' line.
86 """
87 return self.as_string(unixfrom=1)
88
89 def as_string(self, unixfrom=0):
90 """Return the entire formatted message as a string.
91 Optional `unixfrom' when true, means include the Unix From_ envelope
92 header.
93 """
Barry Warsaw8ba76e82002-06-02 19:05:51 +000094 from email.Generator import Generator
Barry Warsawba925802001-09-23 03:17:28 +000095 fp = StringIO()
96 g = Generator(fp)
Barry Warsaw8ba76e82002-06-02 19:05:51 +000097 g.flatten(self, unixfrom=unixfrom)
Barry Warsawba925802001-09-23 03:17:28 +000098 return fp.getvalue()
99
100 def is_multipart(self):
101 """Return true if the message consists of multiple parts."""
102 if type(self._payload) is ListType:
103 return 1
104 return 0
105
106 #
107 # Unix From_ line
108 #
109 def set_unixfrom(self, unixfrom):
110 self._unixfrom = unixfrom
111
112 def get_unixfrom(self):
113 return self._unixfrom
114
115 #
116 # Payload manipulation.
117 #
118 def add_payload(self, payload):
119 """Add the given payload to the current payload.
120
121 If the current payload is empty, then the current payload will be made
122 a scalar, set to the given value.
123 """
Barry Warsaw409a4c02002-04-10 21:01:31 +0000124 warnings.warn('add_payload() is deprecated, use attach() instead.',
125 DeprecationWarning, 2)
Barry Warsawba925802001-09-23 03:17:28 +0000126 if self._payload is None:
127 self._payload = payload
128 elif type(self._payload) is ListType:
129 self._payload.append(payload)
130 elif self.get_main_type() not in (None, 'multipart'):
131 raise Errors.MultipartConversionError(
132 'Message main Content-Type: must be "multipart" or missing')
133 else:
134 self._payload = [self._payload, payload]
135
Barry Warsaw409a4c02002-04-10 21:01:31 +0000136 def attach(self, payload):
137 """Add the given payload to the current payload.
138
139 The current payload will always be a list of objects after this method
140 is called. If you want to set the payload to a scalar object
141 (e.g. because you're attaching a message/rfc822 subpart), use
142 set_payload() instead.
143 """
144 if self._payload is None:
145 self._payload = [payload]
146 else:
147 self._payload.append(payload)
Barry Warsawba925802001-09-23 03:17:28 +0000148
149 def get_payload(self, i=None, decode=0):
150 """Return the current payload exactly as is.
151
152 Optional i returns that index into the payload.
153
154 Optional decode is a flag indicating whether the payload should be
155 decoded or not, according to the Content-Transfer-Encoding: header.
156 When true and the message is not a multipart, the payload will be
157 decoded if this header's value is `quoted-printable' or `base64'. If
158 some other encoding is used, or the header is missing, the payload is
159 returned as-is (undecoded). If the message is a multipart and the
160 decode flag is true, then None is returned.
161 """
162 if i is None:
163 payload = self._payload
164 elif type(self._payload) is not ListType:
165 raise TypeError, i
166 else:
167 payload = self._payload[i]
168 if decode:
169 if self.is_multipart():
170 return None
171 cte = self.get('content-transfer-encoding', '')
172 if cte.lower() == 'quoted-printable':
173 return Utils._qdecode(payload)
174 elif cte.lower() == 'base64':
175 return Utils._bdecode(payload)
176 # Everything else, including encodings with 8bit or 7bit are returned
177 # unchanged.
178 return payload
179
180
Barry Warsaw409a4c02002-04-10 21:01:31 +0000181 def set_payload(self, payload, charset=None):
182 """Set the payload to the given value.
Barry Warsawba925802001-09-23 03:17:28 +0000183
Barry Warsaw409a4c02002-04-10 21:01:31 +0000184 Optionally set the charset, which must be a Charset instance."""
185 self._payload = payload
186 if charset is not None:
187 self.set_charset(charset)
188
189 def set_charset(self, charset):
190 """Set the charset of the payload to a given character set.
191
192 charset can be a string or a Charset object. If it is a string, it
193 will be converted to a Charset object by calling Charset's
194 constructor. If charset is None, the charset parameter will be
195 removed from the Content-Type: field. Anything else will generate a
196 TypeError.
197
198 The message will be assumed to be a text message encoded with
199 charset.input_charset. It will be converted to charset.output_charset
200 and encoded properly, if needed, when generating the plain text
201 representation of the message. MIME headers (MIME-Version,
202 Content-Type, Content-Transfer-Encoding) will be added as needed.
203 """
204 if charset is None:
205 self.del_param('charset')
206 self._charset = None
207 return
208 if isinstance(charset, StringType):
209 charset = Charset.Charset(charset)
210 if not isinstance(charset, Charset.Charset):
211 raise TypeError, charset
212 # BAW: should we accept strings that can serve as arguments to the
213 # Charset constructor?
214 self._charset = charset
215 if not self.has_key('MIME-Version'):
216 self.add_header('MIME-Version', '1.0')
217 if not self.has_key('Content-Type'):
218 self.add_header('Content-Type', 'text/plain',
219 charset=charset.get_output_charset())
220 else:
221 self.set_param('charset', charset.get_output_charset())
222 if not self.has_key('Content-Transfer-Encoding'):
223 cte = charset.get_body_encoding()
224 if callable(cte):
225 cte(self)
226 else:
227 self.add_header('Content-Transfer-Encoding', cte)
228
229 def get_charset(self):
230 """Return the Charset object associated with the message's payload."""
231 return self._charset
Tim Peters8ac14952002-05-23 15:15:30 +0000232
Barry Warsawba925802001-09-23 03:17:28 +0000233 #
234 # MAPPING INTERFACE (partial)
235 #
236 def __len__(self):
Barry Warsawbeb59452001-09-26 05:41:51 +0000237 """Return the total number of headers, including duplicates."""
Barry Warsawba925802001-09-23 03:17:28 +0000238 return len(self._headers)
239
240 def __getitem__(self, name):
241 """Get a header value.
242
243 Return None if the header is missing instead of raising an exception.
244
245 Note that if the header appeared multiple times, exactly which
246 occurrance gets returned is undefined. Use getall() to get all
247 the values matching a header field name.
248 """
249 return self.get(name)
250
251 def __setitem__(self, name, val):
252 """Set the value of a header.
253
254 Note: this does not overwrite an existing header with the same field
255 name. Use __delitem__() first to delete any existing headers.
256 """
257 self._headers.append((name, val))
258
259 def __delitem__(self, name):
260 """Delete all occurrences of a header, if present.
261
262 Does not raise an exception if the header is missing.
263 """
264 name = name.lower()
265 newheaders = []
266 for k, v in self._headers:
267 if k.lower() <> name:
268 newheaders.append((k, v))
269 self._headers = newheaders
270
271 def __contains__(self, key):
272 return key.lower() in [k.lower() for k, v in self._headers]
273
274 def has_key(self, name):
275 """Return true if the message contains the header."""
Barry Warsawbeb59452001-09-26 05:41:51 +0000276 missing = []
277 return self.get(name, missing) is not missing
Barry Warsawba925802001-09-23 03:17:28 +0000278
279 def keys(self):
280 """Return a list of all the message's header field names.
281
282 These will be sorted in the order they appeared in the original
283 message, and may contain duplicates. Any fields deleted and
284 re-inserted are always appended to the header list.
285 """
286 return [k for k, v in self._headers]
287
288 def values(self):
289 """Return a list of all the message's header values.
290
291 These will be sorted in the order they appeared in the original
292 message, and may contain duplicates. Any fields deleted and
Barry Warsawbf7c52c2001-11-24 16:56:56 +0000293 re-inserted are always appended to the header list.
Barry Warsawba925802001-09-23 03:17:28 +0000294 """
295 return [v for k, v in self._headers]
296
297 def items(self):
298 """Get all the message's header fields and values.
299
300 These will be sorted in the order they appeared in the original
301 message, and may contain duplicates. Any fields deleted and
Barry Warsawbf7c52c2001-11-24 16:56:56 +0000302 re-inserted are always appended to the header list.
Barry Warsawba925802001-09-23 03:17:28 +0000303 """
304 return self._headers[:]
305
306 def get(self, name, failobj=None):
307 """Get a header value.
308
309 Like __getitem__() but return failobj instead of None when the field
310 is missing.
311 """
312 name = name.lower()
313 for k, v in self._headers:
314 if k.lower() == name:
315 return v
316 return failobj
317
318 #
319 # Additional useful stuff
320 #
321
322 def get_all(self, name, failobj=None):
323 """Return a list of all the values for the named field.
324
325 These will be sorted in the order they appeared in the original
326 message, and may contain duplicates. Any fields deleted and
Greg Ward6253c2d2001-11-24 15:49:53 +0000327 re-inserted are always appended to the header list.
Barry Warsaw9300a752001-10-09 15:48:29 +0000328
329 If no such fields exist, failobj is returned (defaults to None).
Barry Warsawba925802001-09-23 03:17:28 +0000330 """
331 values = []
332 name = name.lower()
333 for k, v in self._headers:
334 if k.lower() == name:
335 values.append(v)
Barry Warsaw9300a752001-10-09 15:48:29 +0000336 if not values:
337 return failobj
Barry Warsawba925802001-09-23 03:17:28 +0000338 return values
339
340 def add_header(self, _name, _value, **_params):
341 """Extended header setting.
342
343 name is the header field to add. keyword arguments can be used to set
344 additional parameters for the header field, with underscores converted
345 to dashes. Normally the parameter will be added as key="value" unless
346 value is None, in which case only the key will be added.
347
348 Example:
349
350 msg.add_header('content-disposition', 'attachment', filename='bud.gif')
351
352 """
353 parts = []
354 for k, v in _params.items():
355 if v is None:
356 parts.append(k.replace('_', '-'))
357 else:
Barry Warsaw409a4c02002-04-10 21:01:31 +0000358 parts.append(_formatparam(k.replace('_', '-'), v))
Barry Warsawba925802001-09-23 03:17:28 +0000359 if _value is not None:
360 parts.insert(0, _value)
361 self._headers.append((_name, SEMISPACE.join(parts)))
362
363 def get_type(self, failobj=None):
364 """Returns the message's content type.
365
366 The returned string is coerced to lowercase and returned as a single
367 string of the form `maintype/subtype'. If there was no Content-Type:
368 header in the message, failobj is returned (defaults to None).
369 """
370 missing = []
371 value = self.get('content-type', missing)
372 if value is missing:
373 return failobj
Barry Warsawbeb59452001-09-26 05:41:51 +0000374 return paramre.split(value)[0].lower()
Barry Warsawba925802001-09-23 03:17:28 +0000375
376 def get_main_type(self, failobj=None):
377 """Return the message's main content type if present."""
378 missing = []
379 ctype = self.get_type(missing)
380 if ctype is missing:
381 return failobj
382 parts = ctype.split('/')
383 if len(parts) > 0:
384 return ctype.split('/')[0]
385 return failobj
386
387 def get_subtype(self, failobj=None):
388 """Return the message's content subtype if present."""
389 missing = []
390 ctype = self.get_type(missing)
391 if ctype is missing:
392 return failobj
393 parts = ctype.split('/')
394 if len(parts) > 1:
395 return ctype.split('/')[1]
396 return failobj
397
Barry Warsawbeb59452001-09-26 05:41:51 +0000398 def _get_params_preserve(self, failobj, header):
399 # Like get_params() but preserves the quoting of values. BAW:
400 # should this be part of the public interface?
401 missing = []
402 value = self.get(header, missing)
403 if value is missing:
404 return failobj
405 params = []
406 for p in paramre.split(value):
407 try:
408 name, val = p.split('=', 1)
Barry Warsaw409a4c02002-04-10 21:01:31 +0000409 name = name.rstrip()
410 val = val.lstrip()
Barry Warsawbeb59452001-09-26 05:41:51 +0000411 except ValueError:
412 # Must have been a bare attribute
413 name = p
414 val = ''
415 params.append((name, val))
Barry Warsaw908dc4b2002-06-29 05:56:15 +0000416 params = Utils.decode_params(params)
Barry Warsawbeb59452001-09-26 05:41:51 +0000417 return params
418
Barry Warsaw409a4c02002-04-10 21:01:31 +0000419 def get_params(self, failobj=None, header='content-type', unquote=1):
Barry Warsawba925802001-09-23 03:17:28 +0000420 """Return the message's Content-Type: parameters, as a list.
421
Barry Warsawbeb59452001-09-26 05:41:51 +0000422 The elements of the returned list are 2-tuples of key/value pairs, as
423 split on the `=' sign. The left hand side of the `=' is the key,
424 while the right hand side is the value. If there is no `=' sign in
425 the parameter the value is the empty string. The value is always
Barry Warsaw409a4c02002-04-10 21:01:31 +0000426 unquoted, unless unquote is set to a false value.
Barry Warsawbeb59452001-09-26 05:41:51 +0000427
Barry Warsawba925802001-09-23 03:17:28 +0000428 Optional failobj is the object to return if there is no Content-Type:
429 header. Optional header is the header to search instead of
Barry Warsaw409a4c02002-04-10 21:01:31 +0000430 Content-Type:.
Barry Warsawba925802001-09-23 03:17:28 +0000431 """
432 missing = []
Barry Warsawbeb59452001-09-26 05:41:51 +0000433 params = self._get_params_preserve(missing, header)
434 if params is missing:
Barry Warsawba925802001-09-23 03:17:28 +0000435 return failobj
Barry Warsaw409a4c02002-04-10 21:01:31 +0000436 if unquote:
Barry Warsaw908dc4b2002-06-29 05:56:15 +0000437 return [(k, _unquotevalue(v)) for k, v in params]
Barry Warsaw409a4c02002-04-10 21:01:31 +0000438 else:
439 return params
Barry Warsawba925802001-09-23 03:17:28 +0000440
Barry Warsaw409a4c02002-04-10 21:01:31 +0000441 def get_param(self, param, failobj=None, header='content-type', unquote=1):
Barry Warsawba925802001-09-23 03:17:28 +0000442 """Return the parameter value if found in the Content-Type: header.
443
444 Optional failobj is the object to return if there is no Content-Type:
445 header. Optional header is the header to search instead of
446 Content-Type:
Barry Warsawbeb59452001-09-26 05:41:51 +0000447
448 Parameter keys are always compared case insensitively. Values are
Barry Warsaw409a4c02002-04-10 21:01:31 +0000449 always unquoted, unless unquote is set to a false value.
Barry Warsawba925802001-09-23 03:17:28 +0000450 """
Barry Warsawbeb59452001-09-26 05:41:51 +0000451 if not self.has_key(header):
Barry Warsawba925802001-09-23 03:17:28 +0000452 return failobj
Barry Warsawbeb59452001-09-26 05:41:51 +0000453 for k, v in self._get_params_preserve(failobj, header):
454 if k.lower() == param.lower():
Barry Warsaw409a4c02002-04-10 21:01:31 +0000455 if unquote:
Barry Warsaw908dc4b2002-06-29 05:56:15 +0000456 return _unquotevalue(v)
Barry Warsaw409a4c02002-04-10 21:01:31 +0000457 else:
458 return v
Barry Warsawba925802001-09-23 03:17:28 +0000459 return failobj
460
Barry Warsaw409a4c02002-04-10 21:01:31 +0000461 def set_param(self, param, value, header='Content-Type', requote=1):
462 """Set a parameter in the Content-Type: header.
463
464 If the parameter already exists in the header, its value will be
465 replaced with the new value.
466
467 If header is Content-Type: and has not yet been defined in this
468 message, it will be set to "text/plain" and the new parameter and
469 value will be appended, as per RFC 2045.
470
471 An alternate header can specified in the header argument, and
472 all parameters will be quoted as appropriate unless requote is
473 set to a false value.
474 """
475 if not self.has_key(header) and header.lower() == 'content-type':
476 ctype = 'text/plain'
477 else:
478 ctype = self.get(header)
479 if not self.get_param(param, header=header):
480 if not ctype:
481 ctype = _formatparam(param, value, requote)
482 else:
483 ctype = SEMISPACE.join(
484 [ctype, _formatparam(param, value, requote)])
485 else:
486 ctype = ''
487 for old_param, old_value in self.get_params(header=header,
488 unquote=requote):
489 append_param = ''
490 if old_param.lower() == param.lower():
491 append_param = _formatparam(param, value, requote)
492 else:
493 append_param = _formatparam(old_param, old_value, requote)
494 if not ctype:
495 ctype = append_param
496 else:
497 ctype = SEMISPACE.join([ctype, append_param])
498 if ctype <> self.get(header):
499 del self[header]
500 self[header] = ctype
501
502 def del_param(self, param, header='content-type', requote=1):
503 """Remove the given parameter completely from the Content-Type header.
504
505 The header will be re-written in place without param or its value.
506 All values will be quoted as appropriate unless requote is set to a
507 false value.
508 """
509 if not self.has_key(header):
510 return
511 new_ctype = ''
512 for p, v in self.get_params(header, unquote=requote):
513 if p.lower() <> param.lower():
514 if not new_ctype:
515 new_ctype = _formatparam(p, v, requote)
516 else:
517 new_ctype = SEMISPACE.join([new_ctype,
518 _formatparam(p, v, requote)])
519 if new_ctype <> self.get(header):
520 del self[header]
521 self[header] = new_ctype
522
523 def set_type(self, type, header='Content-Type', requote=1):
524 """Set the main type and subtype for the Content-Type: header.
525
526 type must be a string in the form "maintype/subtype", otherwise a
527 ValueError is raised.
528
529 This method replaces the Content-Type: header, keeping all the
530 parameters in place. If requote is false, this leaves the existing
531 header's quoting as is. Otherwise, the parameters will be quoted (the
532 default).
533
534 An alternate header can be specified in the header argument. When the
535 Content-Type: header is set, we'll always also add a MIME-Version:
536 header.
537 """
538 # BAW: should we be strict?
539 if not type.count('/') == 1:
540 raise ValueError
541 # Set the Content-Type: you get a MIME-Version:
542 if header.lower() == 'content-type':
543 del self['mime-version']
544 self['MIME-Version'] = '1.0'
545 if not self.has_key(header):
546 self[header] = type
547 return
548 params = self.get_params(header, unquote=requote)
549 del self[header]
550 self[header] = type
551 # Skip the first param; it's the old type.
552 for p, v in params[1:]:
553 self.set_param(p, v, header, requote)
554
Barry Warsawba925802001-09-23 03:17:28 +0000555 def get_filename(self, failobj=None):
556 """Return the filename associated with the payload if present.
557
558 The filename is extracted from the Content-Disposition: header's
559 `filename' parameter, and it is unquoted.
560 """
561 missing = []
562 filename = self.get_param('filename', missing, 'content-disposition')
563 if filename is missing:
564 return failobj
Barry Warsaw908dc4b2002-06-29 05:56:15 +0000565 if isinstance(filename, TupleType):
566 # It's an RFC 2231 encoded parameter
567 newvalue = _unquotevalue(filename)
568 return unicode(newvalue[2], newvalue[0])
569 else:
570 newvalue = _unquotevalue(filename.strip())
571 return newvalue
Barry Warsawba925802001-09-23 03:17:28 +0000572
573 def get_boundary(self, failobj=None):
574 """Return the boundary associated with the payload if present.
575
576 The boundary is extracted from the Content-Type: header's `boundary'
577 parameter, and it is unquoted.
578 """
579 missing = []
580 boundary = self.get_param('boundary', missing)
581 if boundary is missing:
582 return failobj
Barry Warsaw908dc4b2002-06-29 05:56:15 +0000583 return _unquotevalue(boundary.strip())
Barry Warsawba925802001-09-23 03:17:28 +0000584
585 def set_boundary(self, boundary):
586 """Set the boundary parameter in Content-Type: to 'boundary'.
587
588 This is subtly different than deleting the Content-Type: header and
589 adding a new one with a new boundary parameter via add_header(). The
590 main difference is that using the set_boundary() method preserves the
591 order of the Content-Type: header in the original message.
592
593 HeaderParseError is raised if the message has no Content-Type: header.
594 """
Barry Warsawbeb59452001-09-26 05:41:51 +0000595 missing = []
596 params = self._get_params_preserve(missing, 'content-type')
597 if params is missing:
Barry Warsawba925802001-09-23 03:17:28 +0000598 # There was no Content-Type: header, and we don't know what type
599 # to set it to, so raise an exception.
600 raise Errors.HeaderParseError, 'No Content-Type: header found'
601 newparams = []
602 foundp = 0
Barry Warsawbeb59452001-09-26 05:41:51 +0000603 for pk, pv in params:
604 if pk.lower() == 'boundary':
605 newparams.append(('boundary', '"%s"' % boundary))
Barry Warsawba925802001-09-23 03:17:28 +0000606 foundp = 1
607 else:
Barry Warsawbeb59452001-09-26 05:41:51 +0000608 newparams.append((pk, pv))
Barry Warsawba925802001-09-23 03:17:28 +0000609 if not foundp:
610 # The original Content-Type: header had no boundary attribute.
611 # Tack one one the end. BAW: should we raise an exception
612 # instead???
Barry Warsawbeb59452001-09-26 05:41:51 +0000613 newparams.append(('boundary', '"%s"' % boundary))
Barry Warsawba925802001-09-23 03:17:28 +0000614 # Replace the existing Content-Type: header with the new value
615 newheaders = []
616 for h, v in self._headers:
617 if h.lower() == 'content-type':
Barry Warsawbeb59452001-09-26 05:41:51 +0000618 parts = []
619 for k, v in newparams:
620 if v == '':
621 parts.append(k)
622 else:
623 parts.append('%s=%s' % (k, v))
624 newheaders.append((h, SEMISPACE.join(parts)))
625
Barry Warsawba925802001-09-23 03:17:28 +0000626 else:
627 newheaders.append((h, v))
628 self._headers = newheaders
629
Barry Warsaw8c1aac22002-05-19 23:44:19 +0000630 try:
631 from email._compat22 import walk
632 except SyntaxError:
633 # Must be using Python 2.1
634 from email._compat21 import walk
Barry Warsawba925802001-09-23 03:17:28 +0000635
636 def get_charsets(self, failobj=None):
637 """Return a list containing the charset(s) used in this message.
Tim Peters527e64f2001-10-04 05:36:56 +0000638
Barry Warsawba925802001-09-23 03:17:28 +0000639 The returned list of items describes the Content-Type: headers'
640 charset parameter for this message and all the subparts in its
641 payload.
642
643 Each item will either be a string (the value of the charset parameter
644 in the Content-Type: header of that part) or the value of the
645 'failobj' parameter (defaults to None), if the part does not have a
646 main MIME type of "text", or the charset is not defined.
647
648 The list will contain one string for each part of the message, plus
649 one for the container message (i.e. self), so that a non-multipart
650 message will still return a list of length 1.
651 """
652 return [part.get_param('charset', failobj) for part in self.walk()]