Hello community, here is the log from the commit of package offlineimap for openSUSE:Factory checked in at 2012-02-14 19:04:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/offlineimap (Old) and /work/SRC/openSUSE:Factory/.offlineimap.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "offlineimap", Maintainer is "gre...@suse.com" Changes: -------- --- /work/SRC/openSUSE:Factory/offlineimap/offlineimap.changes 2012-01-19 16:56:12.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.offlineimap.new/offlineimap.changes 2012-02-14 19:04:31.000000000 +0100 @@ -1,0 +2,14 @@ +Sun Feb 5 11:21:16 UTC 2012 - g...@opensuse.org + +- update to version 6.5.2.1 + * fix python2.6 compatibility with the TTYUI backend (crash) + * fix TTYUI regression from 6.5.2 in refresh loop (crash) + * fix crashes related to UIDVALIDITY returning "None" + * make folders containing quotes work rather than crashing + * remove the Gmail "realdelete" option, as it could lead to + potential data loss. + * improve delete msg performance with SQLITE backend + * enforce basic UI when using the --info switch + * beginning of a test suite + +------------------------------------------------------------------- Old: ---- offlineimap-v6.5.2.tar.gz New: ---- offlineimap-v6.5.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ offlineimap.spec ++++++ --- /var/tmp/diff_new_pack.eY4StL/_old 2012-02-14 19:04:35.000000000 +0100 +++ /var/tmp/diff_new_pack.eY4StL/_new 2012-02-14 19:04:35.000000000 +0100 @@ -15,8 +15,9 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # + Name: offlineimap -Version: 6.5.2 +Version: 6.5.2.1 Release: 0 Summary: Powerful IMAP/Maildir Synchronization Tool License: GPL-2.0+ @@ -47,7 +48,7 @@ %prep -%setup -q -n spaetz-offlineimap-091272d +%setup -q -n %{name} sed -i '/^#!\/usr\/bin\/env/d' offlineimap/imaplib2.py %build ++++++ offlineimap-v6.5.2.tar.gz -> offlineimap-v6.5.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/Changelog.rst new/offlineimap/Changelog.rst --- old/spaetz-offlineimap-091272d/Changelog.rst 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/Changelog.rst 2012-02-04 21:11:44.000000000 +0100 @@ -11,6 +11,28 @@ on releases. And because I'm lazy, it will also be used as a draft for the releases announces. +OfflineIMAP v6.5.2.1 (2012-04-04) +===================================== + +* Fix python2.6 compatibility with the TTYUI backend (crash) + +* Fix TTYUI regression from 6.5.2 in refresh loop (crash) + +* Fix crashes related to UIDVALIDITY returning "None" + +* Beginning of a test suite. So far there is only one test. Configure + test/credentials.conf and invoke with "python setup.py test" + +* Make folders containing quotes work rather than crashing + (reported by Mark Eichin) + +* Improve delete msg performance with SQLITE backend + +* Enforce basic UI when using the --info switch + +* Remove the Gmail "realdelete" option, as it could lead to potential + data loss. + OfflineIMAP v6.5.2 (2012-01-17) =============================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/SubmittingPatches.rst new/offlineimap/SubmittingPatches.rst --- old/spaetz-offlineimap-091272d/SubmittingPatches.rst 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/SubmittingPatches.rst 2012-02-04 21:11:44.000000000 +0100 @@ -1,6 +1,7 @@ .. -*- coding: utf-8 -*- .. _mailing list: http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project +.. _commits mailing list: http://lists.offlineimap.org/listinfo.cgi/commits-offlineimap.org ================================================= Checklist (and a short version for the impatient) @@ -320,6 +321,12 @@ tell you if your patch is merged in pu if you rebase on top of master). +* You can follow upstream commits on +`CIA.vc <http://cia.vc/stats/project/offlineimap>`, +`Ohloh <http://www.ohloh.net/p/offlineimap>`, +`GitHub <https://github.com/spaetz/offlineimap/commits/>`, +or on the `commits mailing list`_. + .. * Read the git mailing list, the maintainer regularly posts messages entitled "What's cooking in git.git" and "What's in git.git" giving the status of various proposed changes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/docs/FAQ.rst new/offlineimap/docs/FAQ.rst --- old/spaetz-offlineimap-091272d/docs/FAQ.rst 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/docs/FAQ.rst 2012-02-04 21:11:44.000000000 +0100 @@ -288,20 +288,30 @@ The `sslcacertfile` file must contain an SSL certificate (or a concatenated certificates chain) in PEM format. (See the documentation of -`ssl.wrap_socket`_'s `certfile` parameter for the gory details.) The following -command should generate a file in the proper format:: +`ssl.wrap_socket`_'s `certfile` parameter for the gory details.) You can use either openssl or gnutls to create a certificate file in the required format. +#. via openssl:: openssl s_client -CApath /etc/ssl/certs -connect ${hostname}:imaps -showcerts \ | perl -ne 'print if /BEGIN/../END/; print STDERR if /return/' > $sslcacertfile ^D -Before using the resulting file, ensure that openssl verified the certificate -successfully. +#. via gnutls:: + gnutls-cli --print-cert -p imaps ${host} </dev/null | sed -n \ + | '/^-----BEGIN CERT/,/^-----END CERT/p' > $sslcacertfile The path `/etc/ssl/certs` is not standardized; your system may store SSL certificates elsewhere. (On some systems it may be in `/usr/local/share/certs/`.) +Before using the resulting file, ensure that openssl verified the certificate +successfully. In case of problems, you can test the certificate using a command such as (credits to Daniel Shahaf for this) to verify the certificate:: + + % openssl s_client -CAfile $sslcacertfile -connect ${hostname}:imaps 2>&1 </dev/null + +If the server uses STARTTLS, pass the -starttls option and the 'imap' port. + +Also, you can test using gnutls:: + gnutls-cli --x509cafile certs/mail.mydomain.eu.cert -p 993 mail.mydomain.eu IMAP Server Notes ================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/docs/dev-doc-src/API.rst new/offlineimap/docs/dev-doc-src/API.rst --- old/spaetz-offlineimap-091272d/docs/dev-doc-src/API.rst 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/docs/dev-doc-src/API.rst 2012-02-04 21:11:44.000000000 +0100 @@ -13,6 +13,11 @@ from offlineimap import OfflineImap +The file ``SubmittingPatches.rst`` in the source distribution documents a +number of resources and conventions you may find useful. It will eventually +be merged into the main documentation. +.. TODO: merge SubmittingPatches.rst to the main documentation + :mod:`offlineimap` -- The OfflineImap module ============================================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/__init__.py new/offlineimap/offlineimap/__init__.py --- old/spaetz-offlineimap-091272d/offlineimap/__init__.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/__init__.py 2012-02-04 21:11:44.000000000 +0100 @@ -1,7 +1,7 @@ __all__ = ['OfflineImap'] __productname__ = 'OfflineIMAP' -__version__ = "6.5.2" +__version__ = "6.5.2.1" __copyright__ = "Copyright 2002-2012 John Goerzen & contributors" __author__ = "John Goerzen" __author_email__= "j...@complete.org" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/accounts.py new/offlineimap/offlineimap/accounts.py --- old/spaetz-offlineimap-091272d/offlineimap/accounts.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/accounts.py 2012-02-04 21:11:44.000000000 +0100 @@ -378,9 +378,11 @@ statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) - if localfolder.getuidvalidity() == None: - # This is a new folder, so delete the status cache to be sure - # we don't have a conflict. + if localfolder.get_uidvalidity() == None: + # This is a new folder, so delete the status cache to be + # sure we don't have a conflict. + # TODO: This does not work. We always return a value, need + # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() @@ -398,22 +400,24 @@ localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) - # If either the local or the status folder has messages and there is a UID - # validity problem, warn and abort. If there are no messages, UW IMAPd - # loses UIDVALIDITY. But we don't really need it if both local folders are - # empty. So, in that case, just save it off. + # If either the local or the status folder has messages and + # there is a UID validity problem, warn and abort. If there are + # no messages, UW IMAPd loses UIDVALIDITY. But we don't really + # need it if both local folders are empty. So, in that case, + # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): - if not localfolder.isuidvalidityok(): + if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return - if not remotefolder.isuidvalidityok(): + if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: - localfolder.saveuidvalidity() - remotefolder.saveuidvalidity() + # Both folders empty, just save new UIDVALIDITY + localfolder.save_uidvalidity() + remotefolder.save_uidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/folder/Base.py new/offlineimap/offlineimap/folder/Base.py --- old/spaetz-offlineimap-091272d/offlineimap/folder/Base.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/folder/Base.py 2012-02-04 21:11:44.000000000 +0100 @@ -109,23 +109,32 @@ basename = re.sub('(^|\/)\.$','\\1dot', basename) return basename - def isuidvalidityok(self): - """Does the cached UID match the real UID + def check_uidvalidity(self): + """Tests if the cached UIDVALIDITY match the real current one - If required it caches the UID. In this case the function is not - threadsafe. So don't attempt to call it from concurrent threads.""" + If required it saves the UIDVALIDITY value. In this case the + function is not threadsafe. So don't attempt to call it from + concurrent threads. - if self.getsaveduidvalidity() != None: - return self.getsaveduidvalidity() == self.getuidvalidity() + :returns: Boolean indicating the match. Returns True in case it + implicitely saved the UIDVALIDITY.""" + + if self.get_saveduidvalidity() != None: + return self.get_saveduidvalidity() == self.get_uidvalidity() else: - self.saveuidvalidity() - return 1 + self.save_uidvalidity() + return True def _getuidfilename(self): + """provides UIDVALIDITY cache filename for class internal purposes""" return os.path.join(self.repository.getuiddir(), self.getfolderbasename()) - def getsaveduidvalidity(self): + def get_saveduidvalidity(self): + """Return the previously cached UIDVALIDITY value + + :returns: UIDVALIDITY as (long) number or None, if None had been + saved yet.""" if hasattr(self, '_base_saved_uidvalidity'): return self._base_saved_uidvalidity uidfilename = self._getuidfilename() @@ -137,21 +146,24 @@ file.close() return self._base_saved_uidvalidity - def saveuidvalidity(self): - """Save the UID value of the folder to the status + def save_uidvalidity(self): + """Save the UIDVALIDITY value of the folder to the cache This function is not threadsafe, so don't attempt to call it from concurrent threads.""" - newval = self.getuidvalidity() + newval = self.get_uidvalidity() uidfilename = self._getuidfilename() - file = open(uidfilename + ".tmp", "wt") - file.write("%d\n" % newval) - file.close() + with open(uidfilename + ".tmp", "wt") as file: + file.write("%d\n" % newval) os.rename(uidfilename + ".tmp", uidfilename) self._base_saved_uidvalidity = newval - def getuidvalidity(self): + def get_uidvalidity(self): + """Retrieve the current connections UIDVALIDITY value + + This function needs to be implemented by each Backend + :returns: UIDVALIDITY as a (long) number""" raise NotImplementedException def cachemessagelist(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/folder/Gmail.py new/offlineimap/offlineimap/folder/Gmail.py --- old/spaetz-offlineimap-091272d/offlineimap/folder/Gmail.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/folder/Gmail.py 2012-02-04 21:11:44.000000000 +0100 @@ -18,16 +18,18 @@ """Folder implementation to support features of the Gmail IMAP server. """ - from IMAP import IMAPFolder -from offlineimap import imaputil - class GmailFolder(IMAPFolder): """Folder implementation to support features of the Gmail IMAP server. - Specifically, deleted messages are moved to folder `Gmail.TRASH_FOLDER` - (by default: ``[Gmail]/Trash``) prior to expunging them, since - Gmail maps to IMAP ``EXPUNGE`` command to "remove label". + + Removing a message from a folder will only remove the "label" from + the message and keep it in the "All mails" folder. To really delete + a message it needs to be copied to the Trash folder. However, this + is dangerous as our folder moves are implemented as a 1) delete in + one folder and 2) append to the other. If 2 comes before 1, this + will effectively delete the message from all folders. So we cannot + do that until we have a smarter folder move mechanism. For more information on the Gmail IMAP server: http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815 @@ -35,31 +37,6 @@ def __init__(self, imapserver, name, repository): super(GmailFolder, self).__init__(imapserver, name, repository) - self.realdelete = repository.getrealdelete(name) self.trash_folder = repository.gettrashfolder(name) - #: Gmail will really delete messages upon EXPUNGE in these folders + # Gmail will really delete messages upon EXPUNGE in these folders self.real_delete_folders = [ self.trash_folder, repository.getspamfolder() ] - - def deletemessages_noconvert(self, uidlist): - uidlist = [uid for uid in uidlist if uid in self.messagelist] - if not len(uidlist): - return - - if self.realdelete and not (self.getname() in self.real_delete_folders): - # IMAP expunge is just "remove label" in this folder, - # so map the request into a "move into Trash" - - imapobj = self.imapserver.acquireconnection() - try: - imapobj.select(self.getfullname()) - result = imapobj.uid('copy', - imaputil.uid_sequence(uidlist), - self.trash_folder) - assert result[0] == 'OK', \ - "Bad IMAPlib result: %s" % result[0] - finally: - self.imapserver.releaseconnection(imapobj) - for uid in uidlist: - del self.messagelist[uid] - else: - IMAPFolder.deletemessages_noconvert(self, uidlist) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/folder/IMAP.py new/offlineimap/offlineimap/folder/IMAP.py --- old/spaetz-offlineimap-091272d/offlineimap/folder/IMAP.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/folder/IMAP.py 2012-02-04 21:11:44.000000000 +0100 @@ -65,17 +65,23 @@ def getcopyinstancelimit(self): return 'MSGCOPY_' + self.repository.getname() - def getuidvalidity(self): + def get_uidvalidity(self): + """Retrieve the current connections UIDVALIDITY value + + UIDVALIDITY value will be cached on the first call. + :returns: The UIDVALIDITY as (long) number.""" + if hasattr(self, '_uidvalidity'): + # use cached value if existing + return self._uidvalidity imapobj = self.imapserver.acquireconnection() try: - # SELECT receives UIDVALIDITY response + # SELECT (if not already done) and get current UIDVALIDITY self.selectro(imapobj) - # note: we would want to use .response() here but that - # often seems to return [None], even though we have - # data. TODO - uidval = imapobj._get_untagged_response('UIDVALIDITY') - assert uidval != [None], "response('UIDVALIDITY') returned [None]!" - return long(uidval[-1]) + typ, uidval = imapobj.response('UIDVALIDITY') + assert uidval != [None] and uidval != None, \ + "response('UIDVALIDITY') returned [None]!" + self._uidvalidity = long(uidval[-1]) + return self._uidvalidity finally: self.imapserver.releaseconnection(imapobj) @@ -532,12 +538,13 @@ self.ui.msgtoreadonly(self, uid, content, flags) return uid - #Do the APPEND + # Clean out existing APPENDUID responses and do APPEND try: - (typ, dat) = imapobj.append(self.getfullname(), + imapobj.response('APPENDUID') # flush APPENDUID responses + typ, dat = imapobj.append(self.getfullname(), imaputil.flagsmaildir2imap(flags), date, content) - retry_left = 0 # Mark as success + retry_left = 0 # Mark as success except imapobj.abort, e: # connection has been reset, release connection and retry. retry_left -= 1 @@ -560,43 +567,38 @@ "failed (error). Server reponded: %s\nMessage content was: " "%s" % (self, self.getrepository(), str(e), dbg_output), OfflineImapError.ERROR.MESSAGE) - # Checkpoint. Let it write out stuff, etc. Eg searches for - # just uploaded messages won't work if we don't do this. - (typ,dat) = imapobj.check() - assert(typ == 'OK') - - # get the new UID. Test for APPENDUID response even if the - # server claims to not support it, as e.g. Gmail does :-( - if use_uidplus or imapobj._get_untagged_response('APPENDUID', True): + + # get the new UID, default to 0 (=unknown) + uid = 0 + if use_uidplus: # get new UID from the APPENDUID response, it could look # like OK [APPENDUID 38505 3955] APPEND completed with - # 38505 bein folder UIDvalidity and 3955 the new UID. - # note: we would want to use .response() here but that - # often seems to return [None], even though we have - # data. TODO - resp = imapobj._get_untagged_response('APPENDUID') - if resp == [None]: + # 38505 being folder UIDvalidity and 3955 the new UID. + typ, resp = imapobj.response('APPENDUID') + if resp == [None] or resp == None: self.ui.warn("Server supports UIDPLUS but got no APPENDUID " "appending a message.") - return 0 - uid = long(resp[-1].split(' ')[1]) + else: + uid = long(resp[-1].split(' ')[1]) else: - # we don't support UIDPLUS + # Don't support UIDPLUS + # Checkpoint. Let it write out stuff, etc. Eg searches for + # just uploaded messages won't work if we don't do this. + typ, dat = imapobj.check() + assert(typ == 'OK') + uid = self.savemessage_searchforheader(imapobj, headername, headervalue) - # See docs for savemessage in Base.py for explanation of this and other return values + # If everything failed up to here, search the message + # manually TODO: rather than inserting and searching for our + # custom header, we should be searching the Message-ID and + # compare the message size... if uid == 0: - self.ui.debug('imap', 'savemessage: first attempt to get new UID failed. \ - Going to run a NOOP and try again.') - assert(imapobj.noop()[0] == 'OK') - uid = self.savemessage_searchforheader(imapobj, headername, - headervalue) - if uid == 0: - self.ui.debug('imap', 'savemessage: second attempt to get new UID failed. \ - Going to try search headers manually') - uid = self.savemessage_fetchheaders(imapobj, headername, headervalue) - + self.ui.debug('imap', 'savemessage: attempt to get new UID ' + 'UID failed. Search headers manually.') + uid = self.savemessage_fetchheaders(imapobj, headername, + headervalue) finally: self.imapserver.releaseconnection(imapobj) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/folder/LocalStatusSQLite.py new/offlineimap/offlineimap/folder/LocalStatusSQLite.py --- old/spaetz-offlineimap-091272d/offlineimap/folder/LocalStatusSQLite.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/folder/LocalStatusSQLite.py 2012-02-04 21:11:44.000000000 +0100 @@ -73,24 +73,32 @@ if version < LocalStatusSQLiteFolder.cur_version: self.upgrade_db(version) - def sql_write(self, sql, vars=None): - """execute some SQL retrying if the db was locked. + def sql_write(self, sql, vars=None, executemany=False): + """Execute some SQL, retrying if the db was locked. - :param sql: the SQL string passed to execute() :param args: the - variable values to `sql`. E.g. (1,2) or {uid:1, flags:'T'}. See - sqlite docs for possibilities. + :param sql: the SQL string passed to execute() + :param vars: the variable values to `sql`. E.g. (1,2) or {uid:1, + flags:'T'}. See sqlite docs for possibilities. + :param executemany: bool indicating whether we want to + perform conn.executemany() or conn.execute(). :returns: the Cursor() or raises an Exception""" success = False while not success: self._dblock.acquire() try: if vars is None: - cursor = self.connection.execute(sql) + if executemany: + cursor = self.connection.executemany(sql) + else: + cursor = self.connection.execute(sql) else: - cursor = self.connection.execute(sql, vars) + if executemany: + cursor = self.connection.executemany(sql, vars) + else: + cursor = self.connection.execute(sql, vars) success = True self.connection.commit() - except sqlite.OperationalError, e: + except sqlite.OperationalError as e: if e.args[0] == 'cannot commit - no transaction is active': pass elif e.args[0] == 'database is locked': @@ -231,12 +239,23 @@ flags = ''.join(sorted(flags)) self.sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid)) + def deletemessage(self, uid): + if not uid in self.messagelist: + return + self.sql_write('DELETE FROM status WHERE id=?', (uid, )) + del(self.messagelist[uid]) + def deletemessages(self, uidlist): + """Delete list of UIDs from status cache + + This function uses sqlites executemany() function which is + much faster than iterating through deletemessage() when we have + many messages to delete.""" # Weed out ones not in self.messagelist uidlist = [uid for uid in uidlist if uid in self.messagelist] if not len(uidlist): return + # arg2 needs to be an iterable of 1-tuples [(1,),(2,),...] + self.sql_write('DELETE FROM status WHERE id=?', zip(uidlist, ), True) for uid in uidlist: del(self.messagelist[uid]) - #TODO: we want a way to do executemany(.., uidlist) to delete all - self.sql_write('DELETE FROM status WHERE id=?', (uid, )) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/folder/Maildir.py new/offlineimap/offlineimap/folder/Maildir.py --- old/spaetz-offlineimap-091272d/offlineimap/folder/Maildir.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/folder/Maildir.py 2012-02-04 21:11:44.000000000 +0100 @@ -83,8 +83,10 @@ """Return the absolute file path to the Maildir folder (sans cur|new)""" return self._fullname - def getuidvalidity(self): - """Maildirs have no notion of uidvalidity, so we just return a magic + def get_uidvalidity(self): + """Retrieve the current connections UIDVALIDITY value + + Maildirs have no notion of uidvalidity, so we just return a magic token.""" return 42 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/imaputil.py new/offlineimap/offlineimap/imaputil.py --- old/spaetz-offlineimap-091272d/offlineimap/imaputil.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/imaputil.py 2012-02-04 21:11:44.000000000 +0100 @@ -25,7 +25,12 @@ except NameError: from sets import Set as set -quotere = re.compile('^("(?:[^"]|\\\\")*")') +# find the first quote in a string +quotere = re.compile( + r"""(?P<quote>"(?:\\"|[^"])*") # Quote, possibly containing encoded + # quotation mark + \s*(?P<rest>.*)$ # Whitespace & remainder of string""", + re.VERBOSE) def debug(*args): msg = [] @@ -71,7 +76,7 @@ def flags2hash(flags): """Converts IMAP response string from eg IMAP4.fetch() to a hash. - + E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to {'FLAGS': '(\\Seen Old)', 'UID': '4807'}""" return options2hash(flagsplit(flags)) @@ -124,10 +129,11 @@ retval.extend(imapsplit(arg)) debug("imapsplit() non-string: returning %s" % str(retval)) return retval - + workstr = imapstring.strip() retval = [] while len(workstr): + # handle parenthized fragments (...()...) if workstr[0] == '(': rparenc = 1 # count of right parenthesis to match rpareni = 1 # position to examine @@ -141,9 +147,10 @@ workstr = workstr[rpareni:].lstrip() retval.append(parenlist) elif workstr[0] == '"': - quotelist = quotere.search(workstr).group(1) - workstr = workstr[len(quotelist):].lstrip() - retval.append(quotelist) + # quoted fragments '"...\"..."' + m = quotere.match(workstr) + retval.append(m.group('quote')) + workstr = m.group('rest') else: splits = string.split(workstr, maxsplit = 1) splitslen = len(splits) @@ -162,7 +169,7 @@ # There was not even an unquoted word. break return retval - + flagmap = [('\\Seen', 'S'), ('\\Answered', 'R'), ('\\Flagged', 'F'), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/init.py new/offlineimap/offlineimap/init.py --- old/spaetz-offlineimap-091272d/offlineimap/init.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/init.py 2012-02-04 21:11:44.000000000 +0100 @@ -200,6 +200,7 @@ # TODO, make use of chosen ui for logging logging.warning('Using old interface name, consider using one ' 'of %s' % ', '.join(UI_LIST.keys())) + if options.diagnostics: ui_type = 'basic' # enforce basic UI for --info try: # create the ui class self.ui = UI_LIST[ui_type.lower()](config) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/repository/Gmail.py new/offlineimap/offlineimap/repository/Gmail.py --- old/spaetz-offlineimap-091272d/offlineimap/repository/Gmail.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/repository/Gmail.py 2012-02-04 21:11:44.000000000 +0100 @@ -64,11 +64,6 @@ def getfoldertype(self): return folder.Gmail.GmailFolder - def getrealdelete(self, foldername): - # XXX: `foldername` is currently ignored - the `realdelete` - # setting is repository-wide - return self.getconfboolean('realdelete', 0) - def gettrashfolder(self, foldername): #: Where deleted mail should be moved return self.getconf('trashfolder','[Gmail]/Trash') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/Machine.py new/offlineimap/offlineimap/ui/Machine.py --- old/spaetz-offlineimap-091272d/offlineimap/ui/Machine.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/ui/Machine.py 2012-02-04 21:11:44.000000000 +0100 @@ -45,7 +45,7 @@ def _msg(s, msg): s._printData('_display', msg) - def warn(s, msg, minor = 0): + def warn(self, msg, minor = 0): # TODO, remove and cleanup the unused minor stuff self.logger.warning("%s:%s:%s:%s" % ( 'warn', '', currentThread().getName(), msg)) @@ -70,7 +70,7 @@ def validityproblem(s, folder): s._printData('validityproblem', "%s\n%s\n%s\n%s" % \ (folder.getname(), folder.getrepository().getname(), - folder.getsaveduidvalidity(), folder.getuidvalidity())) + folder.get_saveduidvalidity(), folder.get_uidvalidity())) def connecting(s, hostname, port): s._printData('connecting', "%s\n%s" % (hostname, str(port))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/TTY.py new/offlineimap/offlineimap/ui/TTY.py --- old/spaetz-offlineimap-091272d/offlineimap/ui/TTY.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/ui/TTY.py 2012-02-04 21:11:44.000000000 +0100 @@ -17,6 +17,7 @@ import logging import sys +import time from getpass import getpass from offlineimap import banner from offlineimap.ui.UIBase import UIBase @@ -24,12 +25,14 @@ class TTYFormatter(logging.Formatter): """Specific Formatter that adds thread information to the log output""" def __init__(self, *args, **kwargs): - super(TTYFormatter, self).__init__(*args, **kwargs) + #super() doesn't work in py2.6 as 'logging' uses old-style class + logging.Formatter.__init__(self, *args, **kwargs) self._last_log_thread = None def format(self, record): """Override format to add thread information""" - log_str = super(TTYFormatter, self).format(record) + #super() doesn't work in py2.6 as 'logging' uses old-style class + log_str = logging.Formatter.format(self, record) # If msg comes from a different thread than our last, prepend # thread info. Most look like 'Account sync foo' or 'Folder # sync foo'. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap/ui/UIBase.py new/offlineimap/offlineimap/ui/UIBase.py --- old/spaetz-offlineimap-091272d/offlineimap/ui/UIBase.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap/ui/UIBase.py 2012-02-04 21:11:44.000000000 +0100 @@ -309,9 +309,9 @@ def validityproblem(self, folder): self.logger.warning("UID validity problem for folder %s (repo %s) " "(saved %d; got %d); skipping it. Please see FAQ " - "and manual how to handle this." % \ + "and manual on how to handle this." % \ (folder, folder.getrepository(), - folder.getsaveduidvalidity(), folder.getuidvalidity())) + folder.get_saveduidvalidity(), folder.get_uidvalidity())) def loadmessagelist(self, repos, folder): self.logger.debug("Loading message list for %s[%s]" % ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/offlineimap.conf new/offlineimap/offlineimap.conf --- old/spaetz-offlineimap-091272d/offlineimap.conf 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/offlineimap.conf 2012-02-04 21:11:44.000000000 +0100 @@ -1,27 +1,11 @@ -# Sample configuration file -# Copyright (C) 2002-2011 John Goerzen & contributors -# -# 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 - +# Offlineimap sample configuration file # This file documents all possible options and can be quite scary. # Looking for a quick start? Take a look at offlineimap.conf.minimal. -# Settings support interpolation. This means values can contain python -# format strings which refer to other values in the same section, or -# values in a special DEFAULT section. This allows you for example to -# use common settings for multiple accounts: +# Settings generally support interpolation. This means values can +# contain python format strings which refer to other values in the same +# section, or values in a special DEFAULT section. This allows you for +# example to use common settings for multiple accounts: # # [Repository Gmail1] # trashfolder: %(gmailtrashfolder)s @@ -33,7 +17,6 @@ # gmailtrashfolder = [Google Mail]/Papierkorb # # would set the trashfolder setting for your German gmail accounts. - ################################################## @@ -45,13 +28,12 @@ # This specifies where offlineimap is to store its metadata. # This directory will be created if it does not already exist. -metadata = ~/.offlineimap +#metadata = ~/.offlineimap # This variable specifies which accounts are defined. Separate them # with commas. Account names should be alphanumeric only. # You will need to specify one section per account below. You may # not use "general" for an account name. -# accounts = Test @@ -65,7 +47,7 @@ # since any given sync run never "finishes" due to a timer, you will never # sync your additional accounts if this is 1. -maxsyncaccounts = 1 +#maxsyncaccounts = 1 # You can specify one or more user interface modules for OfflineIMAP # to use. OfflineIMAP will try the first in the list, and if it @@ -82,7 +64,7 @@ # # You can override this with a command-line option -u. -ui = Blinkenlights +#ui = basic # If you try to synchronize messages to a folder which the IMAP server # considers read-only, OfflineIMAP will generate a warning. If you want @@ -92,7 +74,8 @@ # will prevent OfflineIMAP from propagating those changes to the IMAP # server. Note that ignore-readonly is unrelated to the "readonly" # setting which prevents a repository from being modified at all. -ignore-readonly = no + +#ignore-readonly = no ########## Advanced settings @@ -334,8 +317,9 @@ # fingerprint here. OfflineImap will verify that the server fingerprint # has not changed on each connect and refuse to connect otherwise. # You can also configure this in addition to CA certificate validation -# above and it will check both ways. cert_fingerprint = -# <SHA1_of_server_certificate_here> +# above and it will check both ways. + +#cert_fingerprint = <SHA1_of_server_certificate_here> # Specify the port. If not specified, use a default port. # remoteport = 993 @@ -421,7 +405,7 @@ # cases, it may slow things down. The safe answer is 1. You should # probably never set it to a value more than 5. -maxconnections = 2 +#maxconnections = 2 # OfflineIMAP normally closes IMAP server connections between refreshes if # the global option autorefresh is specified. If you wish it to keep the @@ -429,15 +413,15 @@ # false. Keeping the connection open means a faster sync start the # next time and may use fewer server resources on connection, but uses # more server memory. This setting has no effect if autorefresh is not set. - -holdconnectionopen = no +# +#holdconnectionopen = no # If you want to have "keepalives" sent while waiting between syncs, # specify the amount of time IN SECONDS between keepalives here. Note that # sometimes more than this amount of time might pass, so don't make it # tight. This setting has no effect if autorefresh and holdconnectionopen # are not both set. - +# # keepalive = 60 # Normally, OfflineIMAP will expunge deleted messages from the server. @@ -447,11 +431,12 @@ # setting; otherwise, the messgaes will just pile up there forever. # Therefore, this setting is definitely NOT recommended. # -# expunge = no +#expunge = no # Specify whether to process all mail folders on the server, or only # those listed as "subscribed". -subscribedonly = no +# +#subscribedonly = no # You can specify a folder translator. This must be a eval-able # Python expression that takes a foldername arg and returns the new @@ -525,7 +510,8 @@ # repository will not be modified during synchronization. Use to # e.g. backup an IMAP server. The readonly setting can be applied to any # type of Repository (Maildir, Imap, etc). -readonly = False +# +#readonly = False [Repository GmailExample] @@ -539,28 +525,12 @@ # `remoteport`, `tunnel` and `ssl`. (See # http://mail.google.com/support/bin/answer.py?answer=78799&topic=12814) # Any attempt to set those parameters will be silently ignored. -# type = Gmail # Specify the Gmail user name. This is the only mandatory parameter. remoteuser = usern...@gmail.com -# WARNING: READ THIS BEFORE CONSIDERING TO CHANGE IT! Deleting a -# message from a Gmail folder via the IMAP interface will just remove -# that folder's label from the message: the message will continue to -# exist in the '[Gmail]/All Mail' folder. If `realdelete` is set to -# `True`, then deleted messages will be moved to the '[Gmail]/Trash' -# folder. BEWARE: this will immediately delete a messages from *all -# folders* it belongs to! AS OFFLINEIMAP IMPLEMENTS FOLDER MOVES AS 1) -# AN ADD and 2) A DELETE (the order can vary), THIS MEANS THAT A FOLDER -# MOVE CAN CAUSE DATALOSS. DO NOT USE IT AND MOVE MAIL TO -# "[Gmail]/Trash" TO DELETE MAIL FROM "[Gmail]/All Mail"! See the -# analysis at -# http://article.gmane.org/gmane.mail.imap.offlineimap.general/5265 See -# http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815 -# realdelete = no !!!READ ABOVE BEFORE USING - # The trash folder name may be different from [Gmail]/Trash # for example on german googlemail, this setting should be # @@ -571,4 +541,5 @@ # spamfolder = [Google Mail]/Spam # Enable 1-way synchronization. See above for explanation. -readonly = False +# +#readonly = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/setup.py new/offlineimap/setup.py --- old/spaetz-offlineimap-091272d/setup.py 2012-01-17 01:40:41.000000000 +0100 +++ new/offlineimap/setup.py 2012-02-04 21:11:44.000000000 +0100 @@ -20,11 +20,32 @@ # 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 -# END OF COPYRIGHT # - -from distutils.core import setup +import os +from distutils.core import setup, Command import offlineimap +import logging +from test.OLItest import TextTestRunner, TestLoader, OLITestLib + +class TestCommand(Command): + """runs the OLI testsuite""" + description = "Runs the test suite" + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + logging.basicConfig(format='%(message)s') + # set credentials and OfflineImap command to be executed: + OLITestLib(cred_file='./test/credentials.conf', cmd='./offlineimap.py') + suite = TestLoader().discover('./test/tests') + #TODO: failfast does not seem to exist in python2.6? + TextTestRunner(verbosity=2,failfast=True).run(suite) + setup(name = "offlineimap", version = offlineimap.__version__, @@ -36,6 +57,7 @@ 'offlineimap.repository', 'offlineimap.ui'], scripts = ['bin/offlineimap'], license = offlineimap.__copyright__ + \ - ", Licensed under the GPL version 2" + ", Licensed under the GPL version 2", + cmdclass = { 'test': TestCommand} ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/.gitignore new/offlineimap/test/.gitignore --- old/spaetz-offlineimap-091272d/test/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/.gitignore 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,2 @@ +credentials.conf +tmp_* \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/OLItest/TestRunner.py new/offlineimap/test/OLItest/TestRunner.py --- old/spaetz-offlineimap-091272d/test/OLItest/TestRunner.py 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/OLItest/TestRunner.py 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,132 @@ +# Copyright (C) 2012- Sebastian Spaeth & contributors +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import unittest +import logging +import os +import sys +import shutil +import subprocess +import tempfile +from ConfigParser import SafeConfigParser +from . import default_conf + +class OLITestLib(): + cred_file = None + testdir = None + """Absolute path of the current temporary test directory""" + cmd = None + """command that will be executed to invoke offlineimap""" + + def __init__(self, cred_file = None, cmd='offlineimap'): + """ + + :param cred_file: file of the configuration + snippet for authenticating against the test IMAP server(s). + :param cmd: command that will be executed to invoke offlineimap""" + OLITestLib.cred_file = cred_file + if not os.path.isfile(cred_file): + raise UserWarning("Please copy 'credentials.conf.sample' to '%s' " + "and set your credentials there." % cred_file) + OLITestLib.cmd = cmd + + @classmethod + def create_test_dir(cls, suffix=''): + """Creates a test directory and places OLI config there + + Note that this is a class method. There can only be one test + directory at a time. OLITestLib is not suited for running + several tests in parallel. The user is responsible for + cleaning that up herself.""" + # creating temporary dir for testing in same dir as credentials.conf + cls.testdir = os.path.abspath( + tempfile.mkdtemp(prefix='tmp_%s_'%suffix, + dir=os.path.dirname(cls.cred_file))) + cls.create_config_file() + return cls.testdir + + @classmethod + def create_config_file(cls): + """Creates a OLI configuration file + + It is created in testdir (so create_test_dir has to be called + earlier) using the credentials information given (so they had to + be set earlier). Failure to do either of them will raise an + AssertionException.""" + assert cls.cred_file != None + assert cls.testdir != None + config = SafeConfigParser() + config.readfp(default_conf) + config.read(cls.cred_file) + config.set("general", "metadata", cls.testdir) + localfolders = os.path.join(cls.testdir, 'mail') + config.set("Repository Maildir", "localfolders", localfolders) + with open(os.path.join(cls.testdir, 'offlineimap.conf'), "wa") as f: + config.write(f) + + @classmethod + def delete_test_dir(cls): + """Deletes the current test directory + + The users is responsible for cleaning that up herself.""" + if os.path.isdir(cls.testdir): + shutil.rmtree(cls.testdir) + + @classmethod + def run_OLI(cls): + """Runs OfflineImap + + :returns: (rescode, stdout) + """ + try: + output = subprocess.check_output( + [cls.cmd, + "-c%s" % os.path.join(cls.testdir, 'offlineimap.conf')], + shell=False) + except subprocess.CalledProcessError as e: + return (e.returncode, e.output) + return (0, output) + + @classmethod + def create_maildir(cls, folder): + """Create empty maildir 'folder' in our test maildir + + Does not fail if it already exists""" + assert cls.testdir != None + maildir = os.path.join(cls.testdir, 'mail', folder) + for subdir in ('','tmp','cur','new'): + try: + os.mkdir(os.path.join(maildir, subdir)) + except OSError as e: + if e.errno != 17: # 'already exists' is ok. + raise + + @classmethod + def count_maildir_mails(cls, folder): + """Returns the number of mails in maildir 'folder' + + Counting only those in cur&new (ignoring tmp).""" + assert cls.testdir != None + maildir = os.path.join(cls.testdir, 'mail', folder) + + boxes, mails = 0, 0 + for dirpath, dirs, files in os.walk(maildir, False): + if set(dirs) == set(['cur', 'new', 'tmp']): + # New maildir folder + boxes += 1 + #raise RuntimeError("%s is not Maildir" % maildir) + if dirpath.endswith(('/cur', '/new')): + mails += len(files) + return boxes, mails diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/OLItest/__init__.py new/offlineimap/test/OLItest/__init__.py --- old/spaetz-offlineimap-091272d/test/OLItest/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/OLItest/__init__.py 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,34 @@ +# OfflineImap test library +# Copyright (C) 2012- Sebastian Spaeth & contributors +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +__all__ = ['OLITestLib', 'TextTestRunner','TestLoader'] + +__productname__ = 'OfflineIMAP Test suite' +__version__ = '0' +__copyright__ = "Copyright 2012- Sebastian Spaeth & contributors" +__author__ = 'Sebastian Spaeth' +__author_email__= 'sebast...@sspaeth.de' +__description__ = 'Moo' +__license__ = "Licensed under the GNU GPL v2+ (v2 or any later version)" +__homepage__ = "http://offlineimap.org" +banner = """%(__productname__)s %(__version__)s + %(__license__)s""" % locals() + +import unittest +from unittest import TestLoader, TextTestRunner +from globals import default_conf +from TestRunner import OLITestLib diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/OLItest/globals.py new/offlineimap/test/OLItest/globals.py --- old/spaetz-offlineimap-091272d/test/OLItest/globals.py 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/OLItest/globals.py 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,37 @@ +#Constants, that don't rely on anything else in the module +# Copyright (C) 2012- Sebastian Spaeth & contributors +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +from cStringIO import StringIO + +default_conf=StringIO("""[general] +#will be set automatically +metadata = +accounts = test +ui = quiet + +[Account test] +localrepository = Maildir +remoterepository = IMAP + +[Repository Maildir] +Type = Maildir +# will be set automatically during tests +localfolders = + +[Repository IMAP] +type=IMAP +folderfilter= lambda f: f.startswith('INBOX.OLItest') +""") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/README new/offlineimap/test/README --- old/spaetz-offlineimap-091272d/test/README 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/README 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,18 @@ +Documentation for the OfflineImap Test suite. + +How to run the tests +==================== + +- Copy the credentials.conf.sample to credentials.conf and insert + credentials for an IMAP account and a Gmail account. Delete the Gmail + section if you don't have a Gmail account. Do note, that the tests + will change the account and upload/delete/modify it's contents and + folder structure. So don't use a real used account here... + +- go to the top level dir (one above this one) and execute: + 'python setup.py test' + +System requirements +=================== + +This test suite depend on python>=2.7 to run out of the box. If you want to run this with python 2.6 you will need to install the backport from http://pypi.python.org/pypi/unittest2 instead. \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/credentials.conf.sample new/offlineimap/test/credentials.conf.sample --- old/spaetz-offlineimap-091272d/test/credentials.conf.sample 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/credentials.conf.sample 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,13 @@ +[Repository IMAP] +type = IMAP +remotehost = localhost +ssl = no +#sslcacertfile = +#cert_fingerprint = +remoteuser = user@domain +remotepass = SeKr3t + +[Repository Gmail] +type = Gmail +remoteuser = user@domain +remotepass = SeKr3t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/spaetz-offlineimap-091272d/test/tests/test_01_basic.py new/offlineimap/test/tests/test_01_basic.py --- old/spaetz-offlineimap-091272d/test/tests/test_01_basic.py 1970-01-01 01:00:00.000000000 +0100 +++ new/offlineimap/test/tests/test_01_basic.py 2012-02-04 21:11:44.000000000 +0100 @@ -0,0 +1,74 @@ +# Copyright (C) 2012- Sebastian Spaeth & contributors +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import random +import unittest +import logging +import os, sys +from test.OLItest import OLITestLib + +def setUpModule(): + logging.info("Set Up test module %s" % __name__) + tdir = OLITestLib.create_test_dir(suffix=__name__) + +def tearDownModule(): + logging.info("Tear Down test module") + OLITestLib.delete_test_dir() + +#Stuff that can be used +#self.assertEqual(self.seq, range(10)) +# should raise an exception for an immutable sequence +#self.assertRaises(TypeError, random.shuffle, (1,2,3)) +#self.assertTrue(element in self.seq) +#self.assertFalse(element in self.seq) + +class TestBasicFunctions(unittest.TestCase): + #@classmethod + #def setUpClass(cls): + #This is run before all tests in this class + # cls._connection = createExpensiveConnectionObject() + + #@classmethod + #This is run after all tests in this class + #def tearDownClass(cls): + # cls._connection.destroy() + + # This will be run before each test + #def setUp(self): + # self.seq = range(10) + + def test_01_olistartup(self): + """Tests if OLI can be invoked without exceptions + + It syncs all "OLItest* (specified in the default config) to our + local Maildir at keeps it there.""" + code, res = OLITestLib.run_OLI() + self.assertEqual(res, "") + + boxes, mails = OLITestLib.count_maildir_mails('') + logging.warn("%d boxes and %d mails" % (boxes, mails)) + + def test_02_createdir(self): + """Create local OLItest 1 & OLItest "1" maildir, sync + + Folder names with quotes used to fail and have been fixed, so + one is included here as a small challenge.""" + OLITestLib.create_maildir('INBOX.OLItest 1') + OLITestLib.create_maildir('INBOX.OLItest "1"') + code, res = OLITestLib.run_OLI() + #logging.warn("%s %s "% (code, res)) + self.assertEqual(res, "") + boxes, mails = OLITestLib.count_maildir_mails('') + logging.warn("%d boxes and %d mails" % (boxes, mails)) -- To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org For additional commands, e-mail: opensuse-commit+h...@opensuse.org