# HG changeset patch # User Paul Boddie # Date 1391019768 -3600 # Node ID d74c9da90e57c388fc0ad50dcaa15789f01d62ed # Parent 0fd1ed4947637fe728e8dd75a41c03d221470884 Moved cache entry header writing to a separate function. Added tentative IMAP resource retrieval. diff -r 0fd1ed494763 -r d74c9da90e57 MoinRemoteSupport.py --- a/MoinRemoteSupport.py Wed Jan 29 18:22:58 2014 +0100 +++ b/MoinRemoteSupport.py Wed Jan 29 19:22:48 2014 +0100 @@ -8,8 +8,15 @@ from ContentTypeSupport import getContentTypeAndEncoding from MoinMoin.action import cache -from MoinMoin import caching +from MoinMoin import caching, log +from email.parser import Parser +from email.mime.multipart import MIMEMultipart +from urllib import splithost, splitpasswd, splitport, splituser, unquote_plus +from urlparse import urlsplit import urllib2, time +import imaplib + +logging = log.getLogger(__name__) def getCachedResource(request, url, arena, scope, max_cache_age, reader=None): @@ -56,16 +63,20 @@ cache_entry.open(mode="w") try: - # Read from the source and write to the cache. + try: + # Read from the source and write to the cache. - reader(url, cache_entry) + reader(url, cache_entry) + + # In case of an exception, return None. - # In case of an exception, return None. + except IOError: + if cache_entry.exists(): + cache_entry.remove() + return None - except IOError: - if cache_entry.exists(): - cache_entry.remove() - return None + finally: + cache_entry.close() # Open the cache entry and read it. @@ -81,17 +92,105 @@ f = urllib2.urlopen(url) try: - cache_entry.write(url + "\n") - cache_entry.write((f.headers.get("content-type") or "") + "\n") - for key, value in f.headers.items(): - if key.lower() != "content-type": - cache_entry.write("%s: %s\n" % (key, value)) - cache_entry.write("\n") + writeCacheHeaders(url, f.headers, cache_entry) cache_entry.write(f.read()) finally: - cache_entry.close() f.close() +def imapreader(url, cache_entry): + + """ + Retrieve data associated with the given 'url' using the IMAP protocol + specifically, writing it to the 'cache_entry'. + """ + + # NOTE: Should use something like pykolab.imap_utf7.encode here. + + enc = lambda s: s.encode("utf-7") + + # The URL maps to credentials and folder details. + + scheme, netloc, path, query, fragment = urlsplit(url) + credentials, location = splituser(netloc) + username, password = map(unquote_plus, splitpasswd(credentials)) + host, port = splitport(location) + folders = map(unquote_plus, path.split("/")[1:]) + + # Connect and log in to the IMAP server. + + cls = scheme == "imaps" and imaplib.IMAP4_SSL or imaplib.IMAP4 + + if port is None: + i = cls(host) + else: + i = cls(host, int(port)) + + i.login(username, password) + + try: + # Descend to the desired folder. + + for folder in folders: + code, response = i.select(enc(folder), readonly=True) + if code != "OK": + logging.warning("Could not enter folder: %s" % folder) + raise IOError + + # Search for all messages. + # NOTE: This could also be parameterised. + + code, response = i.search(None, "(ALL)") + + if code != "OK": + logging.warning("Could not enter folder: %s" % folder) + raise IOError + + # For each result, obtain the full message, but embed it in a larger + # multipart message. + + message = MIMEMultipart() + + writeCacheHeaders(url, message, cache_entry) + + numbers = response and response[0].split(" ") or [] + + for n in numbers: + code, response = i.fetch(n, "(RFC822.PEEK)") + + if code == "OK" and response: + + # Write the message payload into the cache entry for later + # processing. + + for data in response: + try: + envelope, body = data + message.attach(Parser().parsestr(body)) + except ValueError: + pass + else: + logging.warning("Could not obtain message %d from folder %s" % (n, folder)) + + cache_entry.write(message.as_string()) + + finally: + i.logout() + del i + +def writeCacheHeaders(url, headers, cache_entry): + + """ + For the given 'url', write it and the given 'headers' to the given + 'cache_entry'. + """ + + cache_entry.write(url + "\n") + cache_entry.write((headers.get("content-type") or "") + "\n") + for key, value in headers.items(): + if key.lower() != "content-type": + cache_entry.write("%s: %s\n" % (key, value)) + cache_entry.write("\n") + def getCachedResourceMetadata(f): "Return a metadata dictionary for the given resource file-like object 'f'."