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