aboutsummaryrefslogtreecommitdiffstats
path: root/archivemail.py
diff options
context:
space:
mode:
Diffstat (limited to 'archivemail.py')
-rwxr-xr-xarchivemail.py91
1 files changed, 74 insertions, 17 deletions
diff --git a/archivemail.py b/archivemail.py
index 45a0cdc..afd532a 100755
--- a/archivemail.py
+++ b/archivemail.py
@@ -1371,6 +1371,7 @@ def _archive_imap(mailbox_name, final_archive_name):
user_error("imap server %s has login disabled (hint: "
"try ssl/imaps)" % imap_server)
+ imap_folder = imap_find_mailbox(imap_srv, imap_folder)
roflag = options.dry_run or options.copy_old_mail
# Work around python bug #1277098 (still pending in python << 2.5)
if not roflag:
@@ -1379,25 +1380,10 @@ def _archive_imap(mailbox_name, final_archive_name):
vprint("examining imap folder '%s' read-only" % imap_folder)
else:
vprint("selecting imap folder '%s'" % imap_folder)
- # First try the given folder name, if this doesn't work, try to fix it.
result, response = imap_srv.select(imap_folder, roflag)
if result != 'OK':
- errmsg = "cannot select imap folder; server says '%s'" % response[0]
- if not os.path.sep in imap_folder:
- unexpected_error(errmsg)
- vprint("Selecting '%s' failed; server says: '%s'.\nTrying to "
- "fix mailbox path..." % (imap_folder, response[0]))
- delim = imap_getdelim(imap_srv)
- if not delim:
- unexpected_error(errmsg)
- imap_folder = imap_folder.replace(os.path.sep, delim)
- vprint("Selecting '%s'" % imap_folder)
- result, response = imap_srv.select(imap_folder, roflag)
- if result == 'OK':
- vprint("successfully selected imap folder %s" % imap_folder)
- else:
- # Report original mailbox path.
- unexpected_error(errmsg)
+ unexpected_error("selecting '%s' failed; server says: '%s'." \
+ % (imap_folder, response[0]))
# response is e.g. ['1016'] for 1016 messages in folder
total_msg_count = int(response[0])
vprint("folder has %d message(s)" % total_msg_count)
@@ -1549,6 +1535,77 @@ def imap_getdelim(imap_server):
return delim
+def imap_get_namespace(srv):
+ """Return the IMAP namespace prefixes and hierarchy delimiters."""
+ assert('NAMESPACE' in srv.capabilities)
+ result, response = srv.namespace()
+ if result != 'OK':
+ unexpected_error("Cannot retrieve IMAP namespace; server says: '%s'"
+ % response[0])
+ vprint("NAMESPACE response: %s" % repr(response[0]))
+ # Typical response is e.g.
+ # ['(("INBOX." ".")) NIL (("#shared." ".")("shared." "."))'] or
+ # ['(("" ".")) NIL NIL'], see RFC 2342.
+ # Make a reasonable guess parsing this beast.
+ ns = re.findall(r'\("([^"]*)" (?:"(.)"|NIL)', response[0])
+ assert(ns)
+ return ns
+
+
+def imap_find_mailbox(srv, mailbox):
+ """Find the given mailbox on the IMAP server, correcting an invalid
+ mailbox path if possible. Return the found mailbox name."""
+ for curbox in imap_guess_mailboxnames(srv, mailbox):
+ vprint("Looking for mailbox '%s'..." % curbox)
+ result, response = srv.list(pattern=curbox)
+ if result != 'OK':
+ unexpected_error("LIST command failed; " \
+ "server says: '%s'" % response[0])
+ # Say we queried for the mailbox "foo".
+ # Upon success, response is e.g. ['(\\HasChildren) "." "foo"'].
+ # Upon failure, response is [None]. Funky imaplib!
+ if response[0] != None:
+ break
+ else:
+ user_error("Cannot find mailbox '%s' on server." % mailbox)
+ vprint("Found mailbox '%s'" % curbox)
+ # Catch \NoSelect here to avoid misleading errors later.
+ m = re.match(r'\((?P<attrs>[^\)]*)\)', response[0])
+ if '\\noselect' in m.group('attrs').lower().split():
+ user_error("Server indicates that mailbox '%s' is not selectable" \
+ % curbox)
+ return curbox
+
+
+def imap_guess_mailboxnames(srv, mailbox):
+ """Return a list of possible real IMAP mailbox names in descending order
+ of preference, compiled by prepending an IMAP namespace prefix if necessary,
+ and by translating hierarchy delimiters."""
+ if 'NAMESPACE' in srv.capabilities:
+ namespace_response = imap_get_namespace(srv)
+ for nsprefix, hdelim in namespace_response:
+ if mailbox.startswith(nsprefix):
+ mailbox = mailbox[len(nsprefix):]
+ break
+ else:
+ # mailbox doesn't start with a namespace prefix;
+ # choose private namespace, which is the first one.
+ nsprefix, hdelim = namespace_response[0]
+ else:
+ vprint("Server doesn't support NAMESPACE command.")
+ nsprefix = ""
+ hdelim = imap_getdelim(srv)
+ vprint("IMAP namespace prefix: '%s', hierarchy delimiter: '%s'" % \
+ (nsprefix, hdelim))
+ boxnames = [nsprefix + mailbox]
+ if os.path.sep in mailbox:
+ mailbox = mailbox.replace(os.path.sep, hdelim)
+ boxnames.append(mailbox) # could have a valid namespace prefix now
+ if nsprefix:
+ boxnames.append(nsprefix + mailbox)
+ return boxnames
+
+
############### misc functions ###############