/offlineimap/head: changeset 297
Changed to a more account-centric behavior. The refresh time is now a per-account variable. Implemented new account classes. User interfaces must now be updated to take advantage of this.
This commit is contained in:
parent
1691cdbf0f
commit
854eaf3055
443
offlineimap/head/debian/changelog
Normal file
443
offlineimap/head/debian/changelog
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
offlineimap (3.99.6) unstable; urgency=low
|
||||||
|
|
||||||
|
* Beginnings of work to make it work with a threaded Tcl/Tk Tkinter.
|
||||||
|
* Now properly handles folder names that contain parenthesis. Used
|
||||||
|
patch from Kyler Laird in
|
||||||
|
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=173895.
|
||||||
|
Closes: #173895.
|
||||||
|
* Changed to a more account-centric behavior. The refresh time is now
|
||||||
|
a per-account variable. Implemented new account classes. User
|
||||||
|
interfaces must now be updated to take advantage of this.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 2 Jan 2003 13:59:44 -0600
|
||||||
|
|
||||||
|
offlineimap (3.99.5) unstable; urgency=low
|
||||||
|
|
||||||
|
* Added ability to disable expunging on the server.
|
||||||
|
* Fixed infinite loop with preauth. Closes: #169514, #171485.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 03 Dec 2002 06:22:37 -0600
|
||||||
|
|
||||||
|
offlineimap (3.99.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed setup.py installation instructions.
|
||||||
|
* Added more debugging to the CRAM-MD5 authentication module.
|
||||||
|
* CRAM-MD5 *really* fixed this time. Thanks to MJ for the patch.
|
||||||
|
* Adding missing import os to imapserver.py. Thanks to John Wiegley
|
||||||
|
for catching that.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 5 Nov 2002 08:06:45 -0600
|
||||||
|
|
||||||
|
offlineimap (3.99.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* Moved password promting into imapserver.py. Passwords are now asked
|
||||||
|
for on-demand and typos will no longer crash the program (the user
|
||||||
|
will be re-prompted). Closes: #162672.
|
||||||
|
* Falls back to plain-text auth if CRAM-MD5 fails. Fixes [complete.org #42]
|
||||||
|
* Fixed CRAM-MD5 auth so it actually works now.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Mon, 4 Nov 2002 06:16:11 -0600
|
||||||
|
|
||||||
|
offlineimap (3.99.2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Further attempts to fix imapsplit problems.
|
||||||
|
* When an exception occurs, OfflineIMAP will attempt to print the last
|
||||||
|
50 debug messages, whether or not debugging was enabled for this
|
||||||
|
session. This way, even unexpected and non-repeatable errors stand
|
||||||
|
a chance of getting a more detailed log.
|
||||||
|
* Handle uidvalidity file in an atomic fashion. CLoses: #165600.
|
||||||
|
* Supports CRAM-MD5 authentication. Fixes [complete.org #38], and for
|
||||||
|
Debian, Closes: #154165.
|
||||||
|
* Noted CRAM-MD5 support in the "CONFORMING TO" section of the manual.
|
||||||
|
* Certain servers may not always return the UID flag for new messages.
|
||||||
|
This causes an OfflineIMAP exception, though rerunning will fix it.
|
||||||
|
Now, OfflineIMAP will detect the condition and ignore the offending
|
||||||
|
messages (without an exception) until the next sync.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Sat, 02 Nov 2002 10:23:10 -0600
|
||||||
|
|
||||||
|
offlineimap (3.99.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed some syntax errors in imaputil.py
|
||||||
|
* Fixed a syntax error with mbnames
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 9 Oct 2002 19:34:37 -0500
|
||||||
|
|
||||||
|
offlineimap (3.99.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* The next few releases are adding features and reorganizing
|
||||||
|
code in preparation for 4.0.0.
|
||||||
|
* imaputil.py now logs information with IMAP debugging is enabled.
|
||||||
|
* Added folderfilter capability to mbnames recorder. You can now omit
|
||||||
|
specified folders from the mbnames output.
|
||||||
|
* Added a workaround to imaputil.py to deal with a bug in imaplib.py's
|
||||||
|
tuple when a response contains a literal in certain cases.
|
||||||
|
* Split out the code in bin/offlineimap into offlineimap/init.py.
|
||||||
|
Retaining bin/offlineimap as a skeletal piece only. Contains
|
||||||
|
about three lines of code now. This will make many things
|
||||||
|
easier, including debugging.
|
||||||
|
* Added library version check to bin/offlineimap and
|
||||||
|
offlineimap/init.py.
|
||||||
|
* Moved __main__.ui to functions in UIBase: getglobalui() and
|
||||||
|
setglobalui().
|
||||||
|
* Added license comments to some source files that were missing them.
|
||||||
|
* Moved some code from offlineimap/init.py to new file
|
||||||
|
offlineimap/syncmaster.py to help dileneate between code that
|
||||||
|
performs different functions.
|
||||||
|
* Moved threadexited from offlineimap/init.py to
|
||||||
|
offlineimap/threadutil.py.
|
||||||
|
* offlineimap.py is back to ease the use of OfflineIMAP in single-user
|
||||||
|
installations.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Mon, 07 Oct 2002 05:08:08 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.8) unstable; urgency=low
|
||||||
|
|
||||||
|
* Added a work-around for some IMAP servers that respond poorly
|
||||||
|
to LIST "" "". It will now do LIST "" "*", for them only.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Mon, 30 Sep 2002 10:48:01 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.7) unstable; urgency=low
|
||||||
|
|
||||||
|
* Moved executable to bin/offlineimap. This will allow setup.py to
|
||||||
|
properly install it as offlineimap instead of offlineimap.py.
|
||||||
|
* Made sure executables use /usr/bin/env in bangpath.
|
||||||
|
* Font size for Blinkenlights interface is now configurable.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 19 Sep 2002 06:46:56 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.6) unstable; urgency=low
|
||||||
|
|
||||||
|
* Changed indentation in debian/control. Closes: #156327.
|
||||||
|
* Removed calls to folder object deletions. None have been implemented
|
||||||
|
anyway.
|
||||||
|
* folder/Maildir.py: unlink throws OSError, not IOError; fixed.
|
||||||
|
Now handles message deleting race condition properly.
|
||||||
|
Closes: #154497.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 16 Aug 2002 17:43:19 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.5) unstable; urgency=low
|
||||||
|
|
||||||
|
* Now handles uploading messages without Message-Id headers.
|
||||||
|
Closes: #156022.
|
||||||
|
* Applied patch from Tommi Virtanen that adds two new config file
|
||||||
|
options: pythonfile and foldersort. Fixes [complete.org #29], and
|
||||||
|
for Debian, Closes: #155637.
|
||||||
|
* Added documentation for the above features.
|
||||||
|
* Even more resiliant in the face of invalid Date headers. Closes: #155994.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 9 Aug 2002 17:54:01 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* When using nested folders, the Maildir repository handler now properly
|
||||||
|
deals with folders that are nested inside "noselect" folders -- ones
|
||||||
|
that do not actually contain messages and are not provided in the
|
||||||
|
server's LIST response. Fixes [complete.org #32] and, for Debian,
|
||||||
|
Closes: #155866.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 8 Aug 2002 17:54:44 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* -d now takes a parameter: imap or maildir (or both) to specify
|
||||||
|
what type of debugging to do.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 8 Aug 2002 10:02:36 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Updated manual to show new Gray color.
|
||||||
|
* Scrolling behavior is better now; sometimes, with fast-scrolling text,
|
||||||
|
the log would stop scrolling.
|
||||||
|
* Better handling of read-only folders. We will now warn if there is
|
||||||
|
a change, but not propogate it. New config variable ignore-readonly
|
||||||
|
can suppress the warnings. This fixes [complete.org #10] and,
|
||||||
|
for Debian, Closes: #154769.
|
||||||
|
* If a given Maildir folder is new, remove the associated local status
|
||||||
|
cache file, if any. That way, there will not be any chance of
|
||||||
|
propogating hordes of deletes and adds based on old status data.
|
||||||
|
* Added support for /-separated Maildirs -- that is, hierarchical
|
||||||
|
Maildir trees. Fixes [complete.org #28] and, for Debian,
|
||||||
|
Closes: #155460.
|
||||||
|
* Preventitive security: Folder names may not contain ./ or start with /.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 07 Aug 2002 20:22:25 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* There is a new "connecting" event that will appear in all but the
|
||||||
|
Quiet UIs. It has a gray color in Blinkenlights. This event indicates
|
||||||
|
the the program is connecting to a remote server.
|
||||||
|
* Blinkenlights UI log window is now scrolled and has a new
|
||||||
|
config file option "bufferlines" to specify the size of the scroll
|
||||||
|
buffer.
|
||||||
|
* The Blinkenlights window is now non-resizable when the log is disabled.
|
||||||
|
When the log is enabled, the window is resizable, and the changes in
|
||||||
|
size are reflected in the log widget. Therefore, the Bigger Log
|
||||||
|
and Smaller Log items can disappear, and the Log menu now becomes
|
||||||
|
a Show Log or a Hide Log menu option. No sub-menus necessary anymore.
|
||||||
|
This presents a much cleaner feel, more intuitive operation, and
|
||||||
|
faster navigation.
|
||||||
|
* Fix for account name interpolation in dot warning from 3.2.0 from
|
||||||
|
Martijn Pieters.
|
||||||
|
* Backed out check for . in account names for now. Will put it back in
|
||||||
|
when we have a consensus on what exactly to do. Doubt that anyone
|
||||||
|
has a foldername that would conflict with Blinkenlights anyway.
|
||||||
|
* Fix reading the ui.Tk.Blinkenlights bufferlines option.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 24 Jul 2002 17:04:04 -0500
|
||||||
|
|
||||||
|
offlineimap (3.2.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* New BLINKENLIGHTS interface! Mesmerising, isn't it?
|
||||||
|
* New ui.Tk.Blinkenlights section in offlineimap.conf.
|
||||||
|
* New USER INTERFACES section in the manual.
|
||||||
|
* TTYUI isusable() now checks to see if stdout and stdin are TTYs.
|
||||||
|
* Added build-dependency on python2.2-dev. Closes: #154167.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 24 Jul 2002 17:53:20 -0500
|
||||||
|
|
||||||
|
offlineimap (3.1.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Modified imaputil.py and folder/Maildir.py to run faster. Eliminated
|
||||||
|
many regular expressions; pre-compiled many others.
|
||||||
|
* Fixed threadutil's exitnotifyloop to always handle threads in the order
|
||||||
|
they exited, rather than sometimes in the inverse order. This way,
|
||||||
|
make sure to handle thread's exception messages before a thread exited.
|
||||||
|
* Replaced imaplib.py's braindead readline() with a more efficient one.
|
||||||
|
* More optimizations to imaputil and folders for faster operation.
|
||||||
|
* These optimizations, all together, have resulted in OfflineIMAP
|
||||||
|
using approximately half the CPU time of previous versions, fixing
|
||||||
|
[complete.org #6], and... Closes: #153503.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 24 Jul 2002 06:53:16 -0500
|
||||||
|
|
||||||
|
offlineimap (3.1.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* When uploading messages from a Maildir, now convert \r\n to \n in case
|
||||||
|
the message is stored weirdly. That way, everything is uniform.
|
||||||
|
Fixes [complete.org #11].
|
||||||
|
* Manual: added UW IMAPD example with references from docwhat@gerf.org.
|
||||||
|
* New UI modules: Noninteractive.Basic and Noninteractive.Quiet.
|
||||||
|
Fixes [complete.org #14].
|
||||||
|
* Added per-thread profiling support to aid in debugging.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Sun, 21 Jul 2002 16:09:42 -0500
|
||||||
|
|
||||||
|
offlineimap (3.0.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* No longer throws an exception when updating messages with strange
|
||||||
|
Date headers; will just set IMAP Internaldate to the current date.
|
||||||
|
Closes: #153425.
|
||||||
|
* No longer doubles-up reference names for mailboxes. Closes: #153515.
|
||||||
|
* Noted new bug-tracking system in manual and rebuilt manual files.
|
||||||
|
* Now stores incoming messages in 'cur' instead of 'new' if they have
|
||||||
|
the S flag. Closes: #152482.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Sun, 21 Jul 2002 13:46:13 -0500
|
||||||
|
|
||||||
|
offlineimap (3.0.2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed mailbox name recorder to use localfolder.getvisiblename() rather
|
||||||
|
than remotefolder.getvisiblename()
|
||||||
|
* Fixed remotepassfile option. Closes: #153119. Used 1-line patch from
|
||||||
|
Tommi Virtanen.
|
||||||
|
* Now handles cases of not being able to get UID for an uploaded message
|
||||||
|
more gracefully. This could occur if the server doesn't support
|
||||||
|
SEARCH, can't find the message ID, or finds multiple message IDs.
|
||||||
|
Closes: #153241.
|
||||||
|
* Now source is in Subversion. Make version.py log the Subversion
|
||||||
|
revision number.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 15 Jul 2002 06:43:36 -0500
|
||||||
|
|
||||||
|
offlineimap (3.0.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Detabified the source.
|
||||||
|
* Added UI list to the manpage.
|
||||||
|
* Added -o (run only once) option with patch sent in by Martijn Pieters.
|
||||||
|
* Optimized folder/IMAP.py addmessagesflags() with new listjoin() in
|
||||||
|
imaputil. Now, send the server 1:5,7 instead of 1,2,3,4,5,7.
|
||||||
|
* Made folder/Maildir.py/deletemessage() more tolerant if a message
|
||||||
|
asked to be deleted already has been.
|
||||||
|
* In Base.py/copymessageto(), no longer bother calling getmessage()
|
||||||
|
unless a folder's storemessages() returns true. This will also help
|
||||||
|
with syncing to LocalStatus if the user deleted messages in the
|
||||||
|
Maildir since the cachemessagelist() was called.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 12 Jul 2002 07:28:24 -0500
|
||||||
|
|
||||||
|
offlineimap (3.0.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* Introduced a new graphical user interface written with Tkinter.
|
||||||
|
It features a nice view of multi-threaded displays.
|
||||||
|
* The TTY user interface now also displays thread names.
|
||||||
|
* Program-wide, new threads are given descriptive names to aid in
|
||||||
|
debugging and status messages.
|
||||||
|
* Added new module offlineimap/ui/detect.py that is used to detect
|
||||||
|
which user interface to select for a given session. Its operation
|
||||||
|
is governed by the ui config option and the -u command-line option.
|
||||||
|
* Made IMAP folder addmessagesflags() resiliant to a server refusing
|
||||||
|
to return a full set of new message flags. Closes: #152587.
|
||||||
|
* Completely rewrote documentation. OfflineIMAP now has an
|
||||||
|
exhaustive manpage, which is really a manual. It is also shipped
|
||||||
|
in plain text, HTML, PDF, and PostScript formats.
|
||||||
|
* New command-line options:
|
||||||
|
-1 to force no multi-threaded operation
|
||||||
|
-u to force a particular UI
|
||||||
|
-a to specify which accounts to sync
|
||||||
|
-h to print help
|
||||||
|
-c to specify an alternate config file
|
||||||
|
* Added a workaround for UW IMAP problem wherein the server loses
|
||||||
|
uidvalidity whenever a folder is emptied. Now, the program
|
||||||
|
will not consider it a problem if uidvalidity is lost when a folder
|
||||||
|
and the local status cache are both completely empty, since we do
|
||||||
|
not really need to preserve uidvalidity in that case anyway.
|
||||||
|
Closes: #152079.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 11 Jul 2002 22:35:42 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.8) unstable; urgency=low
|
||||||
|
|
||||||
|
* Modified the IMAP folder to use SELECT rather than STATUS more often.
|
||||||
|
Makes the code more robust; handles better with read-only folders;
|
||||||
|
and runs faster, especially for non-threaded useres, where it
|
||||||
|
may eliminate up to 2-3 commands per folder.
|
||||||
|
* Made sure IMAP folder savemessage() does a select. This was a possible
|
||||||
|
bug.
|
||||||
|
* Modified Maildir folder to unlink messages with T flag in
|
||||||
|
cachemessagelist()
|
||||||
|
* My own box now syncs in 3 seconds.
|
||||||
|
* Optimized acquireconnection() to try to give a thread back the
|
||||||
|
connection that it last used, if possible.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 9 Jul 2002 23:29:30 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.7) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed imaplib.py to work better with read-only folders.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 9 Jul 2002 20:24:21 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.6) unstable; urgency=low
|
||||||
|
|
||||||
|
* Added support for holdconnectionopen and keepalive. This feature
|
||||||
|
allows for an IMAP server connection(s) to be held open until
|
||||||
|
the next sync process, permitting faster restart times.
|
||||||
|
* Another try at read-only folder support. This is nasty because I
|
||||||
|
have no way to test it and imaplib's read-only support is weird.
|
||||||
|
* Closing out old bug; fixed in 1.0.2. Closes: #150803.
|
||||||
|
* Optimized algorithm so that a SELECT is never issued for folders
|
||||||
|
that contain no messages.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 9 Jul 2002 20:05:24 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.5) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed a folderfilter example. Partially fixes #152079.
|
||||||
|
* Added folderincludes capability. Partially fixes #152079.
|
||||||
|
* More fixes for read-only folders.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 5 Jul 2002 09:21:52 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* Made OfflineIMAP at least rudimentarily compatible with read-only
|
||||||
|
folders. It will still fail if they get modified locally, though.
|
||||||
|
* Flags are handled case-insensitively. Closes: #151993.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 5 Jul 2002 09:10:29 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* Added support for specifying references. Closes: #151960.
|
||||||
|
* Added -d command-line option to enable imaplib debugging.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 4 Jul 2002 20:39:29 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Added support for remotepassfile. Closes: #151943.
|
||||||
|
* Added support for preauth tunnels.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 4 Jul 2002 14:46:23 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed a bug with not properly propogating foldersep changes.
|
||||||
|
Now, local folders and status folders properly use the foldersep
|
||||||
|
mechanism. This corrects a problem with Exchange servers.
|
||||||
|
* Wrote a major new thread montiring subsystem, defined a new
|
||||||
|
ExitNotifyThread. Handling of Ctrl-C now occurs within 1 second
|
||||||
|
rather than after the whole program terminates. Exceptions that
|
||||||
|
occur in a thread are now caught by the main thread and marshalled
|
||||||
|
over into the UI side of things for dispatch. The entire program will
|
||||||
|
now abort when one thread dies with an exception.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Thu, 4 Jul 2002 09:07:06 -0500
|
||||||
|
|
||||||
|
offlineimap (2.0.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* This code is now multithreaded. New config file options control the
|
||||||
|
behavior. This can make synchronizing several times faster.
|
||||||
|
* Fixed the STATUS call to be compatible with Exchange.
|
||||||
|
* Added the ability to exclude folders.
|
||||||
|
* If upgrading from 1.0.x, you will need to add maxsyncaccounts to the
|
||||||
|
general section and maxconnections to each account sections.
|
||||||
|
There is also a new folderfilter option.
|
||||||
|
You can find examples of all of these in the new offlineimap.conf
|
||||||
|
example file packaged with the distribution.
|
||||||
|
* The Debian package now properly installs the example offlineimap.conf
|
||||||
|
file.
|
||||||
|
* There is a new mailing list available. To join, send SUBSCRIBE
|
||||||
|
to offlineimap-request@complete.org. The posting address is
|
||||||
|
offlineimap@complete.org.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Wed, 3 Jul 2002 19:21:32 -0500
|
||||||
|
|
||||||
|
offlineimap (1.0.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* Deletion of more than one message has been optimized. This could make
|
||||||
|
deleting large numbers of messages far faster -- several orders of
|
||||||
|
magnitude.
|
||||||
|
* Moved more sleep code into ui layer. Fancier sleep actions are now
|
||||||
|
possible. Better handling of Ctrl-C in TTY handler.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Tue, 2 Jul 2002 19:16:04 -0500
|
||||||
|
|
||||||
|
offlineimap (1.0.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed a bug when a message was deleted on the IMAP side and modified
|
||||||
|
on the local side.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Mon, 24 Jun 2002 19:08:21 -0500
|
||||||
|
|
||||||
|
offlineimap (1.0.2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Made sure that LocalStatus does writing atomically. If the program
|
||||||
|
is interrupted during save(), there will always be a complete copy of
|
||||||
|
either the old or the new data.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Mon, 24 Jun 2002 06:57:28 -0500
|
||||||
|
|
||||||
|
offlineimap (1.0.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fixed a bug with writing messages to some IMAP servers. Turns
|
||||||
|
out we need to issue CHECK between APPEND and SEARCH for some.
|
||||||
|
Thanks to Donovan Lange for reporting this bug and helping track it
|
||||||
|
down.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 21 Jun 2002 22:03:12 -0500
|
||||||
|
|
||||||
|
offlineimap (1.0.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* Initial Release. Closes: #150571.
|
||||||
|
|
||||||
|
-- John Goerzen <jgoerzen@complete.org> Fri, 21 Jun 2002 18:54:56 -0500
|
||||||
|
|
||||||
|
Local variables:
|
||||||
|
mode: debian-changelog
|
||||||
|
End:
|
46
offlineimap/head/offlineimap/CustomConfig.py
Normal file
46
offlineimap/head/offlineimap/CustomConfig.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright (C) 2003 John Goerzen
|
||||||
|
# <jgoerzen@complete.org>
|
||||||
|
#
|
||||||
|
# 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 ConfigParser import ConfigParser
|
||||||
|
from offlineimap.localeval import LocalEval
|
||||||
|
import os
|
||||||
|
|
||||||
|
class CustomConfigParser(ConfigParser):
|
||||||
|
def getdefault(self, section, option, default, *args, **kwargs):
|
||||||
|
"""Same as config.get, but returns the "default" option if there
|
||||||
|
is no such option specified."""
|
||||||
|
if self.has_option(section, option):
|
||||||
|
return apply(self.get, [section, option] + list(args), kwargs)
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def getmetadatadir(self):
|
||||||
|
metadatadir = os.path.expanduser(self.getdefault("general", "metadata", "~/.offlineimap"))
|
||||||
|
if not os.path.exists(metadatadir):
|
||||||
|
os.mkdir(metadatadir, 0700)
|
||||||
|
return metadatadir
|
||||||
|
|
||||||
|
def getlocaleval(self):
|
||||||
|
if self.has_option("general", "pythonfile"):
|
||||||
|
path = os.path.expanduser(self.get("general", "pythonfile"))
|
||||||
|
else:
|
||||||
|
path = None
|
||||||
|
return LocalEval(path)
|
||||||
|
|
||||||
|
def getaccountlist(self):
|
||||||
|
return [x for x in self.sections() if x != 'general']
|
||||||
|
|
211
offlineimap/head/offlineimap/accounts.py
Normal file
211
offlineimap/head/offlineimap/accounts.py
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# Copyright (C) 2003 John Goerzen
|
||||||
|
# <jgoerzen@complete.org>
|
||||||
|
#
|
||||||
|
# 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 offlineimap import imapserver, repository, threadutil
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
|
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||||
|
from threading import Event
|
||||||
|
import os
|
||||||
|
|
||||||
|
mailboxes = []
|
||||||
|
|
||||||
|
class Account:
|
||||||
|
def __init__(self, config, name):
|
||||||
|
self.config = config
|
||||||
|
self.name = name
|
||||||
|
self.metadatadir = config.getmetadatadir()
|
||||||
|
self.localeval = config.getlocaleval()
|
||||||
|
self.server = imapserver.ConfigedIMAPServer(config, self.name)
|
||||||
|
self.ui = UIBase.getglobalui()
|
||||||
|
if self.config.has_option(self.name, 'autorefresh'):
|
||||||
|
self.refreshperiod = self.config.getint(self.name, 'autorefresh')
|
||||||
|
else:
|
||||||
|
self.refreshperiod = None
|
||||||
|
self.hold = self.config.has_option(self.name, 'holdconnectionopen') \
|
||||||
|
and self.config.getboolean(self.name, 'holdconnectionopen')
|
||||||
|
if self.config.has_option(self.name, 'keepalive'):
|
||||||
|
self.keepalive = self.config.getint(self.name, 'keepalive')
|
||||||
|
else:
|
||||||
|
self.keepalive = None
|
||||||
|
|
||||||
|
def getconf(self, option, default = None):
|
||||||
|
if default != None:
|
||||||
|
return self.config.get(self.name, option)
|
||||||
|
else:
|
||||||
|
return self.config.getdefault(self.name, option,
|
||||||
|
default)
|
||||||
|
|
||||||
|
def sleeper(self):
|
||||||
|
"""Sleep handler. Returns same value as UIBase.sleep:
|
||||||
|
0 if timeout expired, 1 if there was a request to cancel the timer,
|
||||||
|
and 2 if there is a request to abort the program.
|
||||||
|
|
||||||
|
Also, returns 100 if configured to not sleep at all."""
|
||||||
|
|
||||||
|
if not self.refreshperiod:
|
||||||
|
return 100
|
||||||
|
refreshperiod = self.refreshperiod * 60
|
||||||
|
if self.keepalive:
|
||||||
|
kaevent = Event()
|
||||||
|
kathread = ExitNotifyThread(target = self.server.keepalive,
|
||||||
|
name = "Keep alive " + self.name,
|
||||||
|
args = (self.keepalive, kaevent))
|
||||||
|
kathread.setDaemon(1)
|
||||||
|
kathread.start()
|
||||||
|
sleepresult = self.ui.sleep(refreshperiod)
|
||||||
|
if sleepresult == 2:
|
||||||
|
# Cancel keep-alive, but don't bother terminating threads
|
||||||
|
if self.keepalive:
|
||||||
|
kaevent.set()
|
||||||
|
return sleepresult
|
||||||
|
else:
|
||||||
|
# Cancel keep-alive and wait for thread to terminate.
|
||||||
|
if self.keepalive:
|
||||||
|
kaevent.set()
|
||||||
|
kathread.join()
|
||||||
|
return sleepresult
|
||||||
|
|
||||||
|
class AccountSynchronizationMixin:
|
||||||
|
def syncrunner(self):
|
||||||
|
self.ui.acct(self.name)
|
||||||
|
if not self.refreshperiod:
|
||||||
|
self.sync()
|
||||||
|
self.ui.acctdone(self.name)
|
||||||
|
return
|
||||||
|
looping = 1
|
||||||
|
while looping:
|
||||||
|
self.sync()
|
||||||
|
looping = self.sleeper() != 2
|
||||||
|
self.ui.acctdone(self.name)
|
||||||
|
|
||||||
|
def sync(self):
|
||||||
|
# We don't need an account lock because syncitall() goes through
|
||||||
|
# each account once, then waits for all to finish.
|
||||||
|
try:
|
||||||
|
accountmetadata = os.path.join(self.metadatadir, self.name)
|
||||||
|
if not os.path.exists(accountmetadata):
|
||||||
|
os.mkdir(accountmetadata, 0700)
|
||||||
|
|
||||||
|
remoterepos = repository.IMAP.IMAPRepository(self.config,
|
||||||
|
self.localeval,
|
||||||
|
self.name,
|
||||||
|
self.server)
|
||||||
|
|
||||||
|
# Connect to the Maildirs.
|
||||||
|
localrepos = repository.Maildir.MaildirRepository(os.path.expanduser(self.config.get(self.name, "localfolders")), self.name, self.config)
|
||||||
|
|
||||||
|
# Connect to the local cache.
|
||||||
|
statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata)
|
||||||
|
|
||||||
|
self.ui.syncfolders(remoterepos, localrepos)
|
||||||
|
remoterepos.syncfoldersto(localrepos)
|
||||||
|
|
||||||
|
folderthreads = []
|
||||||
|
for remotefolder in remoterepos.getfolders():
|
||||||
|
thread = InstanceLimitedThread(\
|
||||||
|
instancename = 'FOLDER_' + self.name,
|
||||||
|
target = syncfolder,
|
||||||
|
name = "Folder sync %s[%s]" % \
|
||||||
|
(self.name, remotefolder.getvisiblename()),
|
||||||
|
args = (self.name, remoterepos, remotefolder, localrepos,
|
||||||
|
statusrepos))
|
||||||
|
thread.setDaemon(1)
|
||||||
|
thread.start()
|
||||||
|
folderthreads.append(thread)
|
||||||
|
threadutil.threadsreset(folderthreads)
|
||||||
|
if not self.hold:
|
||||||
|
server.close()
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SyncableAccount(Account, AccountSynchronizationMixin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def syncfolder(accountname, remoterepos, remotefolder, localrepos,
|
||||||
|
statusrepos):
|
||||||
|
global mailboxes
|
||||||
|
ui = UIBase.getglobalui()
|
||||||
|
# Load local folder.
|
||||||
|
localfolder = localrepos.\
|
||||||
|
getfolder(remotefolder.getvisiblename().\
|
||||||
|
replace(remoterepos.getsep(), localrepos.getsep()))
|
||||||
|
# Write the mailboxes
|
||||||
|
mailboxes.append({'accountname': accountname,
|
||||||
|
'foldername': localfolder.getvisiblename()})
|
||||||
|
# Load local folder
|
||||||
|
ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder)
|
||||||
|
ui.loadmessagelist(localrepos, localfolder)
|
||||||
|
localfolder.cachemessagelist()
|
||||||
|
ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys()))
|
||||||
|
|
||||||
|
|
||||||
|
# Load status folder.
|
||||||
|
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.
|
||||||
|
statusfolder.deletemessagelist()
|
||||||
|
|
||||||
|
statusfolder.cachemessagelist()
|
||||||
|
|
||||||
|
|
||||||
|
# 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, save it off.
|
||||||
|
if (len(localfolder.getmessagelist()) or \
|
||||||
|
len(statusfolder.getmessagelist())) and \
|
||||||
|
not localfolder.isuidvalidityok(remotefolder):
|
||||||
|
ui.validityproblem(remotefolder)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
localfolder.saveuidvalidity(remotefolder.getuidvalidity())
|
||||||
|
|
||||||
|
# Load remote folder.
|
||||||
|
ui.loadmessagelist(remoterepos, remotefolder)
|
||||||
|
remotefolder.cachemessagelist()
|
||||||
|
ui.messagelistloaded(remoterepos, remotefolder,
|
||||||
|
len(remotefolder.getmessagelist().keys()))
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
if not statusfolder.isnewfolder():
|
||||||
|
# Delete local copies of remote messages. This way,
|
||||||
|
# if a message's flag is modified locally but it has been
|
||||||
|
# deleted remotely, we'll delete it locally. Otherwise, we
|
||||||
|
# try to modify a deleted message's flags! This step
|
||||||
|
# need only be taken if a statusfolder is present; otherwise,
|
||||||
|
# there is no action taken *to* the remote repository.
|
||||||
|
|
||||||
|
remotefolder.syncmessagesto_delete(localfolder, [localfolder,
|
||||||
|
statusfolder])
|
||||||
|
ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder)
|
||||||
|
localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder])
|
||||||
|
|
||||||
|
# Synchronize remote changes.
|
||||||
|
ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder)
|
||||||
|
remotefolder.syncmessagesto(localfolder)
|
||||||
|
|
||||||
|
# Make sure the status folder is up-to-date.
|
||||||
|
ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder)
|
||||||
|
localfolder.syncmessagesto(statusfolder)
|
||||||
|
statusfolder.save()
|
||||||
|
|
@ -21,7 +21,7 @@ from offlineimap.localeval import LocalEval
|
|||||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||||
from offlineimap.ui import UIBase
|
from offlineimap.ui import UIBase
|
||||||
import re, os, os.path, offlineimap, sys
|
import re, os, os.path, offlineimap, sys
|
||||||
from ConfigParser import ConfigParser
|
from offlineimap.CustomConfig import CustomConfigParser
|
||||||
from threading import *
|
from threading import *
|
||||||
from getopt import getopt
|
from getopt import getopt
|
||||||
|
|
||||||
@ -51,20 +51,14 @@ def startup(versionno):
|
|||||||
threadutil.setprofiledir(profiledir)
|
threadutil.setprofiledir(profiledir)
|
||||||
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
|
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
|
||||||
|
|
||||||
config = ConfigParser()
|
config = CustomConfigParser()
|
||||||
if not os.path.exists(configfilename):
|
if not os.path.exists(configfilename):
|
||||||
sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename)
|
sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
config.read(configfilename)
|
config.read(configfilename)
|
||||||
|
|
||||||
if config.has_option("general", "pythonfile"):
|
ui = offlineimap.ui.detector.findUI(config, options.get('-u'))
|
||||||
path=os.path.expanduser(config.get("general", "pythonfile"))
|
|
||||||
else:
|
|
||||||
path=None
|
|
||||||
localeval = LocalEval(path)
|
|
||||||
|
|
||||||
ui = offlineimap.ui.detector.findUI(config, localeval, options.get('-u'))
|
|
||||||
ui.init_banner()
|
ui.init_banner()
|
||||||
UIBase.setglobalui(ui)
|
UIBase.setglobalui(ui)
|
||||||
|
|
||||||
@ -74,12 +68,9 @@ def startup(versionno):
|
|||||||
if debugtype == 'imap':
|
if debugtype == 'imap':
|
||||||
imaplib.Debug = 5
|
imaplib.Debug = 5
|
||||||
|
|
||||||
if '-o' in options and config.has_option("general", "autorefresh"):
|
if '-o' in options:
|
||||||
config.remove_option("general", "autorefresh")
|
for section in config.getaccountlist():
|
||||||
|
config.remove_option(section, "autorefresh")
|
||||||
metadatadir = os.path.expanduser(config.get("general", "metadata"))
|
|
||||||
if not os.path.exists(metadatadir):
|
|
||||||
os.mkdir(metadatadir, 0700)
|
|
||||||
|
|
||||||
accounts = config.get("general", "accounts")
|
accounts = config.get("general", "accounts")
|
||||||
if '-a' in options:
|
if '-a' in options:
|
||||||
@ -105,17 +96,11 @@ def startup(versionno):
|
|||||||
threadutil.initInstanceLimit(instancename,
|
threadutil.initInstanceLimit(instancename,
|
||||||
config.getint(account, "maxconnections"))
|
config.getint(account, "maxconnections"))
|
||||||
|
|
||||||
mailboxes = []
|
|
||||||
servers = {}
|
|
||||||
|
|
||||||
threadutil.initexitnotify()
|
threadutil.initexitnotify()
|
||||||
t = ExitNotifyThread(target=syncmaster.sync_with_timer,
|
t = ExitNotifyThread(target=syncmaster.syncitall,
|
||||||
name='Sync Runner',
|
name='Sync Runner',
|
||||||
kwargs = {'accounts': accounts,
|
kwargs = {'accounts': accounts,
|
||||||
'metadatadir': metadatadir,
|
'config': config})
|
||||||
'servers': servers,
|
|
||||||
'config': config,
|
|
||||||
'localeval': localeval})
|
|
||||||
t.setDaemon(1)
|
t.setDaemon(1)
|
||||||
t.start()
|
t.start()
|
||||||
try:
|
try:
|
||||||
|
@ -19,9 +19,10 @@
|
|||||||
import os.path
|
import os.path
|
||||||
import re # for folderfilter
|
import re # for folderfilter
|
||||||
|
|
||||||
def genmbnames(config, localeval, boxlist):
|
def genmbnames(config, boxlist):
|
||||||
"""Takes a configparser object and a boxlist, which is a list of hashes
|
"""Takes a configparser object and a boxlist, which is a list of hashes
|
||||||
containing 'accountname' and 'foldername' keys."""
|
containing 'accountname' and 'foldername' keys."""
|
||||||
|
localeval = config.getlocaleval()
|
||||||
if not config.getboolean("mbnames", "enabled"):
|
if not config.getboolean("mbnames", "enabled"):
|
||||||
return
|
return
|
||||||
file = open(os.path.expanduser(config.get("mbnames", "filename")), "wt")
|
file = open(os.path.expanduser(config.get("mbnames", "filename")), "wt")
|
||||||
|
@ -18,187 +18,29 @@
|
|||||||
|
|
||||||
from offlineimap import imaplib, imapserver, repository, folder, mbnames, threadutil, version
|
from offlineimap import imaplib, imapserver, repository, folder, mbnames, threadutil, version
|
||||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||||
|
import offlineimap.accounts
|
||||||
|
from offlineimap.accounts import SyncableAccount
|
||||||
from offlineimap.ui import UIBase
|
from offlineimap.ui import UIBase
|
||||||
import re, os, os.path, offlineimap, sys
|
import re, os, os.path, offlineimap, sys
|
||||||
from ConfigParser import ConfigParser
|
from ConfigParser import ConfigParser
|
||||||
from threading import *
|
from threading import *
|
||||||
|
|
||||||
def syncaccount(accountname, metadatadir, servers, config,
|
def syncaccount(threads, config, accountname):
|
||||||
localeval, *args):
|
account = SyncableAccount(config, accountname)
|
||||||
ui = UIBase.getglobalui()
|
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
|
||||||
# We don't need an account lock because syncitall() goes through
|
target = account.syncrunner,
|
||||||
# each account once, then waits for all to finish.
|
name = "Account sync %s" % accountname)
|
||||||
try:
|
thread.setDaemon(1)
|
||||||
ui.acct(accountname)
|
thread.start()
|
||||||
accountmetadata = os.path.join(metadatadir, accountname)
|
threads.add(thread)
|
||||||
if not os.path.exists(accountmetadata):
|
|
||||||
os.mkdir(accountmetadata, 0700)
|
|
||||||
|
|
||||||
server = None
|
def syncitall(accounts, config):
|
||||||
if accountname in servers:
|
|
||||||
server = servers[accountname]
|
|
||||||
else:
|
|
||||||
server = imapserver.ConfigedIMAPServer(config, accountname)
|
|
||||||
servers[accountname] = server
|
|
||||||
|
|
||||||
remoterepos = repository.IMAP.IMAPRepository(config, localeval, accountname, server)
|
|
||||||
|
|
||||||
# Connect to the Maildirs.
|
|
||||||
localrepos = repository.Maildir.MaildirRepository(os.path.expanduser(config.get(accountname, "localfolders")), accountname, config)
|
|
||||||
|
|
||||||
# Connect to the local cache.
|
|
||||||
statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata)
|
|
||||||
|
|
||||||
ui.syncfolders(remoterepos, localrepos)
|
|
||||||
remoterepos.syncfoldersto(localrepos)
|
|
||||||
ui.acct(accountname)
|
|
||||||
|
|
||||||
folderthreads = []
|
|
||||||
for remotefolder in remoterepos.getfolders():
|
|
||||||
thread = InstanceLimitedThread(\
|
|
||||||
instancename = 'FOLDER_' + accountname,
|
|
||||||
target = syncfolder,
|
|
||||||
name = "Folder sync %s[%s]" % \
|
|
||||||
(accountname, remotefolder.getvisiblename()),
|
|
||||||
args = (accountname, remoterepos, remotefolder, localrepos,
|
|
||||||
statusrepos))
|
|
||||||
thread.setDaemon(1)
|
|
||||||
thread.start()
|
|
||||||
folderthreads.append(thread)
|
|
||||||
threadutil.threadsreset(folderthreads)
|
|
||||||
if not (config.has_option(accountname, 'holdconnectionopen') and \
|
|
||||||
config.getboolean(accountname, 'holdconnectionopen')):
|
|
||||||
server.close()
|
|
||||||
finally:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def syncfolder(accountname, remoterepos, remotefolder, localrepos,
|
|
||||||
statusrepos):
|
|
||||||
ui = UIBase.getglobalui()
|
|
||||||
# Load local folder.
|
|
||||||
localfolder = localrepos.\
|
|
||||||
getfolder(remotefolder.getvisiblename().\
|
|
||||||
replace(remoterepos.getsep(), localrepos.getsep()))
|
|
||||||
# Write the mailboxes
|
|
||||||
mailboxes.append({'accountname': accountname,
|
|
||||||
'foldername': localfolder.getvisiblename()})
|
|
||||||
# Load local folder
|
|
||||||
ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder)
|
|
||||||
ui.loadmessagelist(localrepos, localfolder)
|
|
||||||
localfolder.cachemessagelist()
|
|
||||||
ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys()))
|
|
||||||
|
|
||||||
|
|
||||||
# Load status folder.
|
|
||||||
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.
|
|
||||||
statusfolder.deletemessagelist()
|
|
||||||
|
|
||||||
statusfolder.cachemessagelist()
|
|
||||||
|
|
||||||
|
|
||||||
# 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, save it off.
|
|
||||||
if (len(localfolder.getmessagelist()) or \
|
|
||||||
len(statusfolder.getmessagelist())) and \
|
|
||||||
not localfolder.isuidvalidityok(remotefolder):
|
|
||||||
ui.validityproblem(remotefolder)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
localfolder.saveuidvalidity(remotefolder.getuidvalidity())
|
|
||||||
|
|
||||||
# Load remote folder.
|
|
||||||
ui.loadmessagelist(remoterepos, remotefolder)
|
|
||||||
remotefolder.cachemessagelist()
|
|
||||||
ui.messagelistloaded(remoterepos, remotefolder,
|
|
||||||
len(remotefolder.getmessagelist().keys()))
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
if not statusfolder.isnewfolder():
|
|
||||||
# Delete local copies of remote messages. This way,
|
|
||||||
# if a message's flag is modified locally but it has been
|
|
||||||
# deleted remotely, we'll delete it locally. Otherwise, we
|
|
||||||
# try to modify a deleted message's flags! This step
|
|
||||||
# need only be taken if a statusfolder is present; otherwise,
|
|
||||||
# there is no action taken *to* the remote repository.
|
|
||||||
|
|
||||||
remotefolder.syncmessagesto_delete(localfolder, [localfolder,
|
|
||||||
statusfolder])
|
|
||||||
ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder)
|
|
||||||
localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder])
|
|
||||||
|
|
||||||
# Synchronize remote changes.
|
|
||||||
ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder)
|
|
||||||
remotefolder.syncmessagesto(localfolder)
|
|
||||||
|
|
||||||
# Make sure the status folder is up-to-date.
|
|
||||||
ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder)
|
|
||||||
localfolder.syncmessagesto(statusfolder)
|
|
||||||
statusfolder.save()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def syncitall(accounts, metadatadir, servers, config, localeval):
|
|
||||||
ui = UIBase.getglobalui()
|
|
||||||
global mailboxes
|
|
||||||
mailboxes = [] # Reset.
|
|
||||||
threads = []
|
|
||||||
for accountname in accounts:
|
|
||||||
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
|
|
||||||
target = syncaccount,
|
|
||||||
name = "Account sync %s" % accountname,
|
|
||||||
args = (accountname, metadatadir,
|
|
||||||
servers, config,
|
|
||||||
localeval))
|
|
||||||
thread.setDaemon(1)
|
|
||||||
thread.start()
|
|
||||||
threads.append(thread)
|
|
||||||
# Wait for the threads to finish.
|
|
||||||
threadutil.threadsreset(threads)
|
|
||||||
mbnames.genmbnames(config, localeval, mailboxes)
|
|
||||||
|
|
||||||
def sync_with_timer(accounts, metadatadir, servers, config,
|
|
||||||
localeval):
|
|
||||||
ui = UIBase.getglobalui()
|
|
||||||
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
||||||
syncitall(accounts, metadatadir, servers, config, localeval)
|
ui = UIBase.getglobalui()
|
||||||
if config.has_option('general', 'autorefresh'):
|
threads = threadutil.threadlist()
|
||||||
refreshperiod = config.getint('general', 'autorefresh') * 60
|
offlineimap.accounts.mailboxes = [] # Reset.
|
||||||
while 1:
|
for accountname in accounts:
|
||||||
# Set up keep-alives.
|
syncaccount(threads, config, accountname)
|
||||||
kaevents = {}
|
# Wait for the threads to finish.
|
||||||
kathreads = {}
|
threads.reset()
|
||||||
for accountname in accounts:
|
mbnames.genmbnames(config, offlineimap.accounts.mailboxes)
|
||||||
if config.has_option(accountname, 'holdconnectionopen') and \
|
|
||||||
config.getboolean(accountname, 'holdconnectionopen') and \
|
|
||||||
config.has_option(accountname, 'keepalive'):
|
|
||||||
event = Event()
|
|
||||||
kaevents[accountname] = event
|
|
||||||
thread = ExitNotifyThread(target = servers[accountname].keepalive,
|
|
||||||
name = "Keep alive " + accountname,
|
|
||||||
args = (config.getint(accountname, 'keepalive'), event))
|
|
||||||
thread.setDaemon(1)
|
|
||||||
thread.start()
|
|
||||||
kathreads[accountname] = thread
|
|
||||||
if ui.sleep(refreshperiod) == 2:
|
|
||||||
# Cancel keep-alives, but don't bother terminating threads
|
|
||||||
for event in kaevents.values():
|
|
||||||
event.set()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Cancel keep-alives and wait for threads to terminate.
|
|
||||||
for event in kaevents.values():
|
|
||||||
event.set()
|
|
||||||
for thread in kathreads.values():
|
|
||||||
thread.join()
|
|
||||||
syncitall(accounts, metadatadir, servers, config,
|
|
||||||
localeval)
|
|
||||||
|
17
offlineimap/head/offlineimap/test.py
Normal file
17
offlineimap/head/offlineimap/test.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/python2.2 -i
|
||||||
|
import hmac
|
||||||
|
def getpassword():
|
||||||
|
return 'tanstaaftanstaaf'
|
||||||
|
|
||||||
|
def md5handler(response):
|
||||||
|
challenge = response.strip()
|
||||||
|
print "challenge is", challenge
|
||||||
|
msg = getpassword()
|
||||||
|
reply = hmac.new(challenge, msg)
|
||||||
|
retval = 'tim' + ' ' + \
|
||||||
|
reply.hexdigest()
|
||||||
|
while len(retval) < 64:
|
||||||
|
retval += "\0"
|
||||||
|
|
||||||
|
print "md5handler returning", retval
|
||||||
|
return retval
|
@ -48,6 +48,42 @@ def threadsreset(threadlist):
|
|||||||
for thr in threadlist:
|
for thr in threadlist:
|
||||||
thr.join()
|
thr.join()
|
||||||
|
|
||||||
|
class threadlist:
|
||||||
|
def __init__(self):
|
||||||
|
self.lock = Lock()
|
||||||
|
self.list = []
|
||||||
|
|
||||||
|
def add(self, thread):
|
||||||
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
self.list.append(thread)
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
def remove(self, thread):
|
||||||
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
self.list.remove(thread)
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
if not len(self.list):
|
||||||
|
return None
|
||||||
|
return self.list.pop()
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
while 1:
|
||||||
|
thread = self.pop()
|
||||||
|
if not thread:
|
||||||
|
return
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Exit-notify threads
|
# Exit-notify threads
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -20,7 +20,7 @@ import sys, time
|
|||||||
from UIBase import UIBase
|
from UIBase import UIBase
|
||||||
|
|
||||||
class Basic(UIBase):
|
class Basic(UIBase):
|
||||||
def getpass(s, accountname, config):
|
def getpass(s, accountname, config, errmsg = None):
|
||||||
raise NotImplementedError, "Prompting for a password is not supported in noninteractive mode."
|
raise NotImplementedError, "Prompting for a password is not supported in noninteractive mode."
|
||||||
|
|
||||||
def _msg(s, msg):
|
def _msg(s, msg):
|
||||||
|
@ -140,6 +140,10 @@ class UIBase:
|
|||||||
if s.verbose >= 0:
|
if s.verbose >= 0:
|
||||||
s._msg("***** Processing account %s" % accountname)
|
s._msg("***** Processing account %s" % accountname)
|
||||||
|
|
||||||
|
def acctdone(s, accountname):
|
||||||
|
if s.verbose >= 0:
|
||||||
|
s._msg("***** Finished processing account " + accountname)
|
||||||
|
|
||||||
def syncfolders(s, srcrepos, destrepos):
|
def syncfolders(s, srcrepos, destrepos):
|
||||||
if s.verbose >= 0:
|
if s.verbose >= 0:
|
||||||
s._msg("Copying folder structure from %s to %s" % \
|
s._msg("Copying folder structure from %s to %s" % \
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import offlineimap.ui
|
import offlineimap.ui
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def findUI(config, localeval, chosenUI=None):
|
def findUI(config, chosenUI=None):
|
||||||
uistrlist = ['Tk.Blinkenlights', 'Tk.VerboseUI', 'TTY.TTYUI',
|
uistrlist = ['Tk.Blinkenlights', 'Tk.VerboseUI', 'TTY.TTYUI',
|
||||||
'Noninteractive.Basic', 'Noninteractive.Quiet']
|
'Noninteractive.Basic', 'Noninteractive.Quiet']
|
||||||
namespace={}
|
namespace={}
|
||||||
@ -34,7 +34,7 @@ def findUI(config, localeval, chosenUI=None):
|
|||||||
uistrlist = config.get("general", "ui").replace(" ", "").split(",")
|
uistrlist = config.get("general", "ui").replace(" ", "").split(",")
|
||||||
|
|
||||||
for uistr in uistrlist:
|
for uistr in uistrlist:
|
||||||
uimod = getUImod(uistr, localeval, namespace)
|
uimod = getUImod(uistr, config.getlocaleval(), namespace)
|
||||||
if uimod:
|
if uimod:
|
||||||
uiinstance = uimod(config)
|
uiinstance = uimod(config)
|
||||||
if uiinstance.isusable():
|
if uiinstance.isusable():
|
||||||
|
Loading…
Reference in New Issue
Block a user