| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | """Generic MIME writer.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Classes: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MimeWriter - the only thing here. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import string | 
					
						
							|  |  |  | import mimetools | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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() | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         ...use the subwriter's methods to create the subpart... | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +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 22:14:20 +00:00
										 |  |  |         self._fp = fp | 
					
						
							|  |  |  |         self._headers = [] | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def addheader(self, key, value, prefix=0): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         lines = string.splitfields(value, "\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] = "    " + string.strip(lines[i]) | 
					
						
							|  |  |  |         value = string.joinfields(lines, "\n") + "\n" | 
					
						
							|  |  |  |         line = key + ": " + value | 
					
						
							|  |  |  |         if prefix: | 
					
						
							|  |  |  |             self._headers.insert(0, line) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._headers.append(line) | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def flushheaders(self): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         self._fp.writelines(self._headers) | 
					
						
							|  |  |  |         self._headers = [] | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startbody(self, ctype, plist=[], prefix=1): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +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 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startmultipartbody(self, subtype, boundary=None, plist=[], prefix=1): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         self._boundary = boundary or mimetools.choose_boundary() | 
					
						
							|  |  |  |         return self.startbody("multipart/" + subtype, | 
					
						
							|  |  |  |                               [("boundary", self._boundary)] + plist, | 
					
						
							|  |  |  |                               prefix=prefix) | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def nextpart(self): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         self._fp.write("\n--" + self._boundary + "\n") | 
					
						
							|  |  |  |         return self.__class__(self._fp) | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def lastpart(self): | 
					
						
							| 
									
										
										
										
											1998-03-26 22:14:20 +00:00
										 |  |  |         self._fp.write("\n--" + self._boundary + "--\n") | 
					
						
							| 
									
										
										
										
											1996-08-26 18:33:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     print "To test the MimeWriter module, run TestMimeWriter.py." |