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