Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
194 KB
Referenced Files
None
Subscribers
None
diff --git a/doc/changes.html b/doc/changes.html
index 3152a9214..91f0b22b1 100644
--- a/doc/changes.html
+++ b/doc/changes.html
@@ -1,1487 +1,1490 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<!-- $Id: changes.html,v 1.26 2002/01/15 21:18:28 ken3 Exp $ -->
+<!-- $Id: changes.html,v 1.27 2002/01/18 22:58:45 rjs3 Exp $ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content="HTML Tidy, see www.w3.org" />
<title>Changes to the Cyrus IMAP Server</title>
</head>
<body>
<h1>Changes to the Cyrus IMAP Server</h1>
<h2>Changes to the Cyrus IMAP Server since 2.1.1</h2>
<ul>
<li> now compatible with Berkeley DB4 (Larry M. Rosenbaum,
<tt>lmr@ornl.gov</tt>) </li>
<li> timsieved now supports proxying via <tt>loginuseacl</tt> (Amos
Gouaux, <tt>amos@utdallas.edu</tt>) </li>
<li> Sieve <tt>vacation</tt> now does a case-insensitive comparison of
<tt>:addresses</tt> </li>
+
+<li> Warning-related bug fixes from Henrique de Moras Holschuh
+<tt>hmh@debian.org</tt></li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.1.0</h2>
<ul>
<li> now compatible with Cyrus SASL 2.1.0 </li>
<li> fixed a problem with LMTP AUTH and unix domain sockets </li>
<li> make deleting users faster </li>
<li> add a "-n" switch to <tt>remotepurge</tt></li>
<li> cyradm now does implicit SASL authorization </li>
<li> fix for Sieve <tt>:matches</tt> comparator </li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.16</h2>
<ul>
<li>migrated to SASLv2 (Rob Siemborski)</li>
<li>altnamespace: it is now possible to display user mailboxes as
siblings to the INBOX at the top-level (Ken Murchison)</li>
<li>unixhierarchysep: it is now possible possible to use slash as
the hierarchy seperator, instead of a period. (Ken Murchison,
inspired by David Fuchs, <tt>dfuchs@uniserve.com</tt>)</li>
<li>SSL/TLS session caching (Ken Murchison)</li>
<li>support for IMAP CHILDREN &amp; LISTEXT extensions (Ken
Murchison, work in progress)</li>
<li>check recipient quota &amp; ACL at time of RCPT TO: in
<tt>lmtpd</tt> (Ken Murchison)</li>
<li>support for LMTP STARTTLS &amp; SIZE extensions (Ken
Murchison)</li>
<li>unified deliver.db, using cyrusdb interface, hopefully
improving concurrency and performance (Ken Murchison)</li>
<li>fixed STORE FLAGS () bug (Ken Murchison)</li>
<li>fixed SEARCH SUBJECT vs. SEARCH HEADER SUBJECT bug (Ken
Murchison)</li>
<li>users without an INBOX can have subscriptions (Ken Murchison;
noticing a trend here?)</li>
<li>added cyrusdb_db3_nosync backend, used for duplicatedb and
session cache, to postpone non-critical writes. (Ken
Murchison)</li>
<li>support for STARTTLS and AUTH=ANONYMOUS for timsieved (Ken
Murchison)</li>
<li>do setgid and initgroups in master (as urged by several
people)</li>
<li>added more config info to IMAP ID (in a vain attempt to improve
debugging)</li>
<li>configure now checks for DB3.3</li>
<li>SQUAT (Rob O'Callahan, <tt>roc@cs.cmu.edu</tt>)</li>
<li>change SEARCH HEADER <i>x</i> to SEARCH <i>x</i> utilizing
internal cache where possible (Rob O'Callahan,
<tt>roc@cs.cmu.edu</tt>)</li>
<li>an improved directory hashing option (Gary Mills,
<tt>mills@cc.UManitoba.CA</tt>)</li>
<li>use of EGD for SSL/TLS (Amos Gouaux,
<tt>amos@utdallas.edu</tt>)</li>
<li>separate certs/keys for services (Henning P. Schmiedehausen,
<tt>hps@intermeta.de</tt>)</li>
<li>ability to force ipurge to traverse personal folders (Carsten
Hoeger, <tt>choeger@suse.de</tt>)</li>
<li>fixed zero quota bugs in cyradm (Leena Heino,
<tt>liinu@uta.fi</tt>)</li>
<li>ignore trailing whitespace in imapd.conf</li>
<li>Received: header (with TLS and AUTH info)</li>
<li>added '-i' switch to sendmail command line for SIEVE reject,
redirect and vacation</li>
<li>reconstruct -m works again???</li>
<li>small fixes to notify_unix</li>
<li>added "<tt>duplicatesuppression</tt>" switch to imapd.conf for
enabling/disabling duplicate delivery suppression (Birger Toedtmann,
<tt>birger@takatukaland.de</tt>)</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.15</h2>
<ul>
<li>fixed a longstanding bug in <tt>quota</tt> that would affect
people with unusual top-level hierarchy, fix by John Darrow,
<tt>John.P.Darrow@wheaton.edu</tt>.</li>
<li>some important fixes to db3 interface code, by Walter Wong
<tt>wcw@cmu.edu</tt>, prompted by complaints from Scott Adkins
<tt>adkinss@ohio.edu</tt>.</li>
<li>fixed some memory leaks in imclient and in the Perl IMAP
module, prompted by Toni Andjelkovic <tt>toni@soth.at</tt>.</li>
<li>fixed a longstanding authentication error in the Perl IMAP
module, should remove pesky extra Password: prompt.</li>
<li>fixed some allocation bugs in the managesieve perl module.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.14</h2>
<ul>
<li>fixed memory management bugs in <tt>imapd</tt>, <tt>lmtpd</tt>
that were being hit due to the connection reuse code and causing
subtle and annoying problems.</li>
<li>we now clean up better when deleting a user</li>
<li>fixed an endian bug in <tt>ipurge</tt></li>
<li><tt>pop3d</tt> now can also reuse processes.</li>
<li>fix a bug in <tt>imclient</tt> that would strike when
<tt>cyradm</tt> specifies a mechanism on the command-line. (SASL
mechanism names aren't case sensitive.)</li>
<li>fix some bugs in handling SIGHUP in <tt>master</tt></li>
<li>fix a couple of goofs in <tt>Admin.pm</tt></li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.13</h2>
<ul>
<li>fixed a silly bug with reusing SSL connections</li>
<li><tt>lmtpd</tt> can now service multiple clients in sequence,
hopefully improving performance</li>
<li>changed how Berkeley db databases are opened, hopefully
lessening the chance of deadlock and improving performance</li>
<li>fixed a couple of memory leaks</li>
<li>lessened the chance of a race condition during
<tt>index_check()</tt></li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.12</h2>
<ul>
<li>refactored code so less duplication</li>
<li>added alternate config file for partial virtual domain
support</li>
<li><tt>pop3d</tt> can now disable USER/PASS commands.</li>
<li>STARTTLS now accepts a SSLv23 hello but doesn't allow SSLv23 to
be negotiated.</li>
<li><tt>imtest</tt> no longer buffers to aid use as an automated
layer.</li>
<li><tt>master</tt> now supports maximum number of service
processes via the "maxchild" modifier.</li>
<li>fixed a bug in the Sieve string lexer.</li>
<li>one <tt>imapd</tt> process can now service multiple clients in
sequence, eliminating a large number of forks.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.11</h2>
<ul>
<li>portability fixes involving <tt>setrlimit()</tt></li>
<li>fixed compiler warnings</li>
<li>the STARTTLS command will only accept TLSv1 now, not SSLv2/v3.
The <tt>imaps</tt> port is unaffected by this change.</li>
<li><tt>timsieved</tt> no longer returns garbage strings.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.9</h2>
<ul>
<li>some small memory savings</li>
<li>the "fud" daemon once again works correctly</li>
<li>the IDLE extension now uses signals correctly</li>
<li>problems with libwrap have been resolved</li>
<li><tt>imapd</tt> and <tt>pop3d</tt> now log connections protected
via TLS.</li>
<li>efficiency improvements when searching for a particular
message-id</li>
<li>fixed an envelope-parsing bug affecting SORT and THREAD</li>
<li>made RENAME keep the same mailbox uniqueid, preserving seen
state across renames</li>
<li>STOREing flags to multiple messages in one command is now more
efficient</li>
<li>RENAME now preserves the ACL</li>
<li>LIST is now as efficient as Cyrus v1.6, modulo Berkeley DB
issues.</li>
<li>Sieve zephyr notifications are now correct.</li>
<li>crash in <tt>reconstruct</tt> now fixed.</li>
<li>man pages added for <tt>cyrus.conf</tt>, <tt>master</tt>,
<tt>lmtpd</tt>, <tt>idled</tt>, <tt>ctl_mboxlist</tt>, and
<tt>ctl_deliver</tt>.</li>
<li><tt>master</tt> can now listen on specific interfaces</li>
<li><tt>master</tt> can now reread <tt>/etc/cyrus.conf</tt> on
SIGHUP.</li>
<li><tt>timsieved</tt> now uses symlinks instead of hard
links.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.8</h2>
<ul>
<li>remembered to update this file</li>
<li>bug in <tt>Cyrus::IMAP</tt> perl module affecting cyradm's
setquota fixed</li>
<li>portability fix with <tt>socklen_t</tt></li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.7</h2>
<ul>
<li>preliminary implementation of the IDLE extension (Ken
Murchison, <tt>ken@oceana.com</tt>).</li>
<li>THREAD=REFERENCES now part of the normal build.</li>
<li>tweaks to the installation documentation and suggested Sendmail
configuration</li>
<li>portability fixes and other small bugfixes</li>
<li>added "<tt>-a</tt>" flag to <tt>lmtpd</tt></li>
<li>master process can now export statistics about running
processes via UCD SNMP AgentX</li>
<li>many fixes to Cyrus Murder-related code</li>
<li>fixes to perl code, especially the Sieve interface. added an
IMSP interface to the perl code, but it still needs work.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.6</h2>
<ul>
<li>some number of random static variables eliminated, to save on
memory footprint</li>
<li>recursive RENAME was a little to eager; fixed. RENAME will also
give the client a hint that a sub-RENAME failed. (mostly probably
useful for cyradm, but cyradm doesn't take advantage of it
yet.)</li>
<li>THREAD=X-JWZ has turned into THREAD=REFERENCES (Ken
Murchison)</li>
<li>DELETE wasn't failing cleanly in database code; fixed.</li>
<li>off-by-one bug in seen_db fixed.</li>
<li>starting/committing/aborting transactions now logged more
correctly in cyrsudb_db3</li>
<li>master will now accept port numbers instead of just service
names in cyrus.conf. also logs even more verbosely (see bug
#115.)</li>
<li>libwrap_init() is now inside the loop, since i don't quite
understand the semantics of libwrap calls.</li>
<li>setquota in cyradm now behaves more sanely (and gives correct
usage message).</li>
<li>bugfixes to the managesieve client perl api. (still needs
work.)</li>
<li>small fixes in timsieved.</li>
<li>added a "make dist" target so i won't dread releases as
much.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server since 2.0.5</h2>
<ul>
<li>APPEND now honors the \Seen flag.</li>
<li>mailboxes file can once again be a flat text file.
(compile-time choice)</li>
<li>subscriptions file can be flat text or berkeley db. likewise
for seen state.</li>
<li>unfortunately, the format of the mailboxes file has
changed.</li>
<li>implementation of "JWZ" threading, a first pass on the
forthcoming THREAD=REFERENCES.</li>
<li>bugfixes in libacap.</li>
<li>bugfixes in other Murder related functionality.</li>
<li>removal of dead code.</li>
<li>will now look in CYRUS_PREFIX/etc/imapd.conf if there's no
/etc/imapd.conf.</li>
<li>more paranoid implementation of ID.</li>
<li>more descriptive lmtp errors.</li>
<li>finished implementation of LMTP 8BITMIME.</li>
<li>fixed minor bugs in pop3d.</li>
<li>small test suite for cyrusdb backends added in
<tt>lib/test/</tt>.</li>
<li>added <tt>-DPERL_POLLUTE</tt> to the perl compilation to deal
with Perl 5.6.</li>
<li>small additions to the Sieve library.</li>
<li>As usual, owe lots of thanks to Ken Murchison for his hard work
and awesome implementations.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server SINCE 2.0.4</h2>
<ul>
<li>Now should work with Berkeley DB 3.1, but does <b>not</b>
auto-upgrade 3.0 databases (and, in fact, I haven't written any
upgrade software yet).</li>
<li>SORT and THREAD should now function correctly.</li>
<li>Some configure fixes.</li>
<li>Some fixes for possible race conditions in initializing
services and database structures.</li>
<li>Some non-gcc compile fixes with structure initialization.</li>
<li>Some non gcc compile fixes with structure initialization.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server SINCE 2.0.3</h2>
<ul>
<li>fixed <tt>mbpath</tt> and <tt>ipurge</tt>. Thanks to Alain
Turbide for the bug report.</li>
<li>configure: removed <tt>mmap_private.c</tt>; it was buggy, and
not worth supporting.</li>
<li>configure: improvements in detecting libwrap, typos fixed in
detecting libsasl.</li>
<li>Merged the acapsieve library into libacap.</li>
<li>improvements to the ACAP API.</li>
<li>invariant checks added to the skiplist code.</li>
<li>bugfix in TCL cyradm.</li>
<li>acapmbox.c: bugfixes in handling acap connections.</li>
<li>fix the size given for a unix socket address (changes
throughout the code), patch thanks to Vladimir Kravchenko,
<tt>jimson@null.ru</tt>.</li>
<li>rewrote <tt>deliver</tt> to use the generic LMTP API in
lmtpengine.c. Likewise, implemented the client-side API in
lmtpengine.c. (Still need to implement AUTH.)</li>
<li>added SORT and THREAD support (Ken Murchison,
<tt>ken@oceana.com</tt>.)</li>
<li>In checking an APPEND command, we were rejecting valid system
flags and accepting invalid ones.</li>
<li>minor bug fixes to <tt>proxyd</tt>.</li>
<li>large amount of debugging code added to
<tt>target-acap</tt>.</li>
<li>build fixes to Perl programs.</li>
<li>allow plaintext authentication to <tt>timsieved</tt>.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server in 2.0</h2>
<ul>
<li>The mailboxes file is now a transaction-protected Berkeley
database.</li>
<li>The core delivery process has been moved to <tt>lmtpd</tt>.
<tt>deliver</tt> is now a simple wrapper to create an LMTP
transaction.</li>
<li>master process, responsible for spawning services
(<tt>imapd</tt>, <tt>lmtpd</tt>, etc.) and for routine
housekeeping. Optionally, it can use <tt>libwrap</tt> to allow or
deny connections.</li>
<li>ACAP (Application Configuration Access Protocol) support for
Cyrus Murder: IMAP Aggregator.</li>
<li>Sieve enhancements: regular expressions, notifications,
automatically setting IMAP flags.</li>
<li>SNMP (Simple Network Management Protocol) support for
monitoring usage (e.g. number of users logged in) as well as for
instrumenting protocol usage (e.g. number of times CREATE has been
called).</li>
<li>Perl version of <tt>cyradm</tt> contributed by Brandon Allbery
(<tt>allbery@ece.cmu.edu</tt>). Eventually we expect to transition
to the Perl version away from the TCL version.</li>
<li>Bugfix in modified UTF-7 processing (for mailbox names). Bugfix
in <tt>index_searchcacheheader()</tt>.</li>
<li>Implemented the extension MULTIAPPEND.</li>
<li>RENAME is now hierarchical.</li>
<li>The right that controls whether a mailbox may be deleted is now
"c". (It used to be "d".)</li>
<li>An additional backend for seen state has been created,
<tt>seen_db</tt>. It stores seen state in a per-user database.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.20</h2>
<ul>
<li>Some fixes to the TLS support to gracefully degrade
service.</li>
<li>Sieve now correctly re-sieves messages that are received with
identical message-ids, but different envelopes. It also obeys
plus-addressing on keep actions. (Fixes by Ken Murchison,
<tt>ken@oceana.com</tt>.)</li>
<li>The server wasn't correctly calculating the percentage of quota
used when deciding whether or not to issue a warning.</li>
<li>Implemented single-instance store: deliver, when using LMTP,
will only store one copy of a message per partition, and hard link
it among multiple users. Sites with a large number of partitions
could see a performance decrease.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.19</h2>
<ul>
<li>Added STARTTLS support; requires OpenSSL.</li>
<li>Sieve now uses MDNs to reject messages instead of DSNs,
conforming to the latest Sieve specification. (Ken Murchison)</li>
<li>The duplicate delivery database expiration (deliver -E) was
deleting all entries; fixed.</li>
<li>imtest is now a little smarter about parsing the protocol to
avoid synchronization errors prior to authentication.</li>
<li>timsieved's parser is now written in C; should be no noticeable
changes, but should make compiling it much easier.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.16</h2>
<ul>
<li>Fix to enclosed message parsing (thanks to John Myers).</li>
<li>When trying to skip over non-synchronizing literals during
error recovery, the IMAP server never stopped eating. Fixed.</li>
<li>Added <tt>with-sasldir</tt> as a configure option.</li>
<li>Fixed a bug in cyradm when it got the CAPABILITY list.</li>
<li>Fixed bugs relating to the incomplete SASLfication of
deliver.</li>
<li>Fixed bugs in deliver relating to duplicate delivery
suppression and Sieve vacation functionality.</li>
<li>Fixed a memory leak in deliver.</li>
<li>When looking for SASL options, imapd wasn't defaulting to the
option without the plugin name requesting it. This caused PLAIN
authentications to incorrectly fail.</li>
<li>Changed the expiration time of pts entries to 3 hours; only
affects sites using krb_pts as the authorization method.</li>
<li>Fixed some bugs in imclient; mostly affects cyradm.</li>
<li>Fixed a bug in the Sieve lexer and improved the usefulness of
the Sieve test program.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.13</h2>
<ul>
<li>An annoying memory management bug in imclient was fixed and
it's efficiency was improved somewhat.</li>
<li>Added the sieve client (<tt>installsieve</tt>) and server
(<tt>timsieved</tt>) for getting sieve scripts onto the server and
managing them once they are there.</li>
<li>The default Sieve script layout has changed to
sievedir/u/user/default; this supports multiple Sieve scripts per
user with the ability to switch between them.</li>
<li>Fixed the kerberos-to-local-host bug (patch by Greg Hudson,
<tt>ghudson@mit.edu</tt>).</li>
<li>Started changes to deliver to support LMTP AUTH.</li>
<li>Improved the error messages logged when authentication
fails.</li>
<li>Fixed a bug dealing with argument processing in the arbitron
program.</li>
<li>pop3d now correctly supports SASL AUTH.</li>
<li>imtest will no longer prompt for authentication or
authorization names; instead, it defaults to the current Unix user.
Override on the command line.</li>
<li>Likewise, cyradm will no longer prompt. It now accepts "-m" to
specify what SASL mechanism to use, and the pwcommand option to
authenticate should once again work when used
non-interactively.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.10</h2>
<ul>
<li>Changed the sieve option in the configure script to
<tt>--disable-sieve</tt>.</li>
<li>Updated reconstruct and quota to check for hashed imap spool
directories correctly.</li>
<li>deliver now will not use Sieve if duplicate delivery
suppression is disabled. There was also a bug that caused the
duplicate delivery database to be checked even if dupelim was
disabled.</li>
<li>deliver now uses tm_gmtoff if available to check for the local
timezone.</li>
<li>The default format for reading information from INN has
changed. If you use INN to feed imapd news, you must change your
"<tt>newsfeeds</tt>" file to contain
<pre>
collectnews!:*:Tf,WO:collectnews
</pre>
</li>
<li>The dohash script now takes a "<tt>-i</tt>" option to run
interactively and the "<tt>-f</tt>" option to issue warnings
instead of fatal errors.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.1-BETA</h2>
<ul>
<li>cyradm should now work with all mechanisms (it now handles
empty challenges and responses).</li>
<li>Fixed deliver to deal with arbitrarily long headers</li>
<li>COPY for non-existent sequence numbers returns NO; this
contrasts to UID COPY, which always returns OK.</li>
<li>FETCH for non-existent sequence numbers returns NO; this
contrasts to UID FETCH, which always returns OK.</li>
<li>Fixed a misleading BAD responses to commands that take
sequences.</li>
<li>Added UIDNEXT untagged response to a SELECT (from
<tt>draft-crispin-imapv-07.txt</tt>).</li>
<li>pop3d now correctly passes SASL configuration options to
libsasl.</li>
<li>imtest now correctly flushes the server's output to the
screen.</li>
<li>Added more hashing using a simple but stupid algorithm. Now
whenever there is a mailbox access, quota access, or subscription
access, it goes through a hash function. this is done to help
reduce the number of files/directories in any given directory.</li>
<li>Added the binary <tt>mbpath</tt>. Given a mailbox name, this
binary will print the filesystem path to that mailbox. This way if
you have multiple partitions and hashing turned out, you don't have
to spend as many mental cycles figuring out where the actual
directory is.</li>
<li>deliver now checks <tt>sieveusehomedir</tt> and
<tt>sievedir</tt> in the config file to determine where to look for
sieve scripts.</li>
<li>ptloader now has a workaround for afs 3.5.</li>
<li>clarified an error message in message.c when an unexpected end
of file is encountered.</li>
<li>fixed some random memory leaks in deliver.</li>
<li>fixed a fairly major bug in prot_fill. it was performing
incorrectly when reading only a single character.</li>
<li>fixed a bug in how imtest looked for OK or NO.</li>
<li>fixed a memory leak in imapd.</li>
<li>imapd now allows any user (or member of a group) listed in
"proxyservers" to proxy.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.6.0-BETA</h2>
<ul>
<li>fixed stupid bug in imapd</li>
<li>fixed sasl/config.c interaction</li>
<li>fixed use of stat in imtest</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.24</h2>
<ul>
<li>ANSI C is now required.</li>
<li>imtest's interface has changed, to allow for SASL
authentication. Sorry, but it had to happen. It now also includes a
timing test (-z) which we use to test the SASL layers.</li>
<li>imtest no longer uses a non-synchronizing literal with LOGIN,
so it should work against all IMAP servers.</li>
<li>The prot layer now uses SASL for encryption and authentication.
This changed a large amount of code, and some build
procedures.</li>
<li>As a side effect of SASL, --enable-static-libraries now doesn't
do anything. We are considering compiling cyrus with libtool to
change this.</li>
<li>Error codes returned by programs have changed, and programs
return EX_TEMPFAIL far more than they used to. This is because
Sendmail considers most not-EX_TEMPFAIL errors permanent; now, if
it may not be permanent, EX_TEMPFAIL is returned. (See
lib/exitcodes.h.)</li>
<li>Two bugs fixed: UID FETCH's with no messages in range now
return OK, not BAD. And an obscure bug in LIST case sensitivity is
fixed.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.19</h2>
<ul>
<li>Most of the charset.c code (and mkchartable.c code) has been
replaced thanks to John Myers).</li>
<li>Bug fix in message.c to look up headers in the cache when
they're in the cache correctly; thanks to Chris Newman for the
fix.</li>
<li>Code cleanup here and there (thanks to Bruce Balden).</li>
<li>Annoying (and confusing) lines in syslog every time a message
was delivered if deliver was compiled using dbm saying that deliver
was "unable to fetch entry" have been removed.</li>
<li>Content-Disposition lines were being parsed improperly. If they
had no optional arguments, they were being ignored as if they were
syntactically incorrect. This is fixed, but imapd will continue to
serve wrong information unless cyrus.caches are rebuilt (with
reconstruct) for any message that was added to the mailbox before
this bug was fixed.</li>
<li>The arbitron program now takes a mailbox pattern argument for
the mailbox to run on. The manpage always said it did anyway.</li>
<li>Uninitialized variable fixed in imapd.c with the shutdown file
code.</li>
<li>Minor tweaks to purify build config.</li>
<li>Fix minor memory leak in proc.c where procfname wasn't being
free'd.</li>
<li>Fix brain fart in auth_krb_pts.c where a CLOSE() was done to a
DB handle BEFORE we access the data read from the DB database. This
means we were copying free'd memory into the groups list. Note this
only affects people using DB, AFS and ptloader.</li>
<li>Committed minor syslog log level changes in ptloader and
deliver.</li>
<li>make distclean now does what it's supposed to.</li>
<li>Possibly misnamed experimental --enable-static-libraries switch
that tries to do a good job of building binaries with whatever
static libraries are availible. If you use this, you do so at your
own risk, and if it fails, we will disavow all knowledge of you and
your team. Good luck, Jim.</li>
<li>Add optional third argument to imtest for it to take input from
a file. This is a gross hack.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.14</h2>
<ul>
<li>LIST now honors the reference argument.
<p>This behavior can be turned off by a configuration option
imapd.conf--which may be desirable because of certain clients that
ask for a "mail directory" setting for IMAP which will now cause
problems when it was ignored before. (The default is for the
reference argument to be honored.)</p>
</li>
<li>The <tt>arbitron</tt> program now takes a mailbox pattern
argument for the mailbox to run on. The manpage always said it did
anyway.</li>
<li>Added --disable-server switch to optionally prevent compilation
of server to help sites that just want client libraries (so cyradm
and libcyrus can be compiled on remote systems without installing
stuff into /usr/cyrus/bin, etc.) For now, the server is still
configured in this case, and a Makefile is generated. This could
change in future versions.</li>
<li>Fixed a mmap leak in index.c in index_search_evaluate that
caused problems on complex searches. Thanks to Jeff Schiller for
pointing this out. Fixed a potential leak in mboxlist.c that
happened if a rename went awry. Thanks to Chris Newman for pointing
this out.</li>
<li>Fixed a bug in LIST and LSUB code so that user.* mailboxes will
be printed on every LIST instead of just the first one.</li>
<li>Implemented the <i>POP3 Extension Mechanism</i>, RFC 2449, in
order to advertise the capabilities already supported.</li>
<li>Fixed a bug in mailbox.c that disallowed MUTF-7 representations
of ASCII characters when it shouldn't have. (Thanks to John Myers
for providing a fix and to Per Steinar Iversen for telling me that
I didn't do it before.)</li>
<li>More cleanup the ptloader/auth_krb_pts code. If you use
Kerberos and IMSP, you *MUST* pick up cyrus-imspd-v1.5a6 (or
newer).</li>
<li>A few configure tweaks.</li>
<li>Duplicate delivery changes:
<ul>
<li>Split out duplicate delivery elimination to multiple files.
This should help reduce the lock contention that normally occurs
with this file. To not clutter <i>config_dir</i>, the files will be
located in a subdirectory named <tt>deliverdb</tt>, for example
<tt>/var/imap/deliverdb</tt>. If you don't make this directory,
nothing bad will happen (other than duplicate delivery elimination
will not work).</li>
<li>The time value is now stored as an integer in native byte order
as opposed to converting it to a string before it is stored in the
database.</li>
<li>checkdelivered() now obtains a read lock instead of a write
lock when trying to check for duplicates. Only markdelivered()
grabs a write lock.</li>
</ul>
</li>
<li>Added logic to cause cyradm to abort more cleanly if not given
command line arguments in an interactive session. This gets rid of
the dreaded <tt>application-specific intialization failed</tt>
messages.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.11</h2>
<ul>
<li>The CREATE command now ignores a trailing hierarchy delimiter
instead of ignoring the CREATE command.</li>
<li>UIDPLUS is now always advertised in CAPABILITY and is always
availible. The UIDPLUS extension is a set of optimizations using
UID values instead of sequence numbers and is described in RFC
2359.</li>
<li>Cyrus no longer rejects messages with 8-bit characters in the
headers. Rather than reject the message, characters with the 8th
bit set are changed to 'X'. Internationalization in headers is
supported by the mechanism specified in RFC 2047 (and RFC
1342).</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.10</h2>
<ul>
<li>If ENABLE_EXPERIMENT is set, the server no longer claims to
support OPTIMIZE-1; instead, it claims to support UIDPLUS. The
Getuids command has been removed since it is not in the UIDPLUS
document (draft-myers-imap-optimize-03.txt).</li>
<li>The checks for Tcl in configure are much smarter. The configure
script asks tclsh where its configuration lives, then consults the
shell scripts that have that information. This should work with 7.5
or better, which is what the server requires anyway. (All the
previous checks to look for Tcl libraries are gone; now, configure
runs tclsh and asks it where the Tcl libraries are, then runs the
shell scripts that are in that directory. Since the tclConfig.sh
script may not be in that directory, it looks in .. as well.)</li>
<li>The checks for com_err in configure are a little smarter and
look to see if all the pieces are there before trying to use
them.</li>
<li>Added support for the NAMESPACE extension (if
--enable-experiment is supplied).</li>
<li>Added a "reject8bit" switch to imapd.conf. If set to "true",
messages containing 8-bit-set characters in the headers are
rejected (the previous behavior); if set to "false" or left to the
default value, messages containing 8-bit-set characters have these
characters changed to a constant character ('X').</li>
<li>Added the "fud" program. This is an interm hack designed to
allow allow finger information to be retrieved for cyrus users.
This is experimental and it is not recommend that services be built
arround this feature, since it is likely to be removed in a future
release of the IMAP server.</li>
<li>Bug fix: User defined flags now work properly.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5.2</h2>
<ul>
<li>Fixed a bug with word alignment on Solaris using Kerberos
compiled with Sun's CC. (Several patches were submitted; thanks to
everyone who did so.)</li>
<li>Patches from John Myers, including more glob fixes.</li>
<li>Use the default hash function from DB. Note that this means
that the existing <tt>delivered.db</tt> and <tt>ptscache.db</tt> is
<b>NOT</b> compatible with this release. These files should be
removed.</li>
<li>Provide two debugging programs that dump the databases:
<tt>ptdump</tt> and <tt>dump_deliverdb</tt>.</li>
<li>Multiple changes to ptloader. added a bunch of flags; let it
reauthenticate on its own; added support perl wrapper; added bunch
of debugging information/output; bunch of other cleanups</li>
<li>The mailboxes file is now closed if it isn't likely to be
referenced, hopefully preventing old mailboxes files from hanging
around in memory as frequently.</li>
<li>Added a patch from Eric Hagberg to work around a possible
deadlock condition in mboxlist.c where rename isn't atomic.</li>
<li>Patch from John Myers to get rid of cyrus.seen corruption in
bsearch_mem.</li>
<li>Patch from John Myers and to allow ISO-8859-1 characters in
mailbox names.</li>
<li>Makedepend still runs, and still generates warnings, but these
are squirrled away in makedepend.log.</li>
<li>On mailbox delete, the server will no longer try and unlink
".." and "." as we got a report that it seriously breaks one file
system (even as non-root).</li>
<li>Added some support for Netscape's very misleading "Administrate
My Mail" menu option in Communicator. Allows for a URL to be set in
imapd.conf for the page to refer users to; needs to be turned on
with --enable-netscapehack at compile time to enable it.</li>
<li>Bug swap: imtest quotes password with a non-synchronizing
literal in order to allow weird characters like ) in passwords. But
it doesn't look to see if the server supports non-synchronizing
literals.</li>
<li>If the file "<tt>msg/motd</tt>" exists, the first line is now
sent to clients upon login.</li>
<li>Bug fix: to handle BODY[] properly when fetching news articles
(truncation no longer occurs). (thanks to John Prevost)</li>
<li>The makedepend supplied should now run on Solaris Intel.
(thanks to Chris Newman)</li>
<li>Added some hacks to pwcheck.c for Linux and Digital Unix where
the default protections on the socket don't allow the cyrus user to
read it. (thanks to Lyndon Nerenberg)</li>
<li>Bug fix: Flags beginning with \ are system flags and users can
only create the defined flags. The code to do this before was
confused.</li>
<li>The configure scripts and makefiles have some random
fixes.</li>
<li>Added a contrib directory for reasons of laziness in collecting
patches, not all of which should be in the distribution.</li>
<li>ptloader can now renew its AFS authentication by reading from a
srvtab file.</li>
<li>The configure script now looks for a libcom_err and can use an
installed one if one exists.</li>
<li>Other small bug fixes.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.5</h2>
<ul>
<li>Bug fix: RENAME corrupted mailboxes if they had been EXPUNGEd.
(may have only happened with INBOX, which Pine tickles once a
month.)</li>
<li>Bug fix: auth_newstate now initializes its structures.</li>
<li>Bug fix: pop3d.c, a printf was changed to prot_printf.</li>
<li>Cyrus now sends X-NON-HIERARCHICAL-RENAME to alert clients that
it is not handling RENAME in an IMAP4rev1 compliant manner. This
will be fixed in a subsequent release.</li>
<li>Bug fix: imclient_autenticate now does resolution on the
hostname before authenticating to it. This caused problems when
authenticating to an address that was a CNAME.</li>
<li>Bug fix: LIST %.% (and other multiple hierarchy delimiter
matches) works properly. Several other glob.c fixes are included as
well.</li>
<li>Bug fix: a fetch of exclusively BODY[HEADER.FIELDS...] should
now work properly.</li>
<li>Bug fix: reconstruct now considers a nonexistant INN news
directory to be empty; this makes reconstruct fix the cyrus.* files
in the imap news partition.</li>
<li>Added a manpage for imclient.</li>
<li>Fixed a few other minor bugs.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.4</h2>
<ul>
<li>Implemented the "<tt>IMAP4rev1</tt>" protocol commands. (The
hierarchical behavior of RENAME, which was added late to the
IMAP4rev1 specification, is not implemented.) Changes the minor
version number of the cyrus mailbox database format to 1.
<b>IMPORTANT:</b> it is necessary to run the command
"<tt>reconstruct -r</tt>" as the cyrus user after upgrading the
Cyrus IMAP software from version 1.4 or earlier.</li>
<li>If the file "<tt>msg/shutdown</tt>" exits in the configuration
directory, the IMAP server will issue the first line in the file in
an untagged BYE message and shut down.</li>
<li>Permit SPACE in mailbox names.</li>
<li>Permit the "modified UTF-7" internationalized mailbox name
convention.</li>
<li>"User opened mailbox" messages are now logged at the DEBUG
level instead of the INFO level.</li>
<li>Added <tt>-q</tt> (ignore quota) switch to
<tt>deliver</tt>.</li>
<li>New "<tt>krbck</tt>" program for diagnosing common kerberos
problems.</li>
<li>auth_unix no longer requires users to be in the passwd
file.</li>
<li>AUTHENTICATE command now reports the protection mechanism in
use in the text of the tagged OK response</li>
<li>Make MAILBOX_BADFORMAT and MAILBOX_NOTSUPPORTED temporary
errors.</li>
<li>Use the header cache for SEARCH HEADER</li>
<li>Use "unspecified-domain" instead of server's hostname to fill
out RFC 822 addresses without the "@domain" part.</li>
<li>Make "reconstruct -r" with no args reconstruct every
mailbox.</li>
<li>The configure script now defaults to using unix_pwcheck instead
of unix if the file /etc/shadow exists.</li>
<li>The location of the pwcheck socket directory now defaults to
"<tt>/var/ptclient/</tt>". It is controlled by the
"<tt>--with-statedir=DIR</tt>" option, which defaults to
"<tt>/var</tt>".</li>
<li>Bug fix: by using an certain address form, one could deliver to
a user's mailbox bypassing the ACL's.</li>
<li>Bug fix: un-fold header lines when parsing for the
ENVELOPE.</li>
<li>Delete quota roots when deleting the last mailbox that uses
them. Doesn't catch all cases, but should get over 99% of
them.</li>
<li>Implement plaintextloginpause configuration option, imposes
artificial delay on plaintext password logins.</li>
<li>Implement popminpoll configuration option, limits frequency of
POP3 logins.</li>
<li>Implement AFS PT server group support.</li>
<li>Remove persistence of POP3 LAST value and remove Status:
hack</li>
<li>Support the new ACL command set in the IMAP server.</li>
<li>Bug fix: Have to initialize reply to 0 in pop3d. Was causing
POP3 server to occasionally drop the connection during
authentication.</li>
<li>Bug fix: The COPY command wasn't issuing a [TRYCREATE] when
appropriate for sub-mailboxes of INBOX.</li>
<li>Bug fix: Renaming a mailbox wasn't correctly changing its
UIDVALIDITY.</li>
<li>Bug fix: Renaming a mailbox to itself, in order to move it to a
different partition, was not working correctly.</li>
<li>Update the AUTH support in pop3d to conform to the latest draft
specification.</li>
<li>Update cyradm to use Tcl 7.5 instead of Tcl 7.4</li>
<li>Re-implement large sections of the netnews support. It no
longer requires modifications to INN, as it now expunges the index
entries for expired/canceled articles upon select of the
newsgroup.</li>
<li>Implement newsspool configuration option, for separating the
directories for the news spool and the various cyrus.* IMAP server
index files.</li>
<li>Bug fix: permit empty flag list in APPEND command</li>
<li>Bug fix: deal with truncated Date: header values.</li>
<li>Bug fix: memory mapping code, deal better with 0-length maps,
since mmap() appears to crap out on that boundary condition.</li>
<li>Portability fix: if no strerror, have to define
NEED_SYS_ERRLIST.</li>
<li>Bug fix: used append instead of lappend in cyradmin, preventing
use of any port other than IMAP.</li>
<li>When the client is streaming its commands, the IMAP server
attempts to stream its tagged responses.</li>
<li>Modify zephyr support to compile without Kerberos support.</li>
<li>Add a bunch of prototype declararations to the code.</li>
<li>In deliver, change the MULT support to instead use the LMTP
syntax.</li>
<li>imclient: support tagged intermediate replies and a default
callback.</li>
<li>Implement some experimental protocol extensions for optimizing
disconnected use resynchronization. Most extensions are disabled by
default. Client authors should contact info-cyrus@andrew.cmu.edu if
they wish to experiment with these.</li>
<li>In Makefiles, change $(AR) to ar -- HPUX make is
defective.</li>
<li>In deliver, use HAVE_LIBDB to select use of db over dbm</li>
<li>Add map_stupidshared mapping module for older versions of
Digital Unix. It's not quite as bad as HPUX, but...</li>
<li>Bug fix: in imclient.c, don't free NULL pointers and don't call
htons() on the output of getservbyname(). Have to abort sending the
command if you get a tagged response when sending a literal.</li>
<li>The auth_xxx routines now create/take a state argument instead
of maintaining internal static state.</li>
<li>Solaris mktime() is buggy in some releases. Create and use
mkgmtime() for parsing date strings.</li>
<li>Message parsing routines now use memory mapping, though they
still copy data around in line-sized buffers.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.3</h2>
<ul>
<li>Implemented the "<tt>reconstruct -m</tt>" command, for
reconstructing the <tt>mailboxes</tt> file. <b>IMPORTANT:</b> it is
necessary to run the command "<tt>reconstruct -m</tt>" as the cyrus
user after upgrading the Cyrus IMAP software from version 1.3 or
earlier. We recommend you make a backup copy of the
<tt>mailboxes</tt> file in the configuration directory before
performing the conversion.</li>
<li>Mailbox names are now case sensitive, not case insensitive.
"<tt>INBOX</tt>" is the exception, and is treated as being
case-insensitive.</li>
<li>Personal mailboxes now appear to their owners as being under
the "<tt>INBOX.</tt>" hierarchy. For example, the mailbox
"<tt>user.bovik.work</tt>" appears to the user "<tt>bovik</tt>" as
"<tt>INBOX.work</tt>". The user may still refer to the mailbox with
the name "<tt>user.bovik.work</tt>".</li>
<li>Previously, the code used "<tt>anybody</tt>" as the name of the
group that all users are in, but the documentation used the name
"<tt>anyone</tt>". Changed the code to match the documentation. The
name "<tt>anybody</tt>" will be canonicalized to the name
"<tt>anyone</tt>".</li>
<li>The install document now gives different recommended locations
for the server databases. The recommended location of the
configuration directory changed from "<tt>/usr/cyrus</tt>" to
"<tt>/var/imap</tt>" and the recommended location of the default
partition directory changed from "<tt>/usr/spool/cyrus</tt>" to
"<tt>/var/spool/imap</tt>". It is <b>NOT</b> necessary to change
the locations of these directories when upgrading from version 1.3
or earlier of the Cyrus IMAP server software. If you do wish to
change the locations of these directories to match the new
recommendations, simply rename the directories and change the
appropriate values in your <tt>/etc/imapd.conf</tt> file.</li>
<li>Created a "<tt>make install</tt>" rule. See the <a
href="install.html">installation</a> document for all the new
corresponding <tt>configure</tt> options. Note the recommended
location of the "<tt>imapd</tt>", "<tt>pop3d</tt>", and
"<tt>deliver</tt>" programs has changed, this change needs to be
reflected in the "<tt>inetd.conf</tt>" and "<tt>sendmail.cf</tt>"
files.</li>
<li>New "<tt>login_unix_pwcheck</tt>" module and "<tt>pwcheck</tt>"
daemon, for improved shadow password support. See the
"<tt>pwcheck/README.pwcheck</tt>" file in the distribution for
details.</li>
<li>Renamed the "<tt>login_unix_shadow</tt>" module to
"<tt>login_unix_getspnam</tt>".</li>
<li>Added a mail notification mechanism, using Zephyr.</li>
<li>Added a feature to automatically create user IMAP accounts.
Controlled by the "<tt>autocreatequota</tt>" config option.</li>
<li>Added the "<tt>logtimestamps</tt>" config option, for putting
timestamp information into protocol telemetry logs.</li>
<li>Beefed up the Kerberos checks in Configure to ensure the DES
library routines exist.</li>
<li>On some systems, the "<tt>echo</tt>" command with no arguments
emits a newline. Changed the installation document to instead use
the "<tt>true</tt>" command to create the "<tt>mailboxes</tt>"
file.</li>
<li>Store a redundant copy of a mailbox's ACL in the
<tt>cyrus.header</tt> file, so "<tt>reconstruct -m</tt>" may later
use it as a backup.</li>
<li>Had to remove the declaration of <tt>tcl_RcFileName</tt> for
the latest version of Tcl.</li>
<li>Make much more extensive use of memory mapping. Replace the
binary search module with one that searches a memory mapped
area.</li>
<li>Replaced the yacc-based RFC822 address parser with a hand-coded
one.</li>
<li>Replaced the et (error table) libary with a version that
doesn't require lex or yacc. Remove the lex/yacc checking from
Configure.</li>
<li>Safety feature: most programs now refuse to run as root.</li>
<li>Bug fix: Issue [TRYCREATE] tag on COPY command when
appropriate.</li>
<li>Bug fix: The quoted-printable decoder wasn't ignoring trailing
whitespace, as required by MIME.</li>
<li>Bug fix: Don't spew cascade errors if the server gets an EOF
during/after reading an APPEND literal.</li>
<li>Bug fix: gmtmoff_gmtime.c was returning results with the wrong
sign.</li>
<li>Bug fix: imclient_send was appending spaces to %d and %u and
the response parser was not handling responses that did not contain
a space after the keyword.</li>
<li>Bug fix: rmnews wasn't removing some (un-indexed) article files
correctly.</li>
<li>Completely disabled the dropoff code for now. It will be
completely replaced when IMSP integration is implemented</li>
<li>Added workaround for the Linux mkdir() problem.</li>
<li>In Configure, use a more direct test for a working
shared-memory mmap</li>
<li>In collectnews, avoid O(n**2) behavior when processing articles
that have already expired.</li>
<li>Bug fix: append_addseen() would screw up if no messages were
previously seen.</li>
<li>Added the CMU-specific amssync and cmulocal directories.</li>
<li>Use memmove instead of bcopy.</li>
<li>Implemented the first pass of SMTP/MULT support in
deliver.</li>
<li>Added cacheid parameter to auth_setid(), for AFS PT server
support.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.2</h2>
<ul>
<li>Fixed bug in character set code that broke text searches. Sites
which care about searching headers need to reconstruct their
existing mailboxes.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.1-Beta</h2>
<ul>
<li>Add support for <tt>UIDVALIDITY</tt> special information
token.</li>
<li>Add <tt>syncnews</tt> and <tt>arbitron</tt> programs.</li>
<li>Redo duplicate delivery elimination in <tt>deliver</tt>.</li>
<li>Bug fixed: Must re-read files after acquiring a lock. Cannot
trust the mtime of a file to increment when writing the file--file
could be written to multiple times in the same second.</li>
<li>Bug fixed: <tt>EXAMINE</tt> command should not affect
<tt>\Recent</tt> status.</li>
<li>Update the user's <tt>\Recent</tt> high-water-mark when we
report new messages.</li>
<li>Portability changes</li>
<li>Upgrade to autoconf 2.1</li>
<li>Allow privacy to be turned off at compile-time with
<tt>--disable-privacy</tt> configure switch.</li>
<li>Fix typo in <tt>cyradm</tt> preventing "<tt>all</tt>" from
being recognized.</li>
<li>Include <tt>map_private.c</tt> memory mapping module for
systems like HPUX which have half-working <tt>mmap()</tt>
implementations.</li>
<li>Switch to using UTF-8 for internal search format. Sites which
care about internationalized searching of headers need to
reconstruct all their existing mailboxes.</li>
<li>Fix some errors in the iso-8859-* tables.</li>
<li>Add and correct a bunch of case-independence mappings in the
character tables.</li>
<li>First pass at implementing the <tt>STATUS</tt> extension;
disabled for release.</li>
<li>First pass at implementing IMAP/IMSP server integration. Not
ready for general use.</li>
<li>Add <tt>new_cred</tt> and <tt>free_cred</tt> mechanisms to
authentication modules.</li>
<li>Don't complain when doing "<tt>reconstruct -r foo</tt>" and
<tt>foo</tt> isn't a mailbox.</li>
<li>Add <tt>IMAP_QUOTAROOT_NONEXISTENT</tt> error code.</li>
<li>Bug fix: Avoid divide by zero when quota is zero</li>
<li>Bug fix: In an error case of the ACL handling code, we have to
restore tab before breaking out of loop.</li>
<li>Fix file descriptor leak in quota system.</li>
<li>Change a bunch of int variables to unsigned.</li>
<li>Better error reporting on reads that end up short.</li>
</ul>
<h2>Changes to the Cyrus IMAP Server Since Version 1.0-Beta</h2>
<ul>
<li>Improved <a href="install.html">installation</a> document.</li>
<li>New "<a href="cyradm.1.html"><tt>cyradm</tt></a>"
administrative client.</li>
<li>Changed the syslog facility from "<a
href="install.html#syslog"><tt>local4</tt></a>" to
"<tt>local6</tt>".</li>
<li>Removed the <tt>renounce setuid</tt> check in "<a
href="install.html#deliver"><tt>deliver</tt>"</a>. The
"<tt>deliver</tt>" program must now be <b>non</b>-executable by
<tt>other</tt>.</li>
<li>Fixed a typo in the parsing of <tt>SEARCH DELETED</tt>. (This
bug constantly got tripped by newer C-clients.)</li>
<li>Redesigned the implementation of <tt>SEARCH CHARSET</tt>.<br />
Sites that wish to search for non-ASCII characters in the headers
of existing mailboxes must run <tt>reconstruct</tt> on all their
mailboxes after upgrading to this version.</li>
<li>Added AUTH and KPOP support to the POP3 server.</li>
<li>Added search support for the ISO-2022-JP character set.</li>
<li>Replaced the search engine with a partial Boyer-Moore
algorithm.</li>
<li>Special-case optimized searching US-ASCII text.</li>
<li>Fixed a bug which caused the message parser to spin-loop on a
particular degenerate invalid-MIME case.</li>
<li>Fixed a performance bug in the message parser.</li>
<li>Tracked last-minute changes to the IMAP4 protocol.</li>
<li>Fixed a bug in <tt>UNSUBSCRIBE</tt> which caused too many
subscriptions to be removed.</li>
<li>Added a bunch more "<a
href="install.html#configure"><tt>configure</tt></a>" options.</li>
<li>Ported to HPUX.</li>
<li>Fixed a bug in the <tt>LIST/LSUB \Noselect</tt> code.</li>
<li>Fixed bug in the globbing code which caused the "<tt>*%</tt>"
pattern to work incorrectly.</li>
<li>Client-side Kerberos support is now conditionalized on
<tt>HAVE_ACTE_KRB</tt>, which is set by configure.</li>
<li>Fixed some invalid buffer-alignment assumptions in the Kerberos
code.</li>
<li>Made the lexers compatible with flex. Configure now looks for
and prefers to use <tt>flex</tt> and
<tt>bison</tt>/<tt>byacc</tt>.</li>
<li>Made the IMAP server check for the existence of the mailboxes
file upon startup, in order to give a more informative error
message for this common configuration error.</li>
<li>Fixed other minor bugs.</li>
</ul>
<hr />
-last modified: $Date: 2002/01/15 21:18:28 $ <br />
+last modified: $Date: 2002/01/18 22:58:45 $ <br />
<a href="index.html">Return</a> to the Cyrus IMAP Server Home Page
</body>
</html>
diff --git a/imap/ctl_cyrusdb.c b/imap/ctl_cyrusdb.c
index 40027b518..95cfb433a 100644
--- a/imap/ctl_cyrusdb.c
+++ b/imap/ctl_cyrusdb.c
@@ -1,167 +1,167 @@
/* ctl_cyrusdb.c -- Program to perform operations common to all cyrus DBs
*
* Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
- * $Id: ctl_cyrusdb.c,v 1.4 2001/11/13 17:33:17 leg Exp $
+ * $Id: ctl_cyrusdb.c,v 1.5 2002/01/18 22:58:47 rjs3 Exp $
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <syslog.h>
#include <com_err.h>
#include <errno.h>
#include <time.h>
#include "util.h"
#include "imapconf.h"
#include "exitcodes.h"
#include "cyrusdb.h"
void fatal(const char *message, int code)
{
fprintf(stderr, "fatal error: %s\n", message);
exit(code);
}
void usage(void)
{
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -c\n");
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -r\n");
exit(-1);
}
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
int opt, r, r2;
char *alt_config = NULL;
int flag = 0;
enum { RECOVER, CHECKPOINT, NONE } op = NONE;
char dirname[1024];
char *msg = "";
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
r = r2 = 0;
while ((opt = getopt(argc, argv, "C:rc")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'r':
flag |= CYRUSDB_RECOVER;
msg = "recovering cyrus databases";
if (op == NONE) op = RECOVER;
else usage();
break;
case 'c':
msg = "checkpointing cyrus databases";
if (op == NONE) op = CHECKPOINT;
else usage();
break;
default:
usage();
break;
}
}
if (op == NONE) {
usage();
exit(1);
}
config_init(alt_config, "ctl_cyrusdb");
/* create the name of the db directory */
strcpy(dirname, config_dir);
strcat(dirname, FNAME_DBDIR);
syslog(LOG_NOTICE, "%s", msg);
r = (&cyrusdb_db3)->init(dirname, flag);
if (r) {
syslog(LOG_ERR, "DBERROR: init %s: %s", dirname,
cyrusdb_strerror(r));
fprintf(stderr,
"ctl_cyrusdb: unable to init environment\n");
op = NONE;
}
+ r2 = 0;
switch (op) {
case RECOVER:
- r2 = 0;
break;
case CHECKPOINT:
r2 = (&cyrusdb_db3)->sync();
if (r2) {
syslog(LOG_ERR, "DBERROR: sync %s: %s", dirname,
cyrusdb_strerror(r));
fprintf(stderr,
"ctl_cyrusdb: unable to sync environment\n");
}
break;
default:
break;
}
if (!r) (&cyrusdb_db3)->done();
syslog(LOG_NOTICE, "done %s", msg);
exit(r || r2);
}
diff --git a/imap/cyrdump.c b/imap/cyrdump.c
index 41b88e14b..9837a661e 100644
--- a/imap/cyrdump.c
+++ b/imap/cyrdump.c
@@ -1,344 +1,349 @@
-/* $Id: cyrdump.c,v 1.7 2001/11/19 21:32:44 leg Exp $
+/* $Id: cyrdump.c,v 1.8 2002/01/18 22:58:47 rjs3 Exp $
* Copyright (c) 2001 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <com_err.h>
#include <string.h>
#include <time.h>
/* cyrus includes */
#include "imapconf.h"
#include "sysexits.h"
#include "imap_err.h"
#include "mailbox.h"
#include "xmalloc.h"
#include "mboxlist.h"
#include "imapd.h"
#include "exitcodes.h"
#include "imapurl.h"
int verbose = 0;
static int dump_me(char *name, int matchlen, int maycreate, void *rock);
static void print_seq(const char *tag, const char *attrib,
unsigned *seq, int n);
int usage(const char *name);
+/* available from elsewhere */
+int index_getuidsequence(struct mailbox *mailbox,
+ struct searchargs *searchargs,
+ unsigned **uid_list);
+
/* current namespace */
static struct namespace dump_namespace;
int imapd_exists;
struct protstream *imapd_out = NULL;
struct auth_state *imapd_authstate = NULL;
char *imapd_userid = NULL;
struct incremental_record {
int incruid;
};
int main(int argc, char *argv[])
{
int option;
char buf[MAX_MAILBOX_PATH];
int i, r;
char *alt_config = NULL;
struct incremental_record irec;
if (geteuid() == 0) {
usage(argv[0]);
}
while ((option = getopt(argc, argv, "v")) != EOF) {
switch (option) {
case 'v':
verbose++;
break;
case 'C': /* alt config file */
alt_config = optarg;
break;
default:
usage(argv[0]);
break;
}
}
if (optind == argc) {
usage(argv[0]);
}
config_init(alt_config, "dump");
mboxlist_init(0);
mboxlist_open(NULL);
/* Set namespace -- force standard (internal) */
if ((r = mboxname_init_namespace(&dump_namespace, 1)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
irec.incruid = 0;
for (i = optind; i < argc; i++) {
strlcpy(buf, argv[optind], MAX_MAILBOX_NAME);
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal(&dump_namespace, buf);
(*dump_namespace.mboxlist_findall)(&dump_namespace, buf, 1, 0, 0,
dump_me, &irec);
}
mboxlist_close();
mboxlist_done();
return 0;
}
int usage(const char *name)
{
fprintf(stderr, "usage: %s [-v] [mboxpattern ...]\n", name);
exit(EC_USAGE);
}
void fatal(const char *s, int code)
{
fprintf(stderr, "fatal error: %s\n", s);
exit(code);
}
/* 'boundary' must be at least 100 long */
static void generate_boundary(char *boundary)
{
snprintf(boundary, 100, "dump-%ld-%ld-%ld",
(long) getpid(), (long) time(NULL), (long) rand());
}
static int dump_me(char *name, int matchlen, int maycreate, void *rock)
{
int r;
struct mailbox m;
char boundary[100];
char imapurl[MAX_MAILBOX_PATH];
struct incremental_record *irec = (struct incremental_record *) rock;
struct searchargs searchargs;
unsigned *uids;
unsigned *uidseq;
int i, n, numuids;
memset(&m, 0, sizeof(struct mailbox));
r = mailbox_open_header(name, 0, &m);
if (r) {
if (verbose) {
printf("error opening %s: %s\n", name, error_message(r));
}
return 0;
}
r = mailbox_open_index(&m);
if (!r) r = mailbox_lock_pop(&m);
if (r) {
if (verbose) {
printf("error locking index %s: %s\n", name, error_message(r));
}
mailbox_close(&m);
return 0;
}
mailbox_read_index_header(&m);
index_operatemailbox(&m);
generate_boundary(boundary);
printf("Content-Type: multipart/related; boundary=\"%s\"\n\n", boundary);
printf("--%s\n", boundary);
printf("Content-Type: text/xml\n");
printf("IMAP-Dump-Version: 0\n");
printf("\n");
printf("<imapdump uniqueid=\"%s\">\n", m.uniqueid);
imapurl_toURL(imapurl, config_servername, m.name);
printf(" <mailbox-url>%s</mailbox-url>\n", imapurl);
printf(" <incremental-uid>%d</incremental-uid>\n", irec->incruid);
printf(" <nextuid>%ld</nextuid>\n", m.last_uid + 1);
printf("\n");
memset(&searchargs, 0, sizeof(struct searchargs));
numuids = index_getuidsequence(&m, &searchargs, &uids);
print_seq("uidlist", NULL, uids, numuids);
printf("\n");
printf(" <flags>\n");
searchargs.system_flags_set = FLAG_ANSWERED;
n = index_getuidsequence(&m, &searchargs, &uidseq);
print_seq("flag", "name=\"\\Answered\" user=\"*\"", uidseq, n);
if (uidseq) free(uidseq);
searchargs.system_flags_set = FLAG_DELETED;
n = index_getuidsequence(&m, &searchargs, &uidseq);
print_seq("flag", "name=\"\\Deleted\" user=\"*\"", uidseq, n);
if (uidseq) free(uidseq);
searchargs.system_flags_set = FLAG_DRAFT;
n = index_getuidsequence(&m, &searchargs, &uidseq);
print_seq("flag", "name=\"\\Draft\" user=\"*\"", uidseq, n);
if (uidseq) free(uidseq);
searchargs.system_flags_set = FLAG_FLAGGED;
n = index_getuidsequence(&m, &searchargs, &uidseq);
print_seq("flag", "name=\"\\Flagged\" user=\"*\"", uidseq, n);
if (uidseq) free(uidseq);
printf(" </flags>\n");
printf("</imapdump>\n");
for (i = 0; i < numuids; i++) {
const char *base;
unsigned long len;
if (uids[i] < irec->incruid) {
/* already dumped this message */
/* xxx could do binary search to get to the first
undumped uid */
continue;
}
printf("\n--%s\n", boundary);
printf("Content-Type: message/rfc822\n");
printf("Content-ID: %d\n", uids[i]);
printf("\n");
r = mailbox_map_message(&m, 0, uids[i], &base, &len);
if (r) {
if (verbose) {
printf("error mapping message %d: %s\n", uids[i],
error_message(r));
}
break;
}
fwrite(base, 1, len, stdout);
mailbox_unmap_message(&m, uids[i], &base, &len);
}
printf("\n--%s--\n", boundary);
index_closemailbox(&m);
mailbox_close(&m);
return 0;
}
static void print_seq(const char *tag, const char *attrib,
unsigned *seq, int n)
{
int i;
printf(" <%s%s%s>", tag, attrib ? " " : "", attrib ? attrib : "");
for (i = 0; i < n; i++) {
printf("%u ", seq[i]);
}
printf("</%s>\n", tag);
}
#if 0
char *p, *str;
int str_sz;
int run_start = 0;
int first_time = 1;
p = str = (char *) xmalloc(sizeof(char) * 1024);
str_sz = 1024;
run_start = msgno_list[0];
for (i = 1; i < n; i++) {
if (msgno_list[i] == msgno_list[i-1] + 1) {
/* on a run */
continue;
}
if (first_time) {
first_time = 0;
} else {
*p++ = ',';
}
if (run_start != msgno_list[i-1]) {
/* non-trivial run */
p += sprintf(p, "%d:%d", run_start, msgno_list[i-1]);
} else {
/* singleton */
p += sprintf(p, "%d", msgno_list[i-1]);
}
if (p > (str + str_sz - 20)) {
/* running out of room */
int x;
x = p - str;
str = (char *) xrealloc(str, str_sz *= 2);
p = str + x;
}
run_start = msgno_list[i];
}
/* now handle the last entry */
if (!first_time) {
*p++ = ',';
}
if (run_start != msgno_list[i-1]) {
sprintf(p, "%d:%d", run_start, msgno_list[i-1]);
} else {
sprintf(p, "%d", msgno_list[i-1]);
}
return str;
#endif
void printastring(const char *s)
{
fatal("not implemented", EC_SOFTWARE);
}
diff --git a/imap/duplicate.c b/imap/duplicate.c
index c4a06d21b..f70c6d109 100644
--- a/imap/duplicate.c
+++ b/imap/duplicate.c
@@ -1,342 +1,342 @@
/*
* Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include <errno.h>
#include <db.h>
#include "xmalloc.h"
#include "imap_err.h"
#include "imapconf.h"
#include "exitcodes.h"
#include "util.h"
#include "cyrusdb.h"
#include "duplicate.h"
/* we have binary data and \0 -> MUST use DB3 */
#define DB (&cyrusdb_db3_nosync)
#define FNAME_DELIVERDB "/deliver.db"
static struct db *dupdb = NULL;
static int duplicate_dbopen = 0;
int duplicate_init(char *fname, int myflags)
{
char buf[1024];
int r = 0;
int flags = 0;
/* create the name of the db file */
strcpy(buf, config_dir);
strcat(buf, FNAME_DBDIR);
if (myflags & DUPLICATE_RECOVER) flags |= CYRUSDB_RECOVER;
r = DB->init(buf, flags);
if (r != 0)
syslog(LOG_ERR, "DBERROR: init %s: %s", buf,
cyrusdb_strerror(r));
else {
char *tofree = NULL;
/* create db file name */
if (!fname) {
fname = xmalloc(strlen(config_dir)+sizeof(FNAME_DELIVERDB));
tofree = fname;
strcpy(fname, config_dir);
strcat(fname, FNAME_DELIVERDB);
}
r = DB->open(fname, &dupdb);
if (r != 0)
syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
cyrusdb_strerror(r));
else
duplicate_dbopen = 1;
if (tofree) free(tofree);
}
return r;
}
time_t duplicate_check(char *id, int idlen, char *to, int tolen)
{
char buf[1024];
int r;
const char *data = NULL;
int len = 0;
time_t mark = 0;
if (!duplicate_dbopen) return 0;
if (idlen + tolen > sizeof(buf) - 30) return 0;
memcpy(buf, id, idlen);
buf[idlen] = '\0';
memcpy(buf + idlen + 1, to, tolen);
buf[idlen + tolen + 1] = '\0';
do {
r = DB->fetch(dupdb, buf,
idlen + tolen + 2, /* +2 b/c 1 for the center null;
+1 for the terminating null */
&data, &len, NULL);
} while (r == CYRUSDB_AGAIN);
if (data) {
assert(len == sizeof(time_t));
/* found the record */
memcpy(&mark, data, sizeof(time_t));
} else if (r != CYRUSDB_OK) {
- syslog(LOG_ERR, "duplicate_check: error looking up %s/%d: %s",
+ syslog(LOG_ERR, "duplicate_check: error looking up %s/%s: %s",
id, to,
cyrusdb_strerror(r));
mark = 0;
}
syslog(LOG_DEBUG, "duplicate_check: %-40s %-20s %d",
buf, buf+idlen+1, mark);
return mark;
}
void duplicate_mark(char *id, int idlen, char *to, int tolen, time_t mark)
{
char buf[1024];
int r;
if (!duplicate_dbopen) return;
if (idlen + tolen > sizeof(buf) - 30) return;
memcpy(buf, id, idlen);
buf[idlen] = '\0';
memcpy(buf + idlen + 1, to, tolen);
buf[idlen + tolen + 1] = '\0';
do {
r = DB->store(dupdb, buf,
idlen + tolen + 2, /* +2 b/c 1 for the center null;
+1 for the terminating null */
(char *) &mark, sizeof(mark), NULL);
} while (r == CYRUSDB_AGAIN);
syslog(LOG_DEBUG, "duplicate_mark: %-40s %-20s %d",
buf, buf+idlen+1, mark);
return;
}
struct prunerock {
struct db *db;
time_t expmark;
int count;
int deletions;
};
static int prune_p(void *rock, const char *id, int idlen,
const char *data, int datalen)
{
struct prunerock *prock = (struct prunerock *) rock;
time_t mark;
prock->count++;
/* grab the mark */
memcpy(&mark, data, sizeof(time_t));
/* check if we should prune this entry */
return (mark < prock->expmark);
}
static int prune_cb(void *rock, const char *id, int idlen,
const char *data, int datalen)
{
struct prunerock *prock = (struct prunerock *) rock;
int r;
prock->deletions++;
do {
r = DB->delete(prock->db, id, idlen, NULL);
} while (r == CYRUSDB_AGAIN);
return 0;
}
int duplicate_prune(int days)
{
struct prunerock prock;
if (days < 0) fatal("must specify positive number of days", EC_USAGE);
prock.count = prock.deletions = 0;
prock.expmark = time(NULL) - (days * 60 * 60 * 24);
syslog(LOG_NOTICE, "duplicate_prune: pruning back %d days", days);
/* check each entry in our database */
prock.db = dupdb;
DB->foreach(dupdb, "", 0, &prune_p, &prune_cb, &prock, NULL);
syslog(LOG_NOTICE, "duplicate_prune: purged %d out of %d entries",
prock.deletions, prock.count);
return 0;
}
struct dumprock {
FILE *f;
int count;
};
static int dump_p(void *rock,
const char *key, int keylen,
const char *data, int datalen)
{
struct dumprock *drock = (struct dumprock *) rock;
drock->count++;
return 1;
}
static const char hexcodes[] = "0123456789ABCDEF";
static int dump_cb(void *rock,
const char *key, int keylen,
const char *data, int datalen)
{
struct dumprock *drock = (struct dumprock *) rock;
time_t mark;
char *id, *to, *freeme;
int idlen, i;
assert(datalen == sizeof(time_t));
memcpy(&mark, data, sizeof(time_t));
to = (char*) key + strlen(key) + 1;
id = (char *) key;
idlen = strlen(id);
for (i = 0; i < idlen; i++) {
if (!isprint((unsigned char) id[i])) break;
}
if (i != idlen) {
/* change to hexadecimal */
freeme = (char *) xmalloc(sizeof(char) * idlen * 2 + 1);
for (i = 0; i < idlen; i++) {
freeme[2 * i] = hexcodes[(id[i] >> 4) & 0xf];
freeme[2 * i + 1] = hexcodes[id[i] & 0xf];
}
freeme[2 * idlen] = '\0';
id = freeme;
} else {
freeme = NULL;
}
fprintf(drock->f, "id: %-40s\tto: %-20s\tat: %ld\n", id, to, (long) mark);
if (freeme) free(freeme);
return 0;
}
int duplicate_dump(FILE *f)
{
struct dumprock drock;
drock.f = f;
drock.count = 0;
/* check each entry in our database */
DB->foreach(dupdb, "", 0, &dump_p, &dump_cb, &drock, NULL);
return drock.count;
}
int duplicate_done(void)
{
int r;
if (duplicate_dbopen) {
r = DB->close(dupdb);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing deliverdb: %s",
cyrusdb_strerror(r));
}
duplicate_dbopen = 0;
}
r = DB->done();
return r;
}
diff --git a/imap/idle_idled.c b/imap/idle_idled.c
index 72bf07fa7..ff3a97adb 100644
--- a/imap/idle_idled.c
+++ b/imap/idle_idled.c
@@ -1,229 +1,231 @@
/*
* Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: idle_idled.c,v 1.8 2001/10/02 21:08:10 ken3 Exp $ */
+/* $Id: idle_idled.c,v 1.9 2002/01/18 22:58:47 rjs3 Exp $ */
+
+#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <signal.h>
#include "idle.h"
#include "idled.h"
#include "imapconf.h"
const char *idle_method_desc = "idled";
/* function to report mailbox updates to the client */
static idle_updateproc_t *idle_update = NULL;
/* how often to poll the mailbox */
static time_t idle_period = -1;
/* UNIX socket variables */
static int notify_sock = -1;
static struct sockaddr_un idle_remote;
static int idle_remote_len = 0;
/* Forward declarations */
int idle_send_msg(int msg, struct mailbox *mailbox);
void idle_notify(struct mailbox *mailbox);
/*
* Create connection to idled for sending notifications
*/
int idle_enabled(void)
{
int s;
int fdflags;
struct stat sbuf;
const char *idle_sock;
/* if the socket is already open, return */
if (notify_sock != -1) {
return 1;
}
/* get polling period in case we can't connect to idled
* NOTE: if used, a period of zero disables IDLE
*/
if (idle_period == -1) {
idle_period = config_getint("imapidlepoll", 60);
if (idle_period < 0) idle_period = 0;
}
if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
return idle_period;
}
/* set the mailbox update notifier */
mailbox_set_updatenotifier(idle_notify);
idle_remote.sun_family = AF_UNIX;
idle_sock = config_getstring("idlesocket", NULL);
if (idle_sock) {
strcpy(idle_remote.sun_path, idle_sock);
}
else {
strcpy(idle_remote.sun_path, config_dir);
strcat(idle_remote.sun_path, FNAME_IDLE_SOCK);
}
idle_remote_len = sizeof(idle_remote.sun_family) +
strlen(idle_remote.sun_path) + 1;
/* check that the socket exists */
if (stat(idle_remote.sun_path, &sbuf) < 0) {
close(s);
return idle_period;
}
/* put us in non-blocking mode */
fdflags = fcntl(s, F_GETFD, 0);
if (fdflags != -1) fdflags = fcntl(s, F_SETFL, O_NONBLOCK | fdflags);
if (fdflags == -1) { close(s); return idle_period; }
notify_sock = s;
return 1;
}
static void idle_poll(int sig)
{
switch (sig) {
case SIGUSR1:
idle_update(IDLE_MAILBOX);
break;
case SIGUSR2:
idle_update(IDLE_ALERT);
break;
case SIGALRM:
idle_update(IDLE_MAILBOX|IDLE_ALERT);
alarm(idle_period);
break;
}
}
int idle_init(struct mailbox *mailbox, idle_updateproc_t *proc)
{
struct sigaction action;
idle_update = proc;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
#ifdef SA_RESTART
action.sa_flags |= SA_RESTART;
#endif
action.sa_handler = idle_poll;
/* Tell idled that we're idling */
if (idle_send_msg(IDLE_INIT, mailbox)) {
/* if we can talk to idled, setup the signal handlers */
if ((sigaction(SIGUSR1, &action, NULL) < 0) ||
(sigaction(SIGUSR2, &action, NULL) < 0)) {
syslog(LOG_ERR, "sigaction: %m");
return 0;
}
}
else { /* otherwise, we'll poll with SIGALRM */
if (sigaction(SIGALRM, &action, NULL) < 0) {
syslog(LOG_ERR, "sigaction: %m");
return 0;
}
alarm(idle_period);
}
return 1;
}
void idle_done(struct mailbox *mailbox)
{
/* Tell idled that we're done idling */
idle_send_msg(IDLE_DONE, mailbox);
/* Remove the signal handlers */
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
signal(SIGALRM, SIG_IGN);
}
/*
* Send a message to idled
*/
int idle_send_msg(int msg, struct mailbox *mailbox)
{
idle_data_t idledata;
/* fill the structure */
idledata.msg = msg;
idledata.pid = getpid();
strcpy(idledata.mboxname, mailbox ? mailbox->name : ".");
/* send */
if (sendto(notify_sock, (void *) &idledata,
IDLEDATA_BASE_SIZE+strlen(idledata.mboxname)+1, /* 1 for NULL */
0, (struct sockaddr *) &idle_remote, idle_remote_len) == -1) {
syslog(LOG_ERR, "error sending to idled: %x", msg);
return 0;
}
return 1;
}
/*
* Notify imapidled of a mailbox change
*/
void idle_notify(struct mailbox *mailbox)
{
/* We should try to determine if we need to send this
* (ie, is an imapd is IDLE on 'mailbox'?).
*/
idle_send_msg(IDLE_NOTIFY, mailbox);
}
diff --git a/imap/mboxlist.c b/imap/mboxlist.c
index 1e6974ab9..0276f2dfd 100644
--- a/imap/mboxlist.c
+++ b/imap/mboxlist.c
@@ -1,2374 +1,2374 @@
/* mboxlist.c -- Mailbox list manipulation routines
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/*
- * $Id: mboxlist.c,v 1.156 2001/11/19 20:53:41 leg Exp $
+ * $Id: mboxlist.c,v 1.157 2002/01/18 22:58:47 rjs3 Exp $
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <syslog.h>
#include <com_err.h>
#include <sys/ipc.h>
#include <sys/msg.h>
extern int errno;
#include "acl.h"
#include "auth.h"
#include "glob.h"
#include "assert.h"
#include "imapconf.h"
#include "cyrusdb.h"
#include "util.h"
#include "mailbox.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "xmalloc.h"
#include "acap.h"
#include "acapmbox.h"
#include "mboxname.h"
#include "mboxlist.h"
#define DB CONFIG_DB_MBOX
#define SUBDB CONFIG_DB_SUBS
cyrus_acl_canonproc_t mboxlist_ensureOwnerRights;
struct db *mbdb;
static int mboxlist_dbopen = 0;
static int mboxlist_opensubs();
static void mboxlist_closesubs();
static int mboxlist_changequota(const char *name, int matchlen, int maycreate,
void *rock);
#define FNAME_SUBSSUFFIX ".sub"
/*
* Convert a partition into a path
*/
static int mboxlist_getpath(const char *partition, const char *name,
char **pathp)
{
int partitionlen;
char optionbuf[MAX_MAILBOX_NAME+1];
static char pathresult[MAX_MAILBOX_PATH];
const char *root;
assert(partition && pathp);
partitionlen = strlen(partition);
if (partitionlen > sizeof(optionbuf)-11) {
return IMAP_PARTITION_UNKNOWN;
}
strcpy(optionbuf, "partition-");
strcat(optionbuf, partition);
root = config_getstring(optionbuf, (char *)0);
if (!root) {
return IMAP_PARTITION_UNKNOWN;
}
mailbox_hash_mbox(pathresult, root, name);
*pathp = pathresult;
return 0;
}
char *mboxlist_makeentry(int mbtype, char *part, char *acl)
{
char *mboxent = (char *) xmalloc(sizeof(char) *
(30 + strlen(acl) + strlen(part)));
sprintf(mboxent, "%d %s %s", mbtype, part, acl);
return mboxent;
}
static const int get_deleteright(void)
{
const char *r = config_getstring("deleteright", "c");
return cyrus_acl_strtomask(r);
}
/*
* Lookup 'name' in the mailbox list.
* The capitalization of 'name' is canonicalized to the way it appears
* in the mailbox list.
* If 'path' is non-nil, a pointer to the full pathname of the mailbox
* is placed in the char * pointed to by it. If 'acl' is non-nil, a pointer
* to the mailbox ACL is placed in the char * pointed to by it.
*/
static int mboxlist_mylookup(const char *name, int *typep,
char **pathp, char **partp,
char **aclp,
struct txn **tid, int wrlock)
{
int acllen;
static char partition[MAX_PARTITION_LEN];
static char *aclresult;
static int aclresultalloced;
int r;
const char *data;
char *p, *q;
int datalen;
int namelen;
int mbtype;
namelen = strlen(name);
if (namelen == 0) {
return IMAP_MAILBOX_NONEXISTENT;
}
if (wrlock) {
r = DB->fetchlock(mbdb, name, namelen, &data, &datalen, tid);
} else {
r = DB->fetch(mbdb, name, namelen, &data, &datalen, tid);
}
switch (r) {
case CYRUSDB_OK:
if (data == NULL) {
return IMAP_MAILBOX_NONEXISTENT;
break;
}
/* copy out interesting parts */
mbtype = strtol(data, &p, 10);
if (typep) *typep = mbtype;
if (*p == ' ') p++;
q = partition;
while (*p != ' ') { /* copy out partition name */
*q++ = *p++;
}
*q = '\0';
p++;
if (partp) {
*partp = partition;
}
/* construct pathname if requested */
if (pathp) {
if (mbtype & MBTYPE_REMOTE) {
*pathp = partition;
} else {
r = mboxlist_getpath(partition, name, pathp);
if (r) {
return r;
}
}
}
/* the rest is ACL; return it if requested */
if (aclp) {
acllen = datalen - (p - data);
if (acllen >= aclresultalloced) {
aclresultalloced = acllen + 100;
aclresult = xrealloc(aclresult, aclresultalloced);
}
memcpy(aclresult, p, acllen);
aclresult[acllen] = '\0';
*aclp = aclresult;
}
break;
case CYRUSDB_AGAIN:
return IMAP_AGAIN;
break;
default:
syslog(LOG_ERR, "DBERROR: error fetching %s: %s",
name, cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
return 0;
}
/*
* Lookup 'name' in the mailbox list.
* The capitalization of 'name' is canonicalized to the way it appears
* in the mailbox list.
* If 'path' is non-nil, a pointer to the full pathname of the mailbox
* is placed in the char * pointed to by it. If 'acl' is non-nil, a pointer
* to the mailbox ACL is placed in the char * pointed to by it.
*/
int mboxlist_lookup(const char *name, char **pathp, char **aclp,
void *tid __attribute__((unused)))
{
return mboxlist_mylookup(name, NULL, pathp, NULL, aclp, NULL, 0);
}
int mboxlist_findstage(const char *name, char *stagedir)
{
char optionbuf[MAX_MAILBOX_NAME+1];
const char *root;
char *partition;
int r;
assert(stagedir != NULL);
/* Find mailbox */
r = mboxlist_mylookup(name, NULL, NULL, &partition, NULL, NULL, 0);
switch (r) {
case 0:
break;
default:
return r;
break;
}
strcpy(optionbuf, "partition-");
strcpy(optionbuf + 10, partition);
root = config_getstring(optionbuf, (char *)0);
if (!root) {
return IMAP_PARTITION_UNKNOWN;
}
sprintf(stagedir, "%s/stage./", root);
return 0;
}
/*
* Check/set up for mailbox creation
*/
int
mboxlist_mycreatemailboxcheck(char *name, int mbtype, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state,
char **newacl, char **newpartition,
int RMW, struct txn **tid)
{
int r;
char *p;
char *acl, *path;
char *defaultacl, *identifier, *rights;
char parent[MAX_MAILBOX_NAME+1];
unsigned long parentlen;
char *parentname = NULL;
char *parentpartition = NULL;
char *parentacl = NULL;
unsigned long parentpartitionlen = 0;
unsigned long parentacllen = 0;
/* Check for invalid name/partition */
if (partition && strlen(partition) > MAX_PARTITION_LEN) {
return IMAP_PARTITION_UNKNOWN;
}
r = mboxname_policycheck(name);
if (r) return r;
/* User has admin rights over their own mailbox namespace */
if (mboxname_userownsmailbox(userid, name)) {
isadmin = 1;
}
/* Check to see if new mailbox exists */
r = mboxlist_mylookup(name, NULL, &path, NULL, &acl, tid, RMW);
switch (r) {
case 0:
r = IMAP_MAILBOX_EXISTS;
/* Lie about error if privacy demands */
if (!isadmin &&
!(cyrus_acl_myrights(auth_state, acl) & ACL_LOOKUP)) {
r = IMAP_PERMISSION_DENIED;
}
return r;
break;
case IMAP_MAILBOX_NONEXISTENT:
break;
default:
return r;
break;
}
/* Search for a parent */
strcpy(parent, name);
parentlen = 0;
while ((parentlen==0) && (p = strrchr(parent, '.'))) {
*p = '\0';
r = mboxlist_mylookup(parent, NULL, NULL, &parentpartition,
&parentacl, tid, 0);
switch (r) {
case 0:
parentlen = strlen(parent);
parentname = parent;
parentpartitionlen = strlen(parentpartition);
parentacllen = strlen(parentacl);
break;
case IMAP_MAILBOX_NONEXISTENT:
break;
default:
return r;
break;
}
}
if (parentlen != 0) {
/* check acl */
if (!isadmin && !(cyrus_acl_myrights(auth_state, parentacl) & ACL_CREATE)) {
return IMAP_PERMISSION_DENIED;
}
/* Copy partition, if not specified */
if (partition == NULL) {
partition = xmalloc(parentpartitionlen + 1);
memcpy(partition, parentpartition, parentpartitionlen);
partition[parentpartitionlen] = '\0';
} else {
partition = xstrdup(partition);
}
/* Copy ACL */
acl = xmalloc(parentacllen + 1);
memcpy(acl, parentacl, parentacllen);
acl[parentacllen] = '\0';
/* Canonicalize case of parent prefix */
strlcpy(name, parent, strlen(parent));
} else { /* parentlen == 0, no parent mailbox */
if (!isadmin) {
return IMAP_PERMISSION_DENIED;
}
acl = xstrdup("");
if (!strncmp(name, "user.", 5)) {
if (strchr(name+5, '.')) {
/* Disallow creating user.X.* when no user.X */
free(acl);
return IMAP_PERMISSION_DENIED;
}
/* disallow wildcards in userids with inboxes. */
if (strchr(name, '*') || strchr(name, '%') || strchr(name, '?')) {
return IMAP_MAILBOX_BADNAME;
}
/*
* Users by default have all access to their personal mailbox(es),
* Nobody else starts with any access to same.
*/
identifier = xstrdup(name+5);
if (config_getswitch("unixhierarchysep", 0)) {
/*
* The mailboxname is now in the internal format,
* so we we need to change DOTCHARs back to '.'
* in the identifier in order to have the correct ACL.
*/
for (p = identifier; *p; p++) {
if (*p == DOTCHAR) *p = '.';
}
}
cyrus_acl_set(&acl, identifier, ACL_MODE_SET, ACL_ALL,
(cyrus_acl_canonproc_t *)0, (void *)0);
free(identifier);
} else {
defaultacl = identifier =
xstrdup(config_getstring("defaultacl", "anyone lrs"));
for (;;) {
while (*identifier && isspace((int) *identifier)) identifier++;
rights = identifier;
while (*rights && !isspace((int) *rights)) rights++;
if (!*rights) break;
*rights++ = '\0';
while (*rights && isspace((int) *rights)) rights++;
if (!*rights) break;
p = rights;
while (*p && !isspace((int) *p)) p++;
if (*p) *p++ = '\0';
cyrus_acl_set(&acl, identifier, ACL_MODE_SET, cyrus_acl_strtomask(rights),
(cyrus_acl_canonproc_t *)0, (void *)0);
identifier = p;
}
free(defaultacl);
}
if (!partition) {
partition = (char *)config_defpartition;
if (strlen(partition) > MAX_PARTITION_LEN) {
/* Configuration error */
fatal("name of default partition is too long", EC_CONFIG);
}
}
partition = xstrdup(partition);
}
if (newpartition) *newpartition = partition;
else free(partition);
if (newacl) *newacl = acl;
else free(acl);
return 0;
}
int
mboxlist_createmailboxcheck(char *name, int mbtype, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state,
char **newacl, char **newpartition)
{
return mboxlist_mycreatemailboxcheck(name, mbtype, partition, isadmin,
userid, auth_state, newacl,
newpartition, 0, NULL);
}
/*
* Create a mailbox
*
* 1. start mailboxes transaction
* 2. verify ACL's to best of ability (CRASH: abort)
* 3. open ACAP connection if necessary
* 4. verify parent ACL's if need to
* 5. create ACAP entry and set as reserved (CRASH: ACAP inconsistant)
* 6. create on disk (CRASH: ACAP inconsistant, disk inconsistant)
* 8. commit local transaction (CRASH: ACAP inconsistant)
* 9. set ACAP entry as commited (CRASH: commited)
*
*/
int mboxlist_createmailbox(char *name, int mbtype, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state)
{
int r;
char *acl = NULL;
const char *root = NULL;
char *newpartition = NULL;
struct mailbox newmailbox;
struct txn *tid = NULL;
char *mboxent = NULL;
acapmbox_data_t mboxdata;
int madereserved = 0; /* made reserved entry on ACAP server */
acapmbox_handle_t *acaphandle = NULL;
retry:
tid = NULL;
/* 2. verify ACL's to best of ability (CRASH: abort) */
r = mboxlist_mycreatemailboxcheck(name, mbtype, partition, isadmin,
userid, auth_state,
&acl, &newpartition, 1, &tid);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
default:
goto done;
}
if (!(mbtype & MBTYPE_REMOTE)) {
char buf[MAX_PARTITION_LEN + 30];
/* Get partition's path */
sprintf(buf, "partition-%s", newpartition);
root = config_getstring(buf, (char *)0);
if (!root) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
if (strlen(root)+strlen(name)+20 > MAX_MAILBOX_PATH) {
r = IMAP_MAILBOX_BADNAME;
goto done;
}
}
/* 3. open ACAP connection if necessary */
acaphandle = acapmbox_get_handle();
/* 4. create ACAP entry and set as reserved (CRASH: ACAP inconsistant) */
acapmbox_new(&mboxdata, NULL, name);
r = acapmbox_create(acaphandle, &mboxdata);
if (r) {
syslog(LOG_ERR, "ACAP: unable to reserve %s: %s\n", name,
error_message(r));
goto done;
}
madereserved = 1; /* so we can roll back on failure */
/* 5. add the new entry */
mboxent = mboxlist_makeentry(mbtype, newpartition, acl);
r = DB->store(mbdb, name, strlen(name), mboxent, strlen(mboxent),
&tid);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
goto retry;
break;
default:
- syslog(LOG_ERR, "DBERROR: error updating database: %s",
+ syslog(LOG_ERR, "DBERROR: error updating database %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
done: /* ALL DATABASE OPERATIONS DONE; NEED TO DO FILESYSTEM OPERATIONS */
if (!r && !(mbtype & MBTYPE_REMOTE)) {
char buf[MAX_MAILBOX_PATH];
/* Create new mailbox and move new mailbox list file into place */
mailbox_hash_mbox(buf, root, name);
r = mailbox_create(name, buf, acl, NULL,
((mbtype & MBTYPE_NETNEWS) ?
MAILBOX_FORMAT_NETNEWS :
MAILBOX_FORMAT_NORMAL),
&newmailbox);
mboxdata.uidvalidity = newmailbox.uidvalidity;
mboxdata.acl = acl;
mboxdata.total = newmailbox.exists;
if (!r) {
mailbox_close(&newmailbox);
}
}
if (r) { /* CREATE failed */
int r2;
r2 = 0;
if (tid) r2 = DB->abort(mbdb, tid);
switch (r2) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on abort: %s",
cyrusdb_strerror(r2));
}
/* delete ACAP entry if we made it */
if (madereserved == 1) {
r2 = acapmbox_delete(acaphandle, name);
if (r2) {
syslog(LOG_ERR, "ACAP: unable to unreserve %s: %s\n", name,
error_message(r2));
}
}
} else { /* all is well */
switch (r = DB->commit(mbdb, tid)) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
/* 9. set ACAP entry as commited (CRASH: commited) */
if (!r) {
r = acapmbox_markactive(acaphandle, &mboxdata);
if (r) {
syslog(LOG_ERR, "ACAP: unable to commit %s: %s\n", name,
error_message(r));
}
}
acapmbox_release_handle(acaphandle);
if (acl) free(acl);
if (newpartition) free(newpartition);
if (mboxent) free(mboxent);
return r;
}
/* insert an entry for the proxy */
int mboxlist_insertremote(char *name, int mbtype, char *host, char *acl,
void **rettid)
{
char *mboxent;
int r = 0;
assert(name != NULL && host != NULL);
mboxent = mboxlist_makeentry(mbtype | MBTYPE_REMOTE, host, acl);
/* database put */
r = DB->store(mbdb, name, strlen(name), mboxent, strlen(mboxent), NULL);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
abort(); /* shouldn't happen ! */
break;
default:
- syslog(LOG_ERR, "DBERROR: error updating database: %s",
+ syslog(LOG_ERR, "DBERROR: error updating database %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
free(mboxent);
return r;
}
/*
* Delete a mailbox.
* Deleting the mailbox user.FOO deletes the user "FOO". It may only be
* performed by an admin. The operation removes the user "FOO"'s
* subscriptions and all sub-mailboxes of user.FOO
*
* 1. Begin transaction
* 2. Verify ACL's
* 3. remove from database
* 4. remove from disk
* 5. commit transaction
* 6. Open ACAP connection if necessary
* 7. delete from ACAP
*
*/
int mboxlist_deletemailbox(char *name, int isadmin, char *userid,
struct auth_state *auth_state, int checkacl)
{
int r;
char *acl;
long access;
int deleteuser = 0; /* if we are deleting user.<user> */
struct mailbox mailbox;
int deletequotaroot = 0;
char *path;
struct txn *tid = NULL;
int isremote = 0;
int mbtype;
int deleteright = get_deleteright();
retry:
/* Check for request to delete a user:
user.<x> with no dots after it */
if (!strncmp(name, "user.", 5) && !strchr(name+5, '.')) {
/* Can't DELETE INBOX (your own inbox) */
if (!strcmp(name+5, userid)) {
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
/* Only admins may delete user */
if (!isadmin) { r = IMAP_PERMISSION_DENIED; goto done; }
r = mboxlist_mylookup(name, NULL, NULL, NULL, &acl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default:
DB->abort(mbdb, tid);
goto done;
break;
}
/* Check ACL before doing anything stupid
* We don't have to lie about the error code since we know
* the user is an admin.
*/
if (checkacl &&
(!(cyrus_acl_myrights(auth_state, acl) & deleteright))) {
r = IMAP_PERMISSION_DENIED;
DB->abort(mbdb, tid);
goto done;
}
deleteuser = 1;
}
r = mboxlist_mylookup(name, &mbtype, &path, NULL, &acl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default:
DB->abort(mbdb, tid);
goto done;
}
isremote = mbtype & MBTYPE_REMOTE;
/* check if user has Delete right */
access = cyrus_acl_myrights(auth_state, acl);
if (checkacl && !(access & deleteright)) {
/* User has admin rights over their own mailbox namespace */
if (mboxname_userownsmailbox(userid, name)) {
isadmin = 1;
}
/* Lie about error if privacy demands */
r = (isadmin || (access & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
DB->abort(mbdb, tid);
goto done;
}
/* delete entry */
r = DB->delete(mbdb, name, strlen(name), &tid);
switch (r) {
case CYRUSDB_OK: /* success */
break;
case CYRUSDB_AGAIN:
goto retry;
default:
syslog(LOG_ERR, "DBERROR: error deleting %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
/* commit db operations */
if (!r) {
r = DB->commit(mbdb, tid);
if (r) {
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
if (r || isremote) goto done;
if (!r) r = mailbox_open_header_path(name, path, acl, 0, &mailbox, 0);
if (!r) r = mailbox_delete(&mailbox, deletequotaroot);
if (!r) {
/* open ACAP connection if necessary */
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
/* delete from ACAP */
r = acapmbox_delete(acaphandle, name);
if (r) {
syslog(LOG_ERR,
"ACAP: can't delete mailbox entry '%s': %s",
name, error_message(r));
}
acapmbox_release_handle(acaphandle);
}
/*
* See if we have to remove mailbox's quota root
*/
if (!r && mailbox.quota.root != NULL) {
/* look for any other mailboxes in this quotaroot */
}
done:
return r;
}
/*
* Rename/move a single mailbox (recursive renames are handled at a
* higher level)
*
* 1. start transaction
* 2. verify acls
* 3. open acap connection if needed
* 4. Delete entry from berkeley db
* 5. ACAP make the new entry
* 7. delete from disk
* 8. commit transaction
* 9. set new ACAP entry commited
* 10. delete old ACAP entry
* */
/* note: partition moving should really be moved to another function */
int mboxlist_renamemailbox(char *oldname, char *newname, char *partition,
int isadmin, char *userid,
struct auth_state *auth_state)
{
int r;
long access;
int isusermbox = 0;
int partitionmove = 0;
int mbtype;
char *oldpath = NULL;
char newpath[MAX_MAILBOX_PATH];
struct mailbox newmailbox;
acapmbox_data_t mboxdata;
char *oldacl = NULL, *newacl = NULL;
const char *root = NULL;
struct txn *tid = NULL;
char *newpartition = NULL;
char *mboxent = NULL;
int deleteright = get_deleteright();
int acap_madenew = 0;
retry:
/* lookup the mailbox to make sure it exists and get its acl */
r = mboxlist_mylookup(oldname, &mbtype, &oldpath, NULL, &oldacl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
default:
goto done;
}
/* make a copy of the old ACL so it doesn't get overwritten
by another call to mboxlist_mylookup() */
newacl = xstrdup(oldacl);
/* Check ability to delete old mailbox */
if (!strcmp(oldname, newname) && !(mbtype & MBTYPE_REMOTE)) {
/* Attempt to move mailbox across partition */
if (!isadmin || !partition) {
r = IMAP_MAILBOX_EXISTS;
goto done;
}
partitionmove = 1;
root = config_partitiondir(partition);
if (!root) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
if (!strncmp(root, oldpath, strlen(root)) &&
oldpath[strlen(root)] == '/') {
/* partitions are the same or share common prefix */
r = IMAP_MAILBOX_EXISTS;
goto done;
}
} else if (!strncmp(oldname, "user.", 5) && !strchr(oldname+5, '.')) {
if (!strcmp(oldname+5, userid)) {
/* Special case of renaming inbox */
access = cyrus_acl_myrights(auth_state, oldacl);
if (!(access & deleteright)) {
r = IMAP_PERMISSION_DENIED;
goto done;
}
isusermbox = 1;
} else {
/* Even admins can't rename users */
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
} else {
access = cyrus_acl_myrights(auth_state, oldacl);
if (!(access & deleteright) && !isadmin) {
r = (isadmin || (access & ACL_LOOKUP)) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
/* Check ability to create new mailbox */
if (!partitionmove) {
if (!strncmp(newname, "user.", 5) && !strchr(newname+5, '.')) {
/* Even admins can't rename to user's inboxes */
r = IMAP_MAILBOX_NOTSUPPORTED;
goto done;
}
r = mboxlist_mycreatemailboxcheck(newname, 0, partition, isadmin,
userid, auth_state, NULL,
&newpartition, 1, &tid);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
break;
default: /* not allowed to create the new mailbox */
goto done;
break;
}
} else {
newpartition = xstrdup(partition);
}
if (!(mbtype & MBTYPE_REMOTE)) {
/* Get partition's path */
root = config_partitiondir(newpartition);
if (!root) {
r = IMAP_PARTITION_UNKNOWN;
goto done;
}
}
if (!r && !partitionmove) {
/* 3. open ACAP connection if necessary */
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
/* 5. ACAP make the new entry, set as reserved */
acapmbox_new(&mboxdata, NULL, newname);
r = acapmbox_create(acaphandle, &mboxdata);
if (r != ACAP_OK) {
goto done;
}
acap_madenew = 1;
acapmbox_release_handle(acaphandle);
}
if (!isusermbox) {
/* 4. Delete entry from berkeley db */
r = DB->delete(mbdb, oldname, strlen(oldname), &tid);
switch (r) {
case 0: /* success */
break;
case CYRUSDB_AGAIN:
goto retry;
break;
default:
syslog(LOG_ERR, "DBERROR: error deleting %s: %s",
oldname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
break;
}
}
/* create new entry */
mboxent = mboxlist_makeentry(mbtype, newpartition, newacl);
/* put it into the db */
r = DB->store(mbdb, newname, strlen(newname),
mboxent, strlen(mboxent), &tid);
switch (r) {
case 0:
break;
case CYRUSDB_AGAIN:
goto retry;
default:
syslog(LOG_ERR, "DBERROR: error renaming %s: %s",
newname, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
done: /* ALL DATABASE OPERATIONS DONE; NEED TO DO FILESYSTEM OPERATIONS */
if (!r && !(mbtype & MBTYPE_REMOTE)) {
/* Rename the actual mailbox */
assert(root != NULL); /* from above */
mailbox_hash_mbox(newpath, root, newname);
r = mailbox_rename(oldname, oldpath, newacl, newname,
newpath, isusermbox, NULL, NULL, &newmailbox);
mboxdata.uidvalidity = newmailbox.uidvalidity;
mboxdata.acl = newacl;
mboxdata.total = newmailbox.exists;
if (!r) {
mailbox_close(&newmailbox);
}
}
if (r != 0) {
int r2 = 0;
if (tid) r2 = DB->abort(mbdb, tid);
if (r2) {
syslog(LOG_ERR, "DBERROR: can't abort: %s", cyrusdb_strerror(r2));
}
/* unroll acap operations if necessary */
if (acap_madenew) {
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
r2 = acapmbox_delete(acaphandle, newname);
if (r2) syslog(LOG_ERR, "ACAP: can't rollback %s: %s", newname,
error_message(r));
acapmbox_release_handle(acaphandle);
}
} else {
/* commit now */
switch (r = DB->commit(mbdb, tid)) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
}
if (!r && !partitionmove) {
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
r = acapmbox_markactive(acaphandle, &mboxdata);
if (r) syslog(LOG_ERR, "ACAP: can't commit %s: %s", newname,
error_message(r));
acapmbox_release_handle(acaphandle);
}
if (!r && !partitionmove) {
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
/* delete old ACAP entry */
r = acapmbox_delete(acaphandle, oldname);
if (r) syslog(LOG_ERR, "ACAP: can't delete %s: %s", oldname,
error_message(r));
acapmbox_release_handle(acaphandle);
}
/* free memory */
if (newacl) free(newacl); /* we're done with the new ACL */
if (newpartition) free(newpartition);
if (mboxent) free(mboxent);
return r;
}
/*
* Change the ACL for mailbox 'name' so that 'identifier' has the
* rights enumerated in the string 'rights'. If 'rights' is the null
* pointer, removes the ACL entry for 'identifier'. 'isadmin' is
* nonzero if user is a mailbox admin. 'userid' is the user's login id.
*
*
* 1. Start transaction
* 2. Check rights
* 3. Open ACAP connection if necessary
* 4. Set db entry
* 5. Change on disk
* 6. Commit transaction
* 7. Change ACAP entry
*
*/
int mboxlist_setacl(char *name, char *identifier, char *rights,
int isadmin, char *userid,
struct auth_state *auth_state)
{
int useridlen = strlen(userid);
int r;
int access;
int mode = ACL_MODE_SET;
int isusermbox = 0;
struct mailbox mailbox;
char *acl, *newacl = NULL;
char *partition, *path;
char *mboxent = NULL;
int mbtype;
struct txn *tid = NULL;
if (!strncmp(name, "user.", 5) &&
!strchr(userid, '.') &&
!strncmp(name+5, userid, useridlen) &&
(name[5+useridlen] == '\0' || name[5+useridlen] == '.')) {
isusermbox = 1;
}
retry:
/* lookup the mailbox to make sure it exists and get its acl */
r = mboxlist_mylookup(name, &mbtype, &path, &partition, &acl, &tid, 1);
switch (r) {
case 0:
break;
case IMAP_AGAIN:
goto retry;
default:
goto done;
}
if (!r && !isadmin && !isusermbox) {
access = cyrus_acl_myrights(auth_state, acl);
if (!(access & ACL_ADMIN)) {
r = (access & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
goto done;
}
}
/* Make change to ACL */
newacl = xstrdup(acl);
if (rights) {
mode = ACL_MODE_SET;
if (*rights == '+') {
rights++;
mode = ACL_MODE_ADD;
}
else if (*rights == '-') {
rights++;
mode = ACL_MODE_REMOVE;
}
if (cyrus_acl_set(&newacl, identifier, mode, cyrus_acl_strtomask(rights),
isusermbox ? mboxlist_ensureOwnerRights : 0,
(void *)userid))
{
r = IMAP_INVALID_IDENTIFIER;
goto done;
}
}
else {
if (cyrus_acl_remove(&newacl, identifier,
isusermbox ? mboxlist_ensureOwnerRights : 0,
(void *)userid)) {
r = IMAP_INVALID_IDENTIFIER;
goto done;
}
}
/* ok, make the change */
mboxent = mboxlist_makeentry(mbtype, partition, newacl);
r = DB->store(mbdb, name, strlen(name), mboxent, strlen(mboxent), &tid);
switch (r) {
case 0:
break;
case CYRUSDB_AGAIN:
goto retry;
default:
syslog(LOG_ERR, "DBERROR: error updating acl %s: %s",
name, cyrusdb_strerror(r));
r = IMAP_IOERROR;
goto done;
}
if (!(mbtype & MBTYPE_REMOTE)) {
/* open & lock mailbox header */
r = mailbox_open_header_path(name, path, acl, NULL, &mailbox, 0);
if (!r) {
r = mailbox_lock_header(&mailbox);
if (!r) {
/* set it in the /var/spool part */
(void) mailbox_write_header(&mailbox);
}
mailbox_close(&mailbox);
}
}
done:
if (mboxent) free(mboxent);
if (r) {
int r2;
if ((r2 = DB->abort(mbdb, tid)) != 0) {
syslog(LOG_ERR, "DBERROR: error aborting txn: %s",
cyrusdb_strerror(r2));
r2 = IMAP_IOERROR;
}
} else {
/* commit now */
switch (r = DB->commit(mbdb, tid)) {
case 0:
break;
default:
syslog(LOG_ERR, "DBERROR: failed on commit: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
}
/* 7. Change ACAP entry */
if (!r) {
acapmbox_handle_t *acaphandle = acapmbox_get_handle();
r = acapmbox_setproperty_acl(acaphandle, name, newacl);
}
if (newacl) free(newacl);
return r;
}
struct find_rock {
struct glob *g;
struct namespace *namespace;
int find_namespace;
int inboxoffset;
const char *inboxcase;
const char *usermboxname;
int usermboxnamelen;
int checkmboxlist;
int checkshared;
int isadmin;
struct auth_state *auth_state;
int (*proc)(char *, int, int, void *rock);
void *procrock;
};
/* return non-zero if we like this one */
static int find_p(void *rockp,
const char *key, int keylen,
const char *data, int datalen)
{
struct find_rock *rock = (struct find_rock *) rockp;
long minmatch;
struct glob *g = rock->g;
long matchlen;
minmatch = 0;
if (rock->inboxoffset) {
char namebuf[MAX_MAILBOX_NAME+1];
memcpy(namebuf, key, keylen);
namebuf[keylen] = '\0';
if (rock->inboxoffset) {
namebuf[rock->inboxoffset] = rock->inboxcase[0];
namebuf[rock->inboxoffset+1] = rock->inboxcase[1];
namebuf[rock->inboxoffset+2] = rock->inboxcase[2];
namebuf[rock->inboxoffset+3] = rock->inboxcase[3];
namebuf[rock->inboxoffset+4] = rock->inboxcase[4];
}
matchlen = glob_test(g, namebuf+rock->inboxoffset,
keylen-rock->inboxoffset, &minmatch);
} else {
matchlen = glob_test(g, key, keylen, &minmatch);
}
if (matchlen == -1) return 0;
if (rock->find_namespace != NAMESPACE_INBOX &&
rock->usermboxname &&
keylen >= rock->usermboxnamelen &&
(keylen == rock->usermboxnamelen ||
key[rock->usermboxnamelen] == '.') &&
!strncmp(key, rock->usermboxname, rock->usermboxnamelen)) {
/* this would've been output with the inbox stuff, so skip it */
return 0;
}
if (rock->find_namespace == NAMESPACE_SHARED &&
rock->namespace && rock->namespace->isalt &&
!strncmp(key, "user", 4) &&
(key[4] == '\0' || key[4] == '.')) {
/* this would've been output with the user stuff, so skip it */
return 0;
}
/* check acl */
if (!rock->isadmin) {
/* check the acls */
const char *p, *acl;
int rights;
int acllen;
static char *aclbuf = NULL;
static int aclbufsz = 0;
p = strchr(data, ' ');
if (!p) {
syslog(LOG_ERR, "%s: can't find partition", key);
return 0;
}
p++;
acl = strchr(p, ' ');
if (!acl) {
syslog(LOG_ERR, "%s: can't find acl", key);
return 0;
}
acl++;
acllen = datalen - (acl - data);
if (acllen >= aclbufsz) {
aclbufsz = acllen + 500;
aclbuf = xrealloc(aclbuf, aclbufsz);
}
memcpy(aclbuf, acl, acllen);
aclbuf[acllen] = '\0';
rights = cyrus_acl_myrights(rock->auth_state, aclbuf);
if (!(rights & ACL_LOOKUP)) {
return 0;
}
}
/* if we get here, close enough for us to spend the time
acting interested */
return 1;
}
static int find_cb(void *rockp,
const char *key, int keylen,
const char *data, int datalen)
{
char namebuf[MAX_MAILBOX_NAME+1];
struct find_rock *rock = (struct find_rock *) rockp;
int r = 0;
long minmatch;
struct glob *g = rock->g;
/* foreach match, do this test */
minmatch = 0;
while (minmatch >= 0) {
long matchlen;
memcpy(namebuf, key, keylen);
namebuf[keylen] = '\0';
if (rock->find_namespace != NAMESPACE_INBOX &&
rock->usermboxname &&
!strncmp(namebuf, rock->usermboxname, rock->usermboxnamelen)
&& (keylen == rock->usermboxnamelen ||
namebuf[rock->usermboxnamelen] == '.')) {
/* this would've been output with the inbox stuff, so skip it */
return 0;
}
/* make sure it's in the mailboxes db */
if (rock->checkmboxlist) {
r = mboxlist_lookup(namebuf, NULL, NULL, NULL);
} else {
r = 0; /* don't bother checking */
}
if (!r && rock->inboxoffset) {
namebuf[rock->inboxoffset] = rock->inboxcase[0];
namebuf[rock->inboxoffset+1] = rock->inboxcase[1];
namebuf[rock->inboxoffset+2] = rock->inboxcase[2];
namebuf[rock->inboxoffset+3] = rock->inboxcase[3];
namebuf[rock->inboxoffset+4] = rock->inboxcase[4];
}
matchlen = glob_test(g, namebuf+rock->inboxoffset,
keylen-rock->inboxoffset, &minmatch);
if (matchlen == -1) {
r = 0;
break;
}
switch (r) {
case 0:
/* found the entry; output it */
if (rock->find_namespace == NAMESPACE_SHARED &&
rock->checkshared && rock->namespace) {
/* special case: LIST "" % -- output prefix only */
r = (*rock->proc)(rock->namespace->prefix[NAMESPACE_SHARED],
strlen(rock->namespace->prefix[NAMESPACE_SHARED])-1,
1, rock->procrock);
/* short-circuit the foreach - one mailbox is sufficient */
r = CYRUSDB_DONE;
}
else {
r = (*rock->proc)(namebuf+rock->inboxoffset, matchlen,
1, rock->procrock);
}
break;
case IMAP_MAILBOX_NONEXISTENT:
/* didn't find the entry */
r = 0;
break;
default:
break;
}
if (r) break;
}
return r;
}
/*
* Find all mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox. If 'proc' ever returns
* a nonzero value, mboxlist_findall immediately stops searching
* and returns that value. 'rock' is passed along as an argument to proc in
* case it wants some persistant storage or extra data.
*/
/* Find all mailboxes that match 'pattern'. */
int mboxlist_findall(struct namespace *namespace __attribute__((unused)),
char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(), void *rock)
{
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen;
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = NULL;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = isadmin;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = 0; /* don't duplicate work */
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
/* Build usermboxname */
if (userid && !strchr(userid, '.') &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
strcpy(usermboxname, "user.");
strcat(usermboxname, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
/* Check for INBOX first of all */
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 1, rock);
}
}
else if (!strncmp(pattern, usermboxname, usermboxnamelen) &&
GLOB_TEST(cbrock.g, usermboxname) != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(usermboxname, usermboxnamelen, 1, rock);
}
}
strcpy(usermboxname+usermboxnamelen, ".");
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (r) goto done;
/* Find fixed-string pattern prefix */
for (p = pattern; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?') break;
}
prefixlen = p - pattern;
*p = '\0';
/*
* If user.X.* or INBOX.* can match pattern,
* search for those mailboxes next
*/
if (userid &&
(!strncmp(usermboxname, pattern, usermboxnamelen-1) ||
!strncasecmp("inbox.", pattern, prefixlen < 6 ? prefixlen : 6))) {
if (!strncmp(usermboxname, pattern, usermboxnamelen-1)) {
cbrock.inboxoffset = 0;
}
else {
cbrock.inboxoffset = strlen(userid);
}
cbrock.find_namespace = NAMESPACE_INBOX;
/* iterate through prefixes matching usermboxname */
DB->foreach(mbdb,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
/* search for all remaining mailboxes.
just bother looking at the ones that have the same pattern prefix. */
DB->foreach(mbdb,
pattern, prefixlen,
&find_p, &find_cb, &cbrock,
NULL);
done:
glob_free(&cbrock.g);
return r;
}
int mboxlist_findall_alt(struct namespace *namespace,
char *pattern, int isadmin, char *userid,
struct auth_state *auth_state, int (*proc)(),
void *rock)
{
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1], patbuf[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen, len;
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = namespace;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = isadmin;
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = 0; /* don't duplicate work */
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
/* Build usermboxname */
if (userid && !strchr(userid, '.') &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
strcpy(usermboxname, "user.");
strcat(usermboxname, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
/* Check for INBOX first of all */
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = DB->fetch(mbdb, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 0, rock);
}
}
strcpy(usermboxname+usermboxnamelen, ".");
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (r) goto done;
glob_free(&cbrock.g);
/* Find fixed-string pattern prefix */
for (p = pattern; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?') break;
}
prefixlen = p - pattern;
/*
* Personal (INBOX) namespace
*
* Append pattern to "INBOX.", search for those mailboxes next
*/
if (userid) {
strcpy(patbuf, "INBOX.");
strcat(patbuf, pattern);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.inboxoffset = strlen(userid);
cbrock.find_namespace = NAMESPACE_INBOX;
/* iterate through prefixes matching usermboxname */
DB->foreach(mbdb,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
/*
* Other Users namespace
*
* If "Other Users*" can match pattern, search for those mailboxes next
*/
len = strlen(namespace->prefix[NAMESPACE_USER])-1;
if (!strncmp(namespace->prefix[NAMESPACE_USER], pattern,
prefixlen < len ? prefixlen : len)) {
if (prefixlen < len)
cbrock.g = glob_init(pattern+prefixlen, GLOB_HIERARCHY);
else {
strcpy(patbuf, "user");
strcat(patbuf, pattern+len);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY);
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
/* iterate through prefixes matching usermboxname */
DB->foreach(mbdb,
"user", 4,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
/*
* Shared namespace
*
* search for all remaining mailboxes.
* just bother looking at the ones that have the same pattern prefix.
*/
len = strlen(namespace->prefix[NAMESPACE_SHARED]);
if (!strncmp(namespace->prefix[NAMESPACE_SHARED], pattern,
prefixlen < len - 1 ? prefixlen : len - 1)) {
cbrock.find_namespace = NAMESPACE_SHARED;
cbrock.inboxoffset = 0;
if (prefixlen < len) {
/* Find pattern which matches shared namespace prefix */
for (p = pattern+prefixlen; *p; p++) {
if (*p == '%') continue;
else if (*p == '.') p++;
break;
}
if (!*p) {
/* special case: LIST "" % -- see if we have a shared mbox */
cbrock.g = glob_init("*", GLOB_HIERARCHY);
cbrock.checkshared = 1;
}
else {
cbrock.g = glob_init(p, GLOB_HIERARCHY);
}
DB->foreach(mbdb,
"", 0,
&find_p, &find_cb, &cbrock,
NULL);
}
else if (pattern[len-1] == '.') {
strcpy(patbuf, "");
strcat(patbuf, pattern+len);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY);
pattern[prefixlen] = '\0';
DB->foreach(mbdb,
pattern+len, prefixlen-len,
&find_p, &find_cb, &cbrock,
NULL);
}
}
done:
glob_free(&cbrock.g);
return r;
}
/*
* Set the quota on or create a quota root
*/
int mboxlist_setquota(const char *root, int newquota)
{
char quota_path[MAX_MAILBOX_PATH];
char pattern[MAX_MAILBOX_PATH];
struct quota quota;
int r;
if (!root[0] || root[0] == '.' || strchr(root, '/')
|| strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) {
return IMAP_MAILBOX_BADNAME;
}
memset(&quota, 0, sizeof(struct quota));
quota.root = (char *) root;
mailbox_hash_quota(quota_path, root);
if ((quota.fd = open(quota_path, O_RDWR, 0)) != -1) {
/* Just lock and change it */
r = mailbox_lock_quota(&quota);
quota.limit = newquota;
if (!r) r = mailbox_write_quota(&quota);
if (quota.fd != -1) {
close(quota.fd);
}
return r;
}
/*
* Have to create a new quota root
*/
/* look for a top-level mailbox in the proposed quotaroot */
r = mboxlist_lookup(quota.root, NULL, NULL, NULL);
if (r) {
return r;
}
/* perhaps create .NEW, lock, check if it got recreated, move in place */
quota.lock_count = 1;
quota.used = 0;
quota.limit = newquota;
r = mailbox_write_quota(&quota);
if (r) {
return r;
}
strcpy(pattern, quota.root);
strcat(pattern, ".*");
/* top level mailbox */
mboxlist_changequota(quota.root, 0, 0, &quota);
/* submailboxes - we're using internal names here */
mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_changequota, &quota);
r = mailbox_write_quota(&quota);
if (quota.fd != -1) {
close(quota.fd);
}
return r;
}
/*
* Retrieve internal information, for reconstructing mailboxes file
*/
void mboxlist_getinternalstuff(const char **listfnamep,
const char **newlistfnamep,
const char **basep,
unsigned long * sizep)
{
printf("yikes! don't reconstruct me!\n");
abort();
}
/*
* ACL access canonicalization routine which ensures that 'owner'
* retains lookup, administer, and create rights over a mailbox.
*/
int mboxlist_ensureOwnerRights(rock, identifier, access)
void *rock;
const char *identifier;
int access;
{
char *owner = (char *)rock;
if (strcmp(identifier, owner) != 0) return access;
return access|ACL_LOOKUP|ACL_ADMIN|ACL_CREATE;
}
/*
* Helper function to change the quota root for 'name' to that pointed
* to by the static global struct pointer 'mboxlist_newquota'.
*/
static int mboxlist_changequota(const char *name, int matchlen, int maycreate,
void *rock)
{
int r;
struct mailbox mailbox;
struct quota *mboxlist_newquota = (struct quota *) rock;
assert(rock != NULL);
r = mailbox_open_header(name, 0, &mailbox);
if (r) goto error_noclose;
r = mailbox_lock_header(&mailbox);
if (r) goto error;
r = mailbox_open_index(&mailbox);
if (r) goto error;
r = mailbox_lock_index(&mailbox);
if (r) goto error;
if (mailbox.quota.root) {
if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) {
/* Part of a child quota root */
mailbox_close(&mailbox);
return 0;
}
r = mailbox_lock_quota(&mailbox.quota);
if (r) goto error;
if (mailbox.quota.used >= mailbox.quota_mailbox_used) {
mailbox.quota.used -= mailbox.quota_mailbox_used;
}
else {
mailbox.quota.used = 0;
}
r = mailbox_write_quota(&mailbox.quota);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record free of %u bytes in quota %s",
mailbox.quota_mailbox_used, mailbox.quota.root);
}
mailbox_unlock_quota(&mailbox.quota);
free(mailbox.quota.root);
}
mailbox.quota.root = xstrdup(mboxlist_newquota->root);
r = mailbox_write_header(&mailbox);
if (r) goto error;
mboxlist_newquota->used += mailbox.quota_mailbox_used;
mailbox_close(&mailbox);
return 0;
error:
mailbox_close(&mailbox);
error_noclose:
syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s",
name, mboxlist_newquota->root, error_message(r));
return 0;
}
void mboxlist_init(int myflags)
{
int r;
char dbdir[1024];
int flags = 0;
/* create the name of the db file */
strcpy(dbdir, config_dir);
strcat(dbdir, FNAME_DBDIR);
if (myflags & MBOXLIST_RECOVER) flags |= CYRUSDB_RECOVER;
r = DB->init(dbdir, flags);
if (r != CYRUSDB_OK) {
fatal("can't initialize mboxlist environment", EC_TEMPFAIL);
}
if (myflags & MBOXLIST_SYNC) {
r = DB->sync();
}
r = acap_init();
if (r != ACAP_OK) {
syslog(LOG_ERR,"acap_init failed()");
}
}
void mboxlist_open(char *fname)
{
int ret;
char *tofree = NULL;
/* create db file name */
if (!fname) {
fname = xmalloc(strlen(config_dir)+sizeof(FNAME_MBOXLIST));
tofree = fname;
strcpy(fname, config_dir);
strcat(fname, FNAME_MBOXLIST);
}
ret = DB->open(fname, &mbdb);
if (ret != 0) {
syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
cyrusdb_strerror(ret));
/* Exiting TEMPFAIL because Sendmail thinks this
EC_OSFILE == permanent failure. */
fatal("can't read mailboxes file", EC_TEMPFAIL);
}
if (tofree) free(tofree);
mboxlist_dbopen = 1;
}
void
mboxlist_close(void)
{
int r;
if (mboxlist_dbopen) {
r = DB->close(mbdb);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing mailboxes: %s",
cyrusdb_strerror(r));
}
mboxlist_dbopen = 0;
}
}
void mboxlist_done(void)
{
int r;
r = DB->done();
if (r) {
syslog(LOG_ERR, "DBERROR: error exiting application: %s",
cyrusdb_strerror(r));
}
/* finish ACAP API here */
}
/* hash the userid to a file containing the subscriptions for that user */
char *mboxlist_hash_usersubs(const char *userid)
{
char *fname = xmalloc(strlen(config_dir) + sizeof(FNAME_USERDIR) +
strlen(userid) + sizeof(FNAME_SUBSSUFFIX) + 10);
char c;
c = (char) dir_hash_c(userid);
sprintf(fname, "%s%s%c/%s%s", config_dir, FNAME_USERDIR, c, userid,
FNAME_SUBSSUFFIX);
return fname;
}
/*
* Open the subscription list for 'userid'.
*
* On success, returns zero.
* On failure, returns an error code.
*/
static int
mboxlist_opensubs(const char *userid,
struct db **ret)
{
int r = 0;
char *subsfname;
/* Build subscription list filename */
subsfname = mboxlist_hash_usersubs(userid);
r = SUBDB->open(subsfname, ret);
if (r != CYRUSDB_OK) {
r = IMAP_IOERROR;
}
free(subsfname);
return r;
}
/*
* Close a subscription file
*/
static void mboxlist_closesubs(struct db *sub)
{
SUBDB->close(sub);
}
/*
* Find subscribed mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox.
*/
int mboxlist_findsub(struct namespace *namespace __attribute__((unused)),
char *pattern, int isadmin, char *userid,
struct auth_state *auth_state,
int (*proc)(), void *rock, int force)
{
struct db *subs = NULL;
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen;
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = NULL;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = 1; /* user can always see their subs */
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = !force;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
/* open the subscription file that contains the mailboxes the
user is subscribed to */
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
goto done;
}
/* Build usermboxname */
if (userid && !strchr(userid, '.') &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
strcpy(usermboxname, "user.");
strcat(usermboxname, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
/* Check for INBOX first of all */
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 1, rock);
}
}
else if (!strncmp(pattern, usermboxname, usermboxnamelen) &&
GLOB_TEST(cbrock.g, usermboxname) != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(usermboxname, usermboxnamelen, 1, rock);
}
}
strcpy(usermboxname+usermboxnamelen, ".");
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
if (r) goto done;
/* Find fixed-string pattern prefix */
for (p = pattern; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?') break;
}
prefixlen = p - pattern;
*p = '\0';
/*
* If user.X.* or INBOX.* can match pattern,
* search for those mailboxes next
*/
if (userid &&
(!strncmp(usermboxname, pattern, usermboxnamelen-1) ||
!strncasecmp("inbox.", pattern, prefixlen < 6 ? prefixlen : 6))) {
if (!strncmp(usermboxname, pattern, usermboxnamelen-1)) {
cbrock.inboxoffset = 0;
}
else {
cbrock.inboxoffset = strlen(userid);
}
cbrock.find_namespace = NAMESPACE_INBOX;
/* iterate through prefixes matching usermboxname */
SUBDB->foreach(subs,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
/* search for all remaining mailboxes.
just bother looking at the ones that have the same pattern prefix. */
SUBDB->foreach(subs, pattern, prefixlen,
&find_p, &find_cb, &cbrock, NULL);
done:
if (subs) mboxlist_closesubs(subs);
glob_free(&cbrock.g);
return r;
}
int mboxlist_findsub_alt(struct namespace *namespace,
char *pattern, int isadmin, char *userid,
struct auth_state *auth_state,
int (*proc)(), void *rock, int force)
{
struct db *subs = NULL;
struct find_rock cbrock;
char usermboxname[MAX_MAILBOX_NAME+1], patbuf[MAX_MAILBOX_NAME+1];
int usermboxnamelen = 0;
const char *data;
int datalen;
int r = 0;
char *p;
int prefixlen, len;
cbrock.g = glob_init(pattern, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.namespace = namespace;
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.isadmin = 1; /* user can always see their subs */
cbrock.auth_state = auth_state;
cbrock.checkmboxlist = !force;
cbrock.checkshared = 0;
cbrock.proc = proc;
cbrock.procrock = rock;
/* open the subscription file that contains the mailboxes the
user is subscribed to */
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
goto done;
}
/* Build usermboxname */
if (userid && !strchr(userid, '.') &&
strlen(userid)+5 < MAX_MAILBOX_NAME) {
strcpy(usermboxname, "user.");
strcat(usermboxname, userid);
usermboxnamelen = strlen(usermboxname);
}
else {
userid = 0;
}
/* Check for INBOX first of all */
if (userid) {
if (GLOB_TEST(cbrock.g, "INBOX") != -1) {
r = SUBDB->fetch(subs, usermboxname, usermboxnamelen,
&data, &datalen, NULL);
if (!r && data) {
r = (*proc)(cbrock.inboxcase, 5, 0, rock);
}
}
strcpy(usermboxname+usermboxnamelen, ".");
usermboxnamelen++;
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
if (r) goto done;
glob_free(&cbrock.g);
/* Find fixed-string pattern prefix */
for (p = pattern; *p; p++) {
if (*p == '*' || *p == '%' || *p == '?') break;
}
prefixlen = p - pattern;
/*
* Personal (INBOX) namespace
*
* Append pattern to "INBOX.", search for those subscriptions next
*/
if (userid) {
strcpy(patbuf, "INBOX.");
strcat(patbuf, pattern);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY|GLOB_INBOXCASE);
cbrock.inboxcase = glob_inboxcase(cbrock.g);
cbrock.inboxoffset = strlen(userid);
cbrock.find_namespace = NAMESPACE_INBOX;
/* iterate through prefixes matching usermboxname */
SUBDB->foreach(subs,
usermboxname, usermboxnamelen,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
} else {
cbrock.usermboxname = NULL;
cbrock.usermboxnamelen = 0;
}
if (usermboxnamelen) {
usermboxname[--usermboxnamelen] = '\0';
cbrock.usermboxname = usermboxname;
cbrock.usermboxnamelen = usermboxnamelen;
}
/*
* Other Users namespace
*
* If "Other Users*" can match pattern, search for those subscriptions next
*/
len = strlen(namespace->prefix[NAMESPACE_USER])-1;
if (!strncmp(namespace->prefix[NAMESPACE_USER], pattern,
prefixlen < len ? prefixlen : len)) {
if (prefixlen < len)
cbrock.g = glob_init(pattern+prefixlen, GLOB_HIERARCHY);
else {
strcpy(patbuf, "user");
strcat(patbuf, pattern+len);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY);
}
cbrock.find_namespace = NAMESPACE_USER;
cbrock.inboxoffset = 0;
/* iterate through prefixes matching usermboxname */
SUBDB->foreach(subs,
"user", 4,
&find_p, &find_cb, &cbrock,
NULL);
glob_free(&cbrock.g);
}
/*
* Shared namespace
*
* search for all remaining subscriptions.
* just bother looking at the ones that have the same pattern prefix.
*/
len = strlen(namespace->prefix[NAMESPACE_SHARED]);
if (!strncmp(namespace->prefix[NAMESPACE_SHARED], pattern,
prefixlen < len - 1 ? prefixlen : len - 1)) {
cbrock.find_namespace = NAMESPACE_SHARED;
cbrock.inboxoffset = 0;
if (prefixlen < len) {
/* Find pattern which matches shared namespace prefix */
for (p = pattern+prefixlen; *p; p++) {
if (*p == '%') continue;
else if (*p == '.') p++;
break;
}
if (!*p) {
/* special case: LSUB "" % -- see if we have a shared mbox */
cbrock.g = glob_init("*", GLOB_HIERARCHY);
cbrock.checkshared = 1;
}
else {
cbrock.g = glob_init(p, GLOB_HIERARCHY);
}
SUBDB->foreach(subs,
"", 0,
&find_p, &find_cb, &cbrock,
NULL);
}
else if (pattern[len-1] == '.') {
strcpy(patbuf, "");
strcat(patbuf, pattern+len);
cbrock.g = glob_init(patbuf, GLOB_HIERARCHY);
pattern[prefixlen] = '\0';
SUBDB->foreach(subs,
pattern+len, prefixlen-len,
&find_p, &find_cb, &cbrock,
NULL);
}
}
done:
if (subs) mboxlist_closesubs(subs);
glob_free(&cbrock.g);
return r;
}
/*
* Change 'user's subscription status for mailbox 'name'.
* Subscribes if 'add' is nonzero, unsubscribes otherwise.
* if 'force' is set, force the subscription through even if
* we don't know about 'name'.
*/
int mboxlist_changesub(const char *name, const char *userid,
struct auth_state *auth_state, int add, int force)
{
int r;
char *acl;
struct db *subs;
if ((r = mboxlist_opensubs(userid, &subs)) != 0) {
return r;
}
if (add && !force) {
/* Ensure mailbox exists and can be either seen or read by user */
if ((r = mboxlist_lookup(name, NULL, &acl, NULL))!=0) {
mboxlist_closesubs(subs);
return r;
}
if ((cyrus_acl_myrights(auth_state, acl) & (ACL_READ|ACL_LOOKUP)) == 0) {
mboxlist_closesubs(subs);
return IMAP_MAILBOX_NONEXISTENT;
}
}
if (add) {
r = SUBDB->store(subs, name, strlen(name), "", 0, NULL);
} else {
r = SUBDB->delete(subs, name, strlen(name), NULL);
/* if it didn't exist, that's ok */
if (r == CYRUSDB_EXISTS) r = CYRUSDB_OK;
}
switch (r) {
case CYRUSDB_OK:
r = 0;
break;
default:
r = IMAP_IOERROR;
break;
}
mboxlist_closesubs(subs);
return r;
}
diff --git a/imap/seen_db.c b/imap/seen_db.c
index 1b67f39f1..69b47eabd 100644
--- a/imap/seen_db.c
+++ b/imap/seen_db.c
@@ -1,544 +1,545 @@
/* seen_db.c -- implementation of seen database using per-user berkeley db
- $Id: seen_db.c,v 1.24 2001/11/19 21:32:45 leg Exp $
+ $Id: seen_db.c,v 1.25 2002/01/18 22:58:48 rjs3 Exp $
* Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <config.h>
#include <stdlib.h>
#include <assert.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include "cyrusdb.h"
#include "map.h"
#include "bsearch.h"
#include "util.h"
#include "imapconf.h"
#include "xmalloc.h"
#include "mailbox.h"
#include "imap_err.h"
#define FNAME_SEENSUFFIX ".seen" /* per user seen state extension */
#define FNAME_SEEN "/cyrus.seen" /* for legacy seen state */
enum {
SEEN_VERSION = 1,
SEEN_DEBUG = 0
};
struct seen {
char *user; /* what user is this for? */
const char *uniqueid; /* what mailbox? */
const char *path; /* where is this mailbox? */
struct db *db;
struct txn *tid; /* outstanding txn, if any */
int converting;
};
static struct seen *lastseen = NULL;
/* choose "flat" or "db3" here */
#define DB (&cyrusdb_flat)
static void abortcurrent(struct seen *s)
{
if (s && s->tid) {
int r = DB->abort(s->db, s->tid);
if (r) {
syslog(LOG_ERR, "DBERROR: error aborting txn: %s",
cyrusdb_strerror(r));
}
s->tid = NULL;
}
}
static char *getpath(const char *userid)
{
char *fname = xmalloc(strlen(config_dir) + sizeof(FNAME_USERDIR) +
strlen(userid) + sizeof(FNAME_SEENSUFFIX) + 10);
char c;
c = (char) dir_hash_c(userid);
sprintf(fname, "%s%s%c/%s%s", config_dir, FNAME_USERDIR, c, userid,
FNAME_SEENSUFFIX);
return fname;
}
int seen_open(struct mailbox *mailbox,
const char *user,
struct seen **seendbptr)
{
struct seen *seendb;
char *fname = NULL;
int r;
/* try to reuse the last db handle */
seendb = lastseen;
lastseen = NULL;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_open(%s, %s)",
mailbox->uniqueid, user);
}
/* if this is the db we've already opened, return it */
if (seendb && !strcmp(seendb->user, user)) {
abortcurrent(seendb);
seendb->uniqueid = mailbox->uniqueid;
seendb->path = mailbox->path;
*seendbptr = seendb;
return 0;
}
*seendbptr = NULL;
/* otherwise, close the existing database */
if (seendb) {
abortcurrent(seendb);
r = DB->close(seendb->db);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing seendb: %s",
cyrusdb_strerror(r));
}
free(seendb->user);
} else {
/* create seendb */
seendb = (struct seen *) xmalloc(sizeof(struct seen));
}
/* open the seendb corresponding to user */
fname = getpath(user);
r = DB->open(fname, &seendb->db);
if (r != 0) {
syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
cyrusdb_strerror(r));
r = IMAP_IOERROR;
free(seendb);
free(fname);
return r;
}
syslog(LOG_DEBUG, "seen_db: user %s opened %s", user, fname);
free(fname);
seendb->tid = NULL;
seendb->uniqueid = mailbox->uniqueid;
seendb->path = mailbox->path;
seendb->user = xstrdup(user);
*seendbptr = seendb;
return r;
}
static int seen_readold(struct seen *seendb,
time_t *lastreadptr, unsigned int *lastuidptr,
time_t *lastchangeptr, char **seenuidsptr)
{
char fnamebuf[MAX_MAILBOX_PATH];
struct stat sbuf;
int fd;
const char *base;
const char *buf = 0, *p;
unsigned long len = 0, linelen;
unsigned long offset = 0;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_readold(%s, %s)",
seendb->path, seendb->user);
}
strcpy(fnamebuf, seendb->path);
strcat(fnamebuf, FNAME_SEEN);
fd = open(fnamebuf, O_RDWR, 0);
*lastreadptr = 0;
*lastuidptr = 0;
*lastchangeptr = 0;
if (fd == -1 && errno == ENOENT) {
/* no old-style seen file for this database */
*seenuidsptr = xstrdup("");
return 0;
} else if (fd == -1) {
syslog(LOG_ERR, "error opening '%s': %m", fnamebuf);
return IMAP_IOERROR;
}
if (fstat(fd, &sbuf) == -1) {
close(fd);
return IMAP_IOERROR;
}
map_refresh(fd, 1, &base, &len, sbuf.st_size, fnamebuf, 0);
/* Find record for user */
offset = bsearch_mem(seendb->user, 1, base, len, 0, &linelen);
if (!linelen) {
*seenuidsptr = xstrdup("");
close(fd);
return 0;
}
/* Skip over username we know is there */
buf = base + offset + strlen(seendb->user)+1;
*lastreadptr = strtol(buf, (char **) &p, 10); buf = p;
*lastuidptr = strtol(buf, (char **) &p, 10); buf = p;
*lastchangeptr = strtol(buf, (char **) &p, 10); buf = p;
while (isspace((int) *p)) p++;
buf = p;
/* Scan for end of uids */
while (p < base + offset + linelen && !isspace((int) *p)) p++;
*seenuidsptr = xmalloc(p - buf + 1);
strlcpy(*seenuidsptr, buf, p - buf);
(*seenuidsptr)[p - buf] = '\0';
map_free(&base, &len);
close(fd);
return 0;
}
static int seen_readit(struct seen *seendb,
time_t *lastreadptr, unsigned int *lastuidptr,
time_t *lastchangeptr, char **seenuidsptr,
int rw)
{
int r;
const char *data, *dstart, *dend;
char *p;
int datalen;
int version;
int uidlen;
assert(seendb && seendb->uniqueid);
if (rw) {
r = DB->fetchlock(seendb->db,
seendb->uniqueid, strlen(seendb->uniqueid),
&data, &datalen, &seendb->tid);
} else {
r = DB->fetch(seendb->db,
seendb->uniqueid, strlen(seendb->uniqueid),
&data, &datalen, NULL);
}
switch (r) {
case 0:
break;
case CYRUSDB_AGAIN:
syslog(LOG_DEBUG, "deadlock in seen database for '%s/%s'",
seendb->user, seendb->uniqueid);
return IMAP_AGAIN;
break;
case CYRUSDB_IOERROR:
- syslog(LOG_ERR, "DBERROR: error fetching txn", cyrusdb_strerror(r));
+ syslog(LOG_ERR, "DBERROR: error fetching txn %s",
+ cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
if (data == NULL) {
r = seen_readold(seendb, lastreadptr, lastuidptr,
lastchangeptr, seenuidsptr);
if (r) {
DB->abort(seendb->db, seendb->tid);
}
return r;
}
/* remember that 'data' may not be null terminated ! */
dstart = data;
dend = data + datalen;
version = strtol(data, &p, 10); data = p;
assert(version == SEEN_VERSION);
*lastreadptr = strtol(data, &p, 10); data = p;
*lastuidptr = strtol(data, &p, 10); data = p;
*lastchangeptr = strtol(data, &p, 10); data = p;
while (isspace((int) *p) && p < dend) p++; data = p;
uidlen = dend - data;
*seenuidsptr = xmalloc(uidlen + 1);
memcpy(*seenuidsptr, data, uidlen);
(*seenuidsptr)[uidlen] = '\0';
return 0;
}
int seen_read(struct seen *seendb,
time_t *lastreadptr, unsigned int *lastuidptr,
time_t *lastchangeptr, char **seenuidsptr)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_read(%s, %s)",
seendb->uniqueid, seendb->user);
}
return seen_readit(seendb, lastreadptr, lastuidptr, lastchangeptr,
seenuidsptr, 0);
}
int seen_lockread(struct seen *seendb,
time_t *lastreadptr, unsigned int *lastuidptr,
time_t *lastchangeptr, char **seenuidsptr)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_lockread(%s, %s)",
seendb->uniqueid, seendb->user);
}
return seen_readit(seendb, lastreadptr, lastuidptr, lastchangeptr,
seenuidsptr, 1);
}
int seen_write(struct seen *seendb, time_t lastread, unsigned int lastuid,
time_t lastchange, char *seenuids)
{
int sz = strlen(seenuids) + 50;
char *data = xmalloc(sz);
int datalen;
int r;
assert(seendb && seendb->uniqueid);
assert(seendb->tid);
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_write(%s, %s)",
seendb->uniqueid, seendb->user);
}
sprintf(data, "%d %d %d %d %s", SEEN_VERSION,
(int) lastread, lastuid, (int) lastchange, seenuids);
datalen = strlen(data);
r = DB->store(seendb->db, seendb->uniqueid, strlen(seendb->uniqueid),
data, datalen, &seendb->tid);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_IOERROR:
r = IMAP_AGAIN;
break;
default:
syslog(LOG_ERR, "DBERROR: error updating database: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
break;
}
free(data);
return r;
}
int seen_close(struct seen *seendb)
{
int r;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_close(%s, %s)",
seendb->uniqueid, seendb->user);
}
if (seendb->tid) {
r = DB->commit(seendb->db, seendb->tid);
if (r != CYRUSDB_OK) {
syslog(LOG_ERR, "DBERROR: error committing seen txn; "
"seen state lost: %s", cyrusdb_strerror(r));
DB->abort(seendb->db, seendb->tid);
}
seendb->tid = NULL;
}
seendb->uniqueid = NULL;
seendb->path = NULL;
if (lastseen) {
int r;
/* free the old database hanging around */
abortcurrent(lastseen);
r = DB->close(lastseen->db);
if (r != CYRUSDB_OK) {
syslog(LOG_ERR, "DBERROR: error closing lastseen: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
free(lastseen->user);
free(lastseen);
}
/* this database can now be reused */
lastseen = seendb;
return 0;
}
int seen_create_mailbox(struct mailbox *mailbox)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_create_mailbox(%s)",
mailbox->uniqueid);
}
/* noop */
return 0;
}
int seen_delete_mailbox(struct mailbox *mailbox)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_delete_mailbox(%s)",
mailbox->uniqueid);
}
/* noop */
return 0;
}
int seen_create_user(const char *user)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_create_user(%s)",
user);
}
/* we'll be lazy here and create this when needed */
return 0;
}
int seen_delete_user(const char *user)
{
char *fname = getpath(user);
int r = 0;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_delete_user(%s)",
user);
}
/* erp! */
r = unlink(fname);
if (r < 0) {
syslog(LOG_ERR, "error unlinking %s: %m", fname);
r = IMAP_IOERROR;
}
free(fname);
return r;
}
int seen_copy(struct mailbox *oldmailbox, struct mailbox *newmailbox)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_copy(%s, %s)",
oldmailbox->uniqueid, newmailbox->uniqueid);
}
/* noop */
return 0;
}
/* database better have been locked before this ! */
int seen_unlock(struct seen *seendb)
{
int r;
assert(seendb);
if (!seendb->tid) return 0;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_unlock(%s, %s)",
seendb->uniqueid, seendb->user);
}
r = DB->commit(seendb->db, seendb->tid);
if (r != CYRUSDB_OK) {
syslog(LOG_ERR, "DBERROR: error committing seen txn; "
"seen state lost: %s", cyrusdb_strerror(r));
DB->abort(seendb->db, seendb->tid);
}
seendb->tid = NULL;
return 0;
}
int seen_done(void)
{
int r = 0;
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_done()");
}
if (lastseen) {
abortcurrent(lastseen);
r = DB->close(lastseen->db);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing lastseen: %s",
cyrusdb_strerror(r));
r = IMAP_IOERROR;
}
free(lastseen->user);
free(lastseen);
}
return r;
}
int seen_reconstruct(struct mailbox *mailbox,
time_t report_time,
time_t prune_time,
int (*report_proc)(),
void *report_rock)
{
if (SEEN_DEBUG) {
syslog(LOG_DEBUG, "seen_db: seen_reconstruct()");
}
/* not supported */
return 0;
}
diff --git a/imap/squatter.c b/imap/squatter.c
index 75ade7665..44393e765 100644
--- a/imap/squatter.c
+++ b/imap/squatter.c
@@ -1,483 +1,484 @@
/* squatter.c -- SQUAT-based message indexing tool
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
- * $Id: squatter.c,v 1.4 2001/11/13 17:34:03 leg Exp $
+ * $Id: squatter.c,v 1.5 2002/01/18 22:58:48 rjs3 Exp $
*/
/*
This is the tool that creates SQUAT indexes for Cyrus mailboxes.
SQUAT index files are organised as follows:
There is (at most) one index file for each Cyrus mailbox, named
"cyrus.squat", stored in the mailbox directory.
Source documents are named 'xUID' where UID is the numeric UID of a
message and x is a character denoting a part of the message: 'f' ==
FROM, 't' == TO, 'b' == BCC, 'c' == CC, 's' == SUBJECT, 'h' == other
headers, 'm' == the body. So, a messge with UID 331 could give rise
to several source documents named "f331", "t331", "b331", "c331",
"s331", "h331" and "m331".
There is also a special source document named "validity.N" where N
is the validitity nonce for the mailbox. We use this to detect when
the UIDs have been renumbered since we created the index (in which
case the index is useless and is ignored).
This tool creates new indexes for one or more mailboxes. (We do not
support incremental updates to an index yet.) The index is created
in "cyrus.squat.tmp" and then, if creation was successful, it is
atomically renamed to "cyrus.squat". This guarantees that we don't
interfere with anyone who has the old index open.
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <com_err.h>
#include <syslog.h>
+#include <string.h>
#include "assert.h"
#include "mboxlist.h"
#include "imapconf.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "xmalloc.h"
#include "acl.h"
#include "seen.h"
#include "mboxname.h"
#include "map.h"
#include "squat.h"
#include "imapd.h"
extern char *optarg;
extern int optind;
/* Stuff to make index.c link */
int imapd_exists;
struct protstream *imapd_out = NULL;
struct auth_state *imapd_authstate = NULL;
char *imapd_userid = NULL;
void printastring(const char *s)
{
fatal("not implemented", EC_SOFTWARE);
}
/* end stuff to make index.c link */
/* These stats are gathered 1) per mailbox and 2) for the whole operation. */
typedef struct {
int indexed_bytes; /* How many bytes of processed message text
have we indexed? */
int indexed_messages; /* How many messages have we indexed? */
int index_size; /* How many bytes is the index using? */
time_t start_time; /* When did this operation start? */
time_t end_time; /* When did it end? */
} SquatStats;
static int verbose = 0;
static int mailbox_count = 0;
static SquatStats total_stats;
static void start_stats(SquatStats* stats) {
stats->index_size = 0;
stats->indexed_bytes = 0;
stats->indexed_messages = 0;
stats->start_time = time(NULL);
}
static void stop_stats(SquatStats* stats) {
stats->end_time = time(NULL);
}
static void print_stats(FILE* out, SquatStats* stats) {
fprintf(out, "Indexed %d messages (%d bytes) "
"into %d index bytes in %d seconds\n",
stats->indexed_messages, stats->indexed_bytes,
stats->index_size, (int) (stats->end_time - stats->start_time));
}
static void shut_down(int code) __attribute__((noreturn));
static void shut_down(int code)
{
seen_done();
mboxlist_close();
mboxlist_done();
exit(code);
}
static int usage(const char *name)
{
fprintf(stderr,
"usage: %s [-C <alt_config>] [-r] [-v] mailbox...\n", name);
exit(EC_USAGE);
}
void fatal(const char* s, int code)
{
fprintf(stderr, "squatter: %s\n", s);
exit(code);
}
static void fatal_syserror(const char* s)
{
perror(s);
exit(99);
}
static void fatal_squat_error(const char* s)
{
int err = squat_get_last_error();
switch (err) {
case SQUAT_ERR_OUT_OF_MEMORY:
fprintf(stderr, "SQUAT: Out of memory (%s)\n", s);
break;
case SQUAT_ERR_SYSERR:
perror(s);
break;
default:
/* There are other error codes, but they only apply for searching,
not index construction */
fprintf(stderr, "SQUAT: Unknown error %d (%s)\n", err, s);
}
exit(98);
}
typedef struct {
SquatStats* mailbox_stats;
SquatIndex* index;
struct mailbox* mailbox;
} SquatReceiverData;
/* Cyrus passes the text to index in here, after it has canonicalized
the text. We figure out what source document the text belongs to,
and update the index. */
static void search_text_receiver(int uid, int part, int cmd,
char const* text, int text_len, void* rock) {
SquatReceiverData* d = (SquatReceiverData*)rock;
if ((cmd & SEARCHINDEX_CMD_BEGINPART) != 0) {
char buf[100];
char part_char;
/* Figure out what the name of the source document is going to be. */
switch (part) {
case SEARCHINDEX_PART_FROM: part_char = 'f'; break;
case SEARCHINDEX_PART_TO: part_char = 't'; break;
case SEARCHINDEX_PART_CC: part_char = 'c'; break;
case SEARCHINDEX_PART_BCC: part_char = 'b'; break;
case SEARCHINDEX_PART_SUBJECT: part_char = 's'; break;
case SEARCHINDEX_PART_HEADERS: part_char = 'h'; break;
default:
assert(0);
case SEARCHINDEX_PART_BODY:
part_char = 'm';
d->mailbox_stats->indexed_messages++;
total_stats.indexed_messages++;
break;
}
sprintf(buf, "%c%d", part_char, uid);
/* don't index document parts that are going to be empty (or too
short to search) */
if ((cmd & SEARCHINDEX_CMD_ENDPART) != 0
&& ((cmd & SEARCHINDEX_CMD_APPENDPART) == 0
|| text_len < SQUAT_WORD_SIZE)) {
if (verbose > 2) {
printf("Skipping tiny document part '%s' (size %d)\n", buf,
(cmd & SEARCHINDEX_CMD_APPENDPART) == 0 ? 0 : text_len);
}
return;
}
if (verbose > 2) {
printf("Opening document part '%s'\n", buf);
}
if (squat_index_open_document(d->index, buf) != SQUAT_OK) {
fatal_squat_error("Writing index");
}
}
if ((cmd & SEARCHINDEX_CMD_APPENDPART) != 0) {
if (verbose > 3) {
printf("Writing %d bytes into message %d\n", text_len, uid);
}
if (squat_index_append_document(d->index, text, text_len) != SQUAT_OK) {
fatal_squat_error("Writing index data");
}
d->mailbox_stats->indexed_bytes += text_len;
total_stats.indexed_bytes += text_len;
}
if ((cmd & SEARCHINDEX_CMD_ENDPART) != 0) {
if (squat_index_close_document(d->index) != SQUAT_OK) {
fatal_squat_error("Writing index update");
}
}
}
/* Let SQUAT tell us what's going on in the expensive
squat_index_finish function. */
static void stats_callback(void* closure, SquatStatsEvent* params) {
switch (params->generic.type) {
case SQUAT_STATS_COMPLETED_INITIAL_CHAR:
if (verbose > 1) {
if (params->completed_initial_char.num_words > 0) {
printf("Processing index character %d, %d total words, "
"temp file size is %d\n",
params->completed_initial_char.completed_char,
params->completed_initial_char.num_words,
params->completed_initial_char.temp_file_size);
}
}
break;
default:
; /* do nothing */
}
}
/* This is called once for each mailbox we're told to index. */
static int index_me(char *name, int matchlen, int maycreate, void *rock) {
struct mailbox m;
int r;
SquatStats stats;
SquatReceiverData data;
char tmp_file_name[1000];
char index_file_name[1000];
int fd;
SquatOptions options;
struct stat index_file_info;
char uid_validity_buf[30];
data.mailbox_stats = &stats;
data.mailbox = &m;
/* First we have to jump through hoops to open the mailbox and its
Cyrus index. */
memset(&m, 0, sizeof(struct mailbox));
r = mailbox_open_header(name, 0, &m);
if (r) {
if (verbose) {
printf("error opening %s: %s\n", name, error_message(r));
}
return 1;
}
r = mailbox_open_index(&m);
if (!r) r = mailbox_lock_pop(&m);
if (r) {
if (verbose) {
printf("error locking index %s: %s\n", name, error_message(r));
}
mailbox_close(&m);
return 1;
}
syslog(LOG_INFO, "indexing mailbox %s... ", name);
if (verbose > 0) {
printf("Indexing mailbox %s... ", name);
}
snprintf(index_file_name, sizeof(index_file_name),
"%s%s", m.path, FNAME_SQUAT_INDEX);
snprintf(tmp_file_name, sizeof(tmp_file_name),
"%s%s.tmp", m.path, FNAME_SQUAT_INDEX);
if ((fd = open(tmp_file_name, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE))
< 0) {
fatal_syserror("Unable to create temporary index file");
}
options.option_mask = SQUAT_OPTION_TMP_PATH | SQUAT_OPTION_STATISTICS;
options.tmp_path = m.path;
options.stats_callback = stats_callback;
options.stats_callback_closure = NULL;
data.index = squat_index_init(fd, &options);
if (data.index == NULL) {
fatal_squat_error("Initializing index");
}
/* write an empty document at the beginning to record the validity
nonce */
sprintf(uid_validity_buf, "validity.%ld", m.uidvalidity);
if (squat_index_open_document(data.index, uid_validity_buf) != SQUAT_OK
|| squat_index_close_document(data.index) != SQUAT_OK) {
fatal_squat_error("Writing index");
}
start_stats(&stats);
mailbox_read_index_header(&m);
index_operatemailbox(&m);
index_getsearchtext(&m, search_text_receiver, &data);
index_closemailbox(&m);
mailbox_close(&m);
mailbox_count++;
if (squat_index_finish(data.index) != SQUAT_OK) {
fatal_squat_error("Closing index");
}
/* Check how big the resulting file is */
if (fstat(fd, &index_file_info) < 0) {
fatal_syserror("Unable to stat temporary index file");
}
stats.index_size = index_file_info.st_size;
total_stats.index_size += index_file_info.st_size;
if (close(fd) < 0) {
fatal_syserror("Unable to complete writing temporary index file");
}
/* OK, we successfully created the index under the temporary file name.
Let's rename it to make it the real index. */
if (rename(tmp_file_name, index_file_name) < 0) {
fatal_syserror("Unable to rename temporary index file");
}
stop_stats(&stats);
if (verbose > 0) {
print_stats(stdout, &stats);
}
return 0;
}
int main(int argc, char **argv)
{
int opt;
char *alt_config = NULL;
int rflag = 0;
int i;
char buf[MAX_MAILBOX_PATH];
struct namespace squat_namespace;
int r;
if(geteuid() == 0)
fatal("must run as the Cyrus user", EC_USAGE);
setbuf(stdout, NULL);
while ((opt = getopt(argc, argv, "C:rv")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'v': /* verbose */
verbose++;
break;
case 'r': /* recurse */
rflag = 1;
break;
default:
usage("squatter");
}
}
config_init(alt_config, "squatter");
syslog(LOG_NOTICE, "indexing mailboxes");
/* Set namespace -- force standard (internal) */
if ((r = mboxname_init_namespace(&squat_namespace, 1)) != 0) {
fatal(error_message(r), EC_CONFIG);
}
signals_set_shutdown(&shut_down);
signals_add_handlers();
mboxlist_init(0);
mboxlist_open(NULL);
mailbox_initialize();
start_stats(&total_stats);
if (optind == argc) {
if (rflag) {
fprintf(stderr, "please specify a mailbox to recurse from\n");
exit(EC_USAGE);
}
assert(!rflag);
strcpy(buf, "*");
(*squat_namespace.mboxlist_findall)(&squat_namespace, buf, 1,
0, 0, index_me, NULL);
}
for (i = optind; i < argc; i++) {
strlcpy(buf, argv[i], MAX_MAILBOX_NAME);
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal(&squat_namespace, buf);
index_me(buf, 0, 0, NULL);
if (rflag) {
strlcat(buf, ".*", MAX_MAILBOX_NAME);
(*squat_namespace.mboxlist_findall)(&squat_namespace, buf, 1,
0, 0, index_me, NULL);
}
}
if (verbose > 0 && mailbox_count > 1) {
stop_stats(&total_stats);
printf("Total over all mailboxes: ");
print_stats(stdout, &total_stats);
}
syslog(LOG_NOTICE, "done indexing mailboxes");
shut_down(0);
}
diff --git a/imap/user.c b/imap/user.c
index 968779e58..2e1180dde 100644
--- a/imap/user.c
+++ b/imap/user.c
@@ -1,260 +1,262 @@
/* user.c -- User manipulation routines
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/*
- * $Id: user.c,v 1.6 2002/01/08 21:15:38 leg Exp $
+ * $Id: user.c,v 1.7 2002/01/18 22:58:48 rjs3 Exp $
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "imapconf.h"
#include "user.h"
#include "mboxlist.h"
#include "mailbox.h"
#include "util.h"
+#include "seen.h"
static int user_deleteacl(char *name, int matchlen, int maycreate, void* rock)
{
char *ident = (char *) rock;
int r;
char *acl;
char *rights, *nextid;
r = mboxlist_lookup(name, (char **)0, &acl, NULL);
while (!r && acl) {
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
nextid = strchr(rights, '\t');
if (!nextid) break;
*nextid++ = '\0';
if (!strcmp(acl, ident)) {
/* delete ACL for ident */
if (!r) mboxlist_setacl(name, ident, (char *)0,
1, ident, NULL);
}
acl = nextid;
}
return 0;
}
int user_delete(char *user, char *userid, struct auth_state *authstate)
{
char *fname;
char pat[] = "*";
/* delete seen state */
seen_delete_user(user);
/* delete subscriptions */
fname = mboxlist_hash_usersubs(user);
(void) unlink(fname);
free(fname);
/* delete quotas */
user_deletequotas(user);
#if 0
/* deleting all references to the user is too slow right now */
/* delete ACLs - we're using the internal names here */
mboxlist_findall(NULL, pat, 1, userid, authstate, user_deleteacl, user);
#endif
return 0;
}
#if 0
static int user_renameacl(char *name, int matchlen, int maycreate, void* rock)
{
char **ident = (char **) rock;
int r;
char *acl;
char *rights, *nextid;
r = mboxlist_lookup(name, (char **)0, &acl, NULL);
while (!r && acl) {
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
nextid = strchr(rights, '\t');
if (!nextid) break;
*nextid++ = '\0';
if (!strcmp(acl, ident[0])) {
/* copy ACL for old ident to new ident */
r = mboxlist_setacl(name, ident[1], rights, 1, ident[1], NULL);
/* delete ACL for old ident */
if (!r) mboxlist_setacl(name, ident[0], (char *)0,
1, ident[1], NULL);
}
acl = nextid;
}
return 0;
}
static int user_renamesub(char *name, int matchlen, int maycreate, void* rock)
{
char **ident = (char **) rock;
char newname[MAX_MAILBOX_NAME+1];
/* unsubscribe from old folder */
mboxlist_changesub(name, ident[1], NULL, 0, 1);
/* subscribe to new folder */
sprintf(newname, "user.%s.%s", ident[1], name+6+strlen(ident[0]));
mboxlist_changesub(newname, ident[1], NULL, 1, 1);
return 0;
}
int user_rename(char *oldmailboxname, char *newmailboxname,
char *userid, struct auth_state *authstate)
{
char *ident[] = { oldmailboxname+5, newmailboxname+5 };
char pat[MAX_MAILBOX_NAME];
char *oldfname, *newfname;
int r = 0;
/* change ACLs - we're using the internal names here */
strcpy(pat, "*");
mboxlist_findall(NULL, pat, 1, userid, authstate, user_renameacl, ident);
/* rename/change subscriptions */
oldfname = mboxlist_hash_usersubs(ident[0]);
newfname = mboxlist_hash_usersubs(ident[1]);
unlink(newfname);
r = mailbox_copyfile(oldfname, newfname);
if (!r) {
unlink(oldfname);
sprintf(pat, "%s.*", oldmailboxname);
/* we're using internal names here */
mboxlist_findsub(NULL, pat, 1, ident[1], authstate, user_renamesub,
ident, 1);
}
free(oldfname);
free(newfname);
/* rename seendb */
seen_rename_user(ident[0], ident[1]);
/* set quota on INBOX */
user_copyquota(oldmailboxname, newmailboxname);
}
int user_copyquota(char *oldname, char *newname)
{
int r;
struct quota quota;
char buf[MAX_MAILBOX_PATH];
quota.root = oldname;
quota.fd = -1;
mailbox_hash_quota(buf, quota.root);
quota.fd = open(buf, O_RDWR, 0);
if (quota.fd > 0) {
r = mailbox_read_quota(&quota);
if (!r) mboxlist_setquota(newname, quota.limit);
}
}
#endif
int user_deletequotas(const char *user)
{
char c, qpath[MAX_MAILBOX_NAME], *tail;
DIR *dirp;
struct dirent *f;
/* this violates the quota abstraction layer; oh well */
c = dir_hash_c(user);
sprintf(qpath, "%s%s%c/", config_dir, FNAME_QUOTADIR, c);
tail = qpath + strlen(qpath);
dirp = opendir(qpath);
if (dirp) {
while ((f = readdir(dirp)) != NULL) {
if (!strncmp(f->d_name, "user.", 5) &&
!strncmp(f->d_name+5, user, strlen(user)) &&
(f->d_name[5+strlen(user)] == '\0'||
f->d_name[5+strlen(user)] == '.')) {
strcpy(tail, f->d_name);
unlink(qpath);
}
}
closedir(dirp);
}
return 0;
}
diff --git a/sieve/tree.h b/sieve/tree.h
index d5d1f4db6..cc7645a37 100644
--- a/sieve/tree.h
+++ b/sieve/tree.h
@@ -1,131 +1,133 @@
/* tree.h -- abstract syntax tree
* Larry Greenfield
- * $Id: tree.h,v 1.3 2000/02/03 06:51:11 tmartin Exp $
+ * $Id: tree.h,v 1.4 2002/01/18 22:58:49 rjs3 Exp $
*/
/***********************************************************
Copyright 1999 by Carnegie Mellon University
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Carnegie Mellon
University not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
******************************************************************/
#ifndef TREE_H
#define TREE_H
#include "comparator.h"
/* abstract syntax tree for sieve */
typedef struct Stringlist stringlist_t;
typedef struct Patternlist patternlist_t;
typedef struct Commandlist commandlist_t;
typedef struct Test test_t;
typedef struct Testlist testlist_t;
typedef struct Tag tag_t;
typedef struct Taglist taglist_t;
struct Stringlist {
char *s;
stringlist_t *next;
};
struct Patternlist {
void *p;
patternlist_t *next;
};
struct Tag {
int type;
char *arg;
};
struct Taglist {
tag_t *t;
taglist_t *next;
};
struct Test {
int type;
union {
testlist_t *tl; /* anyof, allof */
stringlist_t *sl; /* exists */
struct { /* it's a header test */
int comptag;
comparator_t *comp;
stringlist_t *sl;
patternlist_t *pl;
} h;
struct { /* it's an address or envelope test */
int comptag;
comparator_t *comp;
stringlist_t *sl;
patternlist_t *pl;
int addrpart;
} ae;
test_t *t; /* not */
struct { /* size */
int t; /* tag */
int n; /* param */
} sz;
} u;
};
struct Testlist {
test_t *t;
testlist_t *next;
};
struct Commandlist {
int type;
union {
char *str;
stringlist_t *sl; /* the parameters */
struct { /* it's an if statement */
test_t *t;
commandlist_t *do_then;
commandlist_t *do_else;
} i;
struct { /* it's a vacation action */
char *subject;
int days;
stringlist_t *addresses;
char *message;
int mime;
} v;
struct { /* it's a notify action */
char *priority;
char *message;
stringlist_t *headers_list;
} n;
} u;
struct Commandlist *next;
};
stringlist_t *new_sl(char *s, stringlist_t *n);
+patternlist_t *new_pl(void *pat, patternlist_t *n);
tag_t *new_tag(int type, char *s);
taglist_t *new_taglist(tag_t *t, taglist_t *n);
test_t *new_test(int type);
testlist_t *new_testlist(test_t *t, testlist_t *n);
commandlist_t *new_command(int type);
commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n);
void free_sl(stringlist_t *sl);
+void free_pl(patternlist_t *pl, int comptag);
void free_test(test_t *t);
void free_tree(commandlist_t *cl);
#endif
diff --git a/timsieved/parser.c b/timsieved/parser.c
index 02f82ca4c..d9bd83004 100644
--- a/timsieved/parser.c
+++ b/timsieved/parser.c
@@ -1,635 +1,637 @@
/* parser.c -- parser used by timsieved
* Tim Martin
* 9/21/99
- * $Id: parser.c,v 1.12 2001/11/27 02:25:06 ken3 Exp $
+ * $Id: parser.c,v 1.13 2002/01/18 22:58:51 rjs3 Exp $
*/
/*
* Copyright (c) 1999-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#include "xmalloc.h"
#include "prot.h"
#include "tls.h"
#include "lex.h"
#include "actions.h"
#include "exitcodes.h"
extern sasl_conn_t *sieved_saslconn; /* the sasl connection context */
extern char sieved_clienthost[250];
int authenticated = 0;
int verify_only = 0;
int starttls_done = 0;
#ifdef HAVE_SSL
/* our tls connection, if any */
static SSL *tls_conn = NULL;
#endif /* HAVE_SSL */
+/* from elsewhere */
+void fatal(const char *s, int code);
/* forward declarations */
static int cmd_logout(struct protstream *sieved_out, struct protstream *sieved_in);
static int cmd_authenticate(struct protstream *sieved_out, struct protstream *sieved_in,
mystring_t *mechanism_name, mystring_t *initial_challenge, const char **errmsg);
static int cmd_starttls(struct protstream *sieved_out, struct protstream *sieved_in);
int parser(struct protstream *sieved_out, struct protstream *sieved_in)
{
int token;
const char *error_msg = "Generic Error";
mystring_t *mechanism_name = NULL;
mystring_t *initial_challenge = NULL;
mystring_t *sieve_name = NULL;
mystring_t *sieve_data = NULL;
unsigned long num;
/* get one token from the lexer */
token = timlex(NULL, NULL, sieved_in);
if (!authenticated && (token > 255) && (token!=AUTHENTICATE) &&
(token!=LOGOUT) && (token!=CAPABILITY) &&
(!tls_enabled("sieve") || (token!=STARTTLS)))
{
error_msg = "Authenticate first";
if (token!=EOL)
lex_setrecovering();
goto error;
}
if (verify_only && (token > 255) && (token!=PUTSCRIPT) && (token!=LOGOUT))
{
error_msg = "Script verification only";
if (token!=EOL)
lex_setrecovering();
goto error;
}
switch (token)
{
case AUTHENTICATE:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after AUTHENTICATE";
goto error;
}
if (timlex(&mechanism_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify mechanism name";
goto error;
}
token = timlex(NULL, NULL, sieved_in);
if (token != EOL)
{
/* optional client first challenge */
if (token!=SPACE)
{
error_msg = "Expected SPACE";
goto error;
}
if (timlex(&initial_challenge, NULL, sieved_in)!=STRING)
{
error_msg = "Expected string";
goto error;
}
token = timlex(NULL, NULL, sieved_in);
}
if (token != EOL)
{
error_msg = "Expected EOL";
goto error;
}
if (authenticated)
prot_printf(sieved_out, "NO \"Already authenticated\"\r\n");
else if (cmd_authenticate(sieved_out, sieved_in, mechanism_name, initial_challenge, &error_msg)==FALSE)
{
/* free memory */
free(mechanism_name);
free(initial_challenge);
prot_printf(sieved_out, "NO (\"SASL\" \"%s\") \"Authentication error\"\r\n",error_msg);
prot_flush(sieved_out);
return -1;
}
break;
case CAPABILITY:
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
capabilities(sieved_out, sieved_saslconn);
break;
case HAVESPACE:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after PUTSCRIPT";
goto error;
}
if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script name";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "Expected SPACE";
goto error;
}
if (timlex(NULL, &num, sieved_in)!=NUMBER)
{
error_msg = "Expected Number";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
cmd_havespace(sieved_out, sieve_name, num);
break;
case LOGOUT:
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Garbage after logout command";
goto error;
}
cmd_logout(sieved_out, sieved_in);
return TRUE; /* *do* close the connection */
break;
case GETSCRIPT:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after GETSCRIPT";
goto error;
}
if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script name";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
getscript(sieved_out, sieve_name);
break;
case PUTSCRIPT:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after PUTSCRIPT";
goto error;
}
if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script name";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "Expected SPACE";
goto error;
}
if (timlex(&sieve_data, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script data";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
putscript(sieved_out, sieve_name, sieve_data, verify_only);
break;
case SETACTIVE:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after SETACTIVE";
goto error;
}
if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script name";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
setactive(sieved_out, sieve_name);
break;
case DELETESCRIPT:
if (timlex(NULL, NULL, sieved_in)!=SPACE)
{
error_msg = "SPACE must occur after DELETESCRIPT";
goto error;
}
if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
{
error_msg = "Did not specify script name";
goto error;
}
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
deletescript(sieved_out, sieve_name);
break;
case LISTSCRIPTS:
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
listscripts(sieved_out);
break;
case STARTTLS:
if (timlex(NULL, NULL, sieved_in)!=EOL)
{
error_msg = "Expected EOL";
goto error;
}
cmd_starttls(sieved_out, sieved_in);
break;
default:
error_msg="Expected a command. Got something else";
goto error;
break;
}
/* free memory */
free(mechanism_name);
free(initial_challenge);
free(sieve_name);
free(sieve_data);
prot_flush(sieved_out);
return 0;
error:
/* free memory */
free(mechanism_name);
free(initial_challenge);
free(sieve_name);
free(sieve_data);
prot_printf(sieved_out, "NO \"%s\"\r\n",error_msg);
prot_flush(sieved_out);
return -1;
}
static int cmd_logout(struct protstream *sieved_out, struct protstream *sieved_in)
{
prot_printf(sieved_out, "Ok \"Logout Complete\"\r\n");
prot_flush(sieved_out);
prot_free(sieved_out);
exit(0);
}
static int cmd_authenticate(struct protstream *sieved_out, struct protstream *sieved_in,
mystring_t *mechanism_name, mystring_t *initial_challenge,
const char **errmsg)
{
int sasl_result;
char *mech = string_DATAPTR(mechanism_name);
mystring_t *clientinstr=NULL;
char *clientin = NULL;
unsigned int clientinlen = 0;
const char *serverout=NULL;
unsigned int serveroutlen;
const char *errstr=NULL;
const char *username;
clientinstr = initial_challenge;
if (clientinstr!=NULL)
{
clientin=(char *) malloc(clientinstr->len*2);
if (clientinstr->len) {
sasl_result=sasl_decode64(string_DATAPTR(clientinstr),
clientinstr->len,
clientin, clientinstr->len*2,
&clientinlen);
} else {
clientinlen = 0;
sasl_result = SASL_OK;
}
if (sasl_result!=SASL_OK)
{
*errmsg="error base64 decoding string";
syslog(LOG_NOTICE, "badlogin: %s %s %s",
sieved_clienthost, mech, "error base64 decoding string");
return FALSE;
}
}
sasl_result = sasl_server_start(sieved_saslconn, mech,
clientin, clientinlen,
&serverout, &serveroutlen);
while (sasl_result==SASL_CONTINUE)
{
int token1;
int token2;
mystring_t *str, *blahstr;
char *inbase64;
unsigned int inbase64len;
/* convert to base64 */
inbase64 = malloc( serveroutlen*2+1);
sasl_encode64(serverout, serveroutlen,
inbase64, serveroutlen*2+1, &inbase64len);
/* send out the string always as a literal */
prot_printf(sieved_out, "{%d}\r\n",inbase64len);
prot_write(sieved_out,inbase64,inbase64len);
prot_printf(sieved_out,"\r\n");
token1 = timlex(&str, NULL, sieved_in);
if (token1==STRING)
{
clientin=(char *) malloc(str->len*2);
sasl_result=sasl_decode64(string_DATAPTR(str), str->len,
clientin, str->len*2, &clientinlen);
if (sasl_result!=SASL_OK)
{
*errmsg="error base64 decoding string";
syslog(LOG_NOTICE, "badlogin: %s %s %s",
sieved_clienthost, mech, "error base64 decoding string");
return FALSE;
}
} else {
*errmsg="Expected STRING-xxx1";
return FALSE;
}
token2 = timlex(&blahstr, NULL, sieved_in);
/* we want to see a STRING followed by EOL */
if ((token1==STRING) && (token2==EOL))
{
sasl_result = sasl_server_step(sieved_saslconn,
clientin,
clientinlen,
&serverout, &serveroutlen);
} else {
*errmsg = "expected a STRING followed by an EOL";
syslog(LOG_NOTICE, "badlogin: %s %s %s",
sieved_clienthost, mech, "expected string");
return FALSE;
}
}
if (sasl_result!=SASL_OK)
{
/* convert to user error code */
if(sasl_result == SASL_NOUSER)
sasl_result = SASL_BADAUTH;
*errmsg = (const char *) sasl_errstring(sasl_result,NULL,NULL);
if (errstr!=NULL) {
syslog(LOG_NOTICE, "badlogin: %s %s %d %s",
sieved_clienthost, mech, sasl_result, errstr);
} else {
syslog(LOG_NOTICE, "badlogin: %s %s %s",
sieved_clienthost, mech,
sasl_errstring(sasl_result, NULL, NULL));
}
return FALSE;
}
/* get the userid from SASL */
sasl_result=sasl_getprop(sieved_saslconn, SASL_USERNAME,
(const void **) &username);
if (sasl_result!=SASL_OK)
{
*errmsg = "Internal SASL error";
syslog(LOG_ERR, "SASL: sasl_getprop SASL_USERNAME: %s",
sasl_errstring(sasl_result, NULL, NULL));
return FALSE;
}
verify_only = !strcmp(username, "anonymous");
if (!verify_only) {
if (actions_setuser(username) != TIMSIEVE_OK)
{
*errmsg = "internal error";
syslog(LOG_ERR, "error in actions_setuser()");
return FALSE;
}
}
/* Yay! authenticated */
prot_printf(sieved_out, "OK\r\n");
syslog(LOG_NOTICE, "login: %s %s %s%s %s", sieved_clienthost, username,
mech, starttls_done ? "+TLS" : "", "User logged in");
authenticated = 1;
/* free memory */
return TRUE;
}
#ifdef HAVE_SSL
static int cmd_starttls(struct protstream *sieved_out, struct protstream *sieved_in)
{
int result;
int *layerp;
char *auth_id;
sasl_ssf_t ssf;
/* SASL and openssl have different ideas about whether ssf is signed */
layerp = (int *) &ssf;
if (starttls_done == 1)
{
prot_printf(sieved_out, "NO \"TLS already active\"\r\n");
return TIMSIEVE_FAIL;
}
result=tls_init_serverengine("sieve",
5, /* depth to verify */
1, /* can client auth? */
0, /* require client to auth? */
1); /* TLS only? */
if (result == -1) {
syslog(LOG_ERR, "error initializing TLS");
prot_printf(sieved_out, "NO \"Error initializing TLS\"\r\n");
return TIMSIEVE_FAIL;
}
prot_printf(sieved_out, "OK \"Begin TLS negotiation now\"\r\n");
/* must flush our buffers before starting tls */
prot_flush(sieved_out);
result=tls_start_servertls(0, /* read */
1, /* write */
layerp,
&auth_id,
&tls_conn);
/* if error */
if (result==-1) {
prot_printf(sieved_out, "NO \"Starttls failed\"\r\n");
syslog(LOG_NOTICE, "STARTTLS failed: %s", sieved_clienthost);
return TIMSIEVE_FAIL;
}
/* tell SASL about the negotiated layer */
result = sasl_setprop(sieved_saslconn, SASL_SSF_EXTERNAL, &ssf);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
result = sasl_setprop(sieved_saslconn, SASL_AUTH_EXTERNAL, auth_id);
if (result != SASL_OK) {
fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
}
/* tell the prot layer about our new layers */
prot_settls(sieved_in, tls_conn);
prot_settls(sieved_out, tls_conn);
starttls_done = 1;
return capabilities(sieved_out, sieved_saslconn);
}
#else
static int cmd_starttls(struct protstream *sieved_out, struct protstream *sieved_in)
{
fatal("cmd_starttls() called, but no OpenSSL", EC_SOFTWARE);
}
#endif /* HAVE_SSL */

File Metadata

Mime Type
text/x-diff
Expires
Sat, Apr 4, 9:50 AM (3 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823542
Default Alt Text
(194 KB)

Event Timeline