Step 1 of rearranging per r129

This commit is contained in:
John Goerzen
2005-04-16 20:05:39 +01:00
parent 3c3b2e1ea7
commit 035fa2a96e
41 changed files with 0 additions and 0 deletions

340
offlineimap/head/COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,22 @@
offlineimap Mail syncing software
Copyright (C) 2002 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
imaplib.py comes from Python dev tree and is licensed inder the
GPL-compatible PSF license as follows:
ONLY imaplib.py is Copyright (c) 2001 Python Software Foundation;
All Rights Reserved

38
offlineimap/head/Makefile Normal file
View File

@ -0,0 +1,38 @@
# Copyright (C) 2002 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
SHELL=/bin/bash
clean:
-python2.2 setup.py clean --all
-rm -f `find . -name "*~"`
-rm -f `find . -name "*.pyc"`
-rm -f `find . -name "*.pygc"`
-rm -f `find . -name "*.class"`
-rm -f `find . -name "*.bak"`
-rm -f `find . -name ".cache*"`
-find . -name auth -exec rm -vf {}/password {}/username \;
changelog:
cvs2cl
cvs commit ChangeLog
rm ChangeLog.bak
docs:
man -t -l offlineimap.1 > manual.ps
ps2pdf manual.ps
groff -Tascii -man offlineimap.1 | sed $$'s/.\b//g' > manual.txt
groff -Thtml -man offlineimap.1 > manual.html

11
offlineimap/head/README Normal file
View File

@ -0,0 +1,11 @@
OfflineIMAP
Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
This software comes with ABSOLUTELY NO WARRANTY; see the file
COPYING for details. This is free software, and you are welcome
to distribute it under the conditions laid out in COPYING.
gopher://quux.org/1/devel/offlineimap
http://quux.org/devel/offlineimap
Please see manual.txt; the information previously in README has been moved
there.

6
offlineimap/head/TODO Normal file
View File

@ -0,0 +1,6 @@
* Add an option to handle the network exception that results if a connection
to the IMAP server fails, or there is another socket error.
* Force unidirectional sync for read-only folders.
* Add a -checkins mailing list.

View File

@ -0,0 +1,196 @@
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.
-- John Goerzen <jgoerzen@complete.org> Mon, 15 Jul 2002 19: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:

View File

@ -0,0 +1,39 @@
Source: offlineimap
Section: mail
Priority: optional
Maintainer: John Goerzen <jgoerzen@complete.org>
Build-Depends-Indep: debhelper (>> 3.0.0), python2.2 (>= 2.2.1-4)
Standards-Version: 3.5.2
Package: offlineimap
Architecture: all
Depends: python2.2
Suggests: python2.2-tk
Description: IMAP/Maildir synchronization and reader support
OfflineIMAP is a tool to simplify your e-mail reading. With
OfflimeIMAP, you can:
.
* Read the same mailbox from multiple computers, and have your
changes (deletions, etc.) be automatically reflected on
all computers
.
* Use various mail clients to read a single mail box
.
* Read mail while offline (on a laptop) and have all changes
synchronized when you get connected again
.
* Read IMAP mail with mail readers that do not support IMAP
.
* Use SSL (secure connections) to read IMAP mail even if your reader
doesn't support SSL
.
* Synchronize your mail using a completely safe and fault-tolerant
algorithm. (At least I think it is!)
.
* Customize which mailboxes to synchronize with regular expressions
or lists.
.
* Synchronize your mail two to four times faster than with other tools
or other mail readers' internal IMAP support.
.
In short, OfflineIMAP is a tool to let you read mail how YOU want to.

View File

@ -0,0 +1,23 @@
This is offlineimap, written and maintained by John Goerzen <jgoerzen@complete.org>
on Fri, 21 Jun 2002 14:54:56 -0500.
The original source can always be found at:
ftp://ftp.debian.org/dists/unstable/main/source/
Copyright (C) 2002 John Goerzen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License with
the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL-2;
if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA

View File

@ -0,0 +1,2 @@
usr/bin
usr/sbin

View File

@ -0,0 +1,5 @@
manual.txt
manual.ps
manual.pdf
manual.html

View File

@ -0,0 +1,101 @@
#!/usr/bin/make -f
# Sample debian/rules that uses debhelper.
# GNU copyright 1997 to 1999 by Joey Hess.
# Modified by John Goerzen
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# This is the debhelper compatibility version to use.
export DH_COMPAT=3
PYTHON=python2.2
PACKAGE=offlineimap
ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
CFLAGS += -g
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
INSTALL_PROGRAM += -s
endif
configure: configure-stamp
configure-stamp:
dh_testdir
# Add here commands to configure the package.
#$(PYTHON) setup.py configure
touch configure-stamp
build: build-stamp
build-stamp: configure-stamp
dh_testdir
# Add here commands to compile the package.
$(PYTHON) setup.py build
#/usr/bin/docbook-to-man debian/pygopherd.sgml > pygopherd.1
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
# Add here commands to clean up after the build process.
-$(MAKE) clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# Add here commands to install the package into debian/pygopherd.
#$(MAKE) install DESTDIR=$(CURDIR)/debian/pygopherd
$(PYTHON) setup.py install --root=`pwd`/debian/$(PACKAGE) --no-compile
# Build architecture-dependent files here.
binary-arch: build install
# We have nothing to do by default.
# Build architecture-independent files here.
binary-indep: build install
dh_testdir
dh_testroot
# dh_installdebconf
dh_installdocs
mv debian/$(PACKAGE)/usr/bin/$(PACKAGE).py \
debian/$(PACKAGE)/usr/bin/$(PACKAGE)
dh_installexamples offlineimap.conf
dh_installmenu
# dh_installlogrotate
# dh_installemacsen
# dh_installpam
# dh_installmime
dh_installinit
dh_installcron
dh_installman offlineimap.1
dh_installinfo
# dh_undocumented
dh_installchangelogs
dh_link
dh_strip
dh_compress
dh_fixperms
# dh_makeshlibs
dh_installdeb
# dh_perl
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

View File

@ -0,0 +1,982 @@
<html>
<head>
<meta name="generator" content="groff -Thtml, see www.gnu.org">
<meta name="Content-Style" content="text/css">
<title>OFFLINEIMAP</title>
</head>
<body>
<h1 align=center>OFFLINEIMAP</h1>
<a href="#NAME">NAME</a><br>
<a href="#SYNOPSIS">SYNOPSIS</a><br>
<a href="#DESCRIPTION">DESCRIPTION</a><br>
<a href="#INSTALLATION">INSTALLATION</a><br>
<a href="#CONFIGURATION">CONFIGURATION</a><br>
<a href="#OPTIONS">OPTIONS</a><br>
<a href="#EXAMPLES">EXAMPLES</a><br>
<a href="#ERRORS">ERRORS</a><br>
<a href="#OTHER FREQUENTLY ASKED QUESTIONS">OTHER FREQUENTLY ASKED QUESTIONS</a><br>
<a href="#CONFORMING TO">CONFORMING TO</a><br>
<a href="#NOTES">NOTES</a><br>
<a href="#BUGS">BUGS</a><br>
<a href="#COPYRIGHT">COPYRIGHT</a><br>
<a href="#AUTHOR">AUTHOR</a><br>
<a href="#SEE ALSO">SEE ALSO</a><br>
<hr>
<!-- Creator : groff version 1.17.2 -->
<!-- CreationDate: Mon Jul 15 11:26:37 2002 -->
<a name="NAME"></a>
<h2>NAME</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
OfflineIMAP - Powerful IMAP/Maildir synchronization and reader support</td></table>
<a name="SYNOPSIS"></a>
<h2>SYNOPSIS</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>offlineimap</b> [ <b>-1</b> ] [ <b>-a</b>
<i>accountlist</i> ] [ <b>-c</b> <i>configfile</i> ]<br>
[ <b>-d</b> ] [ <b>-o</b> ] [ <b>-u</b> <i>interface</i>
]</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>offlineimap -h</b> | <b>--help</b></td></table>
<a name="DESCRIPTION"></a>
<h2>DESCRIPTION</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> is a tool to simplify your e-mail
reading. With <b>OfflineIMAP,</b> you can read the same
mailbox from multiple computers. You get a current copy of
your messages on each computer, and changes you make one
place will be visible on all other systems. For instance,
you can delete a message on your home computer, and it will
appear deleted on your work computer as well.
<b>OfflineIMAP</b> is also useful if you want to use a mail
reader that does not have IMAP support, has poor IMAP
support, or does not provide disconnected
operation.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> is <i>FAST;</i> it synchronizes my two
accounts with over 50 folders in 3 seconds. Other similar
tools might take over a minute, and achieve a less-reliable
result. Some mail readers can take over 10 minutes to do the
same thing, and some don't even support it at all. Unlike
other mail tools, <b>OfflineIMAP</b> features a
multi-threaded synchronization algorithm that can
dramatically speed up performance in many situations by
synchronizing several different things
simultaneously.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> is <i>FLEXIBLE;</i> you can customize
which folders are synced via regular expressions, lists, or
Python expressions; a versatile and comprehensive
configuration file is used to control behavior; two user
interfaces are built-in; fine-tuning of synchronization
performance is possible; internal or external automation is
supported; SSL and PREAUTH tunnels are both supported;
offline (or &quot;unplugged&quot;) reading is supported; and
esoteric IMAP features are supported to ensure compatibility
with the widest variety of IMAP servers.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> is <i>SAFE;</i> it uses an algorithm
designed to prevent mail loss at all costs. Because of the
design of this algorithm, even programming errors should not
result in loss of mail. I am so confident in the algorithm
that I use my own personal and work accounts for testing of
<b>OfflineIMAP</b> pre-release, development, and beta
releases.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>METHOD OF OPERATION</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> operates by maintaining a hierarchy of
mail folders in Maildir format locally. Your own mail reader
will read mail from this tree, and need never know that the
mail comes from IMAP. <b>OfflineIMAP</b> will detect changes
to the mail folders on your IMAP server and your own
computer and bi-directionally synchronize them, copying,
marking, and deleting messages as necessary.</td></table>
<a name="INSTALLATION"></a>
<h2>INSTALLATION</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
If you are reading this document via the &quot;man&quot;
command, it is likely that you have no installation tasks to
perform; your system administrator has already installed it.
If you need to install it yourself, you have three options:
a system-wide installation with Debian, system-wide
installation with other systems, and a single-user
installation. You can download the latest version of
OfflineIMAP from
http://quux.org/devel/offlineimap/.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>PREREQUISITES</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
In order to use OfflineIMAP, you need to have these
conditions satisfied:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Your mail server must support IMAP. Most Internet Service
Providers and corporate networks do, and most operating
systems have an IMAP implementation readily
available.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
You must have Python version 2.2.1 or above installed. If
you are running on Debian GNU/Linux, this requirement will
automatically be taken care of for you. If you do not have
Python already, check with your system administrator or
operating system vendor; or, download it from
http://www.python.org/. If you intend to use the Tk
interface, you must have Tkiner (python-tk) installed. If
you intend to use the SSL interface, your Python must have
been built with SSL support.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Have a mail reader that supports the Maildir mailbox format.
Most modern mail readers have this support built-in, so you
can choose from a wide variety of mail servers. This format
is also known as the &quot;qmail&quot; format, so any mail
reader compatible with it will work with
OfflineIMAP.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>DEBIAN SYSTEM-WIDE INSTALLATION</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
If you are tracking Debian unstable, you may install
<b>OfflineIMAP</b> by simply running the following command
as root:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>apt-get install offlineimap</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
If you are not tracking Debian unstable, download the Debian
.deb package from the OfflineIMAP website and then run
<b>dpkg -i</b> to install the downloaded package. Then, go
to CONFIGURATION below. You will type <b>offlineimap</b> to
invoke the program.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>OTHER SYSTEM-WIDE INSTALLATION</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Download the tar.gz version of the package from the website.
Then run these commands:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>tar -zxvf offlineimap-x.y.z.tar.gz<br>
cd offlineimap-x.y.z<br>
python2.2 setup.py</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Some systems will need to use <b>python</b> instead of
<b>python2.2.</b> Next, proceed to configuration. You will
type <b>offlineimap</b> to invoke the program.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>SINGLE-ACCOUNT INSTALLATION</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Download the tar.gz version of the package from the website.
Then run these commands:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>tar -zxvf offlineimap-x.y.z.tar.gz<br>
cd offlineimap-x.y.z</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
When you want to run <b>OfflineIMAP,</b> you will issue the
<b>cd</b> command as above and then type
<b>./offlineimap;</b> there is no installation step
necessary.</td></table>
<a name="CONFIGURATION"></a>
<h2>CONFIGURATION</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> is regulated by a configuration file that
is normally stored in <i>~/.offlineimaprc.</i>
<b>OfflineIMAP</b> ships with a file named
<i>offlineimap.conf</i> that you should copy to that
location and then edit. This file is vital to proper
operation of the system; it sets everything you need to run
<b>OfflineIMAP.</b> Full documentation for the configuration
file is included within the sample file.</td></table>
<a name="OPTIONS"></a>
<h2>OPTIONS</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Most configuration is done via the configuration file.
Nevertheless, there are a few options that you may set for
<b>OfflineIMAP.</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-1</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Disable all multithreading operations and use solely a
single-thread sync. This effectively sets the
<b>maxsyncaccounts</b> and all <b>maxconnections</b>
configuration file variables to 1.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-a</b> <i>accountlist</i></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Overrides the <b>accounts</b> section in the config file.
Lets you specify a particular account or set of accounts to
sync without having to edit the config file. You might use
this to exclude certain accounts, or to sync some accounts
that you normally prefer not to.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-c</b> <i>configfile</i></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Specifies a configuration file to use in lieu of the
default, <i>~/.offlineimaprc.</i></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-d</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Enables IMAP protocol stream and parsing debugging. This is
useful if you are trying to track down a malfunction or
figure out what is going on under the hood. I suggest that
you use this with <b>-1</b> in order to make the results
more sensible. Note that this output will contain full IMAP
protocol in plain text, including passwords, so take care to
remove that from the debugging output before sending it to
anyone else.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-o</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Run only once, ignoring any autorefresh setting in the
config file.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-h, --help</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Show summary of options.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>-u</b> <i>interface</i></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Specifies an alternative user interface module to use. This
overrides the default specified in the configuration file.
The UI specified with <b>-u</b> will be forced to be used,
even if its <b>isuable()</b> method states that it cannot
be. Use this option with care.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
The pre-defined options are <b>Tk.TKUI</b> (a graphical
interface) and <b>TTY.TTYUI</b> (a text-mode
interface).</td></table>
<a name="EXAMPLES"></a>
<h2>EXAMPLES</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Here is an example configuration for a particularly complex
situation; more examples will be added later.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>MULTIPLE ACCOUNTS WITH MUTT</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
This example shows you how to set up <b>OfflineIMAP</b> to
synchronize multiple accounts with the mutt mail
reader.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Start by creating a directory to hold your folders:<b><br>
mkdir ~/Mail</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
In your <i>~/.offlineimaprc,</i> specify this:<b><br>
accounts = Personal, Work</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Make sure that you have both a <b>[Personal]</b> and a
<b>[Work]</b> section, with different localfolder pathnames
and enable <b>[mbnames].</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
In each account section, do something like this:<b><br>
localfolders = ~/Mail/Personal</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Add these lines to your <i>~/.muttrc:</i><b><br>
source ~/path-to-mbnames-muttrc-mailboxes<br>
folder-hook Personal set
from=&quot;youremail@personal.com&quot;<br>
folder-hook Work set from=&quot;youremail@work.com&quot;<br>
set mbox_type=Maildir<br>
set folder=$HOME/Mail<br>
set spoolfile=+Personal/INBOX</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
That's it!</td></table>
<a name="ERRORS"></a>
<h2>ERRORS</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
If you get one of some frequently-encountered or confusing
errors, please check this section.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>UID validity problem for folder</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
IMAP servers use a unique ID (UID) to refer to a specific
message. This number is guaranteed to be unique to a
particular message FOREVER. No other message in the same
folder will ever get the same UID. UIDs are an integral part
of OfflineIMAP's synchronization scheme; they are used to
match up messages on your computer to messages on the
server.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Sometimes, the UIDs on the server might get reset. Usually
this will happen if you delete and then recreate a folder.
When you create a folder, the server will often start the
UID back from 1. But <b>OfflineIMAP</b> might still have the
UIDs from the previous folder by the same name stored.
<b>OfflineIMAP</b> will detect this condition and skip the
folder. This is GOOD, because it prevents data
loss.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
You can fix it by removing your local folder and cache data.
For instance, if your folders are under <i>~/Folders</i> and
the folder with the problem is INBOX, you'd type
this:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>rm -r ~/Folders/INBOX<br>
rm ~/.offlineimap/AccountName/INBOX</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
(replacing AccountName with the account name as specified in
<i>~/.offlineimaprc)</i></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Next time you run <b>OfflineIMAP,</b> it will re-download
the folder with the new UIDs. Note that the procedure
specified above will lose any local changes made to the
folder.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Some IMAP servers are broken and do not support UIDs
properly. If you continue to get this error for all your
folders even after performing the above procedure, it is
likely that your IMAP server falls into this category.
<b>OfflineIMAP</b> is incompatible with such servers. Using
<b>OfflineIMAP</b> with them will not destroy any mail, but
at the same time, it will not actually synchronize it
either. (OfflineIMAP will detect this condition and abort
prior to synchronization)</td></table>
<a name="OTHER FREQUENTLY ASKED QUESTIONS"></a>
<h2>OTHER FREQUENTLY ASKED QUESTIONS</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
There are some other FAQs that might not fit into another
section of this document, and they are enumerated
here.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>What platforms does OfflineIMAP run on?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
It should run on most platforms supported by Python, which
are quite a few.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>I'm using Mutt. Other IMAP sync programs require me to
use set maildir_trash=yes . Do I need to do that with
OfflineIMAP?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
No. <b>OfflineIMAP</b> is smart enough to figure out message
deletion without this extra crutch. You'll get the best
results if you don't use this setting, in fact.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>How do I specify the names of my
folders?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
You do not need to. <b>OfflineIMAP</b> is smart enough to
automatically figure out what folders are present on the
IMAP server and synchronize them. You can use the
<b>folderfilter</b> and <b>foldertrans</b> configuration
file options to request certain folders and rename them as
they come in if you like.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>How can I prevent certain folders from being
synced?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Use the <b>folderfilter</b> option in the configuration
file.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>How can I add or delete a folder?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
<b>OfflineIMAP</b> does not currently provide this feature,
but if you create a new folder on the IMAP server, it will
be created locally automatically.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>Are there any other warnings that I should be aware
of?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Yes; see the NOTES section below.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>What is the mailbox name recorder (mbnames)
for?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
The Mutt mail reader is not capable of automatically
determining the names of your mailboxes. OfflineIMAP can
help it (or many other) programs out be writing these names
out in a format you specify. See the example
offlineimap.conf file for details.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>Can I synchronize multiple accounts with
OfflineIMAP?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Sure. Just name them all in the accounts line in the general
section of the config file, and add a per-account section
for each one.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>Does OfflineIMAP support POP?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
No. POP is not robust enough to do a completely reliable
multi-machine synchronization like OfflineIMAP can do.
OfflineIMAP will not support it.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>Do you support mailbox formats other than
Maildir?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Not at present. There is no technical reason not to; just no
demand yet. Maildir is a superior format
anyway.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>[technical] Why are your Maildir message filenames so
huge?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
<b>OfflineIMAP</b> has two relevant principles: 1) never
modifying your messages in any way and 2) ensuring 100%
reliable synchronizations. In order to do a reliable sync,
<b>OfflineIMAP</b> must have a way to uniquely identify each
e-mail. Three pieces of information are required to do this:
your account name, the folder name, and the message UID. The
account name can be calculated from the path in which your
messages are. The folder name can usually be as well, BUT
some mail clients move messages between folders by simply
moving the file, leaving the name intact.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
So, <b>OfflineIMAP</b> must store both a UID folder ID. The
folder ID is necessary so <b>OfflineIMAP</b> can detect a
message moved to a different folder. <b>OfflineIMAP</b>
stores the UID (U= number) and an md5sum of the foldername
(FMD5= number) to facilitate this.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>What is the speed of OfflineIMAP's sync?</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
<b>OfflineIMAP</b> versions 2.0 and above contain a
multithreaded system. A good way to experiment is by setting
maxsyncaccounts to 3 and maxconnections to 3 in each account
clause.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
This lets OfflineIMAP open up multiple connections
simultaneously. That will let it process multiple folders
and messages at once. In most cases, this will increase
performance of the sync.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Don't set the number too high. If you do that, things might
actually slow down as your link gets saturated. Also, too
many connections can cause mail servers to have excessive
load. Administrators might take unkindly to this, and the
server might bog down. There are many variables in the
optimal setting; experimentation may help.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
An informal benchmark yields these results for my
setup:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
10 minutes with MacOS X Mail.app &quot;manual
cache&quot;<br>
5 minutes with GNUS agent sync<br>
20 seconds with OfflineIMAP 1.x<br>
9 seconds with OfflineIMAP 2.x<br>
3 seconds with OfflineIMAP 3.x &quot;cold start&quot;<br>
2 seconds with OfflineIMAP 3.x &quot;held
connection&quot;</td></table>
<a name="CONFORMING TO"></a>
<h2>CONFORMING TO</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Internet Message Access Protocol version 4rev1 (IMAP 4rev1)
as specified in RFC2060</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Maildir as specified in
http://www.qmail.org/qmail-manual-html/man5/maildir.html and
http://cr.yp.to/proto/maildir.html.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="21%"></td><td width="79%">
Standard Python 2.2.1 as implemented on POSIX-compliant
systems.</td></table>
<a name="NOTES"></a>
<h2>NOTES</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>DELETING LOCAL FOLDERS</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP</b> does a two-way synchronization. That is,
if you make a change to the mail on the server, it will be
propogated to your local copy, and vise-versa. Some people
might think that it would be wise to just delete all their
local mail folders periodically. If you do this with
OfflineIMAP, remember to also remove your local status cache
(~/.offlineimap by default). Otherwise, OfflineIMAP will
take this as an intentional deletion of many messages and
will interpret your action as requesting them to be deleted
from the server as well. (If you don't understand this,
don't worry; you probably won't encounter this
situation)</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="4%"></td><td width="96%">
<b>MAILING LIST</b></td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
There is an OfflineIMAP mailing list available.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
To subscribe, send the text &quot;Subscribe&quot; in the
subject of a mail to offlineimap-request@complete.org. To
post, send the message to
offlineimap@complete.org.</td></table>
<a name="BUGS"></a>
<h2>BUGS</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Should be reported to the author at the address specified
below.</td></table>
<a name="COPYRIGHT"></a>
<h2>COPYRIGHT</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
OfflineIMAP is Copyright (C) 2002 John Goerzen.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
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.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
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.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
You should have received a copy of the GNU General Public
License along with this program; if not, write
to:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
Free Software Foundation, Inc.<br>
59 Temple Place<br>
Suite 330<br>
Boston, MA 02111-1307<br>
USA</td></table>
<a name="AUTHOR"></a>
<h2>AUTHOR</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>OfflineIMAP,</b> its libraries, documentation, and all
included files, except where noted, was written by John
Goerzen &lt;jgoerzen@complete.org&gt; and copyright is held
as stated in the COPYRIGHT section.</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
OfflineIMAP may be downloaded, and information found, from
its homepage via either Gopher or HTTP:</td></table>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
gopher://quux.org/1/devel/offlineimap<br>
http://quux.org/devel/offlineimap</td></table>
<a name="SEE ALSO"></a>
<h2>SEE ALSO</h2>
<table width="100%" border=0 rules="none" frame="void"
cols="2" cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="10%"></td><td width="90%">
<b>mutt</b>(1), <b>python</b>(1).</td></table>
<hr>
</body>
</html>

