|
1 """Generic MIME writer. |
|
2 |
|
3 This module defines the class MimeWriter. The MimeWriter class implements |
|
4 a basic formatter for creating MIME multi-part files. It doesn't seek around |
|
5 the output file nor does it use large amounts of buffer space. You must write |
|
6 the parts out in the order that they should occur in the final file. |
|
7 MimeWriter does buffer the headers you add, allowing you to rearrange their |
|
8 order. |
|
9 |
|
10 """ |
|
11 |
|
12 |
|
13 import mimetools |
|
14 |
|
15 __all__ = ["MimeWriter"] |
|
16 |
|
17 class MimeWriter: |
|
18 |
|
19 """Generic MIME writer. |
|
20 |
|
21 Methods: |
|
22 |
|
23 __init__() |
|
24 addheader() |
|
25 flushheaders() |
|
26 startbody() |
|
27 startmultipartbody() |
|
28 nextpart() |
|
29 lastpart() |
|
30 |
|
31 A MIME writer is much more primitive than a MIME parser. It |
|
32 doesn't seek around on the output file, and it doesn't use large |
|
33 amounts of buffer space, so you have to write the parts in the |
|
34 order they should occur on the output file. It does buffer the |
|
35 headers you add, allowing you to rearrange their order. |
|
36 |
|
37 General usage is: |
|
38 |
|
39 f = <open the output file> |
|
40 w = MimeWriter(f) |
|
41 ...call w.addheader(key, value) 0 or more times... |
|
42 |
|
43 followed by either: |
|
44 |
|
45 f = w.startbody(content_type) |
|
46 ...call f.write(data) for body data... |
|
47 |
|
48 or: |
|
49 |
|
50 w.startmultipartbody(subtype) |
|
51 for each part: |
|
52 subwriter = w.nextpart() |
|
53 ...use the subwriter's methods to create the subpart... |
|
54 w.lastpart() |
|
55 |
|
56 The subwriter is another MimeWriter instance, and should be |
|
57 treated in the same way as the toplevel MimeWriter. This way, |
|
58 writing recursive body parts is easy. |
|
59 |
|
60 Warning: don't forget to call lastpart()! |
|
61 |
|
62 XXX There should be more state so calls made in the wrong order |
|
63 are detected. |
|
64 |
|
65 Some special cases: |
|
66 |
|
67 - startbody() just returns the file passed to the constructor; |
|
68 but don't use this knowledge, as it may be changed. |
|
69 |
|
70 - startmultipartbody() actually returns a file as well; |
|
71 this can be used to write the initial 'if you can read this your |
|
72 mailer is not MIME-aware' message. |
|
73 |
|
74 - If you call flushheaders(), the headers accumulated so far are |
|
75 written out (and forgotten); this is useful if you don't need a |
|
76 body part at all, e.g. for a subpart of type message/rfc822 |
|
77 that's (mis)used to store some header-like information. |
|
78 |
|
79 - Passing a keyword argument 'prefix=<flag>' to addheader(), |
|
80 start*body() affects where the header is inserted; 0 means |
|
81 append at the end, 1 means insert at the start; default is |
|
82 append for addheader(), but insert for start*body(), which use |
|
83 it to determine where the Content-Type header goes. |
|
84 |
|
85 """ |
|
86 |
|
87 def __init__(self, fp): |
|
88 self._fp = fp |
|
89 self._headers = [] |
|
90 |
|
91 def addheader(self, key, value, prefix=0): |
|
92 """Add a header line to the MIME message. |
|
93 |
|
94 The key is the name of the header, where the value obviously provides |
|
95 the value of the header. The optional argument prefix determines |
|
96 where the header is inserted; 0 means append at the end, 1 means |
|
97 insert at the start. The default is to append. |
|
98 |
|
99 """ |
|
100 lines = value.split("\n") |
|
101 while lines and not lines[-1]: del lines[-1] |
|
102 while lines and not lines[0]: del lines[0] |
|
103 for i in range(1, len(lines)): |
|
104 lines[i] = " " + lines[i].strip() |
|
105 value = "\n".join(lines) + "\n" |
|
106 line = key + ": " + value |
|
107 if prefix: |
|
108 self._headers.insert(0, line) |
|
109 else: |
|
110 self._headers.append(line) |
|
111 |
|
112 def flushheaders(self): |
|
113 """Writes out and forgets all headers accumulated so far. |
|
114 |
|
115 This is useful if you don't need a body part at all; for example, |
|
116 for a subpart of type message/rfc822 that's (mis)used to store some |
|
117 header-like information. |
|
118 |
|
119 """ |
|
120 self._fp.writelines(self._headers) |
|
121 self._headers = [] |
|
122 |
|
123 def startbody(self, ctype, plist=[], prefix=1): |
|
124 """Returns a file-like object for writing the body of the message. |
|
125 |
|
126 The content-type is set to the provided ctype, and the optional |
|
127 parameter, plist, provides additional parameters for the |
|
128 content-type declaration. The optional argument prefix determines |
|
129 where the header is inserted; 0 means append at the end, 1 means |
|
130 insert at the start. The default is to insert at the start. |
|
131 |
|
132 """ |
|
133 for name, value in plist: |
|
134 ctype = ctype + ';\n %s=\"%s\"' % (name, value) |
|
135 self.addheader("Content-Type", ctype, prefix=prefix) |
|
136 self.flushheaders() |
|
137 self._fp.write("\n") |
|
138 return self._fp |
|
139 |
|
140 def startmultipartbody(self, subtype, boundary=None, plist=[], prefix=1): |
|
141 """Returns a file-like object for writing the body of the message. |
|
142 |
|
143 Additionally, this method initializes the multi-part code, where the |
|
144 subtype parameter provides the multipart subtype, the boundary |
|
145 parameter may provide a user-defined boundary specification, and the |
|
146 plist parameter provides optional parameters for the subtype. The |
|
147 optional argument, prefix, determines where the header is inserted; |
|
148 0 means append at the end, 1 means insert at the start. The default |
|
149 is to insert at the start. Subparts should be created using the |
|
150 nextpart() method. |
|
151 |
|
152 """ |
|
153 self._boundary = boundary or mimetools.choose_boundary() |
|
154 return self.startbody("multipart/" + subtype, |
|
155 [("boundary", self._boundary)] + plist, |
|
156 prefix=prefix) |
|
157 |
|
158 def nextpart(self): |
|
159 """Returns a new instance of MimeWriter which represents an |
|
160 individual part in a multipart message. |
|
161 |
|
162 This may be used to write the part as well as used for creating |
|
163 recursively complex multipart messages. The message must first be |
|
164 initialized with the startmultipartbody() method before using the |
|
165 nextpart() method. |
|
166 |
|
167 """ |
|
168 self._fp.write("\n--" + self._boundary + "\n") |
|
169 return self.__class__(self._fp) |
|
170 |
|
171 def lastpart(self): |
|
172 """This is used to designate the last part of a multipart message. |
|
173 |
|
174 It should always be used when writing multipart messages. |
|
175 |
|
176 """ |
|
177 self._fp.write("\n--" + self._boundary + "--\n") |
|
178 |
|
179 |
|
180 if __name__ == '__main__': |
|
181 import test.test_MimeWriter |