| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | """Generic MIME writer.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import mimetools | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-20 19:54:20 +00:00
										 |  |  | __all__ = ["MimeWriter"] | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  |     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() | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         ...use the subwriter's methods to create the subpart... | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  |     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): | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         self._fp = fp | 
					
						
							|  |  |  |         self._headers = [] | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def addheader(self, key, value, prefix=0): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-02-09 09:39:08 +00:00
										 |  |  |         lines = value.split("\n") | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         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)): | 
					
						
							| 
									
										
										
										
											2001-02-09 09:39:08 +00:00
										 |  |  |             lines[i] = "    " + lines[i].strip() | 
					
						
							|  |  |  |         value = "\n".join(lines) + "\n" | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         line = key + ": " + value | 
					
						
							|  |  |  |         if prefix: | 
					
						
							|  |  |  |             self._headers.insert(0, line) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._headers.append(line) | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def flushheaders(self): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         self._fp.writelines(self._headers) | 
					
						
							|  |  |  |         self._headers = [] | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startbody(self, ctype, plist=[], prefix=1): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startmultipartbody(self, subtype, boundary=None, plist=[], prefix=1): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         self._boundary = boundary or mimetools.choose_boundary() | 
					
						
							|  |  |  |         return self.startbody("multipart/" + subtype, | 
					
						
							|  |  |  |                               [("boundary", self._boundary)] + plist, | 
					
						
							|  |  |  |                               prefix=prefix) | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def nextpart(self): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         self._fp.write("\n--" + self._boundary + "\n") | 
					
						
							|  |  |  |         return self.__class__(self._fp) | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def lastpart(self): | 
					
						
							| 
									
										
										
										
											2002-05-29 16:18:42 +00:00
										 |  |  |         """This is used to designate the last part of a multipart message.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         It should always be used when writing multipart messages. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											1998-03-26 21:13:24 +00:00
										 |  |  |         self._fp.write("\n--" + self._boundary + "--\n") | 
					
						
							| 
									
										
										
										
											1996-08-26 16:33:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											1998-04-23 13:34:57 +00:00
										 |  |  |     import test.test_MimeWriter |