BIN
offlineimap/head/manual.pdf Normal file

Binary file not shown.

711
offlineimap/head/manual.ps Normal file
View File

@ -0,0 +1,711 @@
%!PS-Adobe-3.0
%%Creator: groff version 1.17.2
%%CreationDate: Mon Jul 15 11:26:30 2002
%%DocumentNeededResources: font Times-Roman
%%+ font Times-Bold
%%+ font Times-Italic
%%DocumentSuppliedResources: procset grops 1.17 2
%%Pages: 6
%%PageOrder: Ascend
%%Orientation: Portrait
%%EndComments
%%BeginProlog
%%BeginResource: procset grops 1.17 2
/setpacking where{
pop
currentpacking
true setpacking
}if
/grops 120 dict dup begin
/SC 32 def
/A/show load def
/B{0 SC 3 -1 roll widthshow}bind def
/C{0 exch ashow}bind def
/D{0 exch 0 SC 5 2 roll awidthshow}bind def
/E{0 rmoveto show}bind def
/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
/G{0 rmoveto 0 exch ashow}bind def
/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
/I{0 exch rmoveto show}bind def
/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
/K{0 exch rmoveto 0 exch ashow}bind def
/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
/M{rmoveto show}bind def
/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
/O{rmoveto 0 exch ashow}bind def
/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
/Q{moveto show}bind def
/R{moveto 0 SC 3 -1 roll widthshow}bind def
/S{moveto 0 exch ashow}bind def
/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
/SF{
findfont exch
[exch dup 0 exch 0 exch neg 0 0]makefont
dup setfont
[exch/setfont cvx]cvx bind def
}bind def
/MF{
findfont
[5 2 roll
0 3 1 roll
neg 0 0]makefont
dup setfont
[exch/setfont cvx]cvx bind def
}bind def
/level0 0 def
/RES 0 def
/PL 0 def
/LS 0 def
/MANUAL{
statusdict begin/manualfeed true store end
}bind def
/PLG{
gsave newpath clippath pathbbox grestore
exch pop add exch pop
}bind def
/BP{
/level0 save def
1 setlinecap
1 setlinejoin
72 RES div dup scale
LS{
90 rotate
}{
0 PL translate
}ifelse
1 -1 scale
}bind def
/EP{
level0 restore
showpage
}bind def
/DA{
newpath arcn stroke
}bind def
/SN{
transform
.25 sub exch .25 sub exch
round .25 add exch round .25 add exch
itransform
}bind def
/DL{
SN
moveto
SN
lineto stroke
}bind def
/DC{
newpath 0 360 arc closepath
}bind def
/TM matrix def
/DE{
TM currentmatrix pop
translate scale newpath 0 0 .5 0 360 arc closepath
TM setmatrix
}bind def
/RC/rcurveto load def
/RL/rlineto load def
/ST/stroke load def
/MT/moveto load def
/CL/closepath load def
/FL{
currentgray exch setgray fill setgray
}bind def
/BL/fill load def
/LW/setlinewidth load def
/RE{
findfont
dup maxlength 1 index/FontName known not{1 add}if dict begin
{
1 index/FID ne{def}{pop pop}ifelse
}forall
/Encoding exch def
dup/FontName exch def
currentdict end definefont pop
}bind def
/DEFS 0 def
/EBEGIN{
moveto
DEFS begin
}bind def
/EEND/end load def
/CNT 0 def
/level1 0 def
/PBEGIN{
/level1 save def
translate
div 3 1 roll div exch scale
neg exch neg exch translate
0 setgray
0 setlinecap
1 setlinewidth
0 setlinejoin
10 setmiterlimit
[]0 setdash
/setstrokeadjust where{
pop
false setstrokeadjust
}if
/setoverprint where{
pop
false setoverprint
}if
newpath
/CNT countdictstack def
userdict begin
/showpage{}def
}bind def
/PEND{
clear
countdictstack CNT sub{end}repeat
level1 restore
}bind def
end def
/setpacking where{
pop
setpacking
}if
%%EndResource
%%IncludeResource: font Times-Roman
%%IncludeResource: font Times-Bold
%%IncludeResource: font Times-Italic
grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72
def/PL 792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron
/scaron/zcaron/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
/.notdef/.notdef/space/exclam/quotedbl/numbersign/dollar/percent
/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen
/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon
/semicolon/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O
/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright/circumflex
/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y
/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase/guillemotleft
/guillemotright/bullet/florin/fraction/perthousand/dagger/daggerdbl
/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen
/brokenbar/section/dieresis/copyright/ordfeminine/guilsinglleft
/logicalnot/minus/registered/macron/degree/plusminus/twosuperior
/threesuperior/acute/mu/paragraph/periodcentered/cedilla/onesuperior
/ordmasculine/guilsinglright/onequarter/onehalf/threequarters
/questiondown/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE
/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex
/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn
/germandbls/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla
/egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis
/eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide/oslash
/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis]def
/Times-Italic@0 ENC0/Times-Italic RE/Times-Bold@0 ENC0/Times-Bold RE
/Times-Roman@0 ENC0/Times-Roman RE
%%EndProlog
%%Page: 1 1
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E/F1 10.95
/Times-Bold@0 SF -.219(NA)72 84 S(ME).219 E F0(Of)108 96 Q
(\215ineIMAP \255 Po)-.25 E
(werful IMAP/Maildir synchronization and reader support)-.25 E F1
(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(of\215ineimap)108 124.8 Q F0
([)2.5 E F2<ad31>2.5 E F0 2.5(][)2.5 G F2<ad61>A/F3 10/Times-Italic@0 SF
(accountlist)2.5 E F0 2.5(][)2.5 G F2<ad63>A F3(con\214g\214le)2.5 E F0
(])2.5 E([)108 136.8 Q F2<ad64>2.5 E F0 2.5(][)2.5 G F2<ad6f>A F0 2.5
(][)2.5 G F2<ad75>A F3(interface)2.5 E F0(])2.5 E F2
(of\215ineimap \255h)108 160.8 Q F0(|)2.5 E F2(\255\255help)2.5 E F1
(DESCRIPTION)72 177.6 Q F2(Of\215ineIMAP)108 189.6 Q F0 .019
(is a tool to simplify your e-mail reading.)2.519 F -.4(Wi)5.019 G(th).4
E F2(Of\215ineIMAP)2.519 E(,)-.92 E F0 .018
(you can read the same mailbox)2.519 F 1.115(from multiple computers.)
108 201.6 R -1.1(Yo)6.115 G 3.615(ug)1.1 G 1.115(et a current cop)-3.615
F 3.616(yo)-.1 G 3.616(fy)-3.616 G 1.116(our messages on each computer)
-3.616 F 3.616(,a)-.4 G 1.116(nd changes you)-3.616 F(mak)108 213.6 Q
2.915(eo)-.1 G .414(ne place will be visible on all other systems.)
-2.915 F -.15(Fo)5.414 G 2.914(ri).15 G .414
(nstance, you can delete a message on your home)-2.914 F(computer)108
225.6 Q 3.5(,a)-.4 G 1(nd it will appear deleted on your w)-3.5 F 1
(ork computer as well.)-.1 F F2(Of\215ineIMAP)6 E F0 1
(is also useful if you)3.5 F -.1(wa)108 237.6 S .827
(nt to use a mail reader that does not ha).1 F 1.127 -.15(ve I)-.2 H
.827(MAP support, has poor IMAP support, or does not pro).15 F(vide)-.15
E(disconnected operation.)108 249.6 Q F2(Of\215ineIMAP)108 266.4 Q F0
(is)3.216 E F3 -1.15(FA)3.216 G(ST)1.15 E(;)-.65 E F0 .716
(it synchronizes my tw)3.216 F 3.216(oa)-.1 G .716(ccounts with o)-3.216
F -.15(ve)-.15 G 3.216(r5).15 G 3.216(0f)-3.216 G .717
(olders in 3 seconds.)-3.216 F .717(Other similar)5.717 F .26
(tools might tak)108 278.4 R 2.76(eo)-.1 G -.15(ve)-2.91 G 2.76(ram).15
G .26(inute, and achie)-2.76 F .56 -.15(ve a l)-.25 H .259
(ess-reliable result.).15 F .259(Some mail readers can tak)5.259 F 2.759
(eo)-.1 G -.15(ve)-2.909 G 2.759(r1).15 G 2.759(0m)-2.759 G(in-)-2.759 E
.157(utes to do the same thing, and some don')108 290.4 R 2.657(te)-.18
G -.15(ve)-2.907 G 2.657(ns).15 G .157(upport it at all.)-2.657 F(Unlik)
5.158 E 2.658(eo)-.1 G .158(ther mail tools,)-2.658 F F2(Of\215ineIMAP)
2.658 E F0(fea-)2.658 E .321(tures a multi-threaded synchronization alg\
orithm that can dramatically speed up performance in man)108 302.4 R
2.82(ys)-.15 G(itu-)-2.82 E(ations by synchronizing se)108 314.4 Q -.15
(ve)-.25 G(ral dif).15 E(ferent things simultaneously)-.25 E(.)-.65 E F2
(Of\215ineIMAP)108 331.2 Q F0(is)3.065 E F3(FLEXIBLE;)3.065 E F0 .566
(you can customize which folders are synced via re)3.065 F .566(gular e)
-.15 F .566(xpressions, lists, or)-.15 F .734(Python e)108 343.2 R .734
(xpressions; a v)-.15 F .734(ersatile and comprehensi)-.15 F 1.034 -.15
(ve c)-.25 H .733(on\214guration \214le is used to control beha).15 F
.733(vior; tw)-.2 F 3.233(ou)-.1 G(ser)-3.233 E(interf)108 355.2 Q .535
(aces are b)-.1 F .535(uilt-in; \214ne-tuning of synchronization perfor\
mance is possible; internal or e)-.2 F .535(xternal automa-)-.15 F .441
(tion is supported; SSL and PREA)108 367.2 R .441
(UTH tunnels are both supported; of)-.55 F .44
(\215ine \(or "unplugged"\) reading is sup-)-.25 F .543(ported; and eso\
teric IMAP features are supported to ensure compatibility with the wide\
st v)108 379.2 R .544(ariety of IMAP)-.25 F(serv)108 391.2 Q(ers.)-.15 E
F2(Of\215ineIMAP)108 408 Q F0(is)4.065 E F3(SAFE;)4.065 E F0 1.565
(it uses an algorithm designed to pre)4.065 F -.15(ve)-.25 G 1.565
(nt mail loss at all costs.).15 F 1.564(Because of the)6.564 F .01
(design of this algorithm, e)108 420 R -.15(ve)-.25 G 2.51(np).15 G .01
(rogramming errors should not result in loss of mail.)-2.51 F 2.511(Ia)
5.011 G 2.511(ms)-2.511 G 2.511(oc)-2.511 G .011(on\214dent in the)
-2.511 F .448(algorithm that I use my o)108 432 R .448
(wn personal and w)-.25 F .448(ork accounts for testing of)-.1 F F2
(Of\215ineIMAP)2.948 E F0 .448(pre-release, de)2.948 F -.15(ve)-.25 G
(lop-).15 E(ment, and beta releases.)108 444 Q F2(METHOD OF OPERA)87
460.8 Q(TION)-.95 E(Of\215ineIMAP)108 472.8 Q F0 .07
(operates by maintaining a hierarch)2.57 F 2.57(yo)-.05 G 2.57(fm)-2.57
G .07(ail folders in Maildir format locally)-2.57 F 5.07(.Y)-.65 G .07
(our o)-6.17 F .07(wn mail)-.25 F .618
(reader will read mail from this tree, and need ne)108 484.8 R -.15(ve)
-.25 G 3.118(rk).15 G(no)-3.118 E 3.118(wt)-.25 G .617
(hat the mail comes from IMAP)-3.118 F(.)-1.11 E F2(Of\215ineIMAP)5.617
E F0 .849(will detect changes to the mail folders on your IMAP serv)108
496.8 R .849(er and your o)-.15 F .849(wn computer and bi-directionally)
-.25 F(synchronize them, cop)108 508.8 Q
(ying, marking, and deleting messages as necessary)-.1 E(.)-.65 E F1
(INST)72 525.6 Q(ALLA)-.986 E(TION)-1.04 E F0 .491
(If you are reading this document via the "man" command, it is lik)108
537.6 R .49(ely that you ha)-.1 F .79 -.15(ve n)-.2 H 2.99(oi).15 G .49
(nstallation tasks to)-2.99 F .043
(perform; your system administrator has already installed it.)108 549.6
R .044(If you need to install it yourself, you ha)5.043 F .344 -.15
(ve t)-.2 H(hree).15 E .39(options: a system-wide installation with Deb\
ian, system-wide installation with other systems, and a single-)108
561.6 R 12.823(user installation.)108 573.6 R -1.1(Yo)17.823 G 15.323
(uc)1.1 G 12.823(an do)-15.323 F 12.823(wnload the latest v)-.25 F
12.823(ersion of Of)-.15 F 12.823(\215ineIMAP from)-.25 F
(http://quux.or)108 585.6 Q(g/de)-.18 E -.15(ve)-.25 G(l/of).15 E
(\215ineimap/.)-.25 E F2(PREREQ)87 602.4 Q(UISITES)-.1 E F0
(In order to use Of)108 614.4 Q(\215ineIMAP)-.25 E 2.5(,y)-1.11 G
(ou need to ha)-2.5 E .3 -.15(ve t)-.2 H(hese conditions satis\214ed:)
.15 E 32.5<8359>108 631.2 S .197(our mail serv)-33.6 F .197
(er must support IMAP)-.15 F 5.197(.M)-1.11 G .197
(ost Internet Service Pro)-5.197 F .197(viders and corporate netw)-.15 F
.196(orks do,)-.1 F(and most operating systems ha)144 643.2 Q .3 -.15
(ve a)-.2 H 2.5(nI).15 G(MAP implementation readily a)-2.5 E -.25(va)-.2
G(ilable.).25 E 32.5<8359>108 660 S .085(ou must ha)-33.6 F .385 -.15
(ve P)-.2 H .085(ython v).15 F .085(ersion 2.2.1 or abo)-.15 F .385 -.15
(ve i)-.15 H 2.585(nstalled. If).15 F .086
(you are running on Debian GNU/Linux,)2.585 F .813
(this requirement will automatically be tak)144 672 R .813
(en care of for you.)-.1 F .812(If you do not ha)5.812 F 1.112 -.15
(ve P)-.2 H .812(ython already).15 F(,)-.65 E 3.88
(check with your system administrator or operating system v)144 684 R
3.88(endor; or)-.15 F 6.38(,d)-.4 G -.25(ow)-6.38 G 3.88(nload it from)
.25 F(http://www)144 696 Q(.p)-.65 E(ython.or)-.1 E 3.378(g/. If)-.18 F
.878(you intend to use the Tk interf)3.378 F .877(ace, you must ha)-.1 F
1.177 -.15(ve T)-.2 H .877(kiner \(p).15 F(ython-tk\))-.1 E 2.859
(installed. If)144 708 R .359(you intend to use the SSL interf)2.859 F
.359(ace, your Python must ha)-.1 F .66 -.15(ve b)-.2 H .36(een b).15 F
.36(uilt with SSL sup-)-.2 F(port.)144 720 Q(John Goerzen)72 768 Q
(July 12, 2002)151.655 E(1)201.915 E EP
%%Page: 2 2
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E 32.5<8348>108 84 S
-2.25 -.2(av e)-32.5 H 2.784(am)2.984 G .284
(ail reader that supports the Maildir mailbox format.)-2.784 F .284
(Most modern mail readers ha)5.284 F .584 -.15(ve t)-.2 H(his).15 E .111
(support b)144 96 R .112(uilt-in, so you can choose from a wide v)-.2 F
.112(ariety of mail serv)-.25 F 2.612(ers. This)-.15 F .112
(format is also kno)2.612 F(wn)-.25 E(as the "qmail" format, so an)144
108 Q 2.5(ym)-.15 G(ail reader compatible with it will w)-2.5 E
(ork with Of)-.1 E(\215ineIMAP)-.25 E(.)-1.11 E/F1 10/Times-Bold@0 SF
(DEBIAN SYSTEM-WIDE INST)87 124.8 Q(ALLA)-.9 E(TION)-.95 E F0 .708
(If you are tracking Debian unstable, you may install)108 136.8 R F1
(Of\215ineIMAP)3.208 E F0 .708(by simply running the follo)3.208 F .708
(wing com-)-.25 F(mand as root:)108 148.8 Q F1
(apt-get install of\215ineimap)108 165.6 Q F0 .339
(If you are not tracking Debian unstable, do)108 182.4 R .34
(wnload the Debian .deb package from the Of)-.25 F .34
(\215ineIMAP website)-.25 F .39(and then run)108 194.4 R F1 .39(dpkg -i)
2.89 F F0 .389(to install the do)2.89 F .389(wnloaded package.)-.25 F
.389(Then, go to CONFIGURA)5.389 F .389(TION belo)-1.11 F 4.189 -.65
(w. Y)-.25 H .389(ou will)-.45 F(type)108 206.4 Q F1(of\215ineimap)2.5 E
F0(to in)2.5 E -.2(vo)-.4 G .2 -.1(ke t).2 H(he program.).1 E F1 -.4(OT)
87 223.2 S(HER SYSTEM-WIDE INST).4 E(ALLA)-.9 E(TION)-.95 E F0(Do)108
235.2 Q(wnload the tar)-.25 E(.gz v)-.55 E
(ersion of the package from the website.)-.15 E
(Then run these commands:)5 E F1(tar -zxvf of\215ineimap-x.y)108 252 Q
(.z.tar)-.7 E(.gz)-1 E(cd of\215ineimap-x.y)108 264 Q(.z)-.7 E
(python2.2 setup.py)108 276 Q F0 1.272(Some systems will need to use)108
292.8 R F1(python)3.773 E F0 1.273(instead of)3.773 F F1(python2.2.)
3.773 E F0(Ne)6.273 E 1.273(xt, proceed to con\214guration.)-.15 F -1.1
(Yo)6.273 G 3.773(uw)1.1 G(ill)-3.773 E(type)108 304.8 Q F1
(of\215ineimap)2.5 E F0(to in)2.5 E -.2(vo)-.4 G .2 -.1(ke t).2 H
(he program.).1 E F1(SINGLE-A)87 321.6 Q(CCOUNT INST)-.55 E(ALLA)-.9 E
(TION)-.95 E F0(Do)108 333.6 Q(wnload the tar)-.25 E(.gz v)-.55 E
(ersion of the package from the website.)-.15 E
(Then run these commands:)5 E F1(tar -zxvf of\215ineimap-x.y)108 350.4 Q
(.z.tar)-.7 E(.gz)-1 E(cd of\215ineimap-x.y)108 362.4 Q(.z)-.7 E F0 .149
(When you w)108 379.2 R .149(ant to run)-.1 F F1(Of\215ineIMAP)2.648 E
(,)-.92 E F0 .148(you will issue the)2.648 F F1(cd)2.648 E F0 .148
(command as abo)2.648 F .448 -.15(ve a)-.15 H .148(nd then type).15 F F1
(./of\215ineimap;)2.648 E F0(there is no installation step necessary)108
391.2 Q(.)-.65 E/F2 10.95/Times-Bold@0 SF(CONFIGURA)72 408 Q(TION)-1.04
E F1(Of\215ineIMAP)108 420 Q F0 .508(is re)3.008 F .508
(gulated by a con\214guration \214le that is normally stored in)-.15 F
/F3 10/Times-Italic@0 SF(~/.of)3.009 E(\215ineimapr)-.18 E(c.)-.37 E F1
(Of\215ineIMAP)5.509 E F0 .004(ships with a \214le named)108 432 R F3
(of)2.503 E(\215ineimap.conf)-.18 E F0 .003(that you should cop)2.503 F
2.503(yt)-.1 G 2.503(ot)-2.503 G .003(hat location and then edit.)-2.503
F .003(This \214le is vital)5.003 F .255
(to proper operation of the system; it sets e)108 444 R -.15(ve)-.25 G
.256(rything you need to run).15 F F1(Of\215ineIMAP)2.756 E(.)-1.1 E F0
.256(Full documentation for)5.256 F
(the con\214guration \214le is included within the sample \214le.)108
456 Q F2(OPTIONS)72 472.8 Q F0 .061
(Most con\214guration is done via the con\214guration \214le.)108 484.8
R(Ne)5.061 E -.15(ve)-.25 G .061(rtheless, there are a fe).15 F 2.561
(wo)-.25 G .061(ptions that you may set)-2.561 F(for)108 496.8 Q F1
(Of\215ineIMAP)2.5 E(.)-1.1 E<ad31>108 513.6 Q F0 .281(Disable all mult\
ithreading operations and use solely a single-thread sync.)25.3 F .281
(This ef)5.281 F(fecti)-.25 E -.15(ve)-.25 G .281(ly sets the).15 F F1
(maxsyncaccounts)144 525.6 Q F0(and all)2.5 E F1(maxconnections)2.5 E F0
(con\214guration \214le v)2.5 E(ariables to 1.)-.25 E F1<ad61>108 542.4
Q F3(accountlist)2.5 E F0(Ov)144 554.4 Q .84(errides the)-.15 F F1
(accounts)3.34 E F0 .84(section in the con\214g \214le.)3.34 F .84
(Lets you specify a particular account or set of)5.84 F 1.647
(accounts to sync without ha)144 566.4 R 1.647
(ving to edit the con\214g \214le.)-.2 F -1.1(Yo)6.648 G 4.148(um)1.1 G
1.648(ight use this to e)-4.148 F 1.648(xclude certain)-.15 F
(accounts, or to sync some accounts that you normally prefer not to.)144
578.4 Q F1<ad63>108 595.2 Q F3(con\214g\214le)2.5 E F0
(Speci\214es a con\214guration \214le to use in lieu of the def)144
607.2 Q(ault,)-.1 E F3(~/.of)2.5 E(\215ineimapr)-.18 E(c.)-.37 E F1
<ad64>108 624 Q F0 1.139(Enables IMAP protocol stream and parsing deb)
24.74 F 3.639(ugging. This)-.2 F 1.138
(is useful if you are trying to track)3.639 F(do)144 636 Q .081
(wn a malfunction or \214gure out what is going on under the hood.)-.25
F 2.582(Is)5.082 G .082(uggest that you use this with)-2.582 F F1<ad31>
144 648 Q F0 .336(in order to mak)2.836 F 2.836(et)-.1 G .335
(he results more sensible.)-2.836 F .335
(Note that this output will contain full IMAP proto-)5.335 F 1.7
(col in plain te)144 660 R 1.7(xt, including passw)-.15 F 1.7
(ords, so tak)-.1 F 4.2(ec)-.1 G 1.7(are to remo)-4.2 F 2 -.15(ve t)-.15
H 1.7(hat from the deb).15 F 1.7(ugging output)-.2 F
(before sending it to an)144 672 Q(yone else.)-.15 E F1<ad6f>108 688.8 Q
F0(Run only once, ignoring an)25.3 E 2.5(ya)-.15 G
(utorefresh setting in the con\214g \214le.)-2.5 E F1
(\255h, \255\255help)108 705.6 Q F0(Sho)144 717.6 Q 2.5(ws)-.25 G
(ummary of options.)-2.5 E(John Goerzen)72 768 Q(July 12, 2002)151.655 E
(2)201.915 E EP
%%Page: 3 3
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E/F1 10/Times-Bold@0
SF<ad75>108 84 Q/F2 10/Times-Italic@0 SF(interface)2.5 E F0 1.133
(Speci\214es an alternati)144 96 R 1.433 -.15(ve u)-.25 H 1.133
(ser interf).15 F 1.133(ace module to use.)-.1 F 1.133(This o)6.133 F
-.15(ve)-.15 G 1.133(rrides the def).15 F 1.132(ault speci\214ed in the)
-.1 F .28(con\214guration \214le.)144 108 R .28(The UI speci\214ed with)
5.28 F F1(-u)2.78 E F0 .281(will be forced to be used, e)2.78 F -.15(ve)
-.25 G 2.781(ni).15 G 2.781(fi)-2.781 G(ts)-2.781 E F1(isuable\(\))2.781
E F0(method)2.781 E(states that it cannot be.)144 120 Q
(Use this option with care.)5 E .158(The pre-de\214ned options are)144
136.8 R F1(Tk.TKUI)2.658 E F0 .158(\(a graphical interf)2.658 F .157
(ace\) and)-.1 F F1(TTY)2.657 E(.TTYUI)-.92 E F0 .157(\(a te)2.657 F
.157(xt-mode inter)-.15 F(-)-.2 E -.1(fa)144 148.8 S(ce\).).1 E/F3 10.95
/Times-Bold@0 SF(EXAMPLES)72 165.6 Q F0(Here is an e)108 177.6 Q
(xample con\214guration for a particularly comple)-.15 E 2.5(xs)-.15 G
(ituation; more e)-2.5 E(xamples will be added later)-.15 E(.)-.55 E F1
(MUL)87 194.4 Q(TIPLE A)-.92 E(CCOUNTS WITH MUTT)-.55 E F0 .513(This e)
108 206.4 R .513(xample sho)-.15 F .513(ws you ho)-.25 F 3.014(wt)-.25 G
3.014(os)-3.014 G .514(et up)-3.014 F F1(Of\215ineIMAP)3.014 E F0 .514
(to synchronize multiple accounts with the mutt mail)3.014 F(reader)108
218.4 Q(.)-.55 E(Start by creating a directory to hold your folders:)108
235.2 Q F1(mkdir ~/Mail)108 247.2 Q F0(In your)108 264 Q F2(~/.of)2.5 E
(\215ineimapr)-.18 E(c,)-.37 E F0(specify this:)2.5 E F1(accounts = P)
108 276 Q(ersonal, W)-.2 E(ork)-.75 E F0(Mak)108 292.8 Q 2.821(es)-.1 G
.321(ure that you ha)-2.821 F .621 -.15(ve b)-.2 H .321(oth a).15 F F1
([P)2.821 E(ersonal])-.2 E F0 .321(and a)2.821 F F1([W)2.821 E(ork])-.75
E F0 .32(section, with dif)2.82 F .32(ferent localfolder pathnames and)
-.25 F(enable)108 304.8 Q F1([mbnames].)2.5 E F0
(In each account section, do something lik)108 321.6 Q 2.5(et)-.1 G
(his:)-2.5 E F1(localf)108 333.6 Q(olders = ~/Mail/P)-.25 E(ersonal)-.2
E F0(Add these lines to your)108 350.4 Q F2(~/.muttr)2.5 E(c:)-.37 E F1
(sour)108 362.4 Q(ce ~/path-to-mbnames-muttr)-.18 E(c-mailboxes)-.18 E
-.25(fo)108 374.4 S(lder).25 E(-hook P)-.37 E(ersonal set fr)-.2 E
(om="y)-.18 E(our)-.25 E(email@personal.com")-.18 E -.25(fo)108 386.4 S
(lder).25 E(-hook W)-.37 E(ork set fr)-.75 E(om="y)-.18 E(our)-.25 E
(email@w)-.18 E(ork.com")-.1 E(set mbox_type=Maildir)108 398.4 Q(set f)
108 410.4 Q(older=$HOME/Mail)-.25 E(set spool\214le=+P)108 422.4 Q
(ersonal/INBO)-.2 E(X)-.4 E F0(That')108 439.2 Q 2.5(si)-.55 G(t!)-2.5 E
F3(ERR)72 456 Q(ORS)-.329 E F0(If you get one of some frequently-encoun\
tered or confusing errors, please check this section.)108 468 Q F1
(UID v)87 484.8 Q(alidity pr)-.1 E(oblem f)-.18 E(or f)-.25 E(older)-.25
E F0 1.637(IMAP serv)108 496.8 R 1.637
(ers use a unique ID \(UID\) to refer to a speci\214c message.)-.15 F
1.638(This number is guaranteed to be)6.637 F 1.11
(unique to a particular message FOREVER.)108 508.8 R 1.109
(No other message in the same folder will e)6.11 F -.15(ve)-.25 G 3.609
(rg).15 G 1.109(et the same)-3.609 F 2.873(UID. UIDs)108 520.8 R .373
(are an inte)2.873 F .373(gral part of Of)-.15 F(\215ineIMAP')-.25 E
2.873(ss)-.55 G .373(ynchronization scheme; the)-2.873 F 2.873(ya)-.15 G
.373(re used to match up mes-)-2.873 F
(sages on your computer to messages on the serv)108 532.8 Q(er)-.15 E(.)
-.55 E .108(Sometimes, the UIDs on the serv)108 549.6 R .108
(er might get reset.)-.15 F .108
(Usually this will happen if you delete and then recreate)5.108 F 3.742
(af)108 561.6 S(older)-3.742 E 6.242(.W)-.55 G 1.242
(hen you create a folder)-6.242 F 3.742(,t)-.4 G 1.242(he serv)-3.742 F
1.242(er will often start the UID back from 1.)-.15 F(But)6.243 E F1
(Of\215ineIMAP)3.743 E F0 .303(might still ha)108 573.6 R .603 -.15
(ve t)-.2 H .303(he UIDs from the pre).15 F .302
(vious folder by the same name stored.)-.25 F F1(Of\215ineIMAP)5.302 E
F0 .302(will detect this)2.802 F(condition and skip the folder)108 585.6
Q 5(.T)-.55 G(his is GOOD, because it pre)-5 E -.15(ve)-.25 G
(nts data loss.).15 E -1.1(Yo)108 602.4 S 2.826(uc)1.1 G .326
(an \214x it by remo)-2.826 F .327
(ving your local folder and cache data.)-.15 F -.15(Fo)5.327 G 2.827(ri)
.15 G .327(nstance, if your folders are under)-2.827 F F2(~/F)2.827 E
(old-)-1.05 E(er)108 614.4 Q(s)-.1 E F0
(and the folder with the problem is INBO)2.5 E(X, you')-.4 E 2.5(dt)-.5
G(ype this:)-2.5 E F1(rm -r ~/F)108 631.2 Q(olders/INBO)-.25 E(X)-.4 E
(rm ~/.of\215ineimap/AccountName/INBO)108 643.2 Q(X)-.4 E F0
(\(replacing AccountName with the account name as speci\214ed in)108 660
Q F2(~/.of)2.5 E(\215ineimapr)-.18 E(c\))-.37 E F0(Ne)108 676.8 Q .802
(xt time you run)-.15 F F1(Of\215ineIMAP)3.302 E(,)-.92 E F0 .802
(it will re-do)3.302 F .802(wnload the folder with the ne)-.25 F 3.302
(wU)-.25 G 3.301(IDs. Note)-3.302 F .801(that the proce-)3.301 F
(dure speci\214ed abo)108 688.8 Q .3 -.15(ve w)-.15 H(ill lose an).15 E
2.5(yl)-.15 G(ocal changes made to the folder)-2.5 E(.)-.55 E .522
(Some IMAP serv)108 705.6 R .522(ers are brok)-.15 F .522
(en and do not support UIDs properly)-.1 F 5.522(.I)-.65 G 3.022(fy)
-5.522 G .522(ou continue to get this error for all)-3.022 F .067
(your folders e)108 717.6 R -.15(ve)-.25 G 2.566(na).15 G .066
(fter performing the abo)-2.566 F .366 -.15(ve p)-.15 H .066
(rocedure, it is lik).15 F .066(ely that your IMAP serv)-.1 F .066(er f)
-.15 F .066(alls into this cat-)-.1 F -.15(eg)108 729.6 S(ory).15 E(.)
-.65 E F1(Of\215ineIMAP)5.983 E F0 .984(is incompatible with such serv)
3.483 F 3.484(ers. Using)-.15 F F1(Of\215ineIMAP)3.484 E F0 .984
(with them will not destro)3.484 F(y)-.1 E(John Goerzen)72 768 Q
(July 12, 2002)151.655 E(3)201.915 E EP
%%Page: 4 4
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E(an)108 84 Q 2.77
(ym)-.15 G .27(ail, b)-2.77 F .27
(ut at the same time, it will not actually synchronize it either)-.2 F
5.269(.\()-.55 G(Of)-5.269 E .269(\215ineIMAP will detect this con-)-.25
F(dition and abort prior to synchronization\))108 96 Q/F1 10.95
/Times-Bold@0 SF -.438(OT)72 124.8 S(HER FREQ).438 E(UENTL)-.11 E 2.738
(YA)-1.007 G(SKED Q)-2.738 E(UESTIONS)-.11 E F0 .683
(There are some other F)108 136.8 R -.55(AQ)-.74 G 3.184(st).55 G .684
(hat might not \214t into another section of this document, and the)
-3.184 F 3.184(ya)-.15 G .684(re enumer)-3.184 F(-)-.2 E(ated here.)108
148.8 Q/F2 10/Times-Bold@0 SF(What platf)108 165.6 Q
(orms does Of\215ineIMAP run on?)-.25 E F0(It should run on most platfo\
rms supported by Python, which are quite a fe)144 177.6 Q -.65(w.)-.25 G
F2 .689(I'm using Mutt. Other IMAP sync pr)108 194.4 R .689(ograms r)
-.18 F(equir)-.18 E 3.189(em)-.18 G 3.189(et)-3.189 G 3.189(ou)-3.189 G
.689(se set maildir_trash=y)-3.189 F .688(es . Do I need to)-.1 F
(do that with Of\215ineIMAP?)108 206.4 Q F0(No.)144 218.4 Q F2
(Of\215ineIMAP)7.27 E F0 2.27
(is smart enough to \214gure out message deletion without this e)4.77 F
2.27(xtra crutch.)-.15 F -1.1(Yo)144 230.4 S(u')1.1 E
(ll get the best results if you don')-.1 E 2.5(tu)-.18 G
(se this setting, in f)-2.5 E(act.)-.1 E F2(Ho)108 247.2 Q 2.5(wd)-.1 G
2.5(oIs)-2.5 G(pecify the names of my f)-2.5 E(olders?)-.25 E F0 -1.1
(Yo)144 259.2 S 3.55(ud)1.1 G 3.55(on)-3.55 G 1.05(ot need to.)-3.55 F
F2(Of\215ineIMAP)6.05 E F0 1.05
(is smart enough to automatically \214gure out what folders are)3.55 F
.679(present on the IMAP serv)144 271.2 R .679(er and synchronize them.)
-.15 F -1.1(Yo)5.679 G 3.178(uc)1.1 G .678(an use the)-3.178 F F2 -.25
(fo)3.178 G(lder\214lter).25 E F0(and)3.178 E F2 -.25(fo)3.178 G
(ldertrans).25 E F0(con\214guration \214le options to request certain f\
olders and rename them as the)144 283.2 Q 2.5(yc)-.15 G
(ome in if you lik)-2.5 E(e.)-.1 E F2(Ho)108 300 Q 2.5(wc)-.1 G(an I pr)
-2.5 E -2.3 -.15(ev e)-.18 H(nt certain f).15 E(olders fr)-.25 E
(om being synced?)-.18 E F0(Use the)144 312 Q F2 -.25(fo)2.5 G
(lder\214lter).25 E F0(option in the con\214guration \214le.)2.5 E F2
(Ho)108 328.8 Q 2.5(wc)-.1 G(an I add or delete a f)-2.5 E(older?)-.25 E
(Of\215ineIMAP)144 340.8 Q F0 .503(does not currently pro)3.003 F .504
(vide this feature, b)-.15 F .504(ut if you create a ne)-.2 F 3.004(wf)
-.25 G .504(older on the IMAP)-3.004 F(serv)144 352.8 Q(er)-.15 E 2.5
(,i)-.4 G 2.5(tw)-2.5 G(ill be created locally automatically)-2.5 E(.)
-.65 E F2(Ar)108 369.6 Q 2.5(et)-.18 G(her)-2.5 E 2.5(ea)-.18 G
(ny other war)-2.5 E(nings that I should be awar)-.15 E 2.5(eo)-.18 G
(f?)-2.5 E F0 -1(Ye)144 381.6 S(s; see the NO)1 E(TES section belo)-.4 E
-.65(w.)-.25 G F2(What is the mailbox name r)108 398.4 Q
(ecorder \(mbnames\) f)-.18 E(or?)-.25 E F0 1.019(The Mutt mail reader \
is not capable of automatically determining the names of your mailbox)
144 410.4 R(es.)-.15 E(Of)144 422.4 Q .265
(\215ineIMAP can help it \(or man)-.25 F 2.765(yo)-.15 G .265
(ther\) programs out be writing these names out in a format you)-2.765 F
(specify)144 434.4 Q 5(.S)-.65 G(ee the e)-5 E(xample of)-.15 E
(\215ineimap.conf \214le for details.)-.25 E F2(Can I synchr)108 451.2 Q
(onize multiple accounts with Of\215ineIMAP?)-.18 E F0 3.345(Sure. Just)
144 463.2 R .845(name them all in the accounts line in the general sect\
ion of the con\214g \214le, and add a)3.345 F(per)144 475.2 Q
(-account section for each one.)-.2 E F2
(Does Of\215ineIMAP support POP?)108 492 Q F0 4.076(No. POP)144 504 R
1.576(is not rob)4.076 F 1.576(ust enough to do a completely reliable m\
ulti-machine synchronization lik)-.2 F(e)-.1 E(Of)144 516 Q
(\215ineIMAP can do.)-.25 E(Of)5 E(\215ineIMAP will not support it.)-.25
E F2(Do y)108 532.8 Q(ou support mailbox f)-.25 E
(ormats other than Maildir?)-.25 E F0 .41(Not at present.)144 544.8 R
.41(There is no technical reason not to; just no demand yet.)5.41 F .409
(Maildir is a superior for)5.409 F(-)-.2 E(mat an)144 556.8 Q(yw)-.15 E
(ay)-.1 E(.)-.65 E F2([technical] Wh)108 573.6 Q 2.5(ya)-.15 G .36 -.18
(re y)-2.5 H(our Maildir message \214lenames so huge?)-.07 E
(Of\215ineIMAP)144 585.6 Q F0 .958(has tw)3.458 F 3.458(or)-.1 G(ele)
-3.458 E -.25(va)-.25 G .958(nt principles: 1\) ne).25 F -.15(ve)-.25 G
3.459(rm).15 G .959(odifying your messages in an)-3.459 F 3.459(yw)-.15
G .959(ay and 2\))-3.559 F .493
(ensuring 100% reliable synchronizations.)144 597.6 R .493
(In order to do a reliable sync,)5.493 F F2(Of\215ineIMAP)2.993 E F0
.493(must ha)2.993 F -.15(ve)-.2 G 3.094(aw)144 609.6 S .594
(ay to uniquely identify each e-mail.)-3.194 F .595
(Three pieces of information are required to do this: your)5.594 F .538
(account name, the folder name, and the message UID.)144 621.6 R .537
(The account name can be calculated from)5.537 F 1.081
(the path in which your messages are.)144 633.6 R 1.082
(The folder name can usually be as well, B)6.082 F 1.082(UT some mail)
-.1 F(clients mo)144 645.6 Q .3 -.15(ve m)-.15 H
(essages between folders by simply mo).15 E(ving the \214le, lea)-.15 E
(ving the name intact.)-.2 E(So,)144 662.4 Q F2(Of\215ineIMAP)3.2 E F0
.7(must store both a UID folder ID.)3.2 F .7
(The folder ID is necessary so)5.7 F F2(Of\215ineIMAP)3.2 E F0 .455
(can detect a message mo)144 674.4 R -.15(ve)-.15 G 2.955(dt).15 G 2.955
(oad)-2.955 G(if)-2.955 E .455(ferent folder)-.25 F(.)-.55 E F2
(Of\215ineIMAP)5.455 E F0 .456(stores the UID \(U= number\) and)2.955 F
(an md5sum of the foldername \(FMD5= number\) to f)144 686.4 Q
(acilitate this.)-.1 E F2(What is the speed of Of\215ineIMAP')108 703.2
Q 2.5(ss)-.37 G(ync?)-2.5 E(Of\215ineIMAP)144 715.2 Q F0 -.15(ve)2.891 G
.391(rsions 2.0 and abo).15 F .691 -.15(ve c)-.15 H .391
(ontain a multithreaded system.).15 F 2.891(Ag)5.391 G .39(ood w)-2.891
F .39(ay to e)-.1 F(xperiment)-.15 E(is by setting maxsyncaccounts to 3\
and maxconnections to 3 in each account clause.)144 727.2 Q
(John Goerzen)72 768 Q(July 12, 2002)151.655 E(4)201.915 E EP
%%Page: 5 5
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E .381(This lets Of)
144 84 R .381(\215ineIMAP open up multiple connections simultaneously)
-.25 F 5.382(.T)-.65 G .382(hat will let it process mul-)-5.382 F
(tiple folders and messages at once.)144 96 Q
(In most cases, this will increase performance of the sync.)5 E(Don')144
112.8 Q 3.104(ts)-.18 G .604(et the number too high.)-3.104 F .603
(If you do that, things might actually slo)5.604 F 3.103(wd)-.25 G -.25
(ow)-3.103 G 3.103(na).25 G 3.103(sy)-3.103 G .603(our link gets)-3.103
F 2.632(saturated. Also,)144 124.8 R .132(too man)2.632 F 2.632(yc)-.15
G .132(onnections can cause mail serv)-2.632 F .132(ers to ha)-.15 F
.433 -.15(ve ex)-.2 H(cessi).15 E .433 -.15(ve l)-.25 H 2.633
(oad. Administra-).15 F .507(tors might tak)144 136.8 R 3.007(eu)-.1 G
.507(nkindly to this, and the serv)-3.007 F .506(er might bog do)-.15 F
3.006(wn. There)-.25 F .506(are man)3.006 F 3.006(yv)-.15 G .506
(ariables in the)-3.256 F(optimal setting; e)144 148.8 Q
(xperimentation may help.)-.15 E
(An informal benchmark yields these results for my setup:)144 165.6 Q
(10 minutes with MacOS X Mail.app "manual cache")144 182.4 Q 2.5(5m)144
194.4 S(inutes with GNUS agent sync)-2.5 E(20 seconds with Of)144 206.4
Q(\215ineIMAP 1.x)-.25 E 2.5(9s)144 218.4 S(econds with Of)-2.5 E
(\215ineIMAP 2.x)-.25 E 2.5(3s)144 230.4 S(econds with Of)-2.5 E
(\215ineIMAP 3.x "cold start")-.25 E 2.5(2s)144 242.4 S(econds with Of)
-2.5 E(\215ineIMAP 3.x "held connection")-.25 E/F1 10.95/Times-Bold@0 SF
(CONFORMING T)72 259.2 Q(O)-.197 E F0 32.5<8349>108 271.2 S
(nternet Message Access Protocol v)-32.5 E(ersion 4re)-.15 E
(v1 \(IMAP 4re)-.25 E(v1\) as speci\214ed in RFC2060)-.25 E 32.5<834d>
108 288 S 8.92(aildir as speci\214ed in http://www)-32.5 F(.qmail.or)
-.65 E(g/qmail-manual-html/man5/maildir)-.18 E 8.92(.html and)-.55 F
(http://cr)144 300 Q(.yp.to/proto/maildir)-.55 E(.html.)-.55 E 32.5
<8353>108 316.8 S
(tandard Python 2.2.1 as implemented on POSIX-compliant systems.)-32.5 E
F1(NO)72 333.6 Q(TES)-.438 E/F2 10/Times-Bold@0 SF
(DELETING LOCAL FOLDERS)87 345.6 Q(Of\215ineIMAP)108 357.6 Q F0 .533
(does a tw)3.033 F(o-w)-.1 E .533(ay synchronization.)-.1 F .532
(That is, if you mak)5.532 F 3.032(eac)-.1 G .532
(hange to the mail on the serv)-3.032 F(er)-.15 E 3.032(,i)-.4 G(t)
-3.032 E .896(will be propog)108 369.6 R .896(ated to your local cop)
-.05 F 2.197 -.65(y, a)-.1 H .897(nd vise-v).65 F 3.397(ersa. Some)-.15
F .897(people might think that it w)3.397 F .897(ould be wise to)-.1 F
1.59(just delete all their local mail folders periodically)108 381.6 R
6.59(.I)-.65 G 4.09(fy)-6.59 G 1.59(ou do this with Of)-4.09 F
(\215ineIMAP)-.25 E 4.09(,r)-1.11 G 1.59(emember to also)-4.09 F(remo)
108 393.6 Q 1.67 -.15(ve y)-.15 H 1.37(our local status cache \(~/.of)
.15 F 1.37(\215ineimap by def)-.25 F 3.87(ault\). Otherwise,)-.1 F(Of)
3.87 E 1.37(\215ineIMAP will tak)-.25 F 3.87(et)-.1 G 1.37(his as an)
-3.87 F .416(intentional deletion of man)108 405.6 R 2.916(ym)-.15 G
.416(essages and will interpret your action as requesting them to be de\
leted from)-2.916 F(the serv)108 417.6 Q(er as well.)-.15 E
(\(If you don')5 E 2.5(tu)-.18 G(nderstand this, don')-2.5 E 2.5(tw)-.18
G(orry; you probably w)-2.6 E(on')-.1 E 2.5(te)-.18 G
(ncounter this situation\))-2.5 E F2(MAILING LIST)87 434.4 Q F0
(There is an Of)108 446.4 Q(\215ineIMAP mailing list a)-.25 E -.25(va)
-.2 G(ilable.).25 E 2.447 -.8(To s)108 463.2 T .847
(ubscribe, send the te).8 F .847
(xt "Subscribe" in the subject of a mail to of)-.15 F
(\215ineimap-request@complete.or)-.25 E 3.348(g. T)-.18 F(o)-.8 E
(post, send the message to of)108 475.2 Q(\215ineimap@complete.or)-.25 E
(g.)-.18 E F1 -.11(BU)72 492 S(GS).11 E F0
(Should be reported to the author at the address speci\214ed belo)108
504 Q -.65(w.)-.25 G F1(COPYRIGHT)72 520.8 Q F0(Of)108 532.8 Q
(\215ineIMAP is Cop)-.25 E(yright \(C\) 2002 John Goerzen.)-.1 E .287
(This program is free softw)108 549.6 R .287(are; you can redistrib)-.1
F .286(ute it and/or modify it under the terms of the GNU General)-.2 F
.766(Public License as published by the Free Softw)108 561.6 R .766
(are F)-.1 F .766(oundation; either v)-.15 F .766
(ersion 2 of the License, or \(at your)-.15 F(option\) an)108 573.6 Q
2.5(yl)-.15 G(ater v)-2.5 E(ersion.)-.15 E .58(This program is distrib)
108 590.4 R .579(uted in the hope that it will be useful, b)-.2 F .579
(ut WITHOUT ANY W)-.2 F(ARRANTY)-1.2 E 3.079(;w)-.92 G(ithout)-3.079 E
-2.15 -.25(ev e)108 602.4 T 2.729(nt).25 G .229(he implied w)-2.729 F
.229(arranty of MERCHANT)-.1 F .229(ABILITY or FITNESS FOR A P)-.93 F
(AR)-.92 E .23(TICULAR PURPOSE.)-.6 F(See)5.23 E
(the GNU General Public License for more details.)108 614.4 Q -1.1(Yo)
108 631.2 S 2.77(us)1.1 G .27(hould ha)-2.77 F .57 -.15(ve r)-.2 H(ecei)
.15 E -.15(ve)-.25 G 2.77(dac).15 G(op)-2.77 E 2.77(yo)-.1 G 2.77(ft)
-2.77 G .27
(he GNU General Public License along with this program; if not, write)
-2.77 F(to:)108 643.2 Q(Free Softw)108 660 Q(are F)-.1 E
(oundation, Inc.)-.15 E(59 T)108 672 Q(emple Place)-.7 E(Suite 330)108
684 Q(Boston, MA)108 696 Q(02111-1307)5 E(USA)108 708 Q(John Goerzen)72
768 Q(July 12, 2002)151.655 E(5)201.915 E EP
%%Page: 6 6
%%BeginPageSetup
BP
%%EndPageSetup
/F0 10/Times-Roman@0 SF 111.22(OFFLINEIMAP\(1\) Of)72 48 R
(\215ineIMAP manual)-.25 E(OFFLINEIMAP\(1\))113.72 E/F1 10.95
/Times-Bold@0 SF -.548(AU)72 84 S(THOR).548 E/F2 10/Times-Bold@0 SF
(Of\215ineIMAP)108 96 Q(,)-.92 E F0 .488
(its libraries, documentation, and all included \214les, e)2.987 F .488
(xcept where noted, w)-.15 F .488(as written by John)-.1 F
(Goerzen <jgoerzen@complete.or)108 108 Q(g> and cop)-.18 E
(yright is held as stated in the COPYRIGHT section.)-.1 E(Of)108 124.8 Q
(\215ineIMAP may be do)-.25 E(wnloaded, and information found, from its\
homepage via either Gopher or HTTP:)-.25 E(gopher://quux.or)108 141.6 Q
(g/1/de)-.18 E -.15(ve)-.25 G(l/of).15 E(\215ineimap)-.25 E
(http://quux.or)108 153.6 Q(g/de)-.18 E -.15(ve)-.25 G(l/of).15 E
(\215ineimap)-.25 E F1(SEE ALSO)72 182.4 Q F2(mutt)108 194.4 Q F0
(\(1\),)A F2(python)2.5 E F0(\(1\).)A(John Goerzen)72 768 Q
(July 12, 2002)151.655 E(6)201.915 E EP
%%Trailer
end
%%EOF

462
offlineimap/head/manual.txt Normal file
View File

@ -0,0 +1,462 @@
OFFLINEIMAP(1) OfflineIMAP manual OFFLINEIMAP(1)
NAME
OfflineIMAP - Powerful IMAP/Maildir synchronization and
reader support
SYNOPSIS
offlineimap [ -1 ] [ -a accountlist ] [ -c configfile ]
[ -d ] [ -o ] [ -u interface ]
offlineimap -h | --help
DESCRIPTION
OfflineIMAP is a tool to simplify your e-mail reading.
With OfflineIMAP, you can read the same mailbox from mul-
tiple computers. You get a current copy of your messages
on each computer, and changes you make one place will be
visible on all other systems. For instance, you can
delete a message on your home computer, and it will appear
deleted on your work computer as well. OfflineIMAP is
also useful if you want to use a mail reader that does not
have IMAP support, has poor IMAP support, or does not pro-
vide disconnected operation.
OfflineIMAP is FAST; it synchronizes my two accounts with
over 50 folders in 3 seconds. Other similar tools might
take over a minute, and achieve a less-reliable result.
Some mail readers can take over 10 minutes to do the same
thing, and some don't even support it at all. Unlike
other mail tools, OfflineIMAP features a multi-threaded
synchronization algorithm that can dramatically speed up
performance in many situations by synchronizing several
different things simultaneously.
OfflineIMAP is FLEXIBLE; you can customize which folders
are synced via regular expressions, lists, or Python
expressions; a versatile and comprehensive configuration
file is used to control behavior; two user interfaces are
built-in; fine-tuning of synchronization performance is
possible; internal or external automation is supported;
SSL and PREAUTH tunnels are both supported; offline (or
"unplugged") reading is supported; and esoteric IMAP fea-
tures are supported to ensure compatibility with the
widest variety of IMAP servers.
OfflineIMAP is SAFE; it uses an algorithm designed to pre-
vent mail loss at all costs. Because of the design of
this algorithm, even programming errors should not result
in loss of mail. I am so confident in the algorithm that
I use my own personal and work accounts for testing of
OfflineIMAP pre-release, development, and beta releases.
METHOD OF OPERATION
OfflineIMAP operates by maintaining a hierarchy of mail
folders in Maildir format locally. Your own mail reader
will read mail from this tree, and need never know that
the mail comes from IMAP. OfflineIMAP will detect changes
to the mail folders on your IMAP server and your own com-
puter and bi-directionally synchronize them, copying,
marking, and deleting messages as necessary.
INSTALLATION
If you are reading this document via the "man" command, it
is likely that you have no installation tasks to perform;
your system administrator has already installed it. If
you need to install it yourself, you have three options: a
system-wide installation with Debian, system-wide instal-
lation with other systems, and a single-user installation.
You can download the latest version of OfflineIMAP from
http://quux.org/devel/offlineimap/.
PREREQUISITES
In order to use OfflineIMAP, you need to have these condi-
tions satisfied:
o Your mail server must support IMAP. Most Internet
Service Providers and corporate networks do, and
most operating systems have an IMAP implementation
readily available.
o You must have Python version 2.2.1 or above
installed. If you are running on Debian GNU/Linux,
this requirement will automatically be taken care
of for you. If you do not have Python already,
check with your system administrator or operating
system vendor; or, download it from
http://www.python.org/. If you intend to use the
Tk interface, you must have Tkiner (python-tk)
installed. If you intend to use the SSL interface,
your Python must have been built with SSL support.
o Have a mail reader that supports the Maildir mail-
box format. Most modern mail readers have this
support built-in, so you can choose from a wide
variety of mail servers. This format is also known
as the "qmail" format, so any mail reader compati-
ble with it will work with OfflineIMAP.
DEBIAN SYSTEM-WIDE INSTALLATION
If you are tracking Debian unstable, you may install
OfflineIMAP by simply running the following command as
root:
apt-get install offlineimap
If you are not tracking Debian unstable, download the
Debian .deb package from the OfflineIMAP website and then
run dpkg -i to install the downloaded package. Then, go
to CONFIGURATION below. You will type offlineimap to
invoke the program.
OTHER SYSTEM-WIDE INSTALLATION
Download the tar.gz version of the package from the web-
site. Then run these commands:
tar -zxvf offlineimap-x.y.z.tar.gz
cd offlineimap-x.y.z
python2.2 setup.py
Some systems will need to use python instead of python2.2.
Next, proceed to configuration. You will type offlineimap
to invoke the program.
SINGLE-ACCOUNT INSTALLATION
Download the tar.gz version of the package from the web-
site. Then run these commands:
tar -zxvf offlineimap-x.y.z.tar.gz
cd offlineimap-x.y.z
When you want to run OfflineIMAP, you will issue the cd
command as above and then type ./offlineimap; there is no
installation step necessary.
CONFIGURATION
OfflineIMAP is regulated by a configuration file that is
normally stored in ~/.offlineimaprc. OfflineIMAP ships
with a file named offlineimap.conf that you should copy to
that location and then edit. This file is vital to proper
operation of the system; it sets everything you need to
run OfflineIMAP. Full documentation for the configuration
file is included within the sample file.
OPTIONS
Most configuration is done via the configuration file.
Nevertheless, there are a few options that you may set for
OfflineIMAP.
-1 Disable all multithreading operations and use
solely a single-thread sync. This effectively sets
the maxsyncaccounts and all maxconnections configu-
ration file variables to 1.
-a accountlist
Overrides the accounts section in the config file.
Lets you specify a particular account or set of
accounts to sync without having to edit the config
file. You might use this to exclude certain
accounts, or to sync some accounts that you nor-
mally prefer not to.
-c configfile
Specifies a configuration file to use in lieu of
the default, ~/.offlineimaprc.
-d Enables IMAP protocol stream and parsing debugging.
This is useful if you are trying to track down a
malfunction or figure out what is going on under
the hood. I suggest that you use this with -1 in
order to make the results more sensible. Note that
this output will contain full IMAP protocol in
plain text, including passwords, so take care to
remove that from the debugging output before send-
ing it to anyone else.
-o Run only once, ignoring any autorefresh setting in
the config file.
-h, --help
Show summary of options.
-u interface
Specifies an alternative user interface module to
use. This overrides the default specified in the
configuration file. The UI specified with -u will
be forced to be used, even if its isuable() method
states that it cannot be. Use this option with
care.
The pre-defined options are Tk.TKUI (a graphical
interface) and TTY.TTYUI (a text-mode interface).
EXAMPLES
Here is an example configuration for a particularly com-
plex situation; more examples will be added later.
MULTIPLE ACCOUNTS WITH MUTT
This example shows you how to set up OfflineIMAP to syn-
chronize multiple accounts with the mutt mail reader.
Start by creating a directory to hold your folders:
mkdir ~/Mail
In your ~/.offlineimaprc, specify this:
accounts = Personal, Work
Make sure that you have both a [Personal] and a [Work]
section, with different localfolder pathnames and enable
[mbnames].
In each account section, do something like this:
localfolders = ~/Mail/Personal
Add these lines to your ~/.muttrc:
source ~/path-to-mbnames-muttrc-mailboxes
folder-hook Personal set from="youremail@personal.com"
folder-hook Work set from="youremail@work.com"
set mbox_type=Maildir
set folder=$HOME/Mail
set spoolfile=+Personal/INBOX
That's it!
ERRORS
If you get one of some frequently-encountered or confusing
errors, please check this section.
UID validity problem for folder
IMAP servers use a unique ID (UID) to refer to a specific
message. This number is guaranteed to be unique to a par-
ticular message FOREVER. No other message in the same
folder will ever get the same UID. UIDs are an integral
part of OfflineIMAP's synchronization scheme; they are
used to match up messages on your computer to messages on
the server.
Sometimes, the UIDs on the server might get reset. Usu-
ally this will happen if you delete and then recreate a
folder. When you create a folder, the server will often
start the UID back from 1. But OfflineIMAP might still
have the UIDs from the previous folder by the same name
stored. OfflineIMAP will detect this condition and skip
the folder. This is GOOD, because it prevents data loss.
You can fix it by removing your local folder and cache
data. For instance, if your folders are under ~/Folders
and the folder with the problem is INBOX, you'd type this:
rm -r ~/Folders/INBOX
rm ~/.offlineimap/AccountName/INBOX
(replacing AccountName with the account name as specified
in ~/.offlineimaprc)
Next time you run OfflineIMAP, it will re-download the
folder with the new UIDs. Note that the procedure speci-
fied above will lose any local changes made to the folder.
Some IMAP servers are broken and do not support UIDs prop-
erly. If you continue to get this error for all your
folders even after performing the above procedure, it is
likely that your IMAP server falls into this category.
OfflineIMAP is incompatible with such servers. Using
OfflineIMAP with them will not destroy any mail, but at
the same time, it will not actually synchronize it either.
(OfflineIMAP will detect this condition and abort prior to
synchronization)
OTHER FREQUENTLY ASKED QUESTIONS
There are some other FAQs that might not fit into another
section of this document, and they are enumerated here.
What platforms does OfflineIMAP run on?
It should run on most platforms supported by
Python, which are quite a few.
I'm using Mutt. Other IMAP sync programs require me to use
set maildir_trash=yes . Do I need to do that with
OfflineIMAP?
No. OfflineIMAP is smart enough to figure out mes-
sage deletion without this extra crutch. You'll
get the best results if you don't use this setting,
in fact.
How do I specify the names of my folders?
You do not need to. OfflineIMAP is smart enough to
automatically figure out what folders are present
on the IMAP server and synchronize them. You can
use the folderfilter and foldertrans configuration
file options to request certain folders and rename
them as they come in if you like.
How can I prevent certain folders from being synced?
Use the folderfilter option in the configuration
file.
How can I add or delete a folder?
OfflineIMAP does not currently provide this fea-
ture, but if you create a new folder on the IMAP
server, it will be created locally automatically.
Are there any other warnings that I should be aware of?
Yes; see the NOTES section below.
What is the mailbox name recorder (mbnames) for?
The Mutt mail reader is not capable of automati-
cally determining the names of your mailboxes.
OfflineIMAP can help it (or many other) programs
out be writing these names out in a format you
specify. See the example offlineimap.conf file for
details.
Can I synchronize multiple accounts with OfflineIMAP?
Sure. Just name them all in the accounts line in
the general section of the config file, and add a
per-account section for each one.
Does OfflineIMAP support POP?
No. POP is not robust enough to do a completely
reliable multi-machine synchronization like
OfflineIMAP can do. OfflineIMAP will not support
it.
Do you support mailbox formats other than Maildir?
Not at present. There is no technical reason not
to; just no demand yet. Maildir is a superior for-
mat anyway.
[technical] Why are your Maildir message filenames so
huge?
OfflineIMAP has two relevant principles: 1) never
modifying your messages in any way and 2) ensuring
100% reliable synchronizations. In order to do a
reliable sync, OfflineIMAP must have a way to
uniquely identify each e-mail. Three pieces of
information are required to do this: your account
name, the folder name, and the message UID. The
account name can be calculated from the path in
which your messages are. The folder name can usu-
ally be as well, BUT some mail clients move mes-
sages between folders by simply moving the file,
leaving the name intact.
So, OfflineIMAP must store both a UID folder ID.
The folder ID is necessary so OfflineIMAP can
detect a message moved to a different folder.
OfflineIMAP stores the UID (U= number) and an
md5sum of the foldername (FMD5= number) to facili-
tate this.
What is the speed of OfflineIMAP's sync?
OfflineIMAP versions 2.0 and above contain a multi-
threaded system. A good way to experiment is by
setting maxsyncaccounts to 3 and maxconnections to
3 in each account clause.
This lets OfflineIMAP open up multiple connections
simultaneously. That will let it process multiple
folders and messages at once. In most cases, this
will increase performance of the sync.
Don't set the number too high. If you do that,
things might actually slow down as your link gets
saturated. Also, too many connections can cause
mail servers to have excessive load. Administra-
tors might take unkindly to this, and the server
might bog down. There are many variables in the
optimal setting; experimentation may help.
An informal benchmark yields these results for my
setup:
10 minutes with MacOS X Mail.app "manual cache"
5 minutes with GNUS agent sync
20 seconds with OfflineIMAP 1.x
9 seconds with OfflineIMAP 2.x
3 seconds with OfflineIMAP 3.x "cold start"
2 seconds with OfflineIMAP 3.x "held connection"
CONFORMING TO
o Internet Message Access Protocol version 4rev1
(IMAP 4rev1) as specified in RFC2060
o Maildir as specified in http://www.qmail.org/qmail-
manual-html/man5/maildir.html and
http://cr.yp.to/proto/maildir.html.
o Standard Python 2.2.1 as implemented on POSIX-com-
pliant systems.
NOTES
DELETING LOCAL FOLDERS
OfflineIMAP does a two-way synchronization. That is, if
you make a change to the mail on the server, it will be
propogated to your local copy, and vise-versa. Some peo-
ple might think that it would be wise to just delete all
their local mail folders periodically. If you do this
with OfflineIMAP, remember to also remove your local sta-
tus cache (~/.offlineimap by default). Otherwise,
OfflineIMAP will take this as an intentional deletion of
many messages and will interpret your action as requesting
them to be deleted from the server as well. (If you don't
understand this, don't worry; you probably won't encounter
this situation)
MAILING LIST
There is an OfflineIMAP mailing list available.
To subscribe, send the text "Subscribe" in the subject of
a mail to offlineimap-request@complete.org. To post, send
the message to offlineimap@complete.org.
BUGS
Should be reported to the author at the address specified
below.
COPYRIGHT
OfflineIMAP is Copyright (C) 2002 John Goerzen.
This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place
Suite 330
Boston, MA 02111-1307
USA
AUTHOR
OfflineIMAP, its libraries, documentation, and all
included files, except where noted, was written by John
Goerzen <jgoerzen@complete.org> and copyright is held as
stated in the COPYRIGHT section.
OfflineIMAP may be downloaded, and information found, from
its homepage via either Gopher or HTTP:
gopher://quux.org/1/devel/offlineimap
http://quux.org/devel/offlineimap
SEE ALSO
mutt(1), python(1).
John Goerzen July 12, 2002 OFFLINEIMAP(1)

View File

@ -0,0 +1,564 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH OFFLINEIMAP 1 "July 12, 2002" "John Goerzen" "OfflineIMAP manual"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
OfflineIMAP \- Powerful IMAP/Maildir synchronization and reader support
.SH SYNOPSIS
.B offlineimap
[
.BI \-1
]
[
.BI \-a \ accountlist
]
[
.BI \-c \ configfile
]
.br
[
.BI \-d
]
[
.BI \-o
]
[
.BI \-u " interface"
]
.\".RI [ -c \ foo ]
.\".RI [ options ] " files" ...
.br
.B offlineimap
.B \-h
|
.B \-\-help
.\".RI [ options ] " files" ...
.SH DESCRIPTION
.B OfflineIMAP
is a tool to simplify your e-mail reading. With
.B OfflineIMAP,
you can read the same mailbox from multiple computers. You get a
current copy of your messages on each computer, and changes you make
one place will be visible on all other systems. For instance, you can
delete a message on your home computer, and it will appear deleted on
your work computer as well.
.B OfflineIMAP
is also useful if you want to use a mail reader that does not have
IMAP support, has poor IMAP support, or does not provide disconnected
operation.
.PP
.B OfflineIMAP
is
.I FAST;
it synchronizes my two accounts with over 50 folders in 3 seconds.
Other similar tools might take over a minute, and achieve a
less-reliable result. Some mail readers can take over 10 minutes to
do the same thing, and some don't even support it at all. Unlike
other mail tools,
.B OfflineIMAP
features a multi-threaded synchronization algorithm that can
dramatically speed up performance in many situations by synchronizing
several different things simultaneously.
.PP
.B OfflineIMAP
is
.I FLEXIBLE;
you can customize which folders are synced via regular expressions, lists, or
Python expressions; a versatile and comprehensive configuration file
is used to control behavior; two user interfaces are built-in;
fine-tuning of synchronization performance is possible; internal or
external automation is supported; SSL and PREAUTH tunnels are both
supported; offline (or "unplugged") reading is supported; and
esoteric IMAP features are supported to ensure compatibility with the
widest variety of IMAP servers.
.PP
.B OfflineIMAP
is
.I SAFE;
it uses an algorithm designed to prevent mail loss at all costs.
Because of the design of this algorithm, even programming errors
should not result in loss of mail. I am so confident in the algorithm
that I use my own personal and work accounts for testing of
.B OfflineIMAP
pre-release, development, and beta releases.
.SS "METHOD OF OPERATION"
.B OfflineIMAP
operates by maintaining a hierarchy of mail folders in Maildir format
locally. Your own mail reader will read mail from this tree, and need
never know that the mail comes from IMAP.
.B OfflineIMAP
will detect changes to the mail folders on your IMAP server and your
own computer and bi-directionally synchronize them, copying, marking,
and deleting messages as necessary.
.SH INSTALLATION
If you are reading this document via the "man" command, it is likely
that you have no installation tasks to perform; your system
administrator has already installed it. If you need to install it
yourself, you have three options: a system-wide installation with
Debian, system-wide installation with other systems, and a single-user
installation. You can download the latest version of OfflineIMAP from
.UR http://quux.org/devel/offlineimap/
http://quux.org/devel/offlineimap/.
.UE
.SS PREREQUISITES
In order to use OfflineIMAP, you need to have these conditions
satisfied:
.IP \(bu
Your mail server must support IMAP. Most Internet Service Providers
and corporate networks do, and most operating systems have an IMAP
implementation readily available.
.IP \(bu
You must have Python version 2.2.1 or above installed. If you are
running on Debian GNU/Linux, this requirement will automatically be
taken care of for you. If you do not have Python already, check with
your system administrator or operating system vendor; or, download it
from
.UR http://www.python.org/
http://www.python.org/.
.UE
If you intend to use the Tk interface, you must have Tkiner
(python-tk) installed. If you intend to use the SSL interface, your
Python must have been built with SSL support.
.IP \(bu
Have a mail reader that supports the Maildir mailbox format. Most
modern mail readers have this support built-in, so you can choose from
a wide variety of mail servers. This format is also known as the
"qmail" format, so any mail reader compatible with it will work with
OfflineIMAP.
.SS DEBIAN SYSTEM-WIDE INSTALLATION
If you are tracking Debian unstable, you may install
.B OfflineIMAP
by simply running the following command as root:
.PP
.B apt-get install offlineimap
.PP
If you are not tracking Debian unstable, download the Debian .deb
package from the OfflineIMAP website
and then run
.B dpkg -i
to install the downloaded package. Then, go to CONFIGURATION below.
You will type
.B offlineimap
to invoke the program.
.SS OTHER SYSTEM-WIDE INSTALLATION
Download the tar.gz version of the package from the website. Then run
these commands:
.PP
.B tar -zxvf offlineimap-x.y.z.tar.gz
.br
.B cd offlineimap-x.y.z
.br
.B python2.2 setup.py
.PP
Some systems will need to use
.B python
instead of
.B python2.2.
Next, proceed to configuration. You will type
.B offlineimap
to invoke the program.
.SS SINGLE-ACCOUNT INSTALLATION
Download the tar.gz version of the package from the website. Then run
these commands:
.PP
.B tar -zxvf offlineimap-x.y.z.tar.gz
.br
.B cd offlineimap-x.y.z
.PP
When you want to run
.B OfflineIMAP,
you will issue the
.B cd
command as above and then type
.B ./offlineimap;
there is no installation step necessary.
.\"##################################################
.SH CONFIGURATION
.B OfflineIMAP
is regulated by a configuration file that is normally stored in
.I ~/.offlineimaprc.
.B OfflineIMAP
ships with a file named
.I offlineimap.conf
that you should copy to that location and then edit. This file is
vital to proper operation of the system; it sets everything you need
to run
.B OfflineIMAP.
Full documentation for the configuration file is included within the
sample file.
.\"##################################################
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
.\" respectively.
.\"\fBofflineimap\fP is a program that...
.SH OPTIONS
Most configuration is done via the configuration file. Nevertheless,
there are a few options that you may set for
.B OfflineIMAP.
.TP
.B \-1
Disable all multithreading operations and use solely a single-thread
sync. This effectively sets the
.B maxsyncaccounts
and all
.B maxconnections
configuration file variables to 1.
.TP
.BI \-a \ accountlist
Overrides the
.B accounts
section in the config file. Lets you specify a particular account or
set of accounts to sync without having to edit the config file. You
might use this to exclude certain accounts, or to sync some accounts
that you normally prefer not to.
.TP
.BI \-c \ configfile
Specifies a configuration file to use in lieu of the default,
.I ~/.offlineimaprc.
.TP
.BI \-d
Enables IMAP protocol stream and parsing debugging. This is useful if
you are trying to track down a malfunction or figure out what is going
on under the hood. I suggest that you use this with
.BI \-1
in order to make the results more sensible. Note that this output
will contain full IMAP protocol in plain text, including passwords, so
take care to remove that from the debugging output before sending it
to anyone else.
.TP
.B \-o
Run only once, ignoring any autorefresh setting in the config file.
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.BI \-u \ interface
Specifies an alternative user interface module to use. This overrides
the default specified in the configuration file. The UI specified
with
.B -u
will be forced to be used, even if its
.B isuable()
method states that it cannot be. Use this option with care.
.IP
The pre-defined options are
.B Tk.TKUI
(a graphical interface)
and
.B TTY.TTYUI
(a text-mode interface).
.\".TP
.\".B \-v, \-\-version
.\"Show version of program.
.SH EXAMPLES
Here is an example configuration for a particularly complex situation;
more examples will be added later.
.SS MULTIPLE ACCOUNTS WITH MUTT
This example shows you how to set up
.B OfflineIMAP
to synchronize multiple accounts with the mutt mail reader.
.PP
Start by creating a directory to hold your folders:
.br
.B mkdir ~/Mail
.PP
In your
.I ~/.offlineimaprc,
specify this:
.br
.B accounts = Personal, Work
.PP
Make sure that you have both a
.B [Personal]
and a
.B [Work]
section, with different localfolder pathnames and enable
.B [mbnames].
.PP
In each account section, do something like this:
.br
.B localfolders = ~/Mail/Personal
.PP
Add these lines to your
.I ~/.muttrc:
.br
.B source ~/path-to-mbnames-muttrc-mailboxes
.br
.B folder-hook Personal set from="youremail@personal.com"
.br
.B folder-hook Work set from="youremail@work.com"
.br
.B set mbox_type=Maildir
.br
.B set folder=$HOME/Mail
.br
.B set spoolfile=+Personal/INBOX
.PP
That's it!
.SH ERRORS
If you get one of some frequently-encountered or confusing errors,
please check this section.
.SS UID validity problem for folder
IMAP servers use a unique ID (UID) to refer to a specific message.
This number is guaranteed to be unique to a particular message
FOREVER. No other message in the same folder will ever get the same
UID. UIDs are an integral part of OfflineIMAP's synchronization
scheme; they are used to match up messages on your computer to
messages on the server.
.PP
Sometimes, the UIDs on the server might get reset. Usually this will
happen if you delete and then recreate a folder. When you create a
folder, the server will often start the UID back from 1. But
.B OfflineIMAP
might still have the UIDs from the previous folder by the
same name stored.
.B OfflineIMAP
will detect this condition and skip the
folder. This is GOOD, because it prevents data loss.
.PP
You can fix it by removing your local folder and cache data. For
instance, if your folders are under
.I ~/Folders
and the folder with the
problem is INBOX, you'd type this:
.PP
.B rm -r ~/Folders/INBOX
.br
.B rm ~/.offlineimap/AccountName/INBOX
.PP
(replacing AccountName with the account name as specified in
.I ~/.offlineimaprc)
.PP
Next time you run
.B OfflineIMAP,
it will re-download the folder with the
new UIDs. Note that the procedure specified above will lose any local
changes made to the folder.
.PP
Some IMAP servers are broken and do not support UIDs properly. If you
continue to get this error for all your folders even after performing
the above procedure, it is likely that your IMAP server falls into
this category.
.B OfflineIMAP
is incompatible with such servers. Using
.B OfflineIMAP
with them will not destroy any mail, but at the same time,
it will not actually synchronize it either. (OfflineIMAP will detect
this condition and abort prior to synchronization)
.SH OTHER FREQUENTLY ASKED QUESTIONS
There are some other FAQs that might not fit into another section of
this document, and they are enumerated here.
.TP
.B What platforms does OfflineIMAP run on?
It should run on most platforms supported by Python, which are quite a
few.
.TP
.B I'm using Mutt. Other IMAP sync programs require me to use "set maildir_trash=yes". Do I need to do that with OfflineIMAP?
No.
.B OfflineIMAP
is smart enough to figure out message deletion without this extra
crutch. You'll get the best results if you don't use this setting, in
fact.
.TP
.B How do I specify the names of my folders?
You do not need to.
.B OfflineIMAP
is smart enough to automatically figure out what folders are present
on the IMAP server and synchronize them. You can use the
.B folderfilter
and
.B foldertrans
configuration file options to request certain folders and rename them
as they come in if you like.
.TP
.B How can I prevent certain folders from being synced?
Use the
.B folderfilter
option in the configuration file.
.TP
.B How can I add or delete a folder?
.B OfflineIMAP
does not currently provide this feature, but if you create a new
folder on the IMAP server, it will be created locally automatically.
.TP
.B Are there any other warnings that I should be aware of?
Yes; see the NOTES section below.
.TP
.B What is the mailbox name recorder (mbnames) for?
The Mutt mail reader is not capable of automatically determining
the names of your mailboxes. OfflineIMAP can help it (or many other)
programs out be writing these names out in a format you specify. See
the example offlineimap.conf file for details.
.TP
.B Can I synchronize multiple accounts with OfflineIMAP?
Sure. Just name them all in the accounts line in the general
section of the config file, and add a per-account section for each one.
.TP
.B Does OfflineIMAP support POP?
No. POP is not robust enough to do a completely reliable
multi-machine synchronization like OfflineIMAP can do. OfflineIMAP
will not support it.
.TP
.B Do you support mailbox formats other than Maildir?
Not at present. There is no technical reason not to; just no
demand yet. Maildir is a superior format anyway.
.TP
.B [technical] Why are your Maildir message filenames so huge?
.B OfflineIMAP
has two relevant principles: 1) never modifying your
messages in any way and 2) ensuring 100% reliable synchronizations.
In order to do a reliable sync,
.B OfflineIMAP
must have a way to
uniquely identify each e-mail. Three pieces of information are
required to do this: your account name, the folder name, and the
message UID. The account name can be calculated from the path in
which your messages are. The folder name can usually be as well, BUT
some mail clients move messages between folders by simply moving the
file, leaving the name intact.
.IP
So,
.B OfflineIMAP
must store both a UID folder ID. The folder ID is
necessary so
.B OfflineIMAP
can detect a message moved to a different
folder.
.B OfflineIMAP
stores the UID (U= number) and an md5sum of the
foldername (FMD5= number) to facilitate this.
.TP
.B What is the speed of OfflineIMAP's sync?
.B OfflineIMAP
versions 2.0 and above contain a multithreaded system. A good way to
experiment is by setting maxsyncaccounts to 3 and maxconnections to 3
in each account clause.
.IP
This lets OfflineIMAP open up multiple connections simultaneously.
That will let it process multiple folders and messages at once. In
most cases, this will increase performance of the sync.
.IP
Don't set the number too high. If you do that, things might actually
slow down as your link gets saturated. Also, too many connections can
cause mail servers to have excessive load. Administrators might take
unkindly to this, and the server might bog down. There are many
variables in the optimal setting; experimentation may help.
.IP
An informal benchmark yields these results for my setup:
.IP
10 minutes with MacOS X Mail.app "manual cache"
.br
5 minutes with GNUS agent sync
.br
20 seconds with OfflineIMAP 1.x
.br
9 seconds with OfflineIMAP 2.x
.br
3 seconds with OfflineIMAP 3.x "cold start"
.br
2 seconds with OfflineIMAP 3.x "held connection"
.SH CONFORMING TO
.IP \(bu
Internet Message Access Protocol version 4rev1 (IMAP 4rev1) as
specified in RFC2060
.IP \(bu
Maildir as specified in
.UR http://www.qmail.org/qmail-manual-html/man5/maildir.html
http://www.qmail.org/qmail-manual-html/man5/maildir.html
.UE
and
.UR http://cr.yp.to/proto/maildir.html
http://cr.yp.to/proto/maildir.html.
.UE
.IP \(bu
Standard Python 2.2.1 as implemented on POSIX-compliant systems.
.SH NOTES
.SS DELETING LOCAL FOLDERS
.B OfflineIMAP
does a two-way synchronization. That is, if you
make a change to the mail on the server, it will be propogated to your
local copy, and vise-versa. Some people might think that it would be
wise to just delete all their local mail folders periodically. If you
do this with OfflineIMAP, remember to also remove your local status
cache (~/.offlineimap by default). Otherwise, OfflineIMAP will take
this as an intentional deletion of many messages and will interpret
your action as requesting them to be deleted from the server as well.
(If you don't understand this, don't worry; you probably won't
encounter this situation)
.SS MAILING LIST
There is an OfflineIMAP mailing list available.
.PP
To subscribe, send the text "Subscribe" in the subject of a mail to
offlineimap-request@complete.org. To post, send the message to
offlineimap@complete.org.
.SH BUGS
Should be reported to the author at the address specified below.
.SH COPYRIGHT
OfflineIMAP is Copyright (C) 2002 John Goerzen.
.PP
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.
.PP
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.
.PP
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
.PP
Free Software Foundation, Inc.
.br
59 Temple Place
.br
Suite 330
.br
Boston, MA 02111-1307
.br
USA
.SH AUTHOR
.B OfflineIMAP,
its libraries, documentation, and all included files, except where
noted, was written by John Goerzen <jgoerzen@complete.org> and
copyright is held as stated in the COPYRIGHT section.
.PP
OfflineIMAP may be downloaded, and information found, from its
homepage via either Gopher or HTTP:
.PP
.UR gopher://quux.org/1/devel/offlineimap
gopher://quux.org/1/devel/offlineimap
.UE
.br
.UR http://quux.org/devel/offlineimap
http://quux.org/devel/offlineimap
.UE
.SH SEE ALSO
.BR mutt (1),
.BR python (1).
.\".BR bar (1),
.\".BR baz (1).
.\".br
.\"The programs are documented fully by
.\".IR "The Rise and Fall of a Fooish Bar" ,
.\"available via the Info system.
.\".SH AUTHOR
.\"This manual page was written by John Goerzen <jgoerzen@complete.org>,
.\"for the Debian GNU/Linux system (but may be used by others).

View File

@ -0,0 +1,246 @@
# Sample configuration file
# Copyright (C) 2002 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
##################################################
# General definitions
##################################################
[general]
# This specifies where offlineimap is to store its metadata.
# This directory will be created if it does not already exist.
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
# You can have offlineimap continue running indefinately, automatically
# syncing your mail periodically. If you want that, specify how
# frequently to do that (in minutes) here.
# autorefresh = 5
# Offlineimap can synchronize more the one account at a time. If you
# want to enable this feature, set the below value to something
# greater than 1. To force it to synchronize only one account at a
# time, leave it at 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
# fails, the second, and so forth.
#
# The pre-defined options are:
# Tk.TkUI -- A graphical interface
# TTY.TTYUI -- a text-based (terminal) interface
#
# You can override this with a command-line option -u.
ui = Tk.TkUI, TTY.TTYUI
##################################################
# Mailbox name recorder
##################################################
[mbnames]
# offlineimap can record your mailbox names in a format you specify.
# You can define the header, each mailbox item, the separator,
# and the footer. Here is an example for Mutt.
# If enabled is yes, all six setting must be specified, even if they
# are just the empty string "".
#
# The header, peritem, sep, and footer are all Python expressions passed
# through eval, so you can (and must) use Python quoting.
enabled = no
filename = ~/Mutt/muttrc.mailboxes
header = "mailboxes "
peritem = "+%(accountname)s/%(foldername)s"
sep = " "
footer = "\n"
##################################################
# Accounts
##################################################
# This is an account definition clause. You'll have one of these
# for each account listed in general/accounts above.
[Test]
########## Basic settings
# Specify local repository. Your IMAP folders will be synchronized
# to maildirs created under this path. OfflineIMAP will create the
# maildirs for you as needed.
localfolders = ~/Test
# Specify the remote hostname.
remotehost = examplehost
# Whether or not to use SSL.
ssl = yes
# Specify the port. If not specified, use a default port.
# remoteport = 993
# Specify the remote user name.
remoteuser = username
# There are three ways to specify the password for the remote IMAP
# server:
#
# 1. No password at all specified in the config file. You will
# be prompted for the password when OfflineIMAP starts.
#
# 2. The remote password stored in this file with the remotepass
# option. Example:
#
# remotepass = mypassword
#
# 3. The remote password stored as a single line in an external
# file, which is referenced by the remotefile option. Example:
#
# remotepassfile = ~/Password.IMAP.Account1
#
# 4. With a preauth tunnel. With this method, you invoke an external
# program that is guaranteed *NOT* to ask for a password, but rather
# to read from stdin and write to stdout an IMAP procotol stream
# that begins life in the PREAUTH state. When you use a tunnel,
# you do NOT specify a user or password (if you do, they'll be
# ignored.) Instead, you specify a preauthtunnel, as this
# example illustrates for Courier IMAP on Debian:
#
# preauthtunnel = ssh -q imaphost '/usr/bin/imapd ./Maildir'
#
########## Advanced settings
# Some IMAP servers need a "reference" which often refers to the
# "folder root". This is most commonly needed with UW IMAP, where
# you might need to specify the directory in which your mail is
# stored. Most users will not need this.
#
# reference = Mail
# You can specify a folder translator. This must be a eval-able
# Python expression that takes a foldername arg and returns the new
# value. I suggest a lambda. This example below will remove "INBOX." from
# the leading edge of folders (great for Courier IMAP users)
#
# WARNING: you MUST construct this such that it NEVER returns
# the same value for two folders, UNLESS the second values are
# filtered out by folderfilter below. Failure to follow this rule
# will result in undefined behavior
#
# nametrans = lambda foldername: re.sub('^INBOX.', '', foldername)
# You can specify which folders to sync. You can do it several ways.
# I'll provide some examples. The folderfilter operates on the
# *UNTRANSLATED* name, if you specify nametrans. It should return
# true if the folder is to be included; false otherwise.
#
# Example 1: synchronizing only INBOX and Sent.
#
# folderfilter = lambda foldername: foldername in ['INBOX', 'Sent']
#
# Example 2: synchronizing everything except Trash.
#
# folderfilter = lambda foldername: foldername not in ['Trash']
#
# Example 3: Using a regular expression to exclude Trash and all folders
# containing the characters "Del".
#
# folderfilter = lambda foldername: not re.search('(^Trash$|Del)', foldername)
#
# If folderfilter is not specified, ALL remote folders will be
# synchronized.
#
# You can span multiple lines by indenting the others. (Use backslashes
# at the end when required by Python syntax) For instance:
#
# folderfilter = lambda foldername: foldername in
# ['INBOX', 'Sent Mail', 'Deleted Items',
# 'Received']
#
# FYI, you could also include every folder with:
#
# folderfilter = lambda foldername: 1
#
# And exclude every folder with:
#
# folderfilter = lambda foldername: 0
# You can specify folderincludes to include additional folders.
# It should return a Python list. This might be used to include a
# folder that was excluded by your folderfilter rule, to include a
# folder that your server does not specify with its LIST option, or
# to include a folder that is outside your basic reference. Some examples:
#
# To include debian.user and debian.personal:
#
# folderincludes = ['debian.user', 'debian.personal']
#
# To include your INBOX (UW IMAPd users will find this useful if they
# specify a reference):
#
# folderincludes = ['INBOX']
#
# To specify a long list:
#
# folderincludes = ['box1', 'box2', 'box3', 'box4',
# 'box5', 'box6']
# OfflineIMAP can use multiple connections to the server in order
# to perform multiple synchronization actions simultaneously.
# This may place a higher burden on the server. In most cases,
# setting this value to 2 or 3 will speed up the sync, but in some
# cases, it may slow things down. The safe answer is 1. You should
# probably never set it to a value more than 5.
maxconnections = 1
# OfflineIMAP normally closes IMAP server connections between refreshes if
# the global option autorefresh is specified. If you wish it to keep the
# connection open, set this to true. If not specified, the default is
# 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
# 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

View File

@ -0,0 +1,299 @@
#!/usr/bin/python2.2
# Copyright (C) 2002 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 imaplib, imaputil, imapserver, repository, folder, mbnames, threadutil, version
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
import re, os, os.path, offlineimap, sys
from ConfigParser import ConfigParser
from threading import *
from getopt import getopt
options = {}
if '--help' in sys.argv[1:]:
sys.stdout.write(version.cmdhelp + "\n")
sys.exit(0)
for optlist in getopt(sys.argv[1:], '1oa:c:du:h')[0]:
options[optlist[0]] = optlist[1]
if '-d' in options:
imaplib.Debug = 5
if '-h' in options:
sys.stdout.write(version.cmdhelp)
sys.stdout.write("\n")
sys.exit(0)
configfilename = os.path.expanduser("~/.offlineimaprc")
if '-c' in options:
configfilename = options['-c']
config = ConfigParser()
if not os.path.exists(configfilename):
sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename)
sys.exit(1)
config.read(configfilename)
if '-u' in options:
ui = offlineimap.ui.detector.getUImod(options['-u'])()
else:
ui = offlineimap.ui.detector.findUI(config)
ui.init_banner()
if '-o' in options and config.has_option("general", "autorefresh"):
config.remove_option("general", "autorefresh")
metadatadir = os.path.expanduser(config.get("general", "metadata"))
if not os.path.exists(metadatadir):
os.mkdir(metadatadir, 0700)
accounts = config.get("general", "accounts")
if '-a' in options:
accounts = options['-a']
accounts = accounts.replace(" ", "")
accounts = accounts.split(",")
server = None
remoterepos = None
localrepos = None
passwords = {}
tunnels = {}
if '-1' in options:
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
else:
threadutil.initInstanceLimit("ACCOUNTLIMIT",
config.getint("general", "maxsyncaccounts"))
# We have to gather passwords here -- don't want to have two threads
# asking for passwords simultaneously.
for account in accounts:
if config.has_option(account, "preauthtunnel"):
tunnels[account] = config.get(account, "preauthtunnel")
elif config.has_option(account, "remotepass"):
passwords[account] = config.get(account, "remotepass")
elif config.has_option(account, "remotepassfile"):
passfile = open(os.path.expanduser(config.get(account, "remotepassfile")))
passwords[account] = passfile.readline().strip()
passfile.close()
else:
passwords[account] = ui.getpass(account, config)
for instancename in ["FOLDER_" + account, "MSGCOPY_" + account]:
if '-1' in options:
threadutil.initInstanceLimit(instancename, 1)
else:
threadutil.initInstanceLimit(instancename,
config.getint(account, "maxconnections"))
mailboxes = []
servers = {}
def syncaccount(accountname, *args):
# We don't need an account lock because syncitall() goes through
# each account once, then waits for all to finish.
try:
ui.acct(accountname)
accountmetadata = os.path.join(metadatadir, accountname)
if not os.path.exists(accountmetadata):
os.mkdir(accountmetadata, 0700)
server = None
if accountname in servers:
server = servers[accountname]
else:
server = imapserver.ConfigedIMAPServer(config, accountname, passwords)
servers[accountname] = server
remoterepos = repository.IMAP.IMAPRepository(config, accountname, server)
# Connect to the Maildirs.
localrepos = repository.Maildir.MaildirRepository(os.path.expanduser(config.get(accountname, "localfolders")))
# Connect to the local cache.
statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata)
ui.syncfolders(remoterepos, localrepos)
remoterepos.syncfoldersto(localrepos)
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):
# 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()))
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():
global mailboxes
mailboxes = [] # Reset.
threads = []
for accountname in accounts:
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
target = syncaccount,
name = "Account sync %s" % accountname,
args = (accountname,))
thread.setDaemon(1)
thread.start()
threads.append(thread)
# Wait for the threads to finish.
threadutil.threadsreset(threads)
mbnames.genmbnames(config, mailboxes)
def sync_with_timer():
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
syncitall()
if config.has_option('general', 'autorefresh'):
refreshperiod = config.getint('general', 'autorefresh') * 60
while 1:
# Set up keep-alives.
kaevents = {}
kathreads = {}
for accountname in accounts:
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()
def threadexited(thread):
if thread.getExitCause() == 'EXCEPTION':
if isinstance(thread.getExitException(), SystemExit):
# Bring a SystemExit into the main thread.
# Do not send it back to UI layer right now.
# Maybe later send it to ui.terminate?
raise SystemExit
ui.threadException(thread) # Expected to terminate
sys.exit(100) # Just in case...
os._exit(100)
elif thread.getExitMessage() == 'SYNC_WITH_TIMER_TERMINATE':
ui.terminate()
# Just in case...
sys.exit(100)
os._exit(100)
else:
ui.threadExited(thread)
threadutil.initexitnotify()
t = ExitNotifyThread(target=sync_with_timer,
name='Sync Runner')
t.setDaemon(1)
t.start()
try:
threadutil.exitnotifymonitorloop(threadexited)
except SystemExit:
raise
except:
ui.mainException() # Also expected to terminate.

View File

@ -0,0 +1,2 @@
import ui, folder, repository, mbnames, threadutil

View File

@ -0,0 +1,289 @@
# Base folder support
# Copyright (C) 2002 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
import __main__
from threading import *
from offlineimap import threadutil
from offlineimap.threadutil import InstanceLimitedThread
class BaseFolder:
def getname(self):
"""Returns name"""
return self.name
def suggeststhreads(self):
"""Returns true if this folder suggests using threads for actions;
false otherwise. Probably only IMAP will return true."""
return 0
def waitforthread(self):
"""For threading folders, waits until there is a resource available
before firing off a thread. For all others, returns immediately."""
pass
def getcopyinstancelimit(self):
"""For threading folders, returns the instancelimitname for
InstanceLimitedThreads."""
raise NotImplementedException
def storesmessages(self):
"""Should be true for any backend that actually saves message bodies.
(Almost all of them). False for the LocalStatus backend. Saves
us from having to slurp up messages just for localstatus purposes."""
return 1
def getvisiblename(self):
return self.name
def getroot(self):
"""Returns the root of the folder, in a folder-specific fashion."""
return self.root
def getsep(self):
"""Returns the separator for this folder type."""
return self.sep
def getfullname(self):
if self.getroot():
return self.getroot() + self.getsep() + self.getname()
else:
return self.getname()
def isuidvalidityok(self, remotefolder):
raise NotImplementedException
def getuidvalidity(self):
raise NotImplementedException
def saveuidvalidity(self, newval):
raise NotImplementedException
def cachemessagelist(self):
"""Reads the message list from disk or network and stores it in
memory for later use. This list will not be re-read from disk or
memory unless this function is called again."""
raise NotImplementedException
def getmessagelist(self):
"""Gets the current message list.
You must call cachemessagelist() before calling this function!"""
raise NotImplementedException
def getmessage(self, uid):
"""Returns the content of the specified message."""
raise NotImplementedException
def savemessage(self, uid, content, flags):
"""Writes a new message, with the specified uid.
If the uid is < 0, the backend should assign a new uid and return it.
If the backend cannot assign a new uid, it returns the uid passed in
WITHOUT saving the message.
IMAP backend should be the only one that can assign a new uid.
If the uid is > 0, the backend should set the uid to this, if it can.
If it cannot set the uid to that, it will save it anyway.
It will return the uid assigned in any case.
"""
raise NotImplementedException
def getmessageflags(self, uid):
"""Returns the flags for the specified message."""
raise NotImplementedException
def savemessageflags(self, uid, flags):
"""Sets the specified message's flags to the given set."""
raise NotImplementedException
def addmessageflags(self, uid, flags):
"""Adds the specified flags to the message's flag set. If a given
flag is already present, it will not be duplicated."""
newflags = self.getmessageflags(uid)
for flag in flags:
if not flag in newflags:
newflags.append(flag)
newflags.sort()
self.savemessageflags(uid, newflags)
def addmessagesflags(self, uidlist, flags):
for uid in uidlist:
self.addmessageflags(uid)
def deletemessageflags(self, uid, flags):
"""Removes each flag given from the message's flag set. If a given
flag is already removed, no action will be taken for that flag."""
newflags = self.getmessageflags(uid)
for flag in flags:
if flag in newflags:
newflags.remove(flag)
newflags.sort()
self.savemessageflags(uid, newflags)
def deletemessage(self, uid):
raise NotImplementedException
def deletemessages(self, uidlist):
for uid in uidlist:
self.deletemessage(uid)
def syncmessagesto_neguid(self, dest, applyto):
"""Pass 1 of folder synchronization.
Look for messages in self with a negative uid. These are messages in
Maildirs that were not added by us. Try to add them to the dests,
and once that succeeds, get the UID, add it to the others for real,
add it to local for real, and delete the fake one."""
for uid in self.getmessagelist().keys():
if uid >= 0:
continue
__main__.ui.copyingmessage(uid, self, applyto)
successobject = None
successuid = None
message = self.getmessage(uid)
flags = self.getmessageflags(uid)
for tryappend in applyto:
successuid = tryappend.savemessage(uid, message, flags)
if successuid > 0:
successobject = tryappend
break
# Did we succeed?
if successobject != None:
# Copy the message to the other remote servers.
for appendserver in [x for x in applyto if x != successobject]:
appendserver.savemessage(successuid, message, flags)
# Copy it to its new name on the local server and delete
# the one without a UID.
self.savemessage(successuid, message, flags)
self.deletemessage(uid)
else:
# Did not find any server to take this message. Ignore.
pass
def copymessageto(self, uid, applyto):
# Sometimes, it could be the case that if a sync takes awhile,
# a message might be deleted from the maildir before it can be
# synced to the status cache. This is only a problem with
# self.getmessage(). So, don't call self.getmessage unless
# really needed.
__main__.ui.copyingmessage(uid, self, applyto)
message = ''
# If any of the destinations actually stores the message body,
# load it up.
for object in applyto:
if object.storesmessages():
message = self.getmessage(uid)
break
flags = self.getmessageflags(uid)
for object in applyto:
newuid = object.savemessage(uid, message, flags)
if newuid > 0 and newuid != uid:
# Change the local uid.
self.savemessage(newuid, message, flags)
self.deletemessage(uid)
uid = newuid
def syncmessagesto_copy(self, dest, applyto):
"""Pass 2 of folder synchronization.
Look for messages present in self but not in dest. If any, add
them to dest."""
threads = []
for uid in self.getmessagelist().keys():
if uid < 0: # Ignore messages that pass 1 missed.
continue
if not uid in dest.getmessagelist():
if self.suggeststhreads():
self.waitforthread()
thread = InstanceLimitedThread(\
self.getcopyinstancelimit(),
target = self.copymessageto,
name = "Copy message %d from %s" % (uid,
self.getvisiblename()),
args = (uid, applyto))
thread.setDaemon(1)
thread.start()
threads.append(thread)
else:
self.copymessageto(uid, applyto)
for thread in threads:
thread.join()
def syncmessagesto_delete(self, dest, applyto):
"""Pass 3 of folder synchronization.
Look for message present in dest but not in self.
If any, delete them."""
deletelist = []
for uid in dest.getmessagelist().keys():
if uid < 0:
continue
if not uid in self.getmessagelist():
deletelist.append(uid)
if len(deletelist):
__main__.ui.deletingmessages(deletelist, applyto)
for object in applyto:
object.deletemessages(deletelist)
def syncmessagesto_flags(self, dest, applyto):
"""Pass 4 of folder synchronization.
Look for any flag matching issues -- set dest message to have the
same flags that we have."""
for uid in self.getmessagelist().keys():
if uid < 0: # Ignore messages missed by pass 1
continue
selfflags = self.getmessageflags(uid)
destflags = dest.getmessageflags(uid)
addflags = [x for x in selfflags if x not in destflags]
if len(addflags):
__main__.ui.addingflags(uid, addflags, applyto)
for object in applyto:
object.addmessageflags(uid, addflags)
delflags = [x for x in destflags if x not in selfflags]
if len(delflags):
__main__.ui.deletingflags(uid, delflags, applyto)
for object in applyto:
object.deletemessageflags(uid, delflags)
def syncmessagesto(self, dest, applyto = None):
"""Syncs messages in this folder to the destination.
If applyto is specified, it should be a list of folders (don't forget
to include dest!) to which all write actions should be applied.
It defaults to [dest] if not specified. It is important that
the UID generator be listed first in applyto; that is, the other
applyto ones should be the ones that "copy" the main action."""
if applyto == None:
applyto = [dest]
self.syncmessagesto_neguid(dest, applyto)
self.syncmessagesto_copy(dest, applyto)
self.syncmessagesto_delete(dest, applyto)
# Now, the message lists should be identical wrt the uids present.
# (except for potential negative uids that couldn't be placed
# anywhere)
self.syncmessagesto_flags(dest, applyto)

View File

@ -0,0 +1,203 @@
# IMAP folder support
# Copyright (C) 2002 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 Base import BaseFolder
from offlineimap import imaputil, imaplib
import rfc822
from StringIO import StringIO
from copy import copy
class IMAPFolder(BaseFolder):
def __init__(self, imapserver, name, visiblename, accountname):
self.name = imaputil.dequote(name)
self.root = imapserver.root
self.sep = imapserver.delim
self.imapserver = imapserver
self.messagelist = None
self.visiblename = visiblename
self.accountname = accountname
def suggeststhreads(self):
return 1
def waitforthread(self):
self.imapserver.connectionwait()
def getcopyinstancelimit(self):
return 'MSGCOPY_' + self.accountname
def getvisiblename(self):
return self.visiblename
def getuidvalidity(self):
imapobj = self.imapserver.acquireconnection()
try:
# Primes untagged_responses
imapobj.select(self.getfullname(), readonly = 1)
return long(imapobj.untagged_responses['UIDVALIDITY'][0])
finally:
self.imapserver.releaseconnection(imapobj)
def cachemessagelist(self):
imapobj = self.imapserver.acquireconnection()
self.messagelist = {}
try:
# Primes untagged_responses
imapobj.select(self.getfullname(), readonly = 1)
maxmsgid = long(imapobj.untagged_responses['EXISTS'][0])
if maxmsgid < 1:
# No messages; return
return
# Now, get the flags and UIDs for these.
# We could conceivably get rid of maxmsgid and just say
# '1:*' here.
response = imapobj.fetch('1:%d' % maxmsgid, '(FLAGS UID)')[1]
finally:
self.imapserver.releaseconnection(imapobj)
for messagestr in response:
# Discard the message number.
messagestr = imaputil.imapsplit(messagestr)[1]
options = imaputil.flags2hash(messagestr)
uid = long(options['UID'])
flags = imaputil.flagsimap2maildir(options['FLAGS'])
self.messagelist[uid] = {'uid': uid, 'flags': flags}
def getmessagelist(self):
return self.messagelist
def getmessage(self, uid):
imapobj = self.imapserver.acquireconnection()
try:
imapobj.select(self.getfullname(), readonly = 1)
return imapobj.uid('fetch', '%d' % uid, '(BODY.PEEK[])')[1][0][1].replace("\r\n", "\n")
finally:
self.imapserver.releaseconnection(imapobj)
def getmessageflags(self, uid):
return self.messagelist[uid]['flags']
def savemessage(self, uid, content, flags):
imapobj = self.imapserver.acquireconnection()
try:
imapobj.select(self.getfullname()) # Needed for search
# This backend always assigns a new uid, so the uid arg is ignored.
# In order to get the new uid, we need to save off the message ID.
message = rfc822.Message(StringIO(content))
mid = imapobj._quote(message.getheader('Message-Id'))
date = imaplib.Time2Internaldate(rfc822.parsedate(message.getheader('Date')))
if content.find("\r\n") == -1: # Convert line endings if not already
content = content.replace("\n", "\r\n")
assert(imapobj.append(self.getfullname(),
imaputil.flagsmaildir2imap(flags),
date, content)[0] == 'OK')
# Checkpoint. Let it write out the messages, etc.
assert(imapobj.check()[0] == 'OK')
# Now find the UID it got.
matchinguids = imapobj.uid('search', None,
'(HEADER Message-Id %s)' % mid)[1][0]
matchinguids = matchinguids.split(' ')
matchinguids.sort()
uid = long(matchinguids[-1])
self.messagelist[uid] = {'uid': uid, 'flags': flags}
return uid
finally:
self.imapserver.releaseconnection(imapobj)
def savemessageflags(self, uid, flags):
imapobj = self.imapserver.acquireconnection()
try:
imapobj.select(self.getfullname())
result = imapobj.uid('store', '%d' % uid, 'FLAGS',
imaputil.flagsmaildir2imap(flags))
assert result[0] == 'OK', 'Error with store: ' + r[1]
finally:
self.imapserver.releaseconnection(imapobj)
result = result[1][0]
if not result:
self.messagelist[uid]['flags'] = flags
else:
flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
def addmessageflags(self, uid, flags):
self.addmessagesflags([uid], flags)
def addmessagesflags(self, uidlist, flags):
imapobj = self.imapserver.acquireconnection()
try:
imapobj.select(self.getfullname())
r = imapobj.uid('store',
imaputil.listjoin(uidlist),
'+FLAGS',
imaputil.flagsmaildir2imap(flags))
assert r[0] == 'OK', 'Error with store: ' + r[1]
r = r[1]
finally:
self.imapserver.releaseconnection(imapobj)
# Some IMAP servers do not always return a result. Therefore,
# only update the ones that it talks about, and manually fix
# the others.
needupdate = copy(uidlist)
for result in r:
if result == None:
# Compensate for servers that don't return anything from
# STORE.
continue
attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
if not ('UID' in attributehash and 'FLAGS' in attributehash):
# Compensate for servers that don't return a UID attribute.
continue
flags = attributehash['FLAGS']
uid = long(attributehash['UID'])
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
try:
needupdate.remove(uid)
except ValueError: # Let it slide if it's not in the list
pass
for uid in needupdate:
for flag in flags:
if not flag in self.messagelist[uid]['flags']:
self.messagelist[uid]['flags'].append(flag)
self.messagelist[uid]['flags'].sort()
def deletemessage(self, uid):
self.deletemessages([uid])
def deletemessages(self, uidlist):
# Weed out ones not in self.messagelist
uidlist = [uid for uid in uidlist if uid in self.messagelist]
if not len(uidlist):
return
self.addmessagesflags(uidlist, ['T'])
imapobj = self.imapserver.acquireconnection()
try:
imapobj.select(self.getfullname())
assert(imapobj.expunge()[0] == 'OK')
finally:
self.imapserver.releaseconnection(imapobj)
for uid in uidlist:
del self.messagelist[uid]

View File

@ -0,0 +1,102 @@
# Local status cache virtual folder
# Copyright (C) 2002 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 Base import BaseFolder
import os
magicline = "OFFLINEIMAP LocalStatus CACHE DATA - DO NOT MODIFY - FORMAT 1"
class LocalStatusFolder(BaseFolder):
def __init__(self, root, name):
self.name = name
self.root = root
self.sep = '.'
self.filename = os.path.join(root, name)
self.messagelist = None
def storesmessages(self):
return 0
def isnewfolder(self):
return not os.path.exists(self.filename)
def getname(self):
return self.name
def getroot(self):
return self.root
def getsep(self):
return self.sep
def getfullname(self):
return self.filename
def cachemessagelist(self):
if self.isnewfolder():
self.messagelist = {}
return
file = open(self.filename, "rt")
self.messagelist = {}
line = file.readline().strip()
assert(line == magicline)
for line in file.xreadlines():
line = line.strip()
uid, flags = line.split(':')
uid = long(uid)
flags = [x for x in flags]
self.messagelist[uid] = {'uid': uid, 'flags': flags}
file.close()
def save(self):
file = open(self.filename + ".tmp", "wt")
file.write(magicline + "\n")
for msg in self.messagelist.values():
flags = msg['flags']
flags.sort()
flags = ''.join(flags)
file.write("%s:%s\n" % (msg['uid'], flags))
file.close()
os.rename(self.filename + ".tmp", self.filename)
def getmessagelist(self):
return self.messagelist
def savemessage(self, uid, content, flags):
if uid < 0:
# We cannot assign a uid.
return uid
if uid in self.messagelist: # already have it
self.savemessageflags(uid, flags)
return uid
self.messagelist[uid] = {'uid': uid, 'flags': flags}
return uid
def getmessageflags(self, uid):
return self.messagelist[uid]['flags']
def savemessageflags(self, uid, flags):
self.messagelist[uid]['flags'] = flags
def deletemessage(self, uid):
if not uid in self.messagelist:
return
del(self.messagelist[uid])

View File

@ -0,0 +1,211 @@
# Maildir folder support
# Copyright (C) 2002 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 Base import BaseFolder
from offlineimap import imaputil
import os.path, os, re, time, socket, md5
timeseq = 0
lasttime = long(0)
def gettimeseq():
global lasttime, timeseq
thistime = long(time.time())
if thistime == lasttime:
timeseq += 1
return timeseq
else:
lasttime = long(time.time())
timeseq = 0
return timeseq
class MaildirFolder(BaseFolder):
def __init__(self, root, name):
self.name = name
self.root = root
self.sep = '.'
self.uidfilename = os.path.join(self.getfullname(), "offlineimap.uidvalidity")
self.messagelist = None
def getfullname(self):
return os.path.join(self.getroot(), self.getname())
def getuidvalidity(self):
if not os.path.exists(self.uidfilename):
return None
file = open(self.uidfilename, "rt")
retval = long(file.readline().strip())
file.close()
return retval
def saveuidvalidity(self, newval):
file = open(self.uidfilename, "wt")
file.write("%d\n" % newval)
file.close()
def isuidvalidityok(self, remotefolder):
myval = self.getuidvalidity()
if myval != None:
return myval == remotefolder.getuidvalidity()
else:
self.saveuidvalidity(remotefolder.getuidvalidity())
return 1
def _scanfolder(self):
"""Cache the message list. Maildir flags are:
R (replied)
S (seen)
T (trashed)
D (draft)
F (flagged)
and must occur in ASCII order."""
retval = {}
files = []
nouidcounter = -1 # Messages without UIDs get
# negative UID numbers.
for dirannex in ['new', 'cur']:
fulldirname = os.path.join(self.getfullname(), dirannex)
files.extend([os.path.join(fulldirname, filename) for
filename in os.listdir(fulldirname)])
for file in files:
messagename = os.path.basename(file)
foldermatch = re.search(',FMD5=([0-9a-f]{32})', messagename)
if (not foldermatch) or \
md5.new(self.getvisiblename()).hexdigest() \
!= foldermatch.group(1):
# If there is no folder MD5 specified, or if it mismatches,
# assume it is a foreign (new) message and generate a
# negative uid for it
uid = nouidcounter
nouidcounter -= 1
else: # It comes from our folder.
uidmatch = re.search(',U=(\d+)', messagename)
uid = None
if not uidmatch:
uid = nouidcounter
nouidcounter -= 1
else:
uid = long(uidmatch.group(1))
flagmatch = re.search(':.*2,([A-Z]+)', messagename)
flags = []
if flagmatch:
flags = [x for x in flagmatch.group(1)]
flags.sort()
if 'T' in flags:
# Message is marked for deletion; just delete it now.
# Otherwise, the T flag will be propogated to the IMAP
# server, and then expunged there, and then deleted here.
# Might as well just delete it now, to help make things
# more robust.
os.unlink(file)
else:
retval[uid] = {'uid': uid,
'flags': flags,
'filename': file}
return retval
def cachemessagelist(self):
self.messagelist = self._scanfolder()
def getmessagelist(self):
return self.messagelist
def getmessage(self, uid):
filename = self.getmessagelist()[uid]['filename']
file = open(filename, 'rt')
retval = file.read()
file.close()
return retval
def savemessage(self, uid, content, flags):
if uid < 0:
# We cannot assign a new uid.
return uid
if uid in self.getmessagelist():
# We already have it.
self.savemessageflags(uid, flags)
return uid
newdir = os.path.join(self.getfullname(), 'new')
tmpdir = os.path.join(self.getfullname(), 'tmp')
messagename = None
attempts = 0
while 1:
if attempts > 15:
raise IOError, "Couldn't write to file %s" % messagename
messagename = '%d_%d.%d.%s,U=%d,FMD5=%s' % \
(long(time.time()),
gettimeseq(),
os.getpid(),
socket.gethostname(),
uid,
md5.new(self.getvisiblename()).hexdigest())
if os.path.exists(os.path.join(tmpdir, messagename)):
time.sleep(2)
attempts += 1
else:
break
file = open(os.path.join(tmpdir, messagename), "wt")
file.write(content)
file.close()
os.link(os.path.join(tmpdir, messagename),
os.path.join(newdir, messagename))
os.unlink(os.path.join(tmpdir, messagename))
self.messagelist[uid] = {'uid': uid, 'flags': [],
'filename': os.path.join(newdir, messagename)}
self.savemessageflags(uid, flags)
return uid
def getmessageflags(self, uid):
return self.getmessagelist()[uid]['flags']
def savemessageflags(self, uid, flags):
oldfilename = self.getmessagelist()[uid]['filename']
newpath, newname = os.path.split(oldfilename)
infostr = ':'
infomatch = re.search('(:.*)$', newname)
if infomatch: # If the info string is present..
infostr = infomatch.group(1)
newname = newname.split(':')[0] # Strip off the info string.
infostr = re.sub('2,[A-Z]*', '', infostr)
flags.sort()
infostr += '2,' + ''.join(flags)
newname += infostr
newfilename = os.path.join(newpath, newname)
if (newfilename != oldfilename):
os.rename(oldfilename, newfilename)
self.getmessagelist()[uid]['flags'] = flags
self.getmessagelist()[uid]['filename'] = newfilename
def getmessageflags(self, uid):
return self.getmessagelist()[uid]['flags']
def deletemessage(self, uid):
if not uid in self.messagelist:
return
filename = self.getmessagelist()[uid]['filename']
try:
os.unlink(filename)
except IOError:
# Can't find the file -- maybe already deleted?
newmsglist = self._scanfolder()
if uid in newmsglist: # Nope, try new filename.
os.unlink(newmsglist[uid]['filename'])
# Yep -- return.
del(self.messagelist[uid])

View File

@ -0,0 +1,2 @@
import Base, IMAP, Maildir, LocalStatus

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
# IMAP server support
# Copyright (C) 2002 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 imaplib, imaputil, threadutil
from threading import *
import thread
class UsefulIMAPMixIn:
def getstate(self):
return self.state
def getselectedfolder(self):
if self.getstate() == 'SELECTED':
return self.selectedfolder
return None
def select(self, mailbox='INBOX', readonly=None):
if self.getselectedfolder() == mailbox:
self.is_readonly = readonly
# No change; return.
return
result = self.__class__.__bases__[1].select(self, mailbox, readonly)
if result[0] != 'OK':
raise ValueError, "Error from select: %s" % str(result)
if self.getstate() == 'SELECTED':
self.selectedfolder = mailbox
else:
self.selectedfolder = None
class UsefulIMAP4(UsefulIMAPMixIn, imaplib.IMAP4): pass
class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplib.IMAP4_SSL): pass
class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplib.IMAP4_Tunnel): pass
class IMAPServer:
def __init__(self, username = None, password = None, hostname = None,
port = None, ssl = 1, maxconnections = 1, tunnel = None,
reference = '""'):
self.username = username
self.password = password
self.hostname = hostname
self.tunnel = tunnel
self.port = port
self.usessl = ssl
self.delim = None
self.root = None
if port == None:
if ssl:
self.port = 993
else:
self.port = 143
self.maxconnections = maxconnections
self.availableconnections = []
self.assignedconnections = []
self.lastowner = {}
self.semaphore = BoundedSemaphore(self.maxconnections)
self.connectionlock = Lock()
self.reference = reference
def getdelim(self):
"""Returns this server's folder delimiter. Can only be called
after one or more calls to acquireconnection."""
return self.delim
def getroot(self):
"""Returns this server's folder root. Can only be called after one
or more calls to acquireconnection."""
return self.root
def releaseconnection(self, connection):
self.connectionlock.acquire()
self.assignedconnections.remove(connection)
self.availableconnections.append(connection)
self.connectionlock.release()
self.semaphore.release()
def acquireconnection(self):
"""Fetches a connection from the pool, making sure to create a new one
if needed, to obey the maximum connection limits, etc.
Opens a connection to the server and returns an appropriate
object."""
self.semaphore.acquire()
self.connectionlock.acquire()
imapobj = None
if len(self.availableconnections): # One is available.
# Try to find one that previously belonged to this thread
# as an optimization. Start from the back since that's where
# they're popped on.
threadid = thread.get_ident()
imapobj = None
for i in range(len(self.availableconnections) - 1, -1, -1):
tryobj = self.availableconnections[i]
if self.lastowner[tryobj] == threadid:
imapobj = tryobj
del(self.availableconnections[i])
break
if not imapobj:
imapobj = self.availableconnections[0]
del(self.availableconnections[0])
self.assignedconnections.append(imapobj)
self.lastowner[imapobj] = thread.get_ident()
self.connectionlock.release()
return imapobj
self.connectionlock.release() # Release until need to modify data
# Generate a new connection.
if self.tunnel:
imapobj = UsefulIMAP4_Tunnel(self.tunnel)
elif self.usessl:
imapobj = UsefulIMAP4_SSL(self.hostname, self.port)
else:
imapobj = UsefulIMAP4(self.hostname, self.port)
if not self.tunnel:
imapobj.login(self.username, self.password)
if self.delim == None:
self.delim, self.root = \
imaputil.imapsplit(imapobj.list(self.reference, '""')[1][0])[1:]
self.delim = imaputil.dequote(self.delim)
self.root = imaputil.dequote(self.root)
self.connectionlock.acquire()
self.assignedconnections.append(imapobj)
self.lastowner[imapobj] = thread.get_ident()
self.connectionlock.release()
return imapobj
def connectionwait(self):
"""Waits until there is a connection available. Note that between
the time that a connection becomes available and the time it is
requested, another thread may have grabbed it. This function is
mainly present as a way to avoid spawning thousands of threads
to copy messages, then have them all wait for 3 available connections.
It's OK if we have maxconnections + 1 or 2 threads, which is what
this will help us do."""
threadutil.semaphorewait(self.semaphore)
def close(self):
# Make sure I own all the semaphores. Let the threads finish
# their stuff. This is a blocking method.
self.connectionlock.acquire()
threadutil.semaphorereset(self.semaphore, self.maxconnections)
for imapobj in self.assignedconnections + self.availableconnections:
imapobj.logout()
self.assignedconnections = []
self.availableconnections = []
self.lastowner = {}
self.connectionlock.release()
def keepalive(self, timeout, event):
"""Sends a NOOP to each connection recorded. It will wait a maximum
of timeout seconds between doing this, and will continue to do so
until the Event object as passed is true. This method is expected
to be invoked in a separate thread, which should be join()'d after
the event is set."""
while 1:
event.wait(timeout)
if event.isSet():
return
self.connectionlock.acquire()
numconnections = len(self.assignedconnections) + \
len(self.availableconnections)
self.connectionlock.release()
threads = []
imapobjs = []
for i in range(numconnections):
imapobj = self.acquireconnection()
imapobjs.append(imapobj)
thr = threadutil.ExitNotifyThread(target = imapobj.noop)
thr.setDaemon(1)
thr.start()
threads.append(thr)
for thr in threads:
# Make sure all the commands have completed.
thr.join()
for imapobj in imapobjs:
self.releaseconnection(imapobj)
class ConfigedIMAPServer(IMAPServer):
"""This class is designed for easier initialization given a ConfigParser
object and an account name. The passwordhash is used if
passwords for certain accounts are known. If the password for this
account is listed, it will be obtained from there."""
def __init__(self, config, accountname, passwordhash = {}):
"""Initialize the object. If the account is not a tunnel,
the password is required."""
host = config.get(accountname, "remotehost")
user = config.get(accountname, "remoteuser")
port = None
if config.has_option(accountname, "remoteport"):
port = config.getint(accountname, "remoteport")
ssl = config.getboolean(accountname, "ssl")
usetunnel = config.has_option(accountname, "preauthtunnel")
reference = '""'
if config.has_option(accountname, "reference"):
reference = config.get(accountname, "reference")
server = None
password = None
if accountname in passwordhash:
password = passwordhash[accountname]
# Connect to the remote server.
if usetunnel:
IMAPServer.__init__(self,
tunnel = config.get(accountname, "preauthtunnel"),
reference = reference,
maxconnections = config.getint(accountname, "maxconnections"))
else:
if not password:
password = config.get(accountname, 'remotepass')
IMAPServer.__init__(self, user, password, host, port, ssl,
config.getint(accountname, "maxconnections"),
reference = reference)

View File

@ -0,0 +1,139 @@
# IMAP utility module
# Copyright (C) 2002 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
import re
def dequote(string):
"""Takes a string which may or may not be quoted and returns it, unquoted.
This function does NOT consider parenthised lists to be quoted.
"""
if not (string[0] == '"' and string[-1] == '"'):
return string
string = string[1:-1] # Strip off quotes.
string = string.replace('\\"', '"')
string = string.replace('\\\\', '\\')
return string
def flagsplit(string):
if string[0] != '(' or string[-1] != ')':
raise ValueError, "Passed string '%s' is not a flag list" % string
return imapsplit(string[1:-1])
def options2hash(list):
retval = {}
counter = 0
while (counter < len(list)):
retval[list[counter]] = list[counter + 1]
counter += 2
return retval
def flags2hash(string):
return options2hash(flagsplit(string))
def imapsplit(string):
"""Takes a string from an IMAP conversation and returns a list containing
its components. One example string is:
(\\HasNoChildren) "." "INBOX.Sent"
The result from parsing this will be:
['(\\HasNoChildren)', '"."', '"INBOX.Sent"']"""
workstr = string
retval = []
while len(workstr):
if re.search('^\s', workstr):
workstr = re.search('^\s(.*)$', workstr).group(1)
elif workstr[0] == '(':
parenlist = re.search('^(\(.*\))', workstr).group(1)
workstr = workstr[len(parenlist):]
retval.append(parenlist)
elif workstr[0] == '"':
quotelist = re.search('^("(?:[^"]|\\\\")*")', workstr).group(1)
workstr = workstr[len(quotelist):]
retval.append(quotelist)
else:
unq = re.search('^(\S+)', workstr).group(1)
workstr = workstr[len(unq):]
retval.append(unq)
return retval
def flagsimap2maildir(string):
flagmap = {'\\seen': 'S',
'\\answered': 'R',
'\\flagged': 'F',
'\\deleted': 'T',
'\\draft': 'D'}
retval = []
imapflaglist = [x.lower() for x in flagsplit(string)]
for imapflag in imapflaglist:
if flagmap.has_key(imapflag):
retval.append(flagmap[imapflag])
retval.sort()
return retval
def flagsmaildir2imap(list):
flagmap = {'S': '\\Seen',
'R': '\\Answered',
'F': '\\Flagged',
'T': '\\Deleted',
'D': '\\Draft'}
retval = []
for mdflag in list:
if flagmap.has_key(mdflag):
retval.append(flagmap[mdflag])
retval.sort()
return '(' + ' '.join(retval) + ')'
def listjoin(list):
start = None
end = None
retval = []
def getlist(start, end):
if start == end:
return(str(start))
else:
return(str(start) + ":" + str(end))
for item in list:
if start == None:
# First item.
start = item
end = item
elif item == end + 1:
# An addition to the list.
end = item
else:
# Here on: starting a new list.
retval.append(getlist(start, end))
start = item
end = item
if start != None:
retval.append(getlist(start, end))
return ",".join(retval)

View File

@ -0,0 +1,34 @@
# Mailbox name generator
# Copyright (C) 2002 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
import os.path
def genmbnames(config, boxlist):
"""Takes a configparser object and a boxlist, which is a list of hashes
containing 'accountname' and 'foldername' keys."""
if not config.getboolean("mbnames", "enabled"):
return
file = open(os.path.expanduser(config.get("mbnames", "filename")), "wt")
file.write(eval(config.get("mbnames", "header")))
itemlist = [eval(config.get("mbnames", "peritem", raw=1)) % item for item in boxlist]
file.write(eval(config.get("mbnames", "sep")).join(itemlist))
file.write(eval(config.get("mbnames", "footer")))
file.close()

View File

@ -0,0 +1,69 @@
# Base repository support
# Copyright (C) 2002 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
class BaseRepository:
def getfolders(self):
"""Returns a list of ALL folders on this server."""
return []
def getsep(self):
raise NotImplementedError
def makefolder(self, foldername):
raise NotImplementedError
def deletefolder(self, foldername):
raise NotImplementedError
def getfolder(self, foldername):
raise NotImplementedError
def syncfoldersto(self, dest):
"""Syncs the folders in this repository to those in dest.
It does NOT sync the contents of those folders."""
src = self
srcfolders = src.getfolders()
destfolders = dest.getfolders()
# Create hashes with the names, but convert the source folders
# to the dest folder's sep.
srchash = {}
for folder in srcfolders:
srchash[folder.getvisiblename().replace(src.getsep(), dest.getsep())] = \
folder
desthash = {}
for folder in destfolders:
desthash[folder.getvisiblename()] = folder
#
# Find new folders.
#
for key in srchash.keys():
if not key in desthash:
dest.makefolder(key)
#
# Find deleted folders.
#
for key in desthash.keys():
if not key in srchash:
dest.deletefolder(key)

View File

@ -0,0 +1,76 @@
# IMAP repository support
# Copyright (C) 2002 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 Base import BaseRepository
from offlineimap import folder, imaputil
import re
from threading import *
class IMAPRepository(BaseRepository):
def __init__(self, config, accountname, imapserver):
"""Initialize an IMAPRepository object. Takes an IMAPServer
object."""
self.imapserver = imapserver
self.config = config
self.accountname = accountname
self.folders = None
self.nametrans = lambda foldername: foldername
self.folderfilter = lambda foldername: 1
self.folderincludes = []
if config.has_option(accountname, 'nametrans'):
self.nametrans = eval(config.get(accountname, 'nametrans'))
if config.has_option(accountname, 'folderfilter'):
self.folderfilter = eval(config.get(accountname, 'folderfilter'))
if config.has_option(accountname, 'folderincludes'):
self.folderincludes = eval(config.get(accountname, 'folderincludes'))
def getsep(self):
return self.imapserver.delim
def getfolder(self, foldername):
return folder.IMAP.IMAPFolder(self.imapserver, foldername,
self.nametrans(foldername),
accountname)
def getfolders(self):
if self.folders != None:
return self.folders
retval = []
imapobj = self.imapserver.acquireconnection()
try:
listresult = imapobj.list(directory = self.imapserver.reference)[1]
finally:
self.imapserver.releaseconnection(imapobj)
for string in listresult:
flags, delim, name = imaputil.imapsplit(string)
flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
if '\\noselect' in flaglist:
continue
foldername = imaputil.dequote(name)
if not self.folderfilter(foldername):
continue
retval.append(folder.IMAP.IMAPFolder(self.imapserver, foldername,
self.nametrans(foldername),
self.accountname))
for foldername in self.folderincludes:
retval.append(folder.IMAP.IMAPFolder(self.imapserver, foldername,
self.nametrans(foldername),
self.accountname))
retval.sort(lambda x, y: cmp(x.getvisiblename(), y.getvisiblename()))
self.folders = retval
return retval

View File

@ -0,0 +1,54 @@
# Local status cache repository support
# Copyright (C) 2002 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 Base import BaseRepository
from offlineimap import folder
import os
class LocalStatusRepository(BaseRepository):
def __init__(self, directory):
self.directory = directory
self.folders = None
def getsep(self):
return '.'
def getfolderfilename(self, foldername):
return os.path.join(self.directory, foldername)
def makefolder(self, foldername):
# "touch" the file.
file = open(self.getfolderfilename(foldername), "ab")
file.close()
# Invalidate the cache.
self.folders = None
def getfolders(self):
retval = []
for folder in os.listdir(self.directory):
retval.append(folder.LocalStatus.LocalStatusFolder(self.directory,
folder))
return retval
def getfolder(self, foldername):
return folder.LocalStatus.LocalStatusFolder(self.directory, foldername)

View File

@ -0,0 +1,67 @@
# Maildir repository support
# Copyright (C) 2002 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 Base import BaseRepository
from offlineimap import folder, imaputil
from mailbox import Maildir
import os
class MaildirRepository(BaseRepository):
def __init__(self, root):
"""Initialize a MaildirRepository object. Takes a path name
to the directory holding all the Maildir directories."""
self.root = root
self.folders = None
def getsep(self):
return '.'
def makefolder(self, foldername):
folderdir = os.path.join(self.root, foldername)
os.mkdir(folderdir, 0700)
for subdir in ['cur', 'new', 'tmp']:
os.mkdir(os.path.join(folderdir, subdir), 0700)
# Invalidate the cache
self.folders = None
def deletefolder(self, foldername):
print "NOT YET IMPLEMENTED: DELETE FOLDER %s" % foldername
def getfolder(self, foldername):
return folder.Maildir.MaildirFolder(self.root, foldername)
def getfolders(self):
if self.folders != None:
return self.folders
retval = []
for dirname in os.listdir(self.root):
fullname = os.path.join(self.root, dirname)
if not os.path.isdir(fullname):
continue
if not (os.path.isdir(os.path.join(fullname, 'cur')) and
os.path.isdir(os.path.join(fullname, 'new')) and
os.path.isdir(os.path.join(fullname, 'tmp'))):
continue
retval.append(folder.Maildir.MaildirFolder(self.root, dirname))
self.folders = retval
return retval

View File

@ -0,0 +1 @@
import IMAP, Base, Maildir, LocalStatus

View File

@ -0,0 +1,165 @@
# Copyright (C) 2002 John Goerzen
# Thread support module
# <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 threading import *
from StringIO import StringIO
import sys, traceback, thread
######################################################################
# General utilities
######################################################################
def semaphorereset(semaphore, originalstate):
"""Wait until the semaphore gets back to its original state -- all acquired
resources released."""
for i in range(originalstate):
semaphore.acquire()
# Now release these.
for i in range(originalstate):
semaphore.release()
def semaphorewait(semaphore):
semaphore.acquire()
semaphore.release()
def threadsreset(threadlist):
for thr in threadlist:
thr.join()
######################################################################
# Exit-notify threads
######################################################################
exitcondition = Condition(Lock())
exitthreads = []
inited = 0
def initexitnotify():
"""Initialize the exit notify system. This MUST be called from the
SAME THREAD that will call monitorloop BEFORE it calls monitorloop.
This SHOULD be called before the main thread starts any other
ExitNotifyThreads, or else it may miss the ability to catch the exit
status from them!"""
pass
def exitnotifymonitorloop(callback):
"""Enter an infinite "monitoring" loop. The argument, callback,
defines the function to call when an ExitNotifyThread has terminated.
That function is called with a single argument -- the ExitNotifyThread
that has terminated. The monitor will not continue to monitor for
other threads until the function returns, so if it intends to perform
long calculations, it should start a new thread itself -- but NOT
an ExitNotifyThread, or else an infinite loop may result. Furthermore,
the monitor will hold the lock all the while the other thread is waiting.
"""
global exitcondition, exitthreads
while 1: # Loop forever.
exitcondition.acquire()
while not len(exitthreads):
exitcondition.wait(1)
while len(exitthreads):
callback(exitthreads.pop())
exitcondition.release()
class ExitNotifyThread(Thread):
"""This class is designed to alert a "monitor" to the fact that a thread has
exited and to provide for the ability for it to find out why."""
def run(self):
global exitcondition, exitthreads
self.threadid = thread.get_ident()
try:
Thread.run(self)
except:
self.setExitCause('EXCEPTION')
self.setExitException(sys.exc_info()[1])
sbuf = StringIO()
traceback.print_exc(file = sbuf)
self.setExitStackTrace(sbuf.getvalue())
else:
self.setExitCause('NORMAL')
if not hasattr(self, 'exitmessage'):
self.setExitMessage(None)
exitcondition.acquire()
exitthreads.append(self)
exitcondition.notify()
exitcondition.release()
def setExitCause(self, cause):
self.exitcause = cause
def getExitCause(self):
"""Returns the cause of the exit, one of:
'EXCEPTION' -- the thread aborted because of an exception
'NORMAL' -- normal termination."""
return self.exitcause
def setExitException(self, exc):
self.exitexception = exc
def getExitException(self):
"""If getExitCause() is 'EXCEPTION', holds the value from
sys.exc_info()[1] for this exception."""
return self.exitexception
def setExitStackTrace(self, st):
self.exitstacktrace = st
def getExitStackTrace(self):
"""If getExitCause() is 'EXCEPTION', returns a string representing
the stack trace for this exception."""
return self.exitstacktrace
def setExitMessage(self, msg):
"""Sets the exit message to be fetched by a subsequent call to
getExitMessage. This message may be any object or type except
None."""
self.exitmessage = msg
def getExitMessage(self):
"""For any exit cause, returns the message previously set by
a call to setExitMessage(), or None if there was no such message
set."""
return self.exitmessage
######################################################################
# Instance-limited threads
######################################################################
instancelimitedsems = {}
instancelimitedlock = Lock()
def initInstanceLimit(instancename, instancemax):
"""Initialize the instance-limited thread implementation to permit
up to intancemax threads with the given instancename."""
instancelimitedlock.acquire()
if not instancelimitedsems.has_key(instancename):
instancelimitedsems[instancename] = BoundedSemaphore(instancemax)
instancelimitedlock.release()
class InstanceLimitedThread(ExitNotifyThread):
def __init__(self, instancename, *args, **kwargs):
self.instancename = instancename
apply(ExitNotifyThread.__init__, (self,) + args, kwargs)
def start(self):
instancelimitedsems[self.instancename].acquire()
ExitNotifyThread.start(self)
def run(self):
try:
ExitNotifyThread.run(self)
finally:
instancelimitedsems[self.instancename].release()

View File

@ -0,0 +1,82 @@
# TTY UI
# Copyright (C) 2002 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 UIBase import UIBase
from getpass import getpass
import select, sys
from threading import *
class TTYUI(UIBase):
def __init__(self, verbose = 0):
self.verbose = 0
self.iswaiting = 0
def _msg(s, msg):
if (currentThread().getName() == 'MainThread'):
print msg
else:
print "%s:\n %s" % (currentThread().getName(), msg)
sys.stdout.flush()
def getpass(s, accountname, config):
return getpass("%s: Enter password for %s on %s: " %
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost")))
def syncingmessages(s, sr, sf, dr, df):
if s.verbose:
UIBase.syncingmessages(s, sr, sf, dr, df)
def loadmessagelist(s, repos, folder):
if s.verbose:
UIBase.syncingmessages(s, repos, folder)
def messagelistloaded(s, repos, folder, count):
if s.verbose:
UIBase.messagelistloaded(s, repos, folder, count)
def sleep(s, sleepsecs):
s.iswaiting = 1
try:
UIBase.sleep(s, sleepsecs)
finally:
s.iswaiting = 0
def mainException(s):
if isinstance(sys.exc_info()[1], KeyboardInterrupt) and \
s.iswaiting:
sys.stdout.write("Timer interrupted at user request; program terminating. \n")
s.terminate()
else:
UIBase.mainException(s)
def sleeping(s, sleepsecs, remainingsecs):
if remainingsecs > 0:
sys.stdout.write("Next sync in %d:%02d (press Enter to sync now, Ctrl-C to abort) \r" % \
(remainingsecs / 60, remainingsecs % 60))
sys.stdout.flush()
else:
sys.stdout.write("Wait done, proceeding with sync.... \n")
if sleepsecs > 0:
if len(select.select([sys.stdin], [], [], sleepsecs)[0]):
sys.stdin.readline()
return 1
return 0

View File

@ -0,0 +1,298 @@
# Tk UI
# Copyright (C) 2002 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 Tkinter import *
from threading import *
import thread, traceback, time
from StringIO import StringIO
from ScrolledText import ScrolledText
from offlineimap import threadutil, version
from Queue import Queue
from UIBase import UIBase
class PasswordDialog:
def __init__(self, accountname, config, master=None):
self.top = Toplevel(master)
self.top.title(version.productname + " Password Entry")
self.label = Label(self.top,
text = "%s: Enter password for %s on %s: " % \
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost")))
self.label.pack()
self.entry = Entry(self.top, show='*')
self.entry.bind("<Return>", self.ok)
self.entry.pack()
self.entry.focus_force()
self.button = Button(self.top, text = "OK", command=self.ok)
self.button.pack()
self.entry.focus_force()
self.top.wait_window(self.label)
def ok(self, args = None):
self.password = self.entry.get()
self.top.destroy()
def getpassword(self):
return self.password
class TextOKDialog:
def __init__(self, title, message, blocking = 1, master = None):
if not master:
self.top = Tk()
else:
self.top = Toplevel(master)
self.top.title(title)
self.text = ScrolledText(self.top, font = "Courier 10")
self.text.pack()
self.text.insert(END, message)
self.text['state'] = DISABLED
self.button = Button(self.top, text = "OK", command=self.ok)
self.button.pack()
if blocking:
self.top.wait_window(self.button)
def ok(self):
self.top.destroy()
class ThreadFrame(Frame):
def __init__(self, master=None):
self.threadextraframe = None
self.thread = currentThread()
self.threadid = thread.get_ident()
Frame.__init__(self, master, relief = RIDGE, borderwidth = 2)
self.pack(fill = 'x')
self.threadlabel = Label(self, foreground = '#FF0000',
text ="Thread %d (%s)" % (self.threadid,
self.thread.getName()))
self.threadlabel.pack()
self.setthread(currentThread())
self.account = "Unknown"
self.mailbox = "Unknown"
self.loclabel = Label(self,
text = "Account/mailbox information unknown")
#self.loclabel.pack()
self.updateloclabel()
self.message = Label(self, text="Messages will appear here.\n",
foreground = '#0000FF')
self.message.pack(fill = 'x')
def setthread(self, newthread):
if newthread:
self.threadlabel['text'] = newthread.getName()
else:
self.threadlabel['text'] = "No thread"
self.destroythreadextraframe()
def destroythreadextraframe(self):
if self.threadextraframe:
self.threadextraframe.destroy()
self.threadextraframe = None
def getthreadextraframe(self):
if self.threadextraframe:
return self.threadextraframe
self.threadextraframe = Frame(self)
self.threadextraframe.pack(fill = 'x')
return self.threadextraframe
def setaccount(self, account):
self.account = account
self.mailbox = "Unknown"
self.updateloclabel()
def setmailbox(self, mailbox):
self.mailbox = mailbox
self.updateloclabel()
def updateloclabel(self):
self.loclabel['text'] = "Processing %s: %s" % (self.account,
self.mailbox)
def appendmessage(self, newtext):
self.message['text'] += "\n" + newtext
def setmessage(self, newtext):
self.message['text'] = newtext
class TkUI(UIBase):
def __init__(self, verbose = 0):
self.verbose = verbose
def isusable(s):
try:
Tk().destroy()
return 1
except TclError:
return 0
def _createTopWindow(self):
self.top = Tk()
self.top.title(version.productname + " " + version.versionstr)
self.threadframes = {}
self.availablethreadframes = []
self.tflock = Lock()
self.notdeleted = 1
t = threadutil.ExitNotifyThread(target = self._runmainloop,
name = "Tk Mainloop")
t.setDaemon(1)
t.start()
t = threadutil.ExitNotifyThread(target = self.idlevacuum,
name = "Tk idle vacuum")
t.setDaemon(1)
t.start()
def _runmainloop(s):
s.top.mainloop()
s.notdeleted = 0
def getpass(s, accountname, config):
pd = PasswordDialog(accountname, config)
return pd.getpassword()
def gettf(s):
threadid = thread.get_ident()
s.tflock.acquire()
try:
if threadid in s.threadframes:
return s.threadframes[threadid]
if len(s.availablethreadframes):
tf = s.availablethreadframes.pop(0)
tf.setthread(currentThread())
else:
tf = ThreadFrame(s.top)
s.threadframes[threadid] = tf
return tf
finally:
s.tflock.release()
def _msg(s, msg):
s.gettf().setmessage(msg)
def threadExited(s, thread):
threadid = thread.threadid
s.tflock.acquire()
if threadid in s.threadframes:
tf = s.threadframes[threadid]
tf.setthread(None)
tf.setaccount("Unknown")
tf.setmessage("Idle")
s.availablethreadframes.append(tf)
del s.threadframes[threadid]
s.tflock.release()
def idlevacuum(s):
while s.notdeleted:
time.sleep(10)
s.tflock.acquire()
while len(s.availablethreadframes):
tf = s.availablethreadframes.pop()
tf.destroy()
s.tflock.release()
def threadException(s, thread):
msg = "Thread '%s' terminated with exception:\n%s" % \
(thread.getName(), thread.getExitStackTrace())
print msg
s.top.destroy()
s.top = None
TextOKDialog("Thread Exception", msg)
s.terminate(100)
def mainException(s):
sbuf = StringIO()
traceback.print_exc(file = sbuf)
msg = "Main program terminated with exception:\n" + sbuf.getvalue()
print msg
s.top.destroy()
s.top = None
TextOKDialog("Main Program Exception", msg)
def warn(s, msg):
TextOKDialog("OfflineIMAP Warning", msg)
def init_banner(s):
s._createTopWindow()
s._msg(version.productname + " " + version.versionstr + ", " +\
version.copyright)
tf = s.gettf().getthreadextraframe()
def showlicense():
TextOKDialog(version.productname + " License",
version.bigcopyright + "\n" +
version.homepage + "\n\n" + version.license,
blocking = 0, master = tf)
b = Button(tf, text = "About", command = showlicense)
b.pack(side = LEFT)
b = Button(tf, text = "Exit", command = s.terminate)
b.pack(side = RIGHT)
def deletingmessages(s, uidlist, destlist):
ds = s.folderlist(destlist)
s._msg("Deleting %d messages in %s" % (len(uidlist), ds))
def _sleep_cancel(s, args = None):
s.sleeping_abort = 1
def sleep(s, sleepsecs):
s.sleeping_abort = 0
tf = s.gettf().getthreadextraframe()
sleepbut = Button(tf, text = 'Sync immediately',
command = s._sleep_cancel)
sleepbut.pack()
UIBase.sleep(s, sleepsecs)
def sleeping(s, sleepsecs, remainingsecs):
if remainingsecs:
s._msg("Next sync in %d:%02d" % (remainingsecs / 60,
remainingsecs % 60))
else:
s._msg("Wait done; synchronizing now.")
s.gettf().destroythreadextraframe()
time.sleep(sleepsecs)
return s.sleeping_abort
################################################## Copied from TTY
def syncingmessages(s, sr, sf, dr, df):
if s.verbose:
UIBase.syncingmessages(s, sr, sf, dr, df)
def loadmessagelist(s, repos, folder):
if s.verbose:
UIBase.syncingmessages(s, repos, folder)
def messagelistloaded(s, repos, folder, count):
if s.verbose:
UIBase.messagelistloaded(s, repos, folder, count)

View File

@ -0,0 +1,171 @@
# UI base class
# Copyright (C) 2002 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 repository
import offlineimap.version
import re, time, sys, traceback
from StringIO import StringIO
class UIBase:
################################################## UTILS
def _msg(s, msg):
"""Generic tool called when no other works."""
raise NotImplementedException
def warn(s, msg):
s._msg("WARNING: " + msg)
def getnicename(s, object):
prelimname = str(object.__class__).split('.')[-1]
# Strip off extra stuff.
return re.sub('(Folder|Repository)', '', prelimname)
def isusable(s):
"""Returns true if this UI object is usable in the current
environment. For instance, an X GUI would return true if it's
being run in X with a valid DISPLAY setting, and false otherwise."""
return 1
################################################## INPUT
def getpass(s, accountname, config):
raise NotImplementedException
def folderlist(s, list):
return ', '.join(["%s[%s]" % (s.getnicename(x), x.getname()) for x in list])
################################################## MESSAGES
def init_banner(s):
"""Called when the UI starts. Must be called before any other UI
call except isusable(). Displays the copyright banner. This is
where the UI should do its setup -- TK, for instance, would
create the application window here."""
s._msg(offlineimap.version.banner)
def acct(s, accountname):
s._msg("***** Processing account %s" % accountname)
def syncfolders(s, srcrepos, destrepos):
s._msg("Copying folder structure from %s to %s" % \
(s.getnicename(srcrepos), s.getnicename(destrepos)))
############################## Folder syncing
def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
"""Called when a folder sync operation is started."""
s._msg("Syncing %s: %s -> %s" % (srcfolder.getname(),
s.getnicename(srcrepos),
s.getnicename(destrepos)))
def validityproblem(s, folder):
s.warn("UID validity problem for folder %s; skipping it" % \
folder.getname())
def loadmessagelist(s, repos, folder):
s._msg("Loading message list for %s[%s]" % (s.getnicename(repos),
folder.getname()))
def messagelistloaded(s, repos, folder, count):
s._msg("Message list for %s[%s] loaded: %d messages" % \
(s.getnicename(repos), folder.getname(), count))
############################## Message syncing
def syncingmessages(s, sr, sf, dr, df):
s._msg("Syncing messages %s[%s] -> %s[%s]" % (s.getnicename(sr),
sf.getname(),
s.getnicename(dr),
df.getname()))
def copyingmessage(s, uid, src, destlist):
ds = s.folderlist(destlist)
s._msg("Copy message %d %s[%s] -> %s" % (uid, s.getnicename(src),
src.getname(), ds))
def deletingmessage(s, uid, destlist):
ds = s.folderlist(destlist)
s._msg("Deleting message %d in %s" % (uid, ds))
def deletingmessages(s, uidlist, destlist):
ds = s.folderlist(destlist)
s._msg("Deleting %d messages (%s) in %s" % \
(len(uidlist),
", ".join([str(u) for u in uidlist]),
ds))
def addingflags(s, uid, flags, destlist):
ds = s.folderlist(destlist)
s._msg("Adding flags %s to message %d on %s" % \
(", ".join(flags), uid, ds))
def deletingflags(s, uid, flags, destlist):
ds = s.folderlist(destlist)
s._msg("Deleting flags %s to message %d on %s" % \
(", ".join(flags), uid, ds))
################################################## Threads
def threadException(s, thread):
"""Called when a thread has terminated with an exception.
The argument is the ExitNotifyThread that has so terminated."""
s._msg("Thread '%s' terminated with exception:\n%s" % \
(thread.getName(), thread.getExitStackTrace()))
s.terminate(100)
def mainException(s):
sbuf = StringIO()
traceback.print_exc(file = sbuf)
s._msg("Main program terminated with exception:\n" +
sbuf.getvalue())
def terminate(s, exitstatus = 0):
"""Called to terminate the application."""
sys.exit(exitstatus)
def threadExited(s, thread):
"""Called when a thread has exited normally. Many UIs will
just ignore this."""
pass
################################################## Other
def sleep(s, sleepsecs):
"""This function does not actually output anything, but handles
the overall sleep, dealing with updates as necessary. It will,
however, call sleeping() which DOES output something.
Returns 0 if timeout expired, 1 if there is a request to cancel
the timer, and 2 if there is a request to abort the program."""
abortsleep = 0
while sleepsecs > 0 and not abortsleep:
abortsleep = s.sleeping(1, sleepsecs)
sleepsecs -= 1
s.sleeping(0, 0) # Done sleeping.
return abortsleep
def sleeping(s, sleepsecs, remainingsecs):
"""Sleep for sleepsecs, remainingsecs to go.
If sleepsecs is 0, indicates we're done sleeping.
Return 0 for normal sleep, or 1 to indicate a request
to sync immediately."""
s._msg("Next refresh in %d seconds" % remainingsecs)
if sleepsecs > 0:
time.sleep(sleepsecs)
return 0

View File

@ -0,0 +1,32 @@
# UI module directory
# Copyright (C) 2002 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
import UIBase
try:
import TTY
except ImportError:
pass
try:
import Tkinter
except ImportError:
pass
else:
import Tk
import detector

View File

@ -0,0 +1,40 @@
# UI base class
# Copyright (C) 2002 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.ui import *
import sys
def findUI(config):
uistrlist = ['Tk.TkUI', 'TTY.TTYUI']
if config.has_option("general", "ui"):
uistrlist = config.get("general", "ui").replace(" ", "").split(",")
for uistr in uistrlist:
uimod = getUImod(uistr)
if uimod:
uiinstance = uimod()
if uiinstance.isusable():
return uiinstance
sys.stderr.write("ERROR: No UIs were found usable!\n")
sys.exit(200)
def getUImod(uistr):
try:
uimod = eval(uistr)
except (AttributeError, NameError):
return None
return uimod

View File

@ -0,0 +1,84 @@
productname = 'OfflineIMAP'
versionstr = "3.0.2"
revno = long('$Rev: 128 $'[6:-2])
revstr = "Rev %d" % revno
datestr = '$Date: 2002-07-17 13:06:27 -0500 (Wed, 17 Jul 2002) $'
versionlist = versionstr.split(".")
major = versionlist[0]
minor = versionlist[1]
patch = versionlist[2]
copyright = "Copyright (C) 2002 John Goerzen"
author = "John Goerzen"
author_email = "jgoerzen@complete.org"
description = "Disconnected Universal IMAP Mail Synchronization/Reader Support"
bigcopyright = """%(productname)s %(versionstr)s (%(revstr)s)
%(copyright)s <%(author_email)s>""" % locals()
banner = bigcopyright + """
This software comes with ABSOLUTELY NO WARRANTY; see the file
COPYING for details. This is free software, and you are welcome
to distribute it under the conditions laid out in COPYING."""
homepage = "http://www.quux.org/devel/offlineimap"
homegopher = "gopher://quux.org/1/devel/offlineimap"
license = """Copyright (C) 2002 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"""
cmdhelp = """
offlineimap [ -1 ] [ -a accountlist ] [ -c configfile ]
[ -d ] [ -u interface ]
offlineimap -h | --help
-1 Disable all multithreading operations and use
solely a single-thread sync. This effectively sets
the maxsyncaccounts and all maxconnections configu-
ration file variables to 1.
-a accountlist
Overrides the accounts section in the config file.
Lets you specify a particular account or set of
accounts to sync without having to edit the config
file. You might use this to exclude certain
accounts, or to sync some accounts that you nor-
mally prefer not to.
-c configfile
Specifies a configuration file to use in lieu of
the default, ~/.offlineimaprc.
-d Enables IMAP protocol stream and parsing debugging.
This is useful if you are trying to track down a
malfunction or figure out what is going on under
the hood. I suggest that you use this with -1 in
order to make the results more sensible. Note that
this output will contain full IMAP protocol in
plain text, including passwords, so take care to
remove that from the debugging output before send-
ing it to anyone else.
-h, --help
Show summary of options.
-u interface
Specifies an alternative user interface module to
use. This overrides the default specified in the
configuration file. The UI specified with -u will
be forced to be used, even if its isuable() method
states that it cannot be. Use this option with
care.
"""

40
offlineimap/head/setup.py Normal file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env python2.2
# $Id: setup.py,v 1.1 2002/06/21 18:10:49 jgoerzen Exp $
# IMAP synchronization
# Module: installer
# COPYRIGHT #
# Copyright (C) 2002 John Goerzen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# END OF COPYRIGHT #
from distutils.core import setup
import offlineimap.version
setup(name = "offlineimap",
version = offlineimap.version.versionstr,
description = offlineimap.version.description,
author = offlineimap.version.author,
author_email = offlineimap.version.author_email,
url = offlineimap.version.homepage,
packages = ['offlineimap', 'offlineimap.folder',
'offlineimap.repository', 'offlineimap.ui'],
scripts = ['offlineimap.py'],
license = offlineimap.version.license
)