SF.net SVN: tmda: [2084] trunk/tmda/TMDA/pythonlib/email
[email protected] Sat, 07 Oct 2006 19:51:09 -0700
| Newsgroups | gmane.mail.spam.tmda.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 2084
http://svn.sourceforge.net/tmda/?rev=2084&view=rev
Author: jasonrm
Date: 2006-10-07 19:50:58 -0700 (Sat, 07 Oct 2006)
Log Message:
-----------
upgrade to Python email package v4.0.1.
Modified Paths:
--------------
trunk/tmda/TMDA/pythonlib/email/__init__.py
trunk/tmda/TMDA/pythonlib/email/_parseaddr.py
Added Paths:
-----------
trunk/tmda/TMDA/pythonlib/email/base64mime.py
trunk/tmda/TMDA/pythonlib/email/charset.py
trunk/tmda/TMDA/pythonlib/email/encoders.py
trunk/tmda/TMDA/pythonlib/email/errors.py
trunk/tmda/TMDA/pythonlib/email/feedparser.py
trunk/tmda/TMDA/pythonlib/email/generator.py
trunk/tmda/TMDA/pythonlib/email/header.py
trunk/tmda/TMDA/pythonlib/email/iterators.py
trunk/tmda/TMDA/pythonlib/email/message.py
trunk/tmda/TMDA/pythonlib/email/parser.py
trunk/tmda/TMDA/pythonlib/email/quoprimime.py
trunk/tmda/TMDA/pythonlib/email/utils.py
Removed Paths:
-------------
trunk/tmda/TMDA/pythonlib/email/Charset.py
trunk/tmda/TMDA/pythonlib/email/Encoders.py
trunk/tmda/TMDA/pythonlib/email/Errors.py
trunk/tmda/TMDA/pythonlib/email/Generator.py
trunk/tmda/TMDA/pythonlib/email/Header.py
trunk/tmda/TMDA/pythonlib/email/Iterators.py
trunk/tmda/TMDA/pythonlib/email/MIMEAudio.py
trunk/tmda/TMDA/pythonlib/email/MIMEBase.py
trunk/tmda/TMDA/pythonlib/email/MIMEImage.py
trunk/tmda/TMDA/pythonlib/email/MIMEMessage.py
trunk/tmda/TMDA/pythonlib/email/MIMEMultipart.py
trunk/tmda/TMDA/pythonlib/email/MIMENonMultipart.py
trunk/tmda/TMDA/pythonlib/email/MIMEText.py
trunk/tmda/TMDA/pythonlib/email/Message.py
trunk/tmda/TMDA/pythonlib/email/Parser.py
trunk/tmda/TMDA/pythonlib/email/Utils.py
trunk/tmda/TMDA/pythonlib/email/_compat21.py
trunk/tmda/TMDA/pythonlib/email/_compat22.py
trunk/tmda/TMDA/pythonlib/email/base64MIME.py
trunk/tmda/TMDA/pythonlib/email/quopriMIME.py
Deleted: trunk/tmda/TMDA/pythonlib/email/Charset.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Charset.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Charset.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,390 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Ben Gertzfield), [email protected] (Barry Warsaw)
-
-# Python 2.3 doesn't come with any Asian codecs by default. Two packages are
-# currently available and supported as of this writing (30-Dec-2003):
-#
-# CJKCodecs
-# http://cjkpython.i18n.org
-# This package contains Chinese, Japanese, and Korean codecs
-
-# JapaneseCodecs
-# http://www.asahi-net.or.jp/~rd6t-kjym/python
-# Some Japanese users prefer this codec package
-
-from types import UnicodeType
-from email.Encoders import encode_7or8bit
-import email.base64MIME
-import email.quopriMIME
-
-def _isunicode(s):
- return isinstance(s, UnicodeType)
-
-# Python 2.2.1 and beyond has these symbols
-try:
- True, False
-except NameError:
- True = 1
- False = 0
-
-
-
-# Flags for types of header encodings
-QP = 1 # Quoted-Printable
-BASE64 = 2 # Base64
-SHORTEST = 3 # the shorter of QP and base64, but only for headers
-
-# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7
-MISC_LEN = 7
-
-DEFAULT_CHARSET = 'us-ascii'
-
-
-
-# Defaults
-CHARSETS = {
- # input header enc body enc output conv
- 'iso-8859-1': (QP, QP, None),
- 'iso-8859-2': (QP, QP, None),
- 'iso-8859-3': (QP, QP, None),
- 'iso-8859-4': (QP, QP, None),
- # iso-8859-5 is Cyrillic, and not especially used
- # iso-8859-6 is Arabic, also not particularly used
- # iso-8859-7 is Greek, QP will not make it readable
- # iso-8859-8 is Hebrew, QP will not make it readable
- 'iso-8859-9': (QP, QP, None),
- 'iso-8859-10': (QP, QP, None),
- # iso-8859-11 is Thai, QP will not make it readable
- 'iso-8859-13': (QP, QP, None),
- 'iso-8859-14': (QP, QP, None),
- 'iso-8859-15': (QP, QP, None),
- 'windows-1252':(QP, QP, None),
- 'viscii': (QP, QP, None),
- 'us-ascii': (None, None, None),
- 'big5': (BASE64, BASE64, None),
- 'gb2312': (BASE64, BASE64, None),
- 'euc-jp': (BASE64, None, 'iso-2022-jp'),
- 'shift_jis': (BASE64, None, 'iso-2022-jp'),
- 'iso-2022-jp': (BASE64, None, None),
- 'koi8-r': (BASE64, BASE64, None),
- 'utf-8': (SHORTEST, BASE64, 'utf-8'),
- # We're making this one up to represent raw unencoded 8-bit
- '8bit': (None, BASE64, 'utf-8'),
- }
-
-# Aliases for other commonly-used names for character sets. Map
-# them to the real ones used in email.
-ALIASES = {
- 'latin_1': 'iso-8859-1',
- 'latin-1': 'iso-8859-1',
- 'latin_2': 'iso-8859-2',
- 'latin-2': 'iso-8859-2',
- 'latin_3': 'iso-8859-3',
- 'latin-3': 'iso-8859-3',
- 'latin_4': 'iso-8859-4',
- 'latin-4': 'iso-8859-4',
- 'latin_5': 'iso-8859-9',
- 'latin-5': 'iso-8859-9',
- 'latin_6': 'iso-8859-10',
- 'latin-6': 'iso-8859-10',
- 'latin_7': 'iso-8859-13',
- 'latin-7': 'iso-8859-13',
- 'latin_8': 'iso-8859-14',
- 'latin-8': 'iso-8859-14',
- 'latin_9': 'iso-8859-15',
- 'latin-9': 'iso-8859-15',
- 'cp949': 'ks_c_5601-1987',
- 'euc_jp': 'euc-jp',
- 'euc_kr': 'euc-kr',
- 'ascii': 'us-ascii',
- }
-
-
-# Map charsets to their Unicode codec strings.
-CODEC_MAP = {
- 'gb2312': 'eucgb2312_cn',
- 'big5': 'big5_tw',
- # Hack: We don't want *any* conversion for stuff marked us-ascii, as all
- # sorts of garbage might be sent to us in the guise of 7-bit us-ascii.
- # Let that stuff pass through without conversion to/from Unicode.
- 'us-ascii': None,
- }
-
-
-
-# Convenience functions for extending the above mappings
-def add_charset(charset, header_enc=None, body_enc=None, output_charset=None):
- """Add character set properties to the global registry.
-
- charset is the input character set, and must be the canonical name of a
- character set.
-
- Optional header_enc and body_enc is either Charset.QP for
- quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for
- the shortest of qp or base64 encoding, or None for no encoding. SHORTEST
- is only valid for header_enc. It describes how message headers and
- message bodies in the input charset are to be encoded. Default is no
- encoding.
-
- Optional output_charset is the character set that the output should be
- in. Conversions will proceed from input charset, to Unicode, to the
- output charset when the method Charset.convert() is called. The default
- is to output in the same character set as the input.
-
- Both input_charset and output_charset must have Unicode codec entries in
- the module's charset-to-codec mapping; use add_codec(charset, codecname)
- to add codecs the module does not know about. See the codecs module's
- documentation for more information.
- """
- if body_enc == SHORTEST:
- raise ValueError, 'SHORTEST not allowed for body_enc'
- CHARSETS[charset] = (header_enc, body_enc, output_charset)
-
-
-def add_alias(alias, canonical):
- """Add a character set alias.
-
- alias is the alias name, e.g. latin-1
- canonical is the character set's canonical name, e.g. iso-8859-1
- """
- ALIASES[alias] = canonical
-
-
-def add_codec(charset, codecname):
- """Add a codec that map characters in the given charset to/from Unicode.
-
- charset is the canonical name of a character set. codecname is the name
- of a Python codec, as appropriate for the second argument to the unicode()
- built-in, or to the encode() method of a Unicode string.
- """
- CODEC_MAP[charset] = codecname
-
-
-
-class Charset:
- """Map character sets to their email properties.
-
- This class provides information about the requirements imposed on email
- for a specific character set. It also provides convenience routines for
- converting between character sets, given the availability of the
- applicable codecs. Given a character set, it will do its best to provide
- information on how to use that character set in an email in an
- RFC-compliant way.
-
- Certain character sets must be encoded with quoted-printable or base64
- when used in email headers or bodies. Certain character sets must be
- converted outright, and are not allowed in email. Instances of this
- module expose the following information about a character set:
-
- input_charset: The initial character set specified. Common aliases
- are converted to their `official' email names (e.g. latin_1
- is converted to iso-8859-1). Defaults to 7-bit us-ascii.
-
- header_encoding: If the character set must be encoded before it can be
- used in an email header, this attribute will be set to
- Charset.QP (for quoted-printable), Charset.BASE64 (for
- base64 encoding), or Charset.SHORTEST for the shortest of
- QP or BASE64 encoding. Otherwise, it will be None.
-
- body_encoding: Same as header_encoding, but describes the encoding for the
- mail message's body, which indeed may be different than the
- header encoding. Charset.SHORTEST is not allowed for
- body_encoding.
-
- output_charset: Some character sets must be converted before the can be
- used in email headers or bodies. If the input_charset is
- one of them, this attribute will contain the name of the
- charset output will be converted to. Otherwise, it will
- be None.
-
- input_codec: The name of the Python codec used to convert the
- input_charset to Unicode. If no conversion codec is
- necessary, this attribute will be None.
-
- output_codec: The name of the Python codec used to convert Unicode
- to the output_charset. If no conversion codec is necessary,
- this attribute will have the same value as the input_codec.
- """
- def __init__(self, input_charset=DEFAULT_CHARSET):
- # RFC 2046, $4.1.2 says charsets are not case sensitive
- input_charset = input_charset.lower()
- # Set the input charset after filtering through the aliases
- self.input_charset = ALIASES.get(input_charset, input_charset)
- # We can try to guess which encoding and conversion to use by the
- # charset_map dictionary. Try that first, but let the user override
- # it.
- henc, benc, conv = CHARSETS.get(self.input_charset,
- (SHORTEST, BASE64, None))
- if not conv:
- conv = self.input_charset
- # Set the attributes, allowing the arguments to override the default.
- self.header_encoding = henc
- self.body_encoding = benc
- self.output_charset = ALIASES.get(conv, conv)
- # Now set the codecs. If one isn't defined for input_charset,
- # guess and try a Unicode codec with the same name as input_codec.
- self.input_codec = CODEC_MAP.get(self.input_charset,
- self.input_charset)
- self.output_codec = CODEC_MAP.get(self.output_charset,
- self.output_charset)
-
- def __str__(self):
- return self.input_charset.lower()
-
- __repr__ = __str__
-
- def __eq__(self, other):
- return str(self) == str(other).lower()
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def get_body_encoding(self):
- """Return the content-transfer-encoding used for body encoding.
-
- This is either the string `quoted-printable' or `base64' depending on
- the encoding used, or it is a function in which case you should call
- the function with a single argument, the Message object being
- encoded. The function should then set the Content-Transfer-Encoding
- header itself to whatever is appropriate.
-
- Returns "quoted-printable" if self.body_encoding is QP.
- Returns "base64" if self.body_encoding is BASE64.
- Returns "7bit" otherwise.
- """
- assert self.body_encoding <> SHORTEST
- if self.body_encoding == QP:
- return 'quoted-printable'
- elif self.body_encoding == BASE64:
- return 'base64'
- else:
- return encode_7or8bit
-
- def convert(self, s):
- """Convert a string from the input_codec to the output_codec."""
- if self.input_codec <> self.output_codec:
- return unicode(s, self.input_codec).encode(self.output_codec)
- else:
- return s
-
- def to_splittable(self, s):
- """Convert a possibly multibyte string to a safely splittable format.
-
- Uses the input_codec to try and convert the string to Unicode, so it
- can be safely split on character boundaries (even for multibyte
- characters).
-
- Returns the string as-is if it isn't known how to convert it to
- Unicode with the input_charset.
-
- Characters that could not be converted to Unicode will be replaced
- with the Unicode replacement character U+FFFD.
- """
- if _isunicode(s) or self.input_codec is None:
- return s
- try:
- return unicode(s, self.input_codec, 'replace')
- except LookupError:
- # Input codec not installed on system, so return the original
- # string unchanged.
- return s
-
- def from_splittable(self, ustr, to_output=True):
- """Convert a splittable string back into an encoded string.
-
- Uses the proper codec to try and convert the string from Unicode back
- into an encoded format. Return the string as-is if it is not Unicode,
- or if it could not be converted from Unicode.
-
- Characters that could not be converted from Unicode will be replaced
- with an appropriate character (usually '?').
-
- If to_output is True (the default), uses output_codec to convert to an
- encoded format. If to_output is False, uses input_codec.
- """
- if to_output:
- codec = self.output_codec
- else:
- codec = self.input_codec
- if not _isunicode(ustr) or codec is None:
- return ustr
- try:
- return ustr.encode(codec, 'replace')
- except LookupError:
- # Output codec not installed
- return ustr
-
- def get_output_charset(self):
- """Return the output character set.
-
- This is self.output_charset if that is not None, otherwise it is
- self.input_charset.
- """
- return self.output_charset or self.input_charset
-
- def encoded_header_len(self, s):
- """Return the length of the encoded header string."""
- cset = self.get_output_charset()
- # The len(s) of a 7bit encoding is len(s)
- if self.header_encoding == BASE64:
- return email.base64MIME.base64_len(s) + len(cset) + MISC_LEN
- elif self.header_encoding == QP:
- return email.quopriMIME.header_quopri_len(s) + len(cset) + MISC_LEN
- elif self.header_encoding == SHORTEST:
- lenb64 = email.base64MIME.base64_len(s)
- lenqp = email.quopriMIME.header_quopri_len(s)
- return min(lenb64, lenqp) + len(cset) + MISC_LEN
- else:
- return len(s)
-
- def header_encode(self, s, convert=False):
- """Header-encode a string, optionally converting it to output_charset.
-
- If convert is True, the string will be converted from the input
- charset to the output charset automatically. This is not useful for
- multibyte character sets, which have line length issues (multibyte
- characters must be split on a character, not a byte boundary); use the
- high-level Header class to deal with these issues. convert defaults
- to False.
-
- The type of encoding (base64 or quoted-printable) will be based on
- self.header_encoding.
- """
- cset = self.get_output_charset()
- if convert:
- s = self.convert(s)
- # 7bit/8bit encodings return the string unchanged (modulo conversions)
- if self.header_encoding == BASE64:
- return email.base64MIME.header_encode(s, cset)
- elif self.header_encoding == QP:
- return email.quopriMIME.header_encode(s, cset, maxlinelen=None)
- elif self.header_encoding == SHORTEST:
- lenb64 = email.base64MIME.base64_len(s)
- lenqp = email.quopriMIME.header_quopri_len(s)
- if lenb64 < lenqp:
- return email.base64MIME.header_encode(s, cset)
- else:
- return email.quopriMIME.header_encode(s, cset, maxlinelen=None)
- else:
- return s
-
- def body_encode(self, s, convert=True):
- """Body-encode a string and convert it to output_charset.
-
- If convert is True (the default), the string will be converted from
- the input charset to output charset automatically. Unlike
- header_encode(), there are no issues with byte boundaries and
- multibyte charsets in email bodies, so this is usually pretty safe.
-
- The type of encoding (base64 or quoted-printable) will be based on
- self.body_encoding.
- """
- if convert:
- s = self.convert(s)
- # 7bit/8bit encodings return the string unchanged (module conversions)
- if self.body_encoding is BASE64:
- return email.base64MIME.body_encode(s)
- elif self.body_encoding is QP:
- return email.quopriMIME.body_encode(s)
- else:
- return s
Deleted: trunk/tmda/TMDA/pythonlib/email/Encoders.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Encoders.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Encoders.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,94 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Module containing encoding functions for Image.Image and Text.Text.
-"""
-
-import base64
-
-
-
-# Helpers
-try:
- from quopri import encodestring as _encodestring
-
- def _qencode(s):
- enc = _encodestring(s, quotetabs=1)
- # Must encode spaces, which quopri.encodestring() doesn't do
- return enc.replace(' ', '=20')
-except ImportError:
- # Python 2.1 doesn't have quopri.encodestring()
- from cStringIO import StringIO
- import quopri as _quopri
-
- def _qencode(s):
- if not s:
- return s
- hasnewline = (s[-1] == '\n')
- infp = StringIO(s)
- outfp = StringIO()
- _quopri.encode(infp, outfp, quotetabs=1)
- # Python 2.x's encode() doesn't encode spaces even when quotetabs==1
- value = outfp.getvalue().replace(' ', '=20')
- if not hasnewline and value[-1] == '\n':
- return value[:-1]
- return value
-
-
-def _bencode(s):
- # We can't quite use base64.encodestring() since it tacks on a "courtesy
- # newline". Blech!
- if not s:
- return s
- hasnewline = (s[-1] == '\n')
- value = base64.encodestring(s)
- if not hasnewline and value[-1] == '\n':
- return value[:-1]
- return value
-
-
-
-def encode_base64(msg):
- """Encode the message's payload in Base64.
-
- Also, add an appropriate Content-Transfer-Encoding header.
- """
- orig = msg.get_payload()
- encdata = _bencode(orig)
- msg.set_payload(encdata)
- msg['Content-Transfer-Encoding'] = 'base64'
-
-
-
-def encode_quopri(msg):
- """Encode the message's payload in quoted-printable.
-
- Also, add an appropriate Content-Transfer-Encoding header.
- """
- orig = msg.get_payload()
- encdata = _qencode(orig)
- msg.set_payload(encdata)
- msg['Content-Transfer-Encoding'] = 'quoted-printable'
-
-
-
-def encode_7or8bit(msg):
- """Set the Content-Transfer-Encoding header to 7bit or 8bit."""
- orig = msg.get_payload()
- if orig is None:
- # There's no payload. For backwards compatibility we use 7bit
- msg['Content-Transfer-Encoding'] = '7bit'
- return
- # We play a trick to make this go fast. If encoding to ASCII succeeds, we
- # know the data must be 7bit, otherwise treat it as 8bit.
- try:
- orig.encode('ascii')
- except UnicodeError:
- msg['Content-Transfer-Encoding'] = '8bit'
- else:
- msg['Content-Transfer-Encoding'] = '7bit'
-
-
-
-def encode_noop(msg):
- """Do nothing."""
Deleted: trunk/tmda/TMDA/pythonlib/email/Errors.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Errors.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Errors.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,26 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""email package exception classes.
-"""
-
-
-
-class MessageError(Exception):
- """Base class for errors in the email package."""
-
-
-class MessageParseError(MessageError):
- """Base class for message parsing errors."""
-
-
-class HeaderParseError(MessageParseError):
- """Error while parsing headers."""
-
-
-class BoundaryError(MessageParseError):
- """Couldn't find terminating boundary."""
-
-
-class MultipartConversionError(MessageError, TypeError):
- """Conversion to a multipart is prohibited."""
Deleted: trunk/tmda/TMDA/pythonlib/email/Generator.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Generator.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Generator.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,398 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Classes to generate plain text from a message object tree.
-"""
-
-import re
-import sys
-import time
-import locale
-import random
-
-from types import ListType, StringType
-from cStringIO import StringIO
-
-from email.Header import Header
-from email.Parser import NLCRE
-
-try:
- from email._compat22 import _isstring
-except SyntaxError:
- from email._compat21 import _isstring
-
-try:
- True, False
-except NameError:
- True = 1
- False = 0
-
-EMPTYSTRING = ''
-SEMISPACE = '; '
-BAR = '|'
-UNDERSCORE = '_'
-NL = '\n'
-NLTAB = '\n\t'
-SEMINLTAB = ';\n\t'
-SPACE8 = ' ' * 8
-
-fcre = re.compile(r'^From ', re.MULTILINE)
-
-def _is8bitstring(s):
- if isinstance(s, StringType):
- try:
- unicode(s, 'us-ascii')
- except UnicodeError:
- return True
- return False
-
-
-
-class Generator:
- """Generates output from a Message object tree.
-
- This basic generator writes the message to the given file object as plain
- text.
- """
- #
- # Public interface
- #
-
- def __init__(self, outfp, mangle_from_=True, maxheaderlen=78):
- """Create the generator for message flattening.
-
- outfp is the output file-like object for writing the message to. It
- must have a write() method.
-
- Optional mangle_from_ is a flag that, when True (the default), escapes
- From_ lines in the body of the message by putting a `>' in front of
- them.
-
- Optional maxheaderlen specifies the longest length for a non-continued
- header. When a header line is longer (in characters, with tabs
- expanded to 8 spaces), than maxheaderlen, the header will be broken on
- semicolons and continued as per RFC 2822. If no semicolon is found,
- then the header is left alone. Set to zero to disable wrapping
- headers. Default is 78, as recommended (but not required by RFC
- 2822.
- """
- self._fp = outfp
- self._mangle_from_ = mangle_from_
- self.__maxheaderlen = maxheaderlen
-
- def write(self, s):
- # Just delegate to the file object
- self._fp.write(s)
-
- def flatten(self, msg, unixfrom=False):
- """Print the message object tree rooted at msg to the output file
- specified when the Generator instance was created.
-
- unixfrom is a flag that forces the printing of a Unix From_ delimiter
- before the first object in the message tree. If the original message
- has no From_ delimiter, a `standard' one is crafted. By default, this
- is False to inhibit the printing of any From_ delimiter.
-
- Note that for subobjects, no From_ line is printed.
- """
- if unixfrom:
- ufrom = msg.get_unixfrom()
- if not ufrom:
- ufrom = 'From nobody ' + time.ctime(time.time())
- print >> self._fp, ufrom
- self._write(msg)
-
- # For backwards compatibility, but this is slower
- __call__ = flatten
-
- def clone(self, fp):
- """Clone this generator with the exact same options."""
- return self.__class__(fp, self._mangle_from_, self.__maxheaderlen)
-
- #
- # Protected interface - undocumented ;/
- #
-
- def _write(self, msg):
- # We can't write the headers yet because of the following scenario:
- # say a multipart message includes the boundary string somewhere in
- # its body. We'd have to calculate the new boundary /before/ we write
- # the headers so that we can write the correct Content-Type:
- # parameter.
- #
- # The way we do this, so as to make the _handle_*() methods simpler,
- # is to cache any subpart writes into a StringIO. The we write the
- # headers and the StringIO contents. That way, subpart handlers can
- # Do The Right Thing, and can still modify the Content-Type: header if
- # necessary.
- oldfp = self._fp
- try:
- self._fp = sfp = StringIO()
- self._dispatch(msg)
- finally:
- self._fp = oldfp
- # Write the headers. First we see if the message object wants to
- # handle that itself. If not, we'll do it generically.
- meth = getattr(msg, '_write_headers', None)
- if meth is None:
- self._write_headers(msg)
- else:
- meth(self)
- self._fp.write(sfp.getvalue())
-
- def _dispatch(self, msg):
- # Get the Content-Type: for the message, then try to dispatch to
- # self._handle_<maintype>_<subtype>(). If there's no handler for the
- # full MIME type, then dispatch to self._handle_<maintype>(). If
- # that's missing too, then dispatch to self._writeBody().
- main = msg.get_content_maintype()
- sub = msg.get_content_subtype()
- specific = UNDERSCORE.join((main, sub)).replace('-', '_')
- meth = getattr(self, '_handle_' + specific, None)
- if meth is None:
- generic = main.replace('-', '_')
- meth = getattr(self, '_handle_' + generic, None)
- if meth is None:
- meth = self._writeBody
- meth(msg)
-
- #
- # Default handlers
- #
-
- def _write_headers(self, msg):
- for h, v in msg.items():
- print >> self._fp, '%s:' % h,
- if self.__maxheaderlen == 0:
- # Explicit no-wrapping
- print >> self._fp, v
- elif isinstance(v, Header):
- # Header instances know what to do
- print >> self._fp, v.encode()
- elif _is8bitstring(v):
- # If we have raw 8bit data in a byte string, we have no idea
- # what the encoding is. There is no safe way to split this
- # string. If it's ascii-subset, then we could do a normal
- # ascii split, but if it's multibyte then we could break the
- # string. There's no way to know so the least harm seems to
- # be to not split the string and risk it being too long.
- print >> self._fp, v
- else:
- # Header's got lots of smarts, so use it.
- print >> self._fp, Header(
- v, maxlinelen=self.__maxheaderlen,
- header_name=h, continuation_ws='\t').encode()
- # A blank line always separates headers from body
- print >> self._fp
-
- #
- # Handlers for writing types and subtypes
- #
-
- def _handle_text(self, msg):
- payload = msg.get_payload()
- if payload is None:
- return
- cset = msg.get_charset()
- if cset is not None:
- payload = cset.body_encode(payload)
- if not _isstring(payload):
- raise TypeError, 'string payload expected: %s' % type(payload)
- if self._mangle_from_:
- payload = fcre.sub('>From ', payload)
- self._fp.write(payload)
-
- # Default body handler
- _writeBody = _handle_text
-
- def _handle_multipart(self, msg):
- # The trick here is to write out each part separately, merge them all
- # together, and then make sure that the boundary we've chosen isn't
- # present in the payload.
- msgtexts = []
- subparts = msg.get_payload()
- if subparts is None:
- # Nothing has ever been attached
- boundary = msg.get_boundary(failobj=_make_boundary())
- print >> self._fp, '--' + boundary
- print >> self._fp, '\n'
- print >> self._fp, '--' + boundary + '--'
- return
- elif _isstring(subparts):
- # e.g. a non-strict parse of a message with no starting boundary.
- self._fp.write(subparts)
- return
- elif not isinstance(subparts, ListType):
- # Scalar payload
- subparts = [subparts]
- for part in subparts:
- s = StringIO()
- g = self.clone(s)
- g.flatten(part, unixfrom=False)
- msgtexts.append(s.getvalue())
- # Now make sure the boundary we've selected doesn't appear in any of
- # the message texts.
- alltext = NL.join(msgtexts)
- # BAW: What about boundaries that are wrapped in double-quotes?
- boundary = msg.get_boundary(failobj=_make_boundary(alltext))
- # If we had to calculate a new boundary because the body text
- # contained that string, set the new boundary. We don't do it
- # unconditionally because, while set_boundary() preserves order, it
- # doesn't preserve newlines/continuations in headers. This is no big
- # deal in practice, but turns out to be inconvenient for the unittest
- # suite.
- if msg.get_boundary() <> boundary:
- msg.set_boundary(boundary)
- # Write out any preamble
- if msg.preamble is not None:
- self._fp.write(msg.preamble)
- # If preamble is the empty string, the length of the split will be
- # 1, but the last element will be the empty string. If it's
- # anything else but does not end in a line separator, the length
- # will be > 1 and not end in an empty string. We need to
- # guarantee a newline after the preamble, but don't add too many.
- plines = NLCRE.split(msg.preamble)
- if plines <> [''] and plines[-1] <> '':
- self._fp.write('\n')
- # First boundary is a bit different; it doesn't have a leading extra
- # newline.
- print >> self._fp, '--' + boundary
- # Join and write the individual parts
- joiner = '\n--' + boundary + '\n'
- self._fp.write(joiner.join(msgtexts))
- print >> self._fp, '\n--' + boundary + '--',
- # Write out any epilogue
- if msg.epilogue is not None:
- if not msg.epilogue.startswith('\n'):
- print >> self._fp
- self._fp.write(msg.epilogue)
-
- def _handle_message_delivery_status(self, msg):
- # We can't just write the headers directly to self's file object
- # because this will leave an extra newline between the last header
- # block and the boundary. Sigh.
- blocks = []
- for part in msg.get_payload():
- s = StringIO()
- g = self.clone(s)
- g.flatten(part, unixfrom=False)
- text = s.getvalue()
- lines = text.split('\n')
- # Strip off the unnecessary trailing empty line
- if lines and lines[-1] == '':
- blocks.append(NL.join(lines[:-1]))
- else:
- blocks.append(text)
- # Now join all the blocks with an empty line. This has the lovely
- # effect of separating each block with an empty line, but not adding
- # an extra one after the last one.
- self._fp.write(NL.join(blocks))
-
- def _handle_message(self, msg):
- s = StringIO()
- g = self.clone(s)
- # The payload of a message/rfc822 part should be a multipart sequence
- # of length 1. The zeroth element of the list should be the Message
- # object for the subpart. Extract that object, stringify it, and
- # write it out.
- g.flatten(msg.get_payload(0), unixfrom=False)
- self._fp.write(s.getvalue())
-
-
-
-class DecodedGenerator(Generator):
- """Generator a text representation of a message.
-
- Like the Generator base class, except that non-text parts are substituted
- with a format string representing the part.
- """
- def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None):
- """Like Generator.__init__() except that an additional optional
- argument is allowed.
-
- Walks through all subparts of a message. If the subpart is of main
- type `text', then it prints the decoded payload of the subpart.
-
- Otherwise, fmt is a format string that is used instead of the message
- payload. fmt is expanded with the following keywords (in
- %(keyword)s format):
-
- type : Full MIME type of the non-text part
- maintype : Main MIME type of the non-text part
- subtype : Sub-MIME type of the non-text part
- filename : Filename of the non-text part
- description: Description associated with the non-text part
- encoding : Content transfer encoding of the non-text part
-
- The default value for fmt is None, meaning
-
- [Non-text (%(type)s) part of message omitted, filename %(filename)s]
- """
- Generator.__init__(self, outfp, mangle_from_, maxheaderlen)
- if fmt is None:
- fmt = ('[Non-text (%(type)s) part of message omitted, '
- 'filename %(filename)s]')
- self._fmt = fmt
-
- def _dispatch(self, msg):
- for part in msg.walk():
- maintype = part.get_main_type('text')
- if maintype == 'text':
- print >> self, part.get_payload(decode=True)
- elif maintype == 'multipart':
- # Just skip this
- pass
- else:
- print >> self, self._fmt % {
- 'type' : part.get_type('[no MIME type]'),
- 'maintype' : part.get_main_type('[no main MIME type]'),
- 'subtype' : part.get_subtype('[no sub-MIME type]'),
- 'filename' : part.get_filename('[no filename]'),
- 'description': part.get('Content-Description',
- '[no description]'),
- 'encoding' : part.get('Content-Transfer-Encoding',
- '[no encoding]'),
- }
-
-
-
-class HeaderParsedGenerator(Generator):
- """Generate text from a Message created by HeaderParser.
-
- Header is generated as usual (by Generator). The payload of a Message
- created by HeaderParser is a raw string. No encoding is necessary. If it
- came in valid, it goes out valid. Conversely, if it came in bogus, it goes
- out bogus.
- """
- def _dispatch(self, msg):
- payload = msg.get_payload()
- if payload is None:
- return
- if not _isstring(payload):
- raise TypeError, 'string payload expected: %s' % type(payload)
- if self._mangle_from_:
- payload = fcre.sub('>From ', payload)
- self._fp.write(payload)
-
-
-
-# Helper
-_width = len(repr(sys.maxint-1))
-_fmt = '%%0%dd' % _width
-
-def _make_boundary(text=None):
- # Craft a random boundary. If text is given, ensure that the chosen
- # boundary doesn't appear in the text.
- token = random.randrange(sys.maxint)
- boundary = ('=' * 15) + (_fmt % token) + '=='
- if text is None:
- return boundary
- b = boundary
- counter = 0
- while True:
- cre = re.compile('^--' + re.escape(b) + '(--)?$', re.MULTILINE)
- if not cre.search(text):
- break
- b = boundary + '.' + str(counter)
- counter += 1
- return b
Deleted: trunk/tmda/TMDA/pythonlib/email/Header.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Header.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Header.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,515 +0,0 @@
-# Copyright (C) 2002 Python Software Foundation
-# Author: [email protected] (Ben Gertzfield), [email protected] (Barry Warsaw)
-
-"""Header encoding and decoding functionality."""
-
-import re
-import binascii
-from types import StringType, UnicodeType
-
-import email.quopriMIME
-import email.base64MIME
-from email.Errors import HeaderParseError
-from email.Charset import Charset
-
-try:
- from email._compat22 import _floordiv
-except SyntaxError:
- # Python 2.1 spells integer division differently
- from email._compat21 import _floordiv
-
-try:
- True, False
-except NameError:
- True = 1
- False = 0
-
-CRLFSPACE = '\r\n '
-CRLF = '\r\n'
-NL = '\n'
-SPACE = ' '
-USPACE = u' '
-SPACE8 = ' ' * 8
-EMPTYSTRING = ''
-UEMPTYSTRING = u''
-
-MAXLINELEN = 76
-
-ENCODE = 1
-DECODE = 2
-
-USASCII = Charset('us-ascii')
-UTF8 = Charset('utf-8')
-
-# Match encoded-word strings in the form =?charset?q?Hello_World?=
-ecre = re.compile(r'''
- =\? # literal =?
- (?P<charset>[^?]*?) # non-greedy up to the next ? is the charset
- \? # literal ?
- (?P<encoding>[qb]) # either a "q" or a "b", case insensitive
- \? # literal ?
- (?P<encoded>.*?) # non-greedy up to the next ?= is the encoded string
- \?= # literal ?=
- ''', re.VERBOSE | re.IGNORECASE)
-
-pcre = re.compile('([,;])')
-
-# Field name regexp, including trailing colon, but not separating whitespace,
-# according to RFC 2822. Character range is from tilde to exclamation mark.
-# For use with .match()
-fcre = re.compile(r'[\041-\176]+:$')
-
-
-
-# Helpers
-_max_append = email.quopriMIME._max_append
-
-
-
-def decode_header(header):
- """Decode a message header value without converting charset.
-
- Returns a list of (decoded_string, charset) pairs containing each of the
- decoded parts of the header. Charset is None for non-encoded parts of the
- header, otherwise a lower-case string containing the name of the character
- set specified in the encoded string.
-
- An email.Errors.HeaderParseError may be raised when certain decoding error
- occurs (e.g. a base64 decoding exception).
- """
- # If no encoding, just return the header
- header = str(header)
- if not ecre.search(header):
- return [(header, None)]
- decoded = []
- dec = ''
- for line in header.splitlines():
- # This line might not have an encoding in it
- if not ecre.search(line):
- decoded.append((line, None))
- continue
- parts = ecre.split(line)
- while parts:
- unenc = parts.pop(0).strip()
- if unenc:
- # Should we continue a long line?
- if decoded and decoded[-1][1] is None:
- decoded[-1] = (decoded[-1][0] + SPACE + unenc, None)
- else:
- decoded.append((unenc, None))
- if parts:
- charset, encoding = [s.lower() for s in parts[0:2]]
- encoded = parts[2]
- dec = None
- if encoding == 'q':
- dec = email.quopriMIME.header_decode(encoded)
- elif encoding == 'b':
- try:
- dec = email.base64MIME.decode(encoded)
- except binascii.Error:
- # Turn this into a higher level exception. BAW: Right
- # now we throw the lower level exception away but
- # when/if we get exception chaining, we'll preserve it.
- raise HeaderParseError
- if dec is None:
- dec = encoded
-
- if decoded and decoded[-1][1] == charset:
- decoded[-1] = (decoded[-1][0] + dec, decoded[-1][1])
- else:
- decoded.append((dec, charset))
- del parts[0:3]
- return decoded
-
-
-
-def make_header(decoded_seq, maxlinelen=None, header_name=None,
- continuation_ws=' '):
- """Create a Header from a sequence of pairs as returned by decode_header()
-
- decode_header() takes a header value string and returns a sequence of
- pairs of the format (decoded_string, charset) where charset is the string
- name of the character set.
-
- This function takes one of those sequence of pairs and returns a Header
- instance. Optional maxlinelen, header_name, and continuation_ws are as in
- the Header constructor.
- """
- h = Header(maxlinelen=maxlinelen, header_name=header_name,
- continuation_ws=continuation_ws)
- for s, charset in decoded_seq:
- # None means us-ascii but we can simply pass it on to h.append()
- if charset is not None and not isinstance(charset, Charset):
- charset = Charset(charset)
- h.append(s, charset)
- return h
-
-
-
-class Header:
- def __init__(self, s=None, charset=None,
- maxlinelen=None, header_name=None,
- continuation_ws=' ', errors='strict'):
- """Create a MIME-compliant header that can contain many character sets.
-
- Optional s is the initial header value. If None, the initial header
- value is not set. You can later append to the header with .append()
- method calls. s may be a byte string or a Unicode string, but see the
- .append() documentation for semantics.
-
- Optional charset serves two purposes: it has the same meaning as the
- charset argument to the .append() method. It also sets the default
- character set for all subsequent .append() calls that omit the charset
- argument. If charset is not provided in the constructor, the us-ascii
- charset is used both as s's initial charset and as the default for
- subsequent .append() calls.
-
- The maximum line length can be specified explicit via maxlinelen. For
- splitting the first line to a shorter value (to account for the field
- header which isn't included in s, e.g. `Subject') pass in the name of
- the field in header_name. The default maxlinelen is 76.
-
- continuation_ws must be RFC 2822 compliant folding whitespace (usually
- either a space or a hard tab) which will be prepended to continuation
- lines.
-
- errors is passed through to the .append() call.
- """
- if charset is None:
- charset = USASCII
- if not isinstance(charset, Charset):
- charset = Charset(charset)
- self._charset = charset
- self._continuation_ws = continuation_ws
- cws_expanded_len = len(continuation_ws.replace('\t', SPACE8))
- # BAW: I believe `chunks' and `maxlinelen' should be non-public.
- self._chunks = []
- if s is not None:
- self.append(s, charset, errors)
- if maxlinelen is None:
- maxlinelen = MAXLINELEN
- if header_name is None:
- # We don't know anything about the field header so the first line
- # is the same length as subsequent lines.
- self._firstlinelen = maxlinelen
- else:
- # The first line should be shorter to take into account the field
- # header. Also subtract off 2 extra for the colon and space.
- self._firstlinelen = maxlinelen - len(header_name) - 2
- # Second and subsequent lines should subtract off the length in
- # columns of the continuation whitespace prefix.
- self._maxlinelen = maxlinelen - cws_expanded_len
-
- def __str__(self):
- """A synonym for self.encode()."""
- return self.encode()
-
- def __unicode__(self):
- """Helper for the built-in unicode function."""
- uchunks = []
- lastcs = None
- for s, charset in self._chunks:
- # We must preserve spaces between encoded and non-encoded word
- # boundaries, which means for us we need to add a space when we go
- # from a charset to None/us-ascii, or from None/us-ascii to a
- # charset. Only do this for the second and subsequent chunks.
- nextcs = charset
- if uchunks:
- if lastcs not in (None, 'us-ascii'):
- if nextcs in (None, 'us-ascii'):
- uchunks.append(USPACE)
- nextcs = None
- elif nextcs not in (None, 'us-ascii'):
- uchunks.append(USPACE)
- lastcs = nextcs
- uchunks.append(unicode(s, str(charset)))
- return UEMPTYSTRING.join(uchunks)
-
- # Rich comparison operators for equality only. BAW: does it make sense to
- # have or explicitly disable <, <=, >, >= operators?
- def __eq__(self, other):
- # other may be a Header or a string. Both are fine so coerce
- # ourselves to a string, swap the args and do another comparison.
- return other == self.encode()
-
- def __ne__(self, other):
- return not self == other
-
- def append(self, s, charset=None, errors='strict'):
- """Append a string to the MIME header.
-
- Optional charset, if given, should be a Charset instance or the name
- of a character set (which will be converted to a Charset instance). A
- value of None (the default) means that the charset given in the
- constructor is used.
-
- s may be a byte string or a Unicode string. If it is a byte string
- (i.e. isinstance(s, StringType) is true), then charset is the encoding
- of that byte string, and a UnicodeError will be raised if the string
- cannot be decoded with that charset. If s is a Unicode string, then
- charset is a hint specifying the character set of the characters in
- the string. In this case, when producing an RFC 2822 compliant header
- using RFC 2047 rules, the Unicode string will be encoded using the
- following charsets in order: us-ascii, the charset hint, utf-8. The
- first character set not to provoke a UnicodeError is used.
-
- Optional `errors' is passed as the third argument to any unicode() or
- ustr.encode() call.
- """
- if charset is None:
- charset = self._charset
- elif not isinstance(charset, Charset):
- charset = Charset(charset)
- # If the charset is our faux 8bit charset, leave the string unchanged
- if charset <> '8bit':
- # We need to test that the string can be converted to unicode and
- # back to a byte string, given the input and output codecs of the
- # charset.
- if isinstance(s, StringType):
- # Possibly raise UnicodeError if the byte string can't be
- # converted to a unicode with the input codec of the charset.
- incodec = charset.input_codec or 'us-ascii'
- ustr = unicode(s, incodec, errors)
- # Now make sure that the unicode could be converted back to a
- # byte string with the output codec, which may be different
- # than the iput coded. Still, use the original byte string.
- outcodec = charset.output_codec or 'us-ascii'
- ustr.encode(outcodec, errors)
- elif isinstance(s, UnicodeType):
- # Now we have to be sure the unicode string can be converted
- # to a byte string with a reasonable output codec. We want to
- # use the byte string in the chunk.
- for charset in USASCII, charset, UTF8:
- try:
- outcodec = charset.output_codec or 'us-ascii'
- s = s.encode(outcodec, errors)
- break
- except UnicodeError:
- pass
- else:
- assert False, 'utf-8 conversion failed'
- self._chunks.append((s, charset))
-
- def _split(self, s, charset, maxlinelen, splitchars):
- # Split up a header safely for use with encode_chunks.
- splittable = charset.to_splittable(s)
- encoded = charset.from_splittable(splittable, True)
- elen = charset.encoded_header_len(encoded)
- # If the line's encoded length first, just return it
- if elen <= maxlinelen:
- return [(encoded, charset)]
- # If we have undetermined raw 8bit characters sitting in a byte
- # string, we really don't know what the right thing to do is. We
- # can't really split it because it might be multibyte data which we
- # could break if we split it between pairs. The least harm seems to
- # be to not split the header at all, but that means they could go out
- # longer than maxlinelen.
- if charset == '8bit':
- return [(s, charset)]
- # BAW: I'm not sure what the right test here is. What we're trying to
- # do is be faithful to RFC 2822's recommendation that ($2.2.3):
- #
- # "Note: Though structured field bodies are defined in such a way that
- # folding can take place between many of the lexical tokens (and even
- # within some of the lexical tokens), folding SHOULD be limited to
- # placing the CRLF at higher-level syntactic breaks."
- #
- # For now, I can only imagine doing this when the charset is us-ascii,
- # although it's possible that other charsets may also benefit from the
- # higher-level syntactic breaks.
- elif charset == 'us-ascii':
- return self._split_ascii(s, charset, maxlinelen, splitchars)
- # BAW: should we use encoded?
- elif elen == len(s):
- # We can split on _maxlinelen boundaries because we know that the
- # encoding won't change the size of the string
- splitpnt = maxlinelen
- first = charset.from_splittable(splittable[:splitpnt], False)
- last = charset.from_splittable(splittable[splitpnt:], False)
- else:
- # Binary search for split point
- first, last = _binsplit(splittable, charset, maxlinelen)
- # first is of the proper length so just wrap it in the appropriate
- # chrome. last must be recursively split.
- fsplittable = charset.to_splittable(first)
- fencoded = charset.from_splittable(fsplittable, True)
- chunk = [(fencoded, charset)]
- return chunk + self._split(last, charset, self._maxlinelen, splitchars)
-
- def _split_ascii(self, s, charset, firstlen, splitchars):
- chunks = _split_ascii(s, firstlen, self._maxlinelen,
- self._continuation_ws, splitchars)
- return zip(chunks, [charset]*len(chunks))
-
- def _encode_chunks(self, newchunks, maxlinelen):
- # MIME-encode a header with many different charsets and/or encodings.
- #
- # Given a list of pairs (string, charset), return a MIME-encoded
- # string suitable for use in a header field. Each pair may have
- # different charsets and/or encodings, and the resulting header will
- # accurately reflect each setting.
- #
- # Each encoding can be email.Utils.QP (quoted-printable, for
- # ASCII-like character sets like iso-8859-1), email.Utils.BASE64
- # (Base64, for non-ASCII like character sets like KOI8-R and
- # iso-2022-jp), or None (no encoding).
- #
- # Each pair will be represented on a separate line; the resulting
- # string will be in the format:
- #
- # =?charset1?q?Mar=EDa_Gonz=E1lez_Alonso?=\n
- # =?charset2?b?SvxyZ2VuIEL2aW5n?="
- chunks = []
- for header, charset in newchunks:
- if not header:
- continue
- if charset is None or charset.header_encoding is None:
- s = header
- else:
- s = charset.header_encode(header)
- # Don't add more folding whitespace than necessary
- if chunks and chunks[-1].endswith(' '):
- extra = ''
- else:
- extra = ' '
- _max_append(chunks, s, maxlinelen, extra)
- joiner = NL + self._continuation_ws
- return joiner.join(chunks)
-
- def encode(self, splitchars=';, '):
- """Encode a message header into an RFC-compliant format.
-
- There are many issues involved in converting a given string for use in
- an email header. Only certain character sets are readable in most
- email clients, and as header strings can only contain a subset of
- 7-bit ASCII, care must be taken to properly convert and encode (with
- Base64 or quoted-printable) header strings. In addition, there is a
- 75-character length limit on any given encoded header field, so
- line-wrapping must be performed, even with double-byte character sets.
-
- This method will do its best to convert the string to the correct
- character set used in email, and encode and line wrap it safely with
- the appropriate scheme for that character set.
-
- If the given charset is not known or an error occurs during
- conversion, this function will return the header untouched.
-
- Optional splitchars is a string containing characters to split long
- ASCII lines on, in rough support of RFC 2822's `highest level
- syntactic breaks'. This doesn't affect RFC 2047 encoded lines.
- """
- newchunks = []
- maxlinelen = self._firstlinelen
- lastlen = 0
- for s, charset in self._chunks:
- # The first bit of the next chunk should be just long enough to
- # fill the next line. Don't forget the space separating the
- # encoded words.
- targetlen = maxlinelen - lastlen - 1
- if targetlen < charset.encoded_header_len(''):
- # Stick it on the next line
- targetlen = maxlinelen
- newchunks += self._split(s, charset, targetlen, splitchars)
- lastchunk, lastcharset = newchunks[-1]
- lastlen = lastcharset.encoded_header_len(lastchunk)
- return self._encode_chunks(newchunks, maxlinelen)
-
-
-
-def _split_ascii(s, firstlen, restlen, continuation_ws, splitchars):
- lines = []
- maxlen = firstlen
- for line in s.splitlines():
- # Ignore any leading whitespace (i.e. continuation whitespace) already
- # on the line, since we'll be adding our own.
- line = line.lstrip()
- if len(line) < maxlen:
- lines.append(line)
- maxlen = restlen
- continue
- # Attempt to split the line at the highest-level syntactic break
- # possible. Note that we don't have a lot of smarts about field
- # syntax; we just try to break on semi-colons, then commas, then
- # whitespace.
- for ch in splitchars:
- if line.find(ch) >= 0:
- break
- else:
- # There's nothing useful to split the line on, not even spaces, so
- # just append this line unchanged
- lines.append(line)
- maxlen = restlen
- continue
- # Now split the line on the character plus trailing whitespace
- cre = re.compile(r'%s\s*' % ch)
- if ch in ';,':
- eol = ch
- else:
- eol = ''
- joiner = eol + ' '
- joinlen = len(joiner)
- wslen = len(continuation_ws.replace('\t', SPACE8))
- this = []
- linelen = 0
- for part in cre.split(line):
- curlen = linelen + max(0, len(this)-1) * joinlen
- partlen = len(part)
- onfirstline = not lines
- # We don't want to split after the field name, if we're on the
- # first line and the field name is present in the header string.
- if ch == ' ' and onfirstline and \
- len(this) == 1 and fcre.match(this[0]):
- this.append(part)
- linelen += partlen
- elif curlen + partlen > maxlen:
- if this:
- lines.append(joiner.join(this) + eol)
- # If this part is longer than maxlen and we aren't already
- # splitting on whitespace, try to recursively split this line
- # on whitespace.
- if partlen > maxlen and ch <> ' ':
- subl = _split_ascii(part, maxlen, restlen,
- continuation_ws, ' ')
- lines.extend(subl[:-1])
- this = [subl[-1]]
- else:
- this = [part]
- linelen = wslen + len(this[-1])
- maxlen = restlen
- else:
- this.append(part)
- linelen += partlen
- # Put any left over parts on a line by themselves
- if this:
- lines.append(joiner.join(this))
- return lines
-
-
-
-def _binsplit(splittable, charset, maxlinelen):
- i = 0
- j = len(splittable)
- while i < j:
- # Invariants:
- # 1. splittable[:k] fits for all k <= i (note that we *assume*,
- # at the start, that splittable[:0] fits).
- # 2. splittable[:k] does not fit for any k > j (at the start,
- # this means we shouldn't look at any k > len(splittable)).
- # 3. We don't know about splittable[:k] for k in i+1..j.
- # 4. We want to set i to the largest k that fits, with i <= k <= j.
- #
- m = (i+j+1) >> 1 # ceiling((i+j)/2); i < m <= j
- chunk = charset.from_splittable(splittable[:m], True)
- chunklen = charset.encoded_header_len(chunk)
- if chunklen <= maxlinelen:
- # m is acceptable, so is a new lower bound.
- i = m
- else:
- # m is not acceptable, so final i must be < m.
- j = m - 1
- # i == j. Invariant #1 implies that splittable[:i] fits, and
- # invariant #2 implies that splittable[:i+1] does not fit, so i
- # is what we're looking for.
- first = charset.from_splittable(splittable[:i], False)
- last = charset.from_splittable(splittable[i:], False)
- return first, last
Deleted: trunk/tmda/TMDA/pythonlib/email/Iterators.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Iterators.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Iterators.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,25 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Various types of useful iterators and generators.
-"""
-
-import sys
-
-try:
- from email._compat22 import body_line_iterator, typed_subpart_iterator
-except SyntaxError:
- # Python 2.1 doesn't have generators
- from email._compat21 import body_line_iterator, typed_subpart_iterator
-
-
-
-def _structure(msg, fp=None, level=0):
- """A handy debugging aid"""
- if fp is None:
- fp = sys.stdout
- tab = ' ' * (level * 4)
- print >> fp, tab + msg.get_content_type()
- if msg.is_multipart():
- for subpart in msg.get_payload():
- _structure(subpart, fp, level+1)
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEAudio.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEAudio.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEAudio.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,71 +0,0 @@
-# Author: Anthony Baxter
-
-"""Class representing audio/* type MIME documents.
-"""
-
-import sndhdr
-from cStringIO import StringIO
-
-from email import Errors
-from email import Encoders
-from email.MIMENonMultipart import MIMENonMultipart
-
-
-
-_sndhdr_MIMEmap = {'au' : 'basic',
- 'wav' :'x-wav',
- 'aiff':'x-aiff',
- 'aifc':'x-aiff',
- }
-
-# There are others in sndhdr that don't have MIME types. :(
-# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma??
-def _whatsnd(data):
- """Try to identify a sound file type.
-
- sndhdr.what() has a pretty cruddy interface, unfortunately. This is why
- we re-do it here. It would be easier to reverse engineer the Unix 'file'
- command and use the standard 'magic' file, as shipped with a modern Unix.
- """
- hdr = data[:512]
- fakefile = StringIO(hdr)
- for testfn in sndhdr.tests:
- res = testfn(hdr, fakefile)
- if res is not None:
- return _sndhdr_MIMEmap.get(res[0])
- return None
-
-
-
-class MIMEAudio(MIMENonMultipart):
- """Class for generating audio/* MIME documents."""
-
- def __init__(self, _audiodata, _subtype=None,
- _encoder=Encoders.encode_base64, **_params):
- """Create an audio/* type MIME document.
-
- _audiodata is a string containing the raw audio data. If this data
- can be decoded by the standard Python `sndhdr' module, then the
- subtype will be automatically included in the Content-Type header.
- Otherwise, you can specify the specific audio subtype via the
- _subtype parameter. If _subtype is not given, and no subtype can be
- guessed, a TypeError is raised.
-
- _encoder is a function which will perform the actual encoding for
- transport of the image data. It takes one argument, which is this
- Image instance. It should use get_payload() and set_payload() to
- change the payload to the encoded form. It should also add any
- Content-Transfer-Encoding or other headers to the message as
- necessary. The default encoding is Base64.
-
- Any additional keyword arguments are passed to the base class
- constructor, which turns them into parameters on the Content-Type
- header.
- """
- if _subtype is None:
- _subtype = _whatsnd(_audiodata)
- if _subtype is None:
- raise TypeError, 'Could not find audio MIME subtype'
- MIMENonMultipart.__init__(self, 'audio', _subtype, **_params)
- self.set_payload(_audiodata)
- _encoder(self)
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEBase.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEBase.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEBase.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,24 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Base class for MIME specializations.
-"""
-
-from email import Message
-
-
-
-class MIMEBase(Message.Message):
- """Base class for MIME specializations."""
-
- def __init__(self, _maintype, _subtype, **_params):
- """This constructor adds a Content-Type: and a MIME-Version: header.
-
- The Content-Type: header is taken from the _maintype and _subtype
- arguments. Additional parameters for this header are taken from the
- keyword arguments.
- """
- Message.Message.__init__(self)
- ctype = '%s/%s' % (_maintype, _subtype)
- self.add_header('Content-Type', ctype, **_params)
- self['MIME-Version'] = '1.0'
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEImage.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEImage.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEImage.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,45 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Class representing image/* type MIME documents.
-"""
-
-import imghdr
-
-from email import Errors
-from email import Encoders
-from email.MIMENonMultipart import MIMENonMultipart
-
-
-
-class MIMEImage(MIMENonMultipart):
- """Class for generating image/* type MIME documents."""
-
- def __init__(self, _imagedata, _subtype=None,
- _encoder=Encoders.encode_base64, **_params):
- """Create an image/* type MIME document.
-
- _imagedata is a string containing the raw image data. If this data
- can be decoded by the standard Python `imghdr' module, then the
- subtype will be automatically included in the Content-Type header.
- Otherwise, you can specify the specific image subtype via the _subtype
- parameter.
-
- _encoder is a function which will perform the actual encoding for
- transport of the image data. It takes one argument, which is this
- Image instance. It should use get_payload() and set_payload() to
- change the payload to the encoded form. It should also add any
- Content-Transfer-Encoding or other headers to the message as
- necessary. The default encoding is Base64.
-
- Any additional keyword arguments are passed to the base class
- constructor, which turns them into parameters on the Content-Type
- header.
- """
- if _subtype is None:
- _subtype = imghdr.what(None, _imagedata)
- if _subtype is None:
- raise TypeError, 'Could not guess image MIME subtype'
- MIMENonMultipart.__init__(self, 'image', _subtype, **_params)
- self.set_payload(_imagedata)
- _encoder(self)
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEMessage.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEMessage.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEMessage.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,32 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Class representing message/* MIME documents.
-"""
-
-from email import Message
-from email.MIMENonMultipart import MIMENonMultipart
-
-
-
-class MIMEMessage(MIMENonMultipart):
- """Class representing message/* MIME documents."""
-
- def __init__(self, _msg, _subtype='rfc822'):
- """Create a message/* type MIME document.
-
- _msg is a message object and must be an instance of Message, or a
- derived class of Message, otherwise a TypeError is raised.
-
- Optional _subtype defines the subtype of the contained message. The
- default is "rfc822" (this is defined by the MIME standard, even though
- the term "rfc822" is technically outdated by RFC 2822).
- """
- MIMENonMultipart.__init__(self, 'message', _subtype)
- if not isinstance(_msg, Message.Message):
- raise TypeError, 'Argument is not an instance of Message'
- # It's convenient to use this base class method. We need to do it
- # this way or we'll get an exception
- Message.Message.attach(self, _msg)
- # And be sure our default type is set correctly
- self.set_default_type('message/rfc822')
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEMultipart.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEMultipart.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEMultipart.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,37 +0,0 @@
-# Copyright (C) 2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Base class for MIME multipart/* type messages.
-"""
-
-from email import MIMEBase
-
-
-
-class MIMEMultipart(MIMEBase.MIMEBase):
- """Base class for MIME multipart/* type messages."""
-
- def __init__(self, _subtype='mixed', boundary=None, *_subparts, **_params):
- """Creates a multipart/* type message.
-
- By default, creates a multipart/mixed message, with proper
- Content-Type and MIME-Version headers.
-
- _subtype is the subtype of the multipart content type, defaulting to
- `mixed'.
-
- boundary is the multipart boundary string. By default it is
- calculated as needed.
-
- _subparts is a sequence of initial subparts for the payload. It
- must be possible to convert this sequence to a list. You can always
- attach new subparts to the message by using the attach() method.
-
- Additional parameters for the Content-Type header are taken from the
- keyword arguments (or passed into the _params argument).
- """
- MIMEBase.MIMEBase.__init__(self, 'multipart', _subtype, **_params)
- if _subparts:
- self.attach(*list(_subparts))
- if boundary:
- self.set_boundary(boundary)
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMENonMultipart.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMENonMultipart.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMENonMultipart.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,24 +0,0 @@
-# Copyright (C) 2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Base class for MIME type messages that are not multipart.
-"""
-
-from email import Errors
-from email import MIMEBase
-
-
-
-class MIMENonMultipart(MIMEBase.MIMEBase):
- """Base class for MIME multipart/* type messages."""
-
- __pychecker__ = 'unusednames=payload'
-
- def attach(self, payload):
- # The public API prohibits attaching multiple subparts to MIMEBase
- # derived subtypes since none of them are, by definition, of content
- # type multipart/*
- raise Errors.MultipartConversionError(
- 'Cannot attach additional subparts to non-multipart/*')
-
- del __pychecker__
Deleted: trunk/tmda/TMDA/pythonlib/email/MIMEText.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/MIMEText.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/MIMEText.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,45 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Class representing text/* type MIME documents.
-"""
-
-import warnings
-from email.MIMENonMultipart import MIMENonMultipart
-from email.Encoders import encode_7or8bit
-
-
-
-class MIMEText(MIMENonMultipart):
- """Class for generating text/* type MIME documents."""
-
- def __init__(self, _text, _subtype='plain', _charset='us-ascii',
- _encoder=None):
- """Create a text/* type MIME document.
-
- _text is the string for this message object.
-
- _subtype is the MIME sub content type, defaulting to "plain".
-
- _charset is the character set parameter added to the Content-Type
- header. This defaults to "us-ascii". Note that as a side-effect, the
- Content-Transfer-Encoding header will also be set.
-
- The use of the _encoder is deprecated. The encoding of the payload,
- and the setting of the character set parameter now happens implicitly
- based on the _charset argument. If _encoder is supplied, then a
- DeprecationWarning is used, and the _encoder functionality may
- override any header settings indicated by _charset. This is probably
- not what you want.
- """
- MIMENonMultipart.__init__(self, 'text', _subtype,
- **{'charset': _charset})
- self.set_payload(_text, _charset)
- if _encoder is not None:
- warnings.warn('_encoder argument is obsolete.',
- DeprecationWarning, 2)
- # Because set_payload() with a _charset will set its own
- # Content-Transfer-Encoding header, we need to delete the
- # existing one or will end up with two of them. :(
- del self['content-transfer-encoding']
- _encoder(self)
Deleted: trunk/tmda/TMDA/pythonlib/email/Message.py
===================================================================
--- trunk/tmda/TMDA/pythonlib/email/Message.py 2006-10-08 02:42:29 UTC (rev 2083)
+++ trunk/tmda/TMDA/pythonlib/email/Message.py 2006-10-08 02:50:58 UTC (rev 2084)
@@ -1,837 +0,0 @@
-# Copyright (C) 2001,2002 Python Software Foundation
-# Author: [email protected] (Barry Warsaw)
-
-"""Basic message object for the email package object model.
-"""
-
-import re
-import uu
-import binascii
-import warnings
-from cStringIO import StringIO
-from types import ListType, TupleType, StringType
-
-# Intrapackage imports
-from email import Utils
-from email import Errors
-from email import Charset
-
-SEMISPACE = '; '
-
-try:
- True, False
-except NameError:
- True = 1
- False = 0
-
-# Regular expression used to split header parameters. BAW: this may be too
-# simple. It isn't strictly RFC 2045 (section 5.1) compliant, but it catches
-# most headers found in the wild. We may eventually need a full fledged
-# parser eventually.
-paramre = re.compile(r'\s*;\s*')
-# Regular expression that matches `special' characters in parameters, the
-# existance of which force quoting of the parameter value.
-tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
-
-
-
-# Helper functions
-def _formatparam(param, value=None, quote=True):
- """Convenience function to format and return a key=value pair.
-
- This will quote the value if needed or if quote is true.
- """
- if value is not None and len(value) > 0:
- # TupleType is used for RFC 2231 encoded parameter values where items
- # are (charset, language, value). charset is a string, not a Charset
- # instance.
- if isinstance(value, TupleType):
- # Encode as per RFC 2231
- param += '*'
- value = Utils.encode_rfc2231(value[2], value[0], value[1])
- # BAW: Please check this. I think that if quote is set it should
- # force quoting even if not necessary.
- if quote or tspecials.search(value):
- return '%s="%s"' % (param, Utils.quote(value))
- else:
- return '%s=%s' % (param, value)
- else:
- return param
-
-def _parseparam(s):
- plist = []
- while s[:1] == ';':
- s = s[1:]
- end = s.find(';')
- while end > 0 and s.count('"', 0, end) % 2:
- end = s.find(';', end + 1)
- if end < 0:
- end = len(s)
- f = s[:end]
- if '=' in f:
- i = f.index('=')
- f = f[:i].strip().lower() + '=' + f[i+1:].strip()
- plist.append(f.strip())
- s = s[end:]
- return plist
-
-
-def _unquotevalue(value):
- if isinstance(value, TupleType):
- return value[0], value[1], Utils.unquote(value[2])
- else:
- return Utils.unquote(value)
-
-
-
-class Message:
- """Basic message object.
-
- A message object is defined as something that has a bunch of RFC 2822
- headers and a payload. It may optionally have an envelope header
- (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a
- multipart or a message/rfc822), then the payload is a list of Message
- objects, otherwise it is a string.
-
- Message objects implement part of the `mapping' interface, which assumes
- there is exactly one occurrance of the header per message. Some headers
- do in fact appear multiple times (e.g. Received) and for those headers,
- you must use the explicit API to set or get all the headers. Not all of
- the mapping methods are implemented.
- """
- def __init__(self):
- self._headers = []
- self._unixfrom = None
- self._payload = None
- self._charset = None
- # Defaults for multipart messages
- self.preamble = self.epilogue = None
- # Default content type
- self._default_type = 'text/plain'
-
- def __str__(self):
- """Return the entire formatted message as a string.
- This includes the headers, body, and envelope header.
- """
- return self.as_string(unixfrom=True)
-
- def as_string(self, unixfrom=False):
- """Return the entire formatted message as a string.
- Optional `unixfrom' when True, means include the Unix From_ envelope
- header.
-
- This is a convenience method and may not generate the message exactly
- as you intend. For more flexibility, use the flatten() method of a
- Generator instance.
- """
- from email.Generator import Generator
- fp = StringIO()
- g = Generator(fp)
- g.flatten(self, unixfrom=unixfrom)
- return fp.getvalue()
-
- def is_multipart(self):
- """Return True if the message consists of multiple parts."""
- if isinstance(self._payload, ListType):
- return True
- return False
-
- #
- # Unix From_ line
- #
- def set_unixfrom(self, unixfrom):
- self._unixfrom = unixfrom
-
- def get_unixfrom(self):
- return self._unixfrom
-
- #
- # Payload manipulation.
- #
- def add_payload(self, payload):
- """Add the given payload to the current payload.
-
- If the current payload is empty, then the current payload will be made
- a scalar, set to the given value.
-
- Note: This method is deprecated. Use .attach() instead.
- """
- warnings.warn('add_payload() is deprecated, use attach() instead.',
- DeprecationWarning, 2)
- if self._payload is None:
- self._payload = payload
- elif isinstance(self._payload, ListType):
- self._payload.append(payload)
- elif self.get_main_type() not in (None, 'multipart'):
- raise Errors.MultipartConversionError(
- 'Message main content type must be "multipart" or missing')
- else:
- self._payload = [self._payload, payload]
-
- def attach(self, payload):
- """Add the given payload to the current payload.
-
- The current payload will always be a list of objects after this method
- is called. If you want to set the payload to a scalar object, use
- set_payload() instead.
- """
- if self._payload is None:
- self._payload = [payload]
- else:
- self._payload.append(payload)
-
- 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, ListType):
- 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)
- payload = sfp.getvalue()
- except uu.Error:
- # Some decoding problem
- return payload
- # Everything else, including encodings with 8bit or 7bit are returned
- # unchanged.
- return payload
-
- def set_payload(self, payload, charset=None):
- """Set the payload to the given value.
-
- Optional charset sets the message's default character set. See
- set_charset() for details.
- """
- self._payload = payload
- if charset is not None:
- self.set_charset(charset)
-
- def set_charset(self, charset):
- """Set the charset of the payload to a given character set.
-
- charset can be a Charset instance, a string naming a character set, or
- None. If it is a string it will be converted to a Charset instance.
- If charset is None, the charset parameter will be removed from the
- Content-Type field. Anything else will generate a TypeError.
-
- The message will be assumed to be of type text/* encoded with
- charset.input_charset. It will be converted to charset.output_charset
- and encoded properly, if needed, when generating the plain text
- representation of the message. MIME headers (MIME-Version,
- Content-Type, Content-Transfer-Encoding) will be added as needed.
-
- """
- if charset is None:
- self.del_param('charset')
- self._charset = None
- return
- if isinstance(charset, StringType):
- charset = Charset.Charset(charset)
- if not isinstance(charset, Charset.Charset):
- raise TypeError, charset
- # BAW: should we accept strings that can serve as arguments to the
- # Charset constructor?
- self._charset = charset
- if not self.has_key('MIME-Version'):
- self.add_header('MIME-Version', '1.0')
- if not self.has_key('Content-Type'):
- self.add_header('Content-Type', 'text/plain',
- charset=charset.get_output_charset())
- else:
- self.set_param('charset', charset.get_output_charset())
- if not self.has_key('Content-Transfer-Encoding'):
- cte = charset.get_body_encoding()
- if callable(cte):
- cte(self)
- else:
- self.add_header('Content-Transfer-Encoding', cte)
-
- def get_charset(self):
- """Return the Charset instance associated with the message's payload.
- """
- return self._charset
-
- #
- # MAPPING INTERFACE (partial)
- #
- def __len__(self):
- """Return the total number of headers, including duplicates."""
- return len(self._headers)
-
- def __getitem__(self, name):
- """Get a header value.
-
- Return None if the header is missing instead of raising an exception.
-
- Note that if the header appeared multiple times, exactly which
- occurrance gets returned is undefined. Use getall() to get all
- the values matching a header field name.
- """
- return self.get(name)
-
- def __setitem__(self, name, val):
- """Set the value of a header.
-
- Note: this does not overwrite an existing header with the same field
- name. Use __delitem__() first to delete any existing headers.
- """
- self._headers.append((name, val))
-
- def __delitem__(self, name):
- """Delete all occurrences of a header, if present.
-
- Does not raise an exception if the header is missing.
- """
- name = name.lower()
- newheaders = []
- for k, v in self._headers:
- if k.lower() <> name:
- newheaders.append((k, v))
- self._headers = newheaders
-
- def __contains__(self, name):
- return name.lower() in [k.lower() for k, v in self._headers]
-
- def has_key(self, name):
- """Return true if the message contains the header."""
- missing = []
- return self.get(name, missing) is not missing
-
- def keys(self):
- """Return a list of all the message's header field names.
-
- These will be sorted in the order they appeared in the original
- message, or were added to the message, and may contain duplicates.
- Any fields deleted and re-inserted are always appended to the header
- list.
- """
- return [k for k, v in self._headers]
-
- def values(self):
- """Return a list of all the message's header values.
-
- These will be sorted in the order they appeared in the original
- message, or were added to the message, and may contain duplicates.
- Any fields deleted and re-inserted are always appended to the header
- list.
- """
- return [v for k, v in self._headers]
-
- def items(self):
- """Get all the message's header fields and values.
-
- These will be sorted in the order they appeared in the original
- message, or were added to the message, and may contain duplicates.
- Any fields deleted and re-inserted are always appended to the header
- list.
- """
- return self._headers[:]
-
- def get(self, name, failobj=None):
- """Get a header value.
-
- Like __getitem__() but return failobj instead of None when the field
- is missing.
- """
- name = name.lower()
- for k, v in self._headers:
- if k.lower() == name:
- return v
- return failobj
-
- #
- # Additional useful stuff
- #
-
- def get_all(self, name, failobj=None):
- """Return a list of all the values for the named field.
-
- These will be sorted in the order they appeared in the original
- message, and may contain duplicates. Any fields deleted and
- re-inserted are always appended to the header list.
-
- If no such fields exist, failobj is returned (defaults to None).
- """
- values = []
- name = name.lower()
- for k, v in self._headers:
- if k.lower() == name:
- values.append(v)
- if not values:
- return failobj
- return values
-
- def add_header(self, _name, _value, **_params):
- """Extended header setting.
-
- name is the header field to add. keyword arguments can be used to set
- additional parameters for the header field, with underscores converted
- to dashes. Normally the parameter will be added as key="value" unless
- value is None, in which case only the key will be added.
-
- Example:
-
- msg.add_header('content-disposition', 'attachment', filename='bud.gif')
- """
- parts = []
- for k, v in _params.items():
- if v is None:
- parts.append(k.replace('_', '-'))
- else:
- parts.append(_formatparam(k.replace('_', '-'), v))
- if _value is not None:
- parts.insert(0, _value)
- self._headers.append((_name, SEMISPACE.join(parts)))
-
- def replace_header(self, _name, _value):
- """Replace a header.
-
- Replace the first matching header found in the message, retaining
- header order and case. If no matching header was found, a KeyError is
- raised.
- """
- _name = _name.lower()
- for i, (k, v) in zip(range(len(self._headers)), self._headers):
- if k.lower() == _name:
- self._headers[i] = (k, _value)
- break
- else:
- raise KeyError, _name
-
- #
- # These methods are silently deprecated in favor of get_content_type() and
- # friends (see below). They will be noisily deprecated in email 3.0.
- #
-
- def get_type(self, failobj=None):
- """Returns the message's content type.
-
- The returned string is coerced to lowercase and returned as a single
- string of the form `maintype/subtype'. If there was no Content-Type
- header in the message, failobj is returned (defaults to None).
- """
- missing = []
- value = self.get('content-type', missing)
- if value is missing:
- return failobj
- return paramre.split(value)[0].lower().strip()
-
- def get_main_type(self, failobj=None):
- """Return the message's main content type if present."""
- missing = []
- ctype = self.get_type(missing)
- if ctype is missing:
- return failobj
- if ctype.count('/') <> 1:
- return failobj
- return ctype.split('/')[0]
-
- def get_subtype(self, failobj=None):
- """Return the message's content subtype if present."""
- missing = []
- ctype = self.get_type(missing)
- if ctype is missing:
- return failobj
- if ctype.count('/') <> 1:
- return failobj
- return ctype.split('/')[1]
-
- #
- # Use these three methods instead of the three above.
- #
-
- def get_content_type(self):
- """Return the message's content type.
-
- The returned string is coerced to lower case of the form
- `maintype/subtype'. If there was no Content-Type header in the
- message, the default type as given by get_default_type() will be
- returned. Since according to RFC 2045, messages always have a default
- type this will always return a value.
-
- RFC 2045 defines a message's default type to be text/plain unless it
- appears inside a multipart/digest container, in which case it would be
- message/rfc822.
- """
- missing = []
- value = self.get('content-type', missing)
- if value is missing:
- # This should have no parameters
- return self.get_default_type()
- ctype = paramre.split(value)[0].lower().strip()
- # RFC 2045, section 5.2 says if its invalid, use text/plain
- if ctype.count('/') <> 1:
- return 'text/plain'
- return ctype
-
- def get_content_maintype(self):
- """Return the message's main content type.
-
- This is the `maintype' part of the string returned by
- get_content_type().
- """
- ctype = self.get_content_type()
- return ctype.split('/')[0]
-
- def get_content_subtype(self):
- """Returns the message's sub-content type.
-
- This is the `subtype' part of the string returned by
- get_content_type().
- """
- ctype = self.get_content_type()
- return ctype.split('/')[1]
-
- def get_default_type(self):
- """Return the `default' content type.
-
- Most messages have a default content type of text/plain, except for
- messages that are subparts of multipart/digest containers. Such
- subparts have a default content type of message/rfc822.
- """
- return self._default_type
-
- def set_default_type(self, ctype):
- """Set the `default' content type.
-
- ctype should be either "text/plain" or "message/rfc822", although this
- is not enforced. The default content type is not stored in the
- Content-Type header.
- """
- self._default_type = ctype
-
- def _get_params_preserve(self, failobj, header):
- # Like get_params() but preserves the quoting of values. BAW:
- # should this be part of the public interface?
- missing = []
- value = self.get(header, missing)
- if value is missing:
- return failobj
- params = []
- for p in _parseparam(';' + value):
- try:
- name, val = p.split('=', 1)
- name = name.strip()
- val = val.strip()
- except ValueError:
- # Must have been a bare attribute
- name = p.strip()
- val = ''
- params.append((name, val))
- params = Utils.decode_params(params)
- return params
-
- def get_params(self, failobj=None, header='content-type', unquote=True):
- """Return the message's Content-Type parameters, as a list.
-
- The elements of the returned list are 2-tuples of key/value pairs, as
- split on the `=' sign. The left hand side of the `=' is the key,
- while the right hand side is the value. If there is no `=' sign in
- the parameter the value is the empty string. The value is as
- described in the get_param() method.
-
- Optional failobj is the object to return if there is no Content-Type
- header. Optional header is the header to search instead of
- Content-Type. If unquote is True, the value is unquoted.
- """
- missing = []
- params = self._get_params_preserve(missing, header)
- if params is missing:
- return failobj
- if unquote:
- return [(k, _unquotevalue(v)) for k, v in params]
- else:
- return params
-
- def get_param(self, param, failobj=None, header='content-type',
- unquote=True):
- """Return the parameter value if found in the Content-Type header.
-
- Optional failobj is the object to return if there is no Content-Type
- header, or the Content-Type header has no such parameter. Optional
- header is the header to search instead of Content-Type.
-
- Parameter keys are always compared case insensitively. The return
- value can either be a string, or a 3-tuple if the parameter was RFC
- 2231 encoded. When it's a 3-tuple, the elements of the value are of
- the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and
- LANGUAGE can be None, in which case you should consider VALUE to be
- encoded in the us-ascii charset. You can usually ignore LANGUAGE.
-
- Your application should be prepared to deal with 3-tuple return
- values, and can convert the parameter to a Unicode string like so:
-
- param = msg.get_param('foo')
- if isinstance(param, tuple):
- param = unicode(param[2], param[0] or 'us-ascii')
-
- In any case, the parameter value (either the returned string, or the
- VALUE item in the 3-tuple) is always unquoted, unless unquote is set
- to False.
- """
- if not self.has_key(header):
- return failobj
- for k, v in self._get_params_preserve(failobj, header):
- if k.lower() == param.lower():
- if unquote:
- return _unquotevalue(v)
- else:
- return v
- return failobj
-
- def set_param(self, param, value, header='Content-Type', requote=True,
- charset=None, language=''):
- """Set a parameter in the Content-Type header.
-
- If the parameter already exists in the header, its value will be
- replaced with the new value.
-
- If header is Content-Type and has not yet been defined for this
- message, it will be set to "text/plain" and the new parameter and
- value will be appended as per RFC 2045.
-
- An alternate header can specified in the header argument, and all
- parameters will be quoted as necessary unless requote is False.
-
- If charset is specified, the parameter will be encoded according to RFC
- 2231. Optional language specifies the RFC 2231 language, defaulting
- to the empty string. Both charset and language should be strings.
- """
- if not isinstance(value, TupleType) and charset:
- value = (charset, language, value)
-
- if not self.has_key(header) and header.lower() == 'content-type':
- ctype = 'text/plain'
- else:
- ctype = self.get(header)
- if not self.get_param(param, header=header):
- if not ctype:
- ctype = _formatparam(param, value, requote)
- else:
- ctype = SEMISPACE.join(
- [ctype, _formatparam(param, value, requote)])
- else:
- ctype = ''
- for old_param, old_value in self.get_params(header=header,
- unquote=requote):
- append_param = ''
- if old_param.lower() == param.lower():
- append_param = _formatparam(param, value, requote)
- else:
- append_param = _formatparam(old_param, old_value, requote)
- if not ctype:
- ctype = append_param
- else:
- ctype = SEMISPACE.join([ctype, append_param])
- if ctype <> self.get(header):
- del self[header]
- self[header] = ctype
-
- def del_param(self, param, header='content-type', requote=True):
- """Remove the given parameter completely from the Content-Type header.
-
- The header will be re-written in place without the parameter or its
- value. All values will be quoted as necessary unless requote is
- False. Optional header specifies an alternative to the Content-Type
- header.
- """
- if not self.has_key(header):
- return
- new_ctype = ''
- for p, v in self.get_params(header, unquote=requote):
- if p.lower() <> param.lower():
- if not new_ctype:
- new_ctype = _formatparam(p, v, requote)
- else:
- new_ctype = SEMISPACE.join([new_ctype,
- _formatparam(p, v, requote)])
- if new_ctype <> self.get(header):
- del self[header]
- self[header] = new_ctype
-
- def set_type(self, type, header='Content-Type', requote=True):
- """Set the main type and subtype for the Content-Type header.
-
- type must be a string in the form "maintype/subtype", otherwise a
- ValueError is raised.
-
- This method replaces the Content-Type header, keeping all the
- parameters in place. If requote is False, this leaves the existing
- header's quoting as is. Otherwise, the parameters will be quoted (the
- default).
-
- An alternative header can be specified in the header argument. When
- the Content-Type header is set, we'll always also add a MIME-Version
- header.
- """
- # BAW: should we be strict?
- if not type.count('/') == 1:
- raise ValueError
- # Set the Content-Type, you get a MIME-Version
- if header.lower() == 'content-type':
- del self['mime-version']
- self['MIME-Version'] = '1.0'
- if not self.has_key(header):
- self[header] = type
- return
- params = self.get_params(header, unquote=requote)
- del self[header]
- self[header] = type
- # Skip the first param; it's the old type.
- for p, v in params[1:]:
- self.set_param(p, v, header, requote)
-
- def get_filename(self, failobj=None):
- """Return the filename associated with the payload if present.
-
- The filename is extracted from the Content-Disposition header's
- `filename' parameter, and it is unquoted.
- """
- missing = []
- filename = self.get_param('filename', missing, 'content-disposition')
- if filename is missing:
- return failobj
- if isinstance(filename, TupleType):
- # It's an RFC 2231 encoded parameter
- newvalue = _unquotevalue(filename)
- return unicode(newvalue[2], newvalue[0] or 'us-ascii')
- else:
- newvalue = _unquotevalue(filename.strip())
- return newvalue
-
- def get_boundary(self, failobj=None):
- """Return the boundary associated with the payload if present.
-
- The boundary is extracted from the Content-Type header's `boundary'
- parameter, and it is unquoted.
- """
- missing = []
- boundary = self.get_param('boundary', missing)
- if boundary is missing:
- return failobj
- if isinstance(boundary, TupleType):
- # RFC 2231 encoded, so decode. It better end up as ascii
- charset = boundary[0] or 'us-ascii'
- return unicode(boundary[2], charset).encode('us-ascii')
- return _unquotevalue(boundary.strip())
-
- def set_boundary(self, boundary):
- """Set the boundary parameter in Content-Type to 'boundary'.
-
- This is subtly different than deleting the Content-Type header and
- adding a new one with a new boundary parameter via add_header(). The
- main difference is that using the set_boundary() method preserves the
- order of the Content-Type header in the original message.
-
- HeaderParseError is raised if the message has no Content-Type header.
- """
- missing = []
- params = self._get_params_preserve(missing, 'content-type')
- if params is missing:
- # There was no Content-Type header, and we don't know what type
- # to set it to, so raise an exception.
- raise Errors.HeaderParseError, 'No Content-Type header found'
- newparams = []
- foundp = False
- for pk, pv in params:
- if pk.lower() == 'boundary':
- newparams.append(('boundary', '"%s"' % boundary))
- foundp = True
- else:
- newparams.append((pk, pv))
- if not foundp:
- # The original Content-Type header had no boundary attribute.
- # Tack one on the end. BAW: should we raise an exception
- # instead???
- newparams.append(('boundary', '"%s"' % boundary))
@@ Diff output truncated at 100000 characters. @@
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.