diff --git a/head/offlineimap.py b/head/offlineimap.py index 9257658..ec59e77 100644 --- a/head/offlineimap.py +++ b/head/offlineimap.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from imapsync import imaplib, imaputil, imapserver, repository, folder -import re, getpass, os, os.path +import re, os, os.path, imapsync from ConfigParser import ConfigParser config = ConfigParser() @@ -34,9 +34,10 @@ accounts = accounts.split(",") server = None remoterepos = None localrepos = None +ui = imapsync.ui.TTY.TTYUI() for accountname in accounts: - print "Processing account " + accountname + ui.acct(accountname) accountmetadata = os.path.join(metadatadir, accountname) if not os.path.exists(accountmetadata): os.mkdir(accountmetadata, 0700) @@ -49,7 +50,7 @@ for accountname in accounts: if config.has_option(accountname, "remotepass"): password = config.get(accountname, "remotepass") else: - password = getpass.getpass("Password for %s: " % accountname) + password = ui.getpass(accountname, host, port, user) ssl = config.getboolean(accountname, "ssl") # Connect to the remote server. @@ -62,42 +63,40 @@ for accountname in accounts: # Connect to the local cache. statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata) - - print "Synchronizing folder list..." + ui.syncfolders(remoterepos, localrepos) remoterepos.syncfoldersto(localrepos) - print "Done." + for remotefolder in remoterepos.getfolders(): - print "*** SYNCHRONIZING FOLDER %s" % remotefolder.getname() # Load local folder. localfolder = localrepos.getfolder(remotefolder.getname()) if not localfolder.isuidvalidityok(remotefolder): - print 'UID validity is a problem for this folder; skipping.' + ui.validityproblem(remotefolder) continue - print "Reading local message list...", + ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) + ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() - print len(localfolder.getmessagelist().keys()), "messages." - + ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys())) + # Load remote folder. - print "Reading remote message list...", + ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() - print len(remotefolder.getmessagelist().keys()), "messages." + ui.messagelistloaded(remoterepos, remotefolder, + len(remotefolder.getmessagelist().keys())) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getname()) statusfolder.cachemessagelist() - if statusfolder.isnewfolder(): - print "Local status folder is new; ignoring." - else: - print "Synchronizing local changes." + if not statusfolder.isnewfolder(): + ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder]) # Synchronize remote changes. - print "Synchronizing remote to local..." + ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder) # Make sure the status folder is up-to-date. - print "Updating local status cache..." + ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder) localfolder.syncmessagesto(statusfolder) statusfolder.save() diff --git a/head/offlineimap/__init__.py b/head/offlineimap/__init__.py index e69de29..c7421b3 100644 --- a/head/offlineimap/__init__.py +++ b/head/offlineimap/__init__.py @@ -0,0 +1 @@ +import ui, folder, repository diff --git a/head/offlineimap/folder/Base.py b/head/offlineimap/folder/Base.py index d1255b8..9735002 100644 --- a/head/offlineimap/folder/Base.py +++ b/head/offlineimap/folder/Base.py @@ -119,8 +119,6 @@ class BaseFolder: if applyto == None: applyto = [dest] - print "Pass 1" - # Pass 1 -- Look for messages in self with a negative uid. # These are messages in Maildirs that were not added by us. # Try to add them to the dests, and once that succeeds, get the @@ -130,7 +128,7 @@ class BaseFolder: for uid in self.getmessagelist().keys(): if uid >= 0: continue - print "Uploading new message %d" % uid + __main__.ui.copyingmessage(uid, self, applyto) successobject = None successuid = None message = self.getmessage(uid) @@ -153,8 +151,6 @@ class BaseFolder: # Did not find any server to take this message. Ignore. pass - print "Pass 2" - # Pass 2 -- Look for messages present in self but not in dest. # If any, add them to dest. @@ -162,7 +158,7 @@ class BaseFolder: if uid < 0: # Ignore messages that pass 1 missed. continue if not uid in dest.getmessagelist(): - print "Uploading new message %d" % uid + __main__.ui.copyingmessage(uid, self, applyto) message = self.getmessage(uid) flags = self.getmessageflags(uid) for object in applyto: @@ -173,8 +169,6 @@ class BaseFolder: self.deletemessage(uid) uid = newuid - print "Pass 3" - # Pass 3 -- Look for message present in dest but not in self. # If any, delete them. @@ -182,7 +176,7 @@ class BaseFolder: if uid < 0: continue if not uid in self.getmessagelist(): - print "Deleting message %d" % uid + __main__.ui.deletingmessage(uid, applyto) for object in applyto: object.deletemessage(uid) @@ -190,8 +184,6 @@ class BaseFolder: # (except for potential negative uids that couldn't be placed # anywhere) - print "Pass 4" - # Pass 4 -- Look for any flag identity issues -- set dest messages # to have the same flags that we have here. @@ -203,13 +195,13 @@ class BaseFolder: addflags = [x for x in selfflags if x not in destflags] if len(addflags): - print "Adding flags to %d" % uid, addflags + __main__.ui.addingflags(uid, addflags, applyto) for object in applyto: object.addmessageflags(uid, addflags) delflags = [x for x in destflags if x not in selfflags] if len(delflags): - print "Deleting flags from %d" % uid, delflags + __main__.ui.deletingflags(uid, delflags, applyto) for object in applyto: object.deletemessageflags(uid, delflags) diff --git a/head/offlineimap/ui/TTY.py b/head/offlineimap/ui/TTY.py new file mode 100644 index 0000000..23bf27f --- /dev/null +++ b/head/offlineimap/ui/TTY.py @@ -0,0 +1,11 @@ +import UIBase +from getpass import getpass + +class TTYUI(UIBase.UIBase): + def _msg(s, msg): + print msg + + def getpass(s, accountname, host, port, user): + return getpass("%s: Password required for %s on %s" % + (accountname, user, host)) + diff --git a/head/offlineimap/ui/UIBase.py b/head/offlineimap/ui/UIBase.py new file mode 100644 index 0000000..3b5f2ec --- /dev/null +++ b/head/offlineimap/ui/UIBase.py @@ -0,0 +1,99 @@ +# UI base class +# Copyright (C) 2002 John Goerzen +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from imapsync import repository + +class UIBase: + ################################################## UTILS + def _msg(s, msg): + """Generic tool called when no other works.""" + raise NotImplementedException + + def warn(s, msg): + s._msg("WARNING: " + msg) + + def getnicename(s, object): + return str(object.__class__).split('.')[-1] + + ################################################## INPUT + + def getpass(s, accountname, host, port, user): + raise NotImplementedException + + ################################################## MESSAGES + + def init_banner(s): + "Display the copyright banner." + s._msg("""imapsync + Copyright (C) 2002 John Goerzen. All rights reserved. + This software comes with NO WARRANTY: see the file COPYING for details.""") + + def acct(s, accountname): + s._msg("Processing account %s" % accountname) + + def syncfolders(s, srcrepos, destrepos): + s._msg("Copying folder structure from %s to %s" % \ + (s.getnicename(srcrepos), s.getnicename(destrepos))) + + ############################## Folder syncing + def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder): + """Called when a folder sync operation is started.""" + s._msg("Syncing %s[%s] -> %s[%s]" % (srcrepos, srcfolder, + destrepos, destfolder)) + + def validityproblem(s, folder): + s.warn("UID validity problem for folder %s; skipping it" % \ + folder.getname()) + + def loadmessagelist(s, repos, folder): + s._msg("Loading message list for %s[%s]" % (s.getnicename(repos), + s.getnicename(folder))) + + def messagelistloaded(s, repos, folder, count): + s._msg("Message list for %s[%s] loaded: %d messages" % \ + (s.getnicename(repos), s.getnicename(folder), count)) + + ############################## Message syncing + + def syncingmessages(s, sr, sf, dr, df): + s._msg("Syncing messages %s[%s] -> %s[%s]" % (s.getnicename(sr), + s.getnicename(sf), + s.getnicename(dr), + s.getnicename(df))) + + def copyingmessage(s, uid, src, destlist): + ds = ["%s[%s]" % (s.getnicename(x), x.getname()) for x in destlist].join(', ') + s._msg("Copy message %d %s[%s] -> %s" % (uid, s.getnicename(src), + src.getname(), ds)) + + def deletingmessage(s, uid, destlist): + ds = ["%s[%s]" % (s.getnicename(x), x.getname()) for x in destlist].join(', ') + s._msg("Deleting message %d in %s" % (uid, ds)) + + def addingflags(s, uid, flags, destlist): + ds = ["%s[%s]" % (s.getnicename(x), x.getname()) for x in destlist].join(', ') + s._msg("Adding flags %s to message %d on %s" % \ + (flags.join(", "), uid, ds)) + + def deletingflags(s, uid, flags, destlist): + ds = ["%s[%s]" % (s.getnicename(x), x.getname()) for x in destlist].join(', ') + s._msg("Deleting flags %s to message %d on %s" % \ + (flags.join(", "), uid, ds)) + + + diff --git a/head/offlineimap/ui/__init__.py b/head/offlineimap/ui/__init__.py new file mode 100644 index 0000000..d9a9eeb --- /dev/null +++ b/head/offlineimap/ui/__init__.py @@ -0,0 +1 @@ +import TTY