"""Generic MIME writer. | |
This module defines the class MimeWriter. The MimeWriter class implements | |
a basic formatter for creating MIME multi-part files. It doesn't seek around | |
the output file nor does it use large amounts of buffer space. You must write | |
the parts out in the order that they should occur in the final file. | |
MimeWriter does buffer the headers you add, allowing you to rearrange their | |
order. | |
""" | |
import mimetools | |
__all__ = ["MimeWriter"] | |
import warnings | |
warnings.warn("the MimeWriter module is deprecated; use the email package instead", | |
DeprecationWarning, 2) | |
class MimeWriter: | |
"""Generic MIME writer. | |
Methods: | |
__init__() | |
addheader() | |
flushheaders() | |
startbody() | |
startmultipartbody() | |
nextpart() | |
lastpart() | |
A MIME writer is much more primitive than a MIME parser. It | |
doesn't seek around on the output file, and it doesn't use large | |
amounts of buffer space, so you have to write the parts in the | |
order they should occur on the output file. It does buffer the | |
headers you add, allowing you to rearrange their order. | |
General usage is: | |
f = <open the output file> | |
w = MimeWriter(f) | |
...call w.addheader(key, value) 0 or more times... | |
followed by either: | |
f = w.startbody(content_type) | |
...call f.write(data) for body data... | |
or: | |
w.startmultipartbody(subtype) | |
for each part: | |
subwriter = w.nextpart() | |
...use the subwriter's methods to create the subpart... | |
w.lastpart() | |
The subwriter is another MimeWriter instance, and should be | |
treated in the same way as the toplevel MimeWriter. This way, | |
writing recursive body parts is easy. | |
Warning: don't forget to call lastpart()! | |
XXX There should be more state so calls made in the wrong order | |
are detected. | |
Some special cases: | |
- startbody() just returns the file passed to the constructor; | |
but don't use this knowledge, as it may be changed. | |
- startmultipartbody() actually returns a file as well; | |
this can be used to write the initial 'if you can read this your | |
mailer is not MIME-aware' message. | |
- If you call flushheaders(), the headers accumulated so far are | |
written out (and forgotten); this is useful if you don't need a | |
body part at all, e.g. for a subpart of type message/rfc822 | |
that's (mis)used to store some header-like information. | |
- Passing a keyword argument 'prefix=<flag>' to addheader(), | |
start*body() affects where the header is inserted; 0 means | |
append at the end, 1 means insert at the start; default is | |
append for addheader(), but insert for start*body(), which use | |
it to determine where the Content-Type header goes. | |
""" | |
def __init__(self, fp): | |
self._fp = fp | |
self._headers = [] | |
def addheader(self, key, value, prefix=0): | |
"""Add a header line to the MIME message. | |
The key is the name of the header, where the value obviously provides | |
the value of the header. The optional argument prefix determines | |
where the header is inserted; 0 means append at the end, 1 means | |
insert at the start. The default is to append. | |
""" | |
lines = value.split("\n") | |
while lines and not lines[-1]: del lines[-1] | |
while lines and not lines[0]: del lines[0] | |
for i in range(1, len(lines)): | |
lines[i] = " " + lines[i].strip() | |
value = "\n".join(lines) + "\n" | |
line = key + ": " + value | |
if prefix: | |
self._headers.insert(0, line) | |
else: | |
self._headers.append(line) | |
def flushheaders(self): | |
"""Writes out and forgets all headers accumulated so far. | |
This is useful if you don't need a body part at all; for example, | |
for a subpart of type message/rfc822 that's (mis)used to store some | |
header-like information. | |
""" | |
self._fp.writelines(self._headers) | |
self._headers = [] | |
def startbody(self, ctype, plist=[], prefix=1): | |
"""Returns a file-like object for writing the body of the message. | |
The content-type is set to the provided ctype, and the optional | |
parameter, plist, provides additional parameters for the | |
content-type declaration. The optional argument prefix determines | |
where the header is inserted; 0 means append at the end, 1 means | |
insert at the start. The default is to insert at the start. | |
""" | |
for name, value in plist: | |
ctype = ctype + ';\n %s=\"%s\"' % (name, value) | |
self.addheader("Content-Type", ctype, prefix=prefix) | |
self.flushheaders() | |
self._fp.write("\n") | |
return self._fp | |
def startmultipartbody(self, subtype, boundary=None, plist=[], prefix=1): | |
"""Returns a file-like object for writing the body of the message. | |
Additionally, this method initializes the multi-part code, where the | |
subtype parameter provides the multipart subtype, the boundary | |
parameter may provide a user-defined boundary specification, and the | |
plist parameter provides optional parameters for the subtype. The | |
optional argument, prefix, determines where the header is inserted; | |
0 means append at the end, 1 means insert at the start. The default | |
is to insert at the start. Subparts should be created using the | |
nextpart() method. | |
""" | |
self._boundary = boundary or mimetools.choose_boundary() | |
return self.startbody("multipart/" + subtype, | |
[("boundary", self._boundary)] + plist, | |
prefix=prefix) | |
def nextpart(self): | |
"""Returns a new instance of MimeWriter which represents an | |
individual part in a multipart message. | |
This may be used to write the part as well as used for creating | |
recursively complex multipart messages. The message must first be | |
initialized with the startmultipartbody() method before using the | |
nextpart() method. | |
""" | |
self._fp.write("\n--" + self._boundary + "\n") | |
return self.__class__(self._fp) | |
def lastpart(self): | |
"""This is used to designate the last part of a multipart message. | |
It should always be used when writing multipart messages. | |
""" | |
self._fp.write("\n--" + self._boundary + "--\n") | |
if __name__ == '__main__': | |
import test.test_MimeWriter |