#!/usr/bin/env python
# vim:fileencoding=utf-8
import sys
import imaplib
import poplib
import time
import urllib
import re
class ImapAcct:
def log(self, message):
if self.name:
print "%s [%s]: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), self.name, message)
else:
print "%s: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), message)
sys.stdout.flush()
# usual html escaping for special characters (: and @ MUST be escaped):
# : = %3a / = %2f @ = %40
# + = %2b ( = %28 ) = %29
# ? = %3f = = %3d & = %26
def parseuri(uri):
mo = re.match(r'(imap|imaps)://(.*?):(.*?)@(.*?)/', uri)
res = mo.groups()
res = [ urllib.unquote(i) for i in res ]
return res
parseuri = staticmethod(parseuri)
def getnamespaces(self):
code, reply = self.imap.namespace()
# code, reply = src.imap.fetch(1, '(ALL)')
# courier: '(("INBOX." ".")) NIL (("#shared." ".")("shared." "."))'
# zimbra: '(("" "/")) (("/home/" "/")) NIL'
mo = re.match(r'\(\("(.*?)" "(.*?)"\)\)', reply[0])
self.prefix = mo.group(1)
self.delim = mo.group(2)
self.log("prefix = '%s', delimiter = '%s'" % (self.prefix, self.delim))
def lsubfolders(self):
(code, list) = self.imap.lsub()
self.subfolders = []
for i in list:
if not i: continue # skip empty subscribed lists
mo = re.match('\((.*)\) "(.)" "(.*)"', i)
(attr, delim, name) = mo.groups()
self.delim = delim
self.subfolders.append(name)
self.subfolders.sort()
def ls(self):
tot_msgs = 0
for i in self.folders:
code, reply = self.imap.select(i, True)
subscribed = "UNSUBSCRIBED"
if i in self.subfolders: subscribed = "SUBSCRIBED"
if code == 'OK':
self.log("%10s msgs, %12s Folder %s" % (reply[0], subscribed, i))
tot_msgs += int(reply[0])
else:
self.log("Folder '%s': COULDN'T SELECT!" % i)
self.log("%10d msgs TOTAL" % tot_msgs)
def listfolders(self):
(code, list) = self.imap.list()
self.folders = []
for i in list:
if not i: continue # skip empty subscribed lists
mo = re.match('\((.*)\) "(.)" "(.*)"', i)
(attr, delim, name) = mo.groups()
self.delim = delim
self.folders.append(name)
# self.log("List: '%s'" % name)
self.folders.sort()
def __init__(self, server = None, login = None, password = None, name = None, uri = None):
if (uri):
(login, password, server) = self.parseuri(uri)[1:]
self.server = server
self.login = login
self.password = password
self.name = name
self.prefix = None
self.delim = None
self.log("Logging into %s as user '%s' with password '%s'" % (server, login, password))
self.imap = imaplib.IMAP4(server)
self.imap.login(login, password)
self.imap.select()
# fetch some data we will need later:
self.getnamespaces()
self.listfolders()
self.lsubfolders()
def logout(self):
if self.imap:
self.log("Disconnecting from '%s', user '%s'" % (self.server, self.login))
self.imap.close()
self.imap.logout()
else:
self.log("Disconnecting from '%s', user '%s': not connected" % (self.server, self.login))
def __del__(self):
self.logout()
def fixfoldername(self, name, target):
if (self.prefix, self.delim) == (target.prefix, target.delim):
return name
if name.startswith(self.prefix):
name = name.replace(self.prefix, target.prefix, 1)
name = name.replace(self.delim, target.delim)
# ZIMBRA: contacts folder is special one
# http://www.zimbra.com/forums/installation/10807-writing-contacts-imap-folder.html
if name.lower() == 'contacts': name += '_z'
if name.lower() == 'emailed contacts': name += '_z'
return name
def bench(self, mode):
code, reply = self.imap.select(readonly = True)
self.log("Benchmark started, fetching %s msgs in mode %s..." % (reply[0], mode))
code, reply = self.imap.search(None, 'ALL')
src_uids = reply[0].split()
# stats variables
msgs_fetched = 0;
msgs_bytes = 0;
time_started = time.time()
for s_uid in src_uids:
if (mode == '2'):
# 8 (FLAGS (\Seen $Label1) INTERNALDATE "08-Oct-2008 16:50:28 +0700")
code, reply = self.imap.fetch(s_uid, '(FLAGS INTERNALDATE)')
# fetch message body
code, reply = self.imap.fetch(s_uid, '(RFC822)')
elif (mode == '1'):
code, reply = self.imap.fetch(s_uid, '(FLAGS INTERNALDATE RFC822)')
elif (mode == 'noop'):
code, reply = self.imap.noop()
else:
print "unknown benchmark mode"
sys.exit(1)
msgs_fetched += 1
time_finished = time.time()
self.log("Benchmark completed, mode %s, fetched %d messages in %.2f seconds (%.2f msgs/sec)" %
(mode, msgs_fetched, time_finished - time_started, msgs_fetched/(time_finished - time_started)))
def blindcopy(self, target):
self.log("blind copying folders to server %s, user %s" % (target.name, target.login))
fld_created = 0
fld_subscribed = 0
tot_fetched = 0
tot_created = 0
tot_bytes = 0
time_started = time.time()
# we have it sorted, so we just
for src_folder in self.folders:
trg_folder = self.fixfoldername(src_folder, target)
if not trg_folder in target.folders:
target.log ("Creating '%s'..." % trg_folder)
code, reply = target.imap.create(trg_folder)
if (code == "OK"): fld_created += 1
else:
target.log ("'%s' already exists" % trg_folder)
if not trg_folder in target.subfolders:
target.log ("Subscribing '%s'..." % trg_folder)
code, reply = target.imap.subscribe(trg_folder)
if (code == "OK"): fld_subscribed += 1
else:
target.log ("'%s' already subscribed" % trg_folder)
# next, loop over messages:
code, reply = self.imap.select(src_folder, readonly = True)
self.log("Copying folder %s: %s messages" % (src_folder, reply[0]))
code, reply = target.imap.select(trg_folder)
target.log("To folder %s: %s messages" % (trg_folder, reply[0]))
code, reply = self.imap.search(None, 'ALL')
src_uids = reply[0].split()
# stats variables
msgs_src = len(src_uids);
msgs_fetched = 0;
msgs_created = 0;
msgs_bytes = 0;
for s_uid in src_uids:
# 8 (FLAGS (\Seen $Label1) INTERNALDATE "08-Oct-2008 16:50:28 +0700")
code, reply = self.imap.fetch(s_uid, '(FLAGS INTERNALDATE)')
mo = re.match('^\d+ \(FLAGS \((.*)\) INTERNALDATE (".*")\)$', reply[0])
(flags, date_time) = mo.groups()
# fetch message body
code, reply = self.imap.fetch(s_uid, '(RFC822)')
if (code == 'OK'): msgs_fetched += 1
rfc822_msg = reply[0][1]
# self.log(" Message %s: INTERNALDATE = '%s' FLAGS = '%s' Size = %d" % (
# s_uid, date_time, flags, len(rfc822_msg)
# ))
# target.imap.debug = 4
code, reply = target.imap.append(trg_folder, flags, date_time, rfc822_msg)
if (code == 'OK'):
msgs_created += 1
msgs_bytes += len(rfc822_msg)
self.log("In folder %s: fetched %d messages of %d, created %d in target folder" %
(src_folder, msgs_fetched, msgs_src, msgs_created))
tot_fetched += msgs_fetched
tot_created += msgs_created
tot_bytes += msgs_bytes
# some final stats:
time_finished = time.time()
target.log ("Stats: Copied %d/%d messages (%d bytes) in %d folders in %.2f seconds (%.2f msgs/sec)" %
(tot_created, tot_fetched, tot_bytes, len(self.folders),
time_finished - time_started,
tot_fetched/(time_finished - time_started)
))
def biglog(message):
print "%s: %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), message)
if __name__ == '__main__':
# parse urls
# * print better stats (time, total # of msgs, bytes xferred)
# * login
# * iterate through folders
# * create target folders
# * loop messages
# * copy messages
biglog("Hello, world!")
if sys.stdin.isatty():
print "stdin is a tty, entering interactive mode."
print 'enter "help" to see available commands.'
while 1:
if sys.stdin.isatty(): print "enter command> ",
line = sys.stdin.readline()
if not line: break
# chomp comments
if line.find('#') != -1: line = line[0:line.find('#')]
if line.isspace(): continue
if len(line) == 0: continue
arr = line.split()
cmd, opts = arr[0], arr[1:]
if (cmd == 'cp'):
if len(opts) != 2:
print "bad number of arguments"
continue
src = ImapAcct(uri = opts[0], name = 'SRC')
trg = ImapAcct(uri = opts[1], name = 'TRG')
src.blindcopy(trg)
elif (cmd == 'bench'):
if len(opts) != 2:
print "bad number of arguments"
continue
im = ImapAcct(uri = opts[1], name = 'BENCH')
im.bench(opts[0])
im = None
elif (cmd == 'benchpop3'):
if len(opts) != 1:
print "bad number of arguments"
continue
(schema, login, password, server) = ImapAcct.parseuri(opts[0])
M = poplib.POP3(server)
M.user(login)
M.pass_(password)
numMessages = len(M.list()[1])
msgs_fetched = 0;
msgs_bytes = 0;
time_started = time.time()
biglog("POP3 Benchmark started, fetching %d msgs..." % (numMessages))
for i in range(numMessages):
M.retr(i+1)[1]
msgs_fetched += 1
time_finished = time.time()
biglog("POP3 Benchmark completed, fetched %d messages in %.2f seconds (%.2f msgs/sec)" %
(msgs_fetched, time_finished - time_started, msgs_fetched/(time_finished - time_started)))
M.quit()
elif (cmd == 'ls'):
if len(opts) != 1:
print "bad number of arguments"
continue
im = ImapAcct(uri = opts[0], name = 'IMAP')
im.ls()
im = None
elif (cmd == 'help'):
print """Supported commands:
help - show this text
cp imap://user1:123@srchost/ imap://user2:456@targethost/
ls imap://user1:123@host/
bench [1|2] imap://user1:123@host/
benchpop3 imap://user1:123@host/ (note: it's imap://, not pop3://)
quit, exit - exit a program"""
elif (cmd == 'quit' or cmd == 'exit'):
break
else:
print "Unknown command: '%s'" % cmd
sys.exit(0)