mimeviewer.py
上传用户:gyjinxi
上传日期:2007-01-04
资源大小:159k
文件大小:10k
源码类别:

WEB邮件程序

开发平台:

Python

  1. import os, re, urllib
  2. from string import split, join, find, replace, lower, atoi
  3. from urlparse import urljoin
  4. from htmlentitydefs import entitydefs
  5. from htmllib import HTMLParser
  6. from sgmllib import SGMLParser
  7. from formatter import AbstractFormatter, DumbWriter, NullFormatter
  8. try: from cStringIO import StringIO
  9. except: from StringIO import StringIO
  10. from DocumentTemplate import *
  11. from gttag import *
  12. from Message import *
  13. from bobomailrc import *
  14. from fintl import gettext
  15. _ = gettext
  16. def urlquote(s): return urllib.quote(s, "")
  17. def urlunquote(s): return urllib.unquote(s)
  18. class Text2HTMLParser(HTMLParser):
  19. def handle_image(self, src, alt, *args):
  20. if alt == "(image)": alt = "[%s]" % os.path.basename(src)
  21. self.handle_data(alt)
  22. def anchor_end(self):
  23. if self.anchor: self.anchor = None
  24. def end_title(self):
  25. HTMLParser.end_title(self)
  26. self.formatter.add_literal_data(self.title)
  27. self.formatter.add_hor_rule()
  28. self.formatter.add_line_break()
  29. def start_tr(self, attrs): self.unknown_starttag("tr", attrs)
  30. def end_tr(self): self.do_br([])
  31. def quotedtext(text):
  32. def quotedline(line):
  33. return "> %s" % line
  34. return join(map(quotedline, split(text, "n")), "n")
  35. replace_char = {"n": "<br>"}
  36. for key, value in entitydefs.items():
  37.     replace_char[value] = "&%s;" % key
  38. def escape(s, spaces=0):
  39. nbsp = entitydefs["nbsp"]
  40. new, last = "", ""
  41. for char in s:
  42. if spaces and char == " " and last in [" ", nbsp]:
  43. char = nbsp
  44. new = "%s%s" % (new, replace_char.get(char, char))
  45. last = char
  46. return new
  47.   
  48. def path2pid(path):return join(map(str, path), ".")
  49. def pid2path(pid): return map(int, split(pid, "."))
  50. def getMIMEPart(msg, path=[]):
  51. if type(path) == type(""): path = pid2path(path)
  52. for p in path:
  53. if msg.gettype == "message/rfc822":
  54. msg = Message(StringIO(msg.decode()))
  55. msg = msg.parts[p]
  56. return msg
  57. class MIMEViewer:
  58. def __init__(self, msg, sid):
  59. self.msg = msg
  60. if not hasattr(self.msg, "path"):
  61. self.msg.path = []
  62. self.sid = sid
  63. def toHTML(self):
  64. html = HTMLFile(mime_template)
  65. return html(
  66. index=self.msg.unique_num, num=path2pid(self.msg.path), imagedir=image_dir,
  67. sid=self.sid, filename=self.msg.filename, mimetype=self.msg.gettype())
  68. def toText(self):
  69. o = StringIO()
  70. f = AbstractFormatter(DumbWriter(o))
  71. p = Text2HTMLParser(f)
  72. p.feed(str(self))
  73. p.close()
  74. return o.getvalue()
  75. def __repr__(self):
  76. return self.toText() or _("(no body)")
  77. def __str__(self): 
  78. return self.toHTML() or _("(no body)")
  79. class MultiViewer(MIMEViewer):
  80. def toHTML(self):
  81. body = ""
  82. for i in range(len(self.msg.parts)):
  83. p = self.msg.parts[i]
  84. p.unique_num = self.msg.unique_num
  85. p.path = self.msg.path + [i]
  86. body = "%s%s" % (body, getBody(p, self.sid)) + '<br><hr height="3"><br>'
  87. return body
  88. class AlternativeViewer(MIMEViewer):
  89. prefer = ["text/html"]
  90. def toHTML(self):
  91. viewable = None
  92. for i in range(len(self.msg.parts)):
  93. p = self.msg.parts[i]
  94. p.unique_num = self.msg.unique_num
  95. p.path = self.msg.path + [i]
  96. ctype = p.gettype()
  97. if hasMIMEViewer(ctype):
  98. if ctype == "multipart/related":
  99. for x in p.parts:
  100. ct = x.gettype()
  101. if ct in self.prefer:
  102. ctype = ct
  103. break
  104. if not viewable or ctype in self.prefer:
  105. viewable = p
  106. if viewable:
  107. return getBody(viewable, self.sid)
  108. else:
  109. return MultiViewer(self.msg, self.sid)
  110. class RelatedViewer(MIMEViewer):
  111. handle = ["text/html"]
  112. def unquote(self, s):
  113. if len(s) > 2:
  114. if s[0] == "<" and s[-1] == ">":
  115. return s[1:-1]
  116. return s
  117. def toHTML(self):
  118. maindoc = None
  119. inline = {}
  120. for i in range(len(self.msg.parts)):
  121. p = self.msg.parts[i]
  122. p.unique_num = self.msg.unique_num
  123. p.path = self.msg.path + [i]
  124. cid = p.getheader("Content-Id", None)
  125. if cid: inline[self.unquote(cid)] = path2pid(p.path)
  126. ctype = p.gettype()
  127. if hasMIMEViewer(ctype):
  128. if not maindoc or ctype in self.handle:
  129. maindoc = p
  130. if maindoc:
  131. body = getBody(maindoc, self.sid)
  132. for key, value in inline.items():
  133. body = replace(body, "cid:%s" % key, "GetPart?index:int=%s&num=%s&sid=%s" % (
  134. self.msg.unique_num, value, self.sid))
  135. return body
  136. else:
  137. return MultiViewer(self.msg, self.sid)
  138. class MessageViewer(MIMEViewer):
  139. def toHTML(self):
  140. html = HTMLFile(message_template)
  141. if self.msg.gettype() == "message/rfc822":
  142. path = self.msg.path
  143. un = self.msg.unique_num
  144. self.msg = Message(StringIO(self.msg.decode()))
  145. self.msg.unique_num = un
  146. self.msg.path = path
  147. return html(msg=self.msg, body=getBody(self.msg, self.sid))
  148. urlpat = re.compile(r'(w+://[^>)s]+)')
  149. emailpat=re.compile(r'([-+,.w]+@[-+.w]+)')
  150. class TextViewer(MIMEViewer):
  151.     def toHTML(self):
  152. html = HTMLFile(text_template)
  153. lines = split(self.msg.decode(), "n")
  154. for i in xrange(len(lines)):
  155. start, links, l = 0, {}, escape(lines[i])
  156. while 1:
  157. found = emailpat.search(l[start:])
  158. if found:
  159. url = found.group(1)
  160. links[url] = '<a href="Compose?sid=%s&To=%s">%s</a>' % 
  161.  (self.sid, urlquote(url), escape(url))
  162. else:
  163. found = urlpat.search(l[start:])
  164. if found:
  165. url = found.group(1)
  166. links[url] = '<a href="Goto?%s">%s</a>' % (urlquote(url), escape(url))
  167. else: break
  168. start = start + found.end()
  169. for old, new in links.items():
  170. l = replace(l, old, new)
  171. if len(l) > 1 and lines[i][0] in [">", ":", "|"]:
  172. lines[i] = '<i><font color="darkblue">%s</font></i>' % l
  173. else: lines[i] = l
  174. return html(text=join(lines, "<br>"))
  175. #XXX: translate mailto-links into Compose?to=..
  176. class HTMLMailParser(SGMLParser):
  177. def __init__(self, base=""):
  178. SGMLParser.__init__(self, NullFormatter())
  179. self.base = base
  180. self.data = self.title = ""
  181. self.titlestart = self.stopsave = 0
  182. self.linkcolor = self.vlink = self.alink = ""
  183. self.background = self.bgcolor = self.fgcolor = ""
  184. def fixlink(self, link):
  185. if self.base:
  186. link = urljoin(self.base, link)
  187. link = "Goto?%s" % urlquote(link)
  188. return link
  189. def makeattrs(self, attrs):
  190. r = ""
  191. for key, value in attrs:
  192. if key in ["src", "href", "background"]:
  193. if len(value) > 4 and lower(value[:4]) == "cid:":
  194. pass
  195. else: value = self.fixlink(value)
  196. r = '%s %s="%s"' % (r, key, value)
  197. return r
  198. def makedict(self, attrs):
  199. dict = {}
  200. for key, value in attrs:
  201. dict[lower(key)] = value
  202. return dict
  203. def save(self, data):
  204. if not self.stopsave:
  205. self.data = self.data + data
  206. def handle_comment(self, comment): self.save("<!--%s-->" % comment)
  207. def handle_entityref(self, name): self.save("&%s;" % name)
  208. def handle_charref(self, name): self.save("&#%s;" % name)
  209. def handle_data(self, data):self.save(data)
  210. def do_meta(self, attrs): pass
  211. def start_head(self, attrs): pass
  212. def end_head(self): pass
  213. def start_html(self, attrs): pass
  214. def end_html(self): pass
  215. def start_frameset(self, attrs): pass
  216. def end_frameset(self): pass
  217. def do_frame(self, attrs): pass
  218. def start_title(self, attrs): self.titlestart = len(self.data)
  219. def end_title(self): self.title = self.data[self.titlestart:]
  220. def start_a(self, attrs):
  221. html = "<a%s>" % self.makeattrs(attrs)
  222. if self.linkcolor:
  223. html = '%s<font color="%s">' % (html, self.linkcolor)
  224. self.save(html)
  225. def end_a(self):
  226. if self.linkcolor: self.save("</font>")
  227. self.save("</a>")
  228. def start_body(self, attrs):
  229. self.data = "" 
  230. opts = self.makedict(attrs)
  231. self.background = opts.get("background", "")
  232. self.bgcolor = opts.get("bgcolor", "")
  233. self.fgcolor = opts.get("text", "")
  234. self.linkcolor = opts.get("link", "")
  235. self.vlink = opts.get("vlink", "")
  236. self.alink = opts.get("alink", "")
  237. def end_body(self): self.stopsave = 1
  238. def do_base(self, attrs):
  239. opts = self.makedict(attrs)
  240. if opts.has_key("href"):
  241. self.base = opts["href"]
  242. def unknown_starttag(self, tag, attrs):
  243. html = "<%s%s>" % (tag, self.makeattrs(attrs))
  244. self.save(html)
  245. def unknown_endtag(self, tag):
  246. html = "</%s>" % tag
  247. self.save(html)
  248. class HTMLViewer(MIMEViewer):
  249. def toHTML(self):
  250. parser = HTMLMailParser(self.msg.getheader("Content-Base", None))
  251. parser.feed(self.msg.decode())
  252. html = HTMLFile(html_template)
  253. return html(mail=parser)
  254. #XXX: Fix this hack. -> rfc2426
  255. # V-Cards are much more complex
  256. class VCardViewer(MIMEViewer):
  257. def toHTML(self):
  258. class VCard:
  259. def __init__(self, vc={}): self.__dict__ = vc
  260. data = self.msg.decode()
  261. lines = split(data, "n")
  262. entries = {}
  263. for l in lines:
  264. f = find(l, ":")
  265. entries[l[:f]] = l[f+1:]
  266. card = VCard(entries)
  267. card.street, card.city, card.state, card.zip, 
  268.  card.country = split(entries.get("adr", ""), ";")[2:]
  269. html = HTMLFile(vcard_template)
  270. return html(card=card, sid=self.sid)
  271. class ImageViewer(MIMEViewer):
  272. def toHTML(self):
  273. html = HTMLFile(image_template)
  274. return html(
  275. index=self.msg.unique_num, num=path2pid(self.msg.path),
  276. sid=self.sid, filename=self.msg.filename)
  277. mime_viewers = {
  278. "message/rfc822": MessageViewer,
  279. "message/delivery-status": TextViewer,
  280. "multipart/alternative": AlternativeViewer,
  281. "multipart/mixed": MultiViewer,
  282. "multipart/report": MultiViewer,
  283. "multipart/related": RelatedViewer,
  284.     "text/plain": TextViewer,
  285. "text/html": HTMLViewer,
  286. "text/x-vcard": VCardViewer,
  287. "image/jpeg": ImageViewer,
  288. "image/png": ImageViewer,
  289. "image/x-xpixmap": ImageViewer,
  290. "image/gif": ImageViewer,
  291.     }
  292. getMIMEViewer = mime_viewers.get
  293. hasMIMEViewer = mime_viewers.has_key
  294. def getBody(msg, sid):
  295. ctype = msg.gettype()
  296. MIMEViewerClass = getMIMEViewer(ctype, MIMEViewer)
  297. return str(MIMEViewerClass(msg, sid))
  298.  
  299. if __name__ == "__main__":
  300. s = urlquote("Test/123!")
  301. print s, urlunquote(s)
  302. from fetchmail import MailSpool
  303. class Auth: userid = "henning"
  304. ml = MailSpool()
  305. ml.login(Auth())
  306. msg = ml[18]
  307. print MessageViewer(msg, 123)