mirror of
https://git.code.sf.net/p/archivemail/code
synced 2025-03-11 23:32:48 +00:00
IMAP: be NAMESPACE-aware; improved probing for guessed mailbox names.
* Automatically add NAMESPACE prefix to the mailbox path if necessary, * Explicitely check for guessed mailbox names with LIST instead of just trying to SELECT them. * Updated documentation about NAMESPACE handling.
This commit is contained in:
parent
37816fd659
commit
78b4923832
4 changed files with 86 additions and 30 deletions
|
@ -16,6 +16,7 @@ Version 0.7.3 - UNRELEASED
|
|||
"Aidant") Closes: #1878940.
|
||||
* New option --all to archive all messages in a mailbox. Closes: #1764846.
|
||||
* Fixed a crash when archiving maildirs with --days=0. (Thanks John Goerzen)
|
||||
* IMAP: automatically add NAMESPACE prefix to a mailbox path if necessary.
|
||||
|
||||
Version 0.7.2 - 9 November 2007
|
||||
|
||||
|
|
|
@ -314,12 +314,11 @@ username or password.
|
|||
Note that quoting only a substring will not work, and be aware that your shell
|
||||
will probably remove unprotected quotes or backslashes.
|
||||
.PP
|
||||
\fBIMAP\fR servers supporting subfolders may use any character as a
|
||||
mailbox path separator, that is, as an equivalent to the slash character on Unix
|
||||
systems.
|
||||
If you are archiving an IMAP subfolder, first \fBarchivemail\fR will try
|
||||
to open a given mailbox name unchanged; if this fails, it will interpret any
|
||||
slashes in the URL as path separators and try again.
|
||||
\fBarchivemail\fR tries to be smart when handling mailbox paths.
|
||||
In particular, it will automatically add an IMAP NAMESPACE
|
||||
prefix to the mailbox path if necessary; and if you are archiving a subfolder,
|
||||
you can use the slash as a path separator instead of the IMAP server's
|
||||
internal representation.
|
||||
.SH "EXAMPLES"
|
||||
.PP
|
||||
.PP
|
||||
|
|
|
@ -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 ###############
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<RefEntry>
|
||||
|
||||
<DocInfo><Date>15 March 2008</Date></DocInfo>
|
||||
<DocInfo><Date>8 April 2008</Date></DocInfo>
|
||||
|
||||
<RefMeta>
|
||||
<RefEntryTitle>archivemail</RefEntryTitle>
|
||||
|
@ -479,12 +479,11 @@ Note that quoting only a substring will not work, and be aware that your shell
|
|||
will probably remove unprotected quotes or backslashes.
|
||||
</Para>
|
||||
<Para>
|
||||
<application/IMAP/ servers supporting subfolders may use any character as a
|
||||
mailbox path separator, that is, as an equivalent to the slash character on Unix
|
||||
systems.
|
||||
If you are archiving an IMAP subfolder, first <command/archivemail/ will try
|
||||
to open a given mailbox name unchanged; if this fails, it will interpret any
|
||||
slashes in the <acronym/URL/ as path separators and try again.
|
||||
<command/archivemail/ tries to be smart when handling mailbox paths.
|
||||
In particular, it will automatically add an <acronym/IMAP/ <literal/NAMESPACE/
|
||||
prefix to the mailbox path if necessary; and if you are archiving a subfolder,
|
||||
you can use the slash as a path separator instead of the <acronym/IMAP/ server's
|
||||
internal representation.
|
||||
</Para>
|
||||
</RefSect3>
|
||||
</RefSect2>
|
||||
|
|
Loading…
Add table
Reference in a new issue