Created
May 22, 2010 23:30
-
-
Save stas/410459 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- alpine-1.0+dfsg.orig/imap/src/c-client/mail.h | |
+++ alpine-1.0+dfsg/imap/src/c-client/mail.h | |
@@ -857,6 +857,7 @@ | |
unsigned int spare7 : 1; /* seventh spare bit */ | |
unsigned int spare8 : 1; /* eighth spare bit */ | |
void *sparep; /* spare pointer */ | |
+ char *maildirp; /* for the Maildir driver */ | |
unsigned long user_flags; /* user-assignable flags */ | |
} MESSAGECACHE; | |
--- alpine-1.0+dfsg.orig/imap/src/osdep/unix/dummy.c | |
+++ alpine-1.0+dfsg/imap/src/osdep/unix/dummy.c | |
@@ -433,7 +433,10 @@ | |
} | |
d = NIL; /* don't \NoSelect dir if it has a driver */ | |
if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) && | |
- (d != &dummydriver)) attributes &= ~LATT_NOSELECT; | |
+ (d != &dummydriver)) { | |
+ attributes &= ~LATT_NOSELECT; | |
+ attributes |= LATT_NOINFERIORS; | |
+ } | |
if (!contents || /* notify main program */ | |
(!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) && | |
(s = mailboxfile (tmp,name)) && | |
@@ -441,7 +444,7 @@ | |
!stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) && | |
SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size))) | |
mm_list (stream,delimiter,name,attributes); | |
- return T; | |
+ return (attributes & LATT_NOINFERIORS) ? NIL : T; | |
} | |
/* Dummy create mailbox | |
--- /dev/null | |
+++ alpine-1.0+dfsg/imap/src/osdep/unix/maildir.c | |
@@ -0,0 +1,2105 @@ | |
+/* maildir.c | |
+ * c-client Maildir driver | |
+ * (** intended only for use with PINE, not for UW-IMAP!! **) | |
+ * | |
+ * Copyright (C) 2004 Glue Logic LLC All rights reserved code()gluelogic.com | |
+ * Portions copyright University of Washington. | |
+ * License: essentially the _modified_ BSD license or X11 license | |
+ * (See http://www.gnu.org/licenses/license-list.html) | |
+ * | |
+ * 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 of the author may not be used to endorse or promote products | |
+ * derived from this software without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ * | |
+ * 2004.02.04 gps-0.01 Glenn Strauss code()gluelogic.com | |
+ * ==Big thanks to Brian Fisk and danah boyd for alpha testing | |
+ * - Major overhaul of maildir patch; largely rewritten | |
+ * - Added support to save message to maildir (old code crashed and burned) | |
+ * - Added bounds checking (old code had precious little) | |
+ * - Ripped out ass-backwards UID code. | |
+ * Temporarily use device inode number for UID (which violates the strict | |
+ * ascending IMAP UID requirement, and inodes can be reused, i.e. they might | |
+ * not be unique across sessions if a file is deleted and the inode reused) | |
+ * NOTE: this means that this code should not be used to add Maildir support | |
+ * to UW-IMAPd. This patch is only intended for adding Maildir support to | |
+ * PINE. This sysadmin prefers other IMAP servers to UW-IMAPd, primarily | |
+ * because some others support Maildir natively and prioritize security over | |
+ * compatibility. | |
+ * - Much better memory resource usage | |
+ * - Concurrency checks and updates for shared mailboxes | |
+ * - Much better unique filenames and compliance with Maildir protocol | |
+ * Also, makes effort to transparently preserve unrecognized flags | |
+ * (and is able to do so with flag updates and copies between maildirs, | |
+ * but is unable to do so for maildir appends since that info is not avail) | |
+ * - Workaround for Pine bug not checking dtb->status before using it | |
+ * (Pine should check that mailbox driver defines status routine, | |
+ * or should fall back to mail_status_default() automatically) | |
+ * 2004.10.04 gps-0.02 Glenn Strauss code()gluelogic.com | |
+ * - Fix NULL pointer dereference (crashing bug) in seldom used codepath | |
+ * (reported by Brian Fisk) | |
+ * - Updated patch offsets and such to work with PINE 4.61 | |
+ * 2004.11.15 gps-0.03 Glenn Strauss code()gluelogic.com | |
+ * - Workaround mail_fetch_structure() not checking return value from | |
+ * maildir_fetchtext(), resulting in a crash when a message is removed out | |
+ * from under PINE while in folder view, and then user goes to read message. | |
+ * (reported by Eduardo Chappa) | |
+ * 2004.11.20 gps-0.04 Glenn Strauss code()gluelogic.com | |
+ * - Workaround mail_fetch_structure() not checking return value from | |
+ * maildir_fetchtext() (2nd try) by rescanning message cache for message. | |
+ * - Rescan message cache and recover if message body fetch or flag update | |
+ * fails, and the cause was that message flags were externally modified. | |
+ * - Silence warning when only flags modified in externally modified folder | |
+ * - Silence all "expunged" messages when reloading externally modified folder | |
+ * (Directly hack into internal PINE client (PER_STREAM_S *)->expunge_count) | |
+ * - Created minimal maildir_list() to support inbox-path = #md/inbox | |
+ * (all reported by Eduardo Chappa) | |
+ * | |
+ * Notes: | |
+ * | |
+ * - YMMV: my compile line for Pine 4.61 under RedHat Fedora Core 1, | |
+ * with Pine defaulting to Maildir when creating mailboxes: | |
+ * ./build lrh DEBUG="-g -O2" NOLDAP \ | |
+ * EXTRASPECIALS="CREATEPROTO=maildirproto EMPTYPROTO=maildirproto" | |
+ * - I do not know why the original maildir patch modified | |
+ * pine4.61/imap/src/osdep/unix/dummy.c | |
+ * pine4.61/pine/mailcmd.c | |
+ * Those modifications are copied from the original maildir patch | |
+ * - Maildir protocol specifies the use of link()/unlink() when creating files | |
+ * in Maildir subdirectories (tmp/ new/ cur/). This implementation uses | |
+ * link()/unlink() when copying files between Maildirs. It uses rename() | |
+ * when updating Maildir flags in the message filename and when moving files | |
+ * between new/ and cur/. Files in new/ and cur/ should be unique according | |
+ * to the Maildir spec, and therefore, if the target file exists, it must have | |
+ * been the result of a failed rename() or link()/unlink() interrupted | |
+ * mid-stream that left files in both places. Therefore, the target should be | |
+ * removed and replaced. rename() will replace an existing file, whereas | |
+ * link() will fail, which is not something that a remote user would be able | |
+ * to fix manually (e.g. client to imapd). (N.B. rename() is not guaranteed | |
+ * atomic on all platforms and the POSIX spec makes no such requirement. | |
+ * And the link()/unlink() combination is, of course, not atomic.) | |
+ * - While this driver supports the Courier Maildir+ extension ",S=..." in | |
+ * filenames, and even adds it where missing, this driver does not verify that | |
+ * the size is correct. This driver does not use the size information from | |
+ * the filename other than to provide a hint about the size to the display. | |
+ * The actual size is determined when the message is read, which is necessary | |
+ * for displaying the From, To, and Subject in the summary listing. | |
+ * - When appending messages to a mailbox, PINE does not provide an interface to | |
+ * the original envelope, so the mtime set on the Maildir message file (used | |
+ * for message arrival time -- time message was delivered locally) is lost | |
+ * and the date in the message headers is used. This limitation is present | |
+ * in all PINE mailbox drivers. | |
+ * - When stream->silent is set, the higher levels of the program are not | |
+ * notified during some callbacks to mid-level APIs and higher level state | |
+ * counters will not be updated. Therefore, this driver makes an attempt not | |
+ * to make any such callbacks when stream->silent is set. This may result in | |
+ * extra (unnecessary) directory scans because the last scantime is not | |
+ * updated, even though it may have been modified by a known action. (It is | |
+ * not modified because other actions should occur whenever last scantime is | |
+ * updated and these actions are skipped to avoid callbacks.) | |
+ * | |
+ * Future possibile optimizations and enhancements: | |
+ * | |
+ * - Add pattern support to maildir_list() and maildir_lsub() | |
+ * (For examples, see mh.c driver) | |
+ * - Consider adding support for custom flags (elt->user_flags?) | |
+ * - Consider implementing maildirquota (Maildir+ from Courier) in | |
+ * maildir_append_msg() and maildir_move_new() | |
+ * - Consider using mmap() on files, when available | |
+ * - Consider blocking Ctrl-C (and/or other signals) in certain places | |
+ * (using MM_CRITICAL() and MM_NOCRITICAL() might take care of this for us) | |
+ * - Look to see if it is possible to avoid the wasteful CRLF translations, | |
+ * maybe by modifying maildir_fetch_msg_core() to directly fill out other | |
+ * elt->private.msg structures. Look into driver DR_CRLF flag. | |
+ * - UID support to enable use of Maildir with UW-IMAPd | |
+ * (and then remove DR_NOSTICKY driver flag) | |
+ * maybe store message filename => UID mapping in a read-only file that is | |
+ * appended to, and locked and cleaned out occasionally. | |
+ * (a la Dovecot's <Maildir>/dovecot-uidlist) | |
+ * (maybe change cur/ directory to temporarily add sticky bit as a lock, and | |
+ * to remove it when done, or others will remove it in an hour. The cur/ | |
+ * directory is stat'd before scans of the directory, so such a lock could | |
+ * be checked without additional syscall overhead and without file locking) | |
+ * Whatever mechanism is chosen, the PINE client should not suffer overhead. | |
+ * (Check LOCAL->svc; see maildir_open() for how LOCAL-svc is filled) | |
+ * - Make maildir_path() stricter in the characters/encodings allowed in the | |
+ * final path segment (maildir folder name) | |
+ * | |
+ * | |
+ * HISTORY (ancient) | |
+ * ------- | |
+ * | |
+ * Maildir Module for PINE 4.0x - fourth release, use with CARE! | |
+ * Author: Mattias Larsson <[email protected]> | |
+ * Version: 21.07.98 | |
+ * | |
+ * Please read the README.maildir file before using this module! | |
+ * If you have any questions, please e-mail [email protected] | |
+ * Multiple inboxes patch by Dean Gaudet <[email protected]> | |
+ * | |
+ * ================================================= | |
+ * | |
+ * Based on the IMAP2 maildir routines by: | |
+ * | |
+ * Author: Eric Green | |
+ * Bloodhounds International Inc. | |
+ * [email protected] | |
+ * | |
+ * Additional contributions from: | |
+ * Aidas Kasparas ([email protected]) | |
+ * | |
+ * Date: 27 April 1997 | |
+ * Last Edited: 13 June 1997 | |
+ * | |
+ * Based (heavily) on mh.c and other c-client library files by Mark Crispin: | |
+ * | |
+ * Mark Crispin | |
+ * Networks and Distributed Computing | |
+ * Computing & Communications | |
+ * University of Washington | |
+ * Administration Building, AG-44 | |
+ * Seattle, WA 98195 | |
+ * Internet: [email protected] | |
+ * | |
+ * Copyright 1995 by the University of Washington | |
+ * | |
+ * 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 appears in all copies and that both the | |
+ * above copyright notice and this permission notice appear in supporting | |
+ * documentation, and that the name of the University of Washington not be | |
+ * used in advertising or publicity pertaining to distribution of the software | |
+ * without specific, written prior permission. This software is made | |
+ * available "as is", and | |
+ * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, | |
+ * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED | |
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN | |
+ * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON 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, TORT | |
+ * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION | |
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ * | |
+ */ | |
+ | |
+/* | |
+ * CONFIGURABLE OPTIONS - PLEASE CHECK THESE OUT | |
+ */ | |
+ | |
+#define NO_MAILDIR_FIDDLE /* disallow Maildir with "Maildir" string in the | |
+ name. This is useful in an ISP setup using the | |
+ IMAP daemon. #undef it if you are running a | |
+ normal pine and know what you are doing */ | |
+#undef NO_MAILDIR_FIDDLE | |
+ | |
+#define NO_ABSOLUTE_PATHS /* if you define this, leading '/'s will be | |
+ removed from mailbox paths and mailbox paths | |
+ will not be allowed to contain "../" | |
+ backreferences. This is also useful in an ISP | |
+ setup with IMAP */ | |
+#undef NO_ABSOLUTE_PATHS | |
+ | |
+#define DIR_MASK 0770 /* mkdir() mode mask. 0700 is default, but in | |
+ group environments with group access to | |
+ mailboxes, 0770 is more convenient. The system | |
+ must be set up properly, e.g. directories should | |
+ be created under group-sticky directories that | |
+ inherit parent group ownership. */ | |
+ | |
+#define FILE_MASK (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) | |
+ /* open() mode mask. Similar to mkdir() mask, | |
+ but for saving message files | |
+ default: (S_IRUSR|S_IWUSR) | |
+ group example: (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) | |
+ */ | |
+ | |
+#define MAILDIR_MAX_FLAGS 32 /* max number of flags to allow on Maildir | |
+ Must be >= 5 */ | |
+ | |
+ | |
+#define restrict /* remove this if your compiler supports the | |
+ 'restrict' keyword; e.g. gcc -std=c99 */ | |
+ | |
+#define PINE_HACK_EXPUNGE /* The c-client library terribly overloads the | |
+ stream->silent flag. Define this to cause the | |
+ maildir driver to modify PINE client private | |
+ structure elements to quell spurious "expunged" | |
+ messages when an external program modifies an | |
+ open maildir. A single "externally modified" | |
+ message will still be given. */ | |
+ | |
+ | |
+/* | |
+ * END CONFIGURATION | |
+ */ | |
+ | |
+ | |
+#include "mail.h" | |
+#include "osdep.h" | |
+#include "misc.h" | |
+#include "dummy.h" | |
+ | |
+#include <stdio.h> | |
+#include <ctype.h> | |
+#include <dirent.h> | |
+#include <errno.h> | |
+#include <limits.h> | |
+#include <pwd.h> | |
+#include <stdlib.h> | |
+#include <sys/stat.h> | |
+#include <sys/time.h> | |
+#include <sys/types.h> | |
+#include <utime.h> | |
+ | |
+/* include pine/pine.h for (PER_STREAM_S *) */ | |
+/* define the minimum necessary so that pine/pine.h #include's cleanly */ | |
+/* (c-client library (badness) does not protect itself from multiple includes)*/ | |
+#ifdef PINE_HACK_EXPUNGE | |
+ #define SigType void | |
+ #define PROTO(args) args | |
+ #define HelpType const char * const * | |
+ #define MAXFOLDER 128 | |
+ #define MAX_SCREEN_COLS 170 | |
+ #if defined(PATH_MAX) | |
+ #define MAXPATH PATH_MAX | |
+ #elif defined(MAXPATHLEN) | |
+ #define MAXPATH MAXPATHLEN | |
+ #else | |
+ #define MAXPATH 256 | |
+ #endif | |
+ #include <setjmp.h> | |
+ #include "../../pico/estruct.h" | |
+ #include "../../pico/pico.h" | |
+ #include "../../alpine/alpine.h" | |
+#endif /* PINE_HACK_EXPUNGE */ | |
+ | |
+#include "maildir.h" | |
+ | |
+/* Driver dispatch used by MAIL */ | |
+ | |
+DRIVER maildirdriver = { | |
+ "maildir", /* driver name */ | |
+ /* driver flags */ | |
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_RECYCLE|DR_NOSTICKY, | |
+ (DRIVER *) NIL, /* next driver */ | |
+ maildir_valid, /* mailbox is valid for us */ | |
+ maildir_parameters, /* manipulate parameters */ | |
+ NIL, /* scan mailboxes */ | |
+ maildir_list, /* find mailboxes */ | |
+ maildir_lsub, /* find subscribed mailboxes */ | |
+ NIL, /* maildir_sub, */ /* subscribe to mailbox */ | |
+ NIL, /* maildir_unsub, */ /* unsubscribe from mailbox */ | |
+ maildir_create, /* create mailbox */ | |
+ maildir_delete, /* delete mailbox */ | |
+ maildir_rename, /* rename mailbox */ | |
+ mail_status_default, /* status of mailbox */ | |
+ maildir_open, /* open mailbox */ | |
+ maildir_close, /* close mailbox */ | |
+ NIL, /* maildir_fetchfast, *//* fetch message "fast" attributes */ | |
+ NIL, /* fetch message flags */ | |
+ NIL, /* fetch overview */ | |
+ NIL, /* fetch message envelopes */ | |
+ maildir_fetchheader, /* fetch message header */ | |
+ maildir_fetchtext, /* fetch message body */ | |
+ NIL, /* fetch partial message text */ | |
+ NIL, /* unique identifier */ | |
+ NIL, /* message number */ | |
+ NIL, /* modify flags */ | |
+ maildir_flagmsg, /* per-message modify flags */ | |
+ NIL, /* search for message based on criteria */ | |
+ NIL, /* sort messages */ | |
+ NIL, /* thread messages */ | |
+ maildir_ping, /* ping mailbox to see if still alive */ | |
+ maildir_check, /* check for new messages */ | |
+ maildir_expunge, /* expunge deleted messages */ | |
+ maildir_copy, /* copy messages to another mailbox */ | |
+ maildir_append, /* append string message to mailbox */ | |
+ NIL /* garbage collect stream */ | |
+}; | |
+ | |
+ /* prototype stream */ | |
+MAILSTREAM maildirproto = {&maildirdriver}; | |
+ | |
+ | |
+static __inline__ int | |
+has_md_prefix(const char * const restrict mailbox) | |
+{ | |
+ return ( mailbox[0] == '#' | |
+ && (mailbox[1] == 'M' || mailbox[1] == 'm') | |
+ && (mailbox[2] == 'D' || mailbox[2] == 'd') | |
+ && mailbox[3] == '/'); | |
+} | |
+ | |
+ | |
+static __inline__ int | |
+is_md_inbox(const char * restrict mailbox) | |
+{ | |
+ if (has_md_prefix(mailbox)) | |
+ mailbox += 4; | |
+ return ( (mailbox[0] == 'I' || mailbox[0] == 'i') | |
+ && (mailbox[1] == 'N' || mailbox[1] == 'n') | |
+ && (mailbox[2] == 'B' || mailbox[2] == 'b') | |
+ && (mailbox[3] == 'O' || mailbox[3] == 'o') | |
+ && (mailbox[4] == 'X' || mailbox[4] == 'x') | |
+ && mailbox[5] == '\0'); | |
+} | |
+ | |
+ | |
+/* generate path string to cur/ subdirectory of given Maildir | |
+ * (caller should (implicitly or explicitly) check that *dst != '\0' on return) | |
+ */ | |
+static char *maildir_path (char * const restrict dst, const size_t dst_size, | |
+ const char * const restrict name) | |
+{ | |
+ const char *maildir = name; | |
+ const char *s = NULL; | |
+ | |
+ /* (skip over internal prefix (case-insensitive) "#md/...", if present) */ | |
+ if (has_md_prefix(name)) | |
+ maildir += 4; | |
+ | |
+ #ifdef NO_ABSOLUTE_PATHS | |
+ while (*maildir == '/') | |
+ maildir++; | |
+ if (!(maildir[0] == '.' && maildir[1] == '.' | |
+ && (maildir[2] == '/' || maildir[2] == '\0')) && maildir[0] != '\0') { | |
+ s = name; | |
+ while ((s = strstr(s, "/..")) && s[3] != '/' && s[3] != '\0') | |
+ ; | |
+ } | |
+ else | |
+ s = ""; | |
+ #endif | |
+ | |
+ if (*maildir != '/') { | |
+ if (is_md_inbox(name)) | |
+ maildir = MAILDIRPATH; | |
+ if (snprintf (dst,dst_size,"%s/%s/cur",myhomedir(),maildir) >= dst_size) | |
+ s = ""; | |
+ } | |
+ else { /* allow absolute paths */ | |
+ if (snprintf (dst, dst_size, "%s/cur", maildir) >= dst_size) | |
+ s = ""; | |
+ } | |
+ | |
+ if (s != NULL) { | |
+ snprintf (dst, dst_size, "Path to maildir (%s) too long or invalid", | |
+ maildir); | |
+ mm_log (dst,WARN); | |
+ *dst = '\0'; | |
+ } | |
+ | |
+ return dst; | |
+} | |
+ | |
+ | |
+static int maildir_isvalid (const char * const restrict name, | |
+ const long justname) | |
+{ | |
+ if (name == NULL | |
+ || name[0] == '\0' /* disallow "", ".", and ".." */ | |
+ || (name[0]=='.' && (name[1]=='\0' || (name[1]=='.' && name[2]=='\0'))) | |
+ || (name[0] == '#' && !has_md_prefix(name))) | |
+ return NIL; | |
+ | |
+ #ifdef NO_MAILDIR_FIDDLE | |
+ /* ignore anything containing Maildir to prevent anyone from fiddling | |
+ with their incoming Maildir directly; it should be accessed via the | |
+ INBOX alias */ | |
+ if (strstr(name, "Maildir")) { | |
+ return NIL; | |
+ } | |
+ #endif | |
+ | |
+ if (justname && *name == '#') | |
+ /* done; only checking validity of string */ | |
+ return T; | |
+ else if (strlen(name) < MAILTMPLEN) { | |
+ /* assume valid local maildir if dir exists and contains cur/ dir */ | |
+ char tmp[MAILTMPLEN]; | |
+ struct stat sbuf; | |
+ | |
+ /* (must be local pine path (no internal prefix of '*' or '{')) */ | |
+ if (name[0] != '*' && name[0] != '{' | |
+ && maildir_path (tmp,sizeof(tmp),name) | |
+ && stat (tmp,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) | |
+ return T; | |
+ } | |
+ | |
+ return NIL; | |
+} | |
+ | |
+ | |
+static int | |
+ascii_sort (const void *a, const void *b) | |
+{ | |
+ return (*((char *)a) - *((char *)b)); | |
+} | |
+ | |
+ | |
+static void | |
+maildir_add_flags (char * const s, size_t size, MESSAGECACHE * const elt, | |
+ char * const addtl_flags) | |
+{ | |
+ char flags[MAILDIR_MAX_FLAGS],aflags[MAILDIR_MAX_FLAGS]; | |
+ size_t i, a = 0; | |
+ | |
+ /* size must be > 0 or else string will not be NIL terminated upon return! | |
+ * size should be > MAILDIR_MAX_FLAGS, or else flags will be silently | |
+ * truncated | |
+ */ | |
+ /* assert(size); */ | |
+ | |
+ /* extract unknown flags (flags after MAILDIR_MAX_FLAGS silently ignored) */ | |
+ if (addtl_flags) { | |
+ for (i = 0; a < sizeof(aflags) && addtl_flags[i]; i++) { | |
+ switch (addtl_flags[i]) { | |
+ case 'S': case 'R': case 'F': case 'T': case 'D': | |
+ break; | |
+ default: | |
+ aflags[a++] = addtl_flags[i]; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ /* write known flags */ | |
+ i = 0; | |
+ if (elt->draft) flags[i++] = 'D'; | |
+ if (elt->flagged) flags[i++] = 'F'; | |
+ if (elt->answered) flags[i++] = 'R'; | |
+ if (elt->seen) flags[i++] = 'S'; | |
+ if (elt->deleted) flags[i++] = 'T'; | |
+ | |
+ /* append unknown flags | |
+ * perform an ASCII sort of the flags (required by Maildir specification) | |
+ * (eliminate all unknown flags if there are too many) */ | |
+ if (a) { | |
+ if (i + a <= sizeof(flags)) { | |
+ memcpy(flags+i,aflags,a); | |
+ if ((i += a) > 1) | |
+ qsort(flags,i,sizeof(char),ascii_sort); | |
+ } | |
+ } | |
+ | |
+ /* commit flags to s and NIL terminate string | |
+ * (flags are silently truncated if not enough space in s) */ | |
+ if ((i = (size > i) ? i : size - 1)) | |
+ memcpy(s, flags, i); | |
+ s[i] = '\0'; | |
+} | |
+ | |
+ | |
+/* predeclare */ | |
+static int | |
+maildir_refresh_elt (MAILSTREAM * const stream, MESSAGECACHE * const elt); | |
+ | |
+ | |
+static int | |
+maildir_flagmsg_rename (MAILSTREAM * const stream, MESSAGECACHE * const elt) | |
+{ | |
+ /* In testing, I found that when marking/unmarking a message for deletion, | |
+ * this routine is called twice, first without the changed flag and then | |
+ * with the changed flag. Calling this routine twice is a little extra | |
+ * work in PINE, but we avoid making any syscalls the case where there is no | |
+ * modification, so this is not a terrible worry -- just a little wasteful. | |
+ */ | |
+ char file_old[MAILTMPLEN],file_new[MAILTMPLEN]; | |
+ char *s,*t; | |
+ const size_t i = elt->maildirp ? strlen(elt->maildirp) : 0; | |
+ const size_t dir_len = strlen(LOCAL->dir); | |
+ | |
+ if (i == 0) | |
+ return; | |
+ | |
+ /* (filenames are of the form "%s/%s:2,DFRST" for dir and file) */ | |
+ /* (+4 for "/" and ":2,")*/ | |
+ if (dir_len+i+4+MAILDIR_MAX_FLAGS >= sizeof(file_new)) | |
+ return NIL; | |
+ memcpy(file_old, LOCAL->dir, dir_len); | |
+ file_old[dir_len] = '/'; | |
+ memcpy(file_old+dir_len+1, elt->maildirp, i+1); | |
+ memcpy(file_new, file_old, dir_len+1+i+1); | |
+ if ((s = strrchr(file_new,':')) && (s[1]=='2' || s[1]=='3') && s[2]==',') { | |
+ /* compare against existing flags and return if no changes needed */ | |
+ s[1] = '2'; | |
+ t = (s += 3); /*(requires flags in ASCII order or will be reordered)*/ | |
+ if ( (elt->draft ? (t = strchr(t,'D')) != NULL : !strchr(t,'D')) | |
+ && (elt->flagged ? (t = strchr(t,'F')) != NULL : !strchr(t,'F')) | |
+ && (elt->answered ? (t = strchr(t,'R')) != NULL : !strchr(t,'R')) | |
+ && (elt->seen ? (t = strchr(t,'S')) != NULL : !strchr(t,'S')) | |
+ && (elt->deleted ? (t = strchr(t,'T')) != NULL : !strchr(t,'T'))) | |
+ return NIL; | |
+ | |
+ } | |
+ else { | |
+ s = file_new+dir_len+1+i; | |
+ *s++ = ':'; *s++ = '2'; *s++ = ','; *s = '\0'; | |
+ } | |
+ | |
+ maildir_add_flags (s, MAILDIR_MAX_FLAGS+1, elt, s); | |
+ | |
+ /* rename the file with new flags */ | |
+ if (rename (file_old,file_new) < 0) { | |
+ if (errno == ENOENT && maildir_refresh_elt(stream, elt)) | |
+ return maildir_flagmsg_rename(stream, elt); | |
+ else { | |
+ snprintf(file_old, sizeof(file_old), | |
+ "Unable to write flags to disk: %s", strerror (errno)); | |
+ mm_log(file_old,ERROR); | |
+ return NIL; | |
+ } | |
+ } | |
+ | |
+ /* update the file name in cache */ | |
+ fs_give ((void **) &elt->maildirp); | |
+ elt->maildirp = cpystr (file_new+dir_len+1); | |
+ return T; | |
+} | |
+ | |
+ | |
+static void | |
+maildir_flagmsg_size (MAILSTREAM * const stream, MESSAGECACHE * const elt) | |
+{ | |
+ /* silently skip adding size if any problems are encountered; not fatal */ | |
+ char path_old[MAILTMPLEN],path_new[MAILTMPLEN]; | |
+ const size_t dir_len = strlen(LOCAL->dir); | |
+ size_t file_len = strlen(elt->maildirp); | |
+ struct stat st; | |
+ /*(expected to succeed because of code in maildir_flagmsg_init())*/ | |
+ char * const f = strrchr(elt->maildirp,':'); | |
+ | |
+ /* (+1 for "/", +13 for ",S=9999999999") */ | |
+ if (f && dir_len + 1 + file_len + 13 < sizeof(path_new)) { | |
+ memcpy(path_old, LOCAL->dir, dir_len); | |
+ path_old[dir_len] = '/'; | |
+ memcpy(path_old+dir_len+1, elt->maildirp, file_len+1); | |
+ file_len = (f - elt->maildirp); | |
+ memcpy(path_new, path_old, dir_len+1+file_len); | |
+ if (stat (path_old,&st) == 0 | |
+ && st.st_size <= 4294967295U) { /*(space in string checked above)*/ | |
+ snprintf(path_new+dir_len+1+file_len, | |
+ sizeof(path_new)-(dir_len+1+file_len), | |
+ ",S=%lu%s", (unsigned long) st.st_size, f); | |
+ if (rename (path_old,path_new) == 0) { | |
+ fs_give ((void **) &elt->maildirp); | |
+ elt->maildirp = cpystr(path_new+dir_len+1); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+static void | |
+maildir_flagmsg_init (MAILSTREAM * const stream, MESSAGECACHE * const elt, | |
+ char * const restrict d_name) | |
+{ | |
+ char *s; | |
+ if (elt->maildirp) | |
+ fs_give ((void **) &elt->maildirp); | |
+ elt->maildirp = cpystr (d_name); | |
+ if ((s = strrchr(elt->maildirp,':')) | |
+ && (s[1] == '2' || s[1] == '3') && s[2] == ',') { | |
+ /* grab flags (":2,..." or old maildir patch ":3,..." at string end) */ | |
+ if (s[1] == '3') { /*(should be rare; convert old PINE maildir patch)*/ | |
+ s[1] = '2'; | |
+ rename(d_name, elt->maildirp); | |
+ } | |
+ s += 3; /*(parse flags even if out of ASCII order)*/ | |
+ elt->draft = (strchr (s,'D') != NULL); | |
+ elt->flagged = (strchr (s,'F') != NULL); | |
+ elt->answered = (strchr (s,'R') != NULL); | |
+ elt->seen = (strchr (s,'S') != NULL); | |
+ elt->deleted = (strchr (s,'T') != NULL); | |
+ /* Courier-IMAP uses "DFRST", too. */ | |
+ /* Dovecot additionally allows 'a'-'z' custom flags */ | |
+ } | |
+ else | |
+ /* (rename file with appended ":2,..." flag marker and flags) */ | |
+ maildir_flagmsg_rename (stream, elt); | |
+ | |
+ /* add file size to filename, if not present | |
+ * code kept separate from above for simplicity, | |
+ * even though it may result in extra rename() syscalls */ | |
+ if (!strstr(elt->maildirp, ",S=")) | |
+ maildir_flagmsg_size(stream, elt); | |
+} | |
+ | |
+ | |
+static __inline__ int | |
+maildir_select (const struct dirent * const restrict name) | |
+{ | |
+ /* message filenames begin with timestamp in seconds */ | |
+ switch (name->d_name[0]) { | |
+ case '0': case '1': case '2': case '3': case '4': | |
+ case '5': case '6': case '7': case '8': case '9': | |
+ return T; | |
+ default : return NIL; | |
+ } | |
+} | |
+ | |
+ | |
+/* PINE provides a terrible prototype for this in the "name of compatibility"?! | |
+ * It uses its own scandir() implemented using qsort() rather than the OS's, if | |
+ * present. scandir() should be prototyped (but is not prototyped by PINE) as: | |
+ * int scandir(const char *dir, struct dirent ***namelist, | |
+ * int(*filter)(const struct dirent *), | |
+ * int(*compar)(const struct dirent **, const struct dirent **)); | |
+ */ | |
+static int | |
+maildir_namesort (const void * const v1, const void * const v2) | |
+{ | |
+ const struct dirent * const * const restrict d1 = v1; | |
+ const struct dirent * const * const restrict d2 = v2; | |
+ /* message filenames begin with timestamp in seconds. | |
+ * sort on that, but if delivered in the same second, | |
+ * sort on filename string (not precise, depending on file name conventions) | |
+ */ | |
+ const unsigned long t1 = strtoul((*d1)->d_name, NULL, 10); | |
+ const unsigned long t2 = strtoul((*d2)->d_name, NULL, 10); | |
+ return (t1 > t2 ? 1 : t1 < t2 ? -1 : strcmp ((*d1)->d_name,(*d2)->d_name)); | |
+} | |
+ | |
+ | |
+/* remove files from tmp/ that are older than 36 hours */ | |
+static void | |
+maildir_remove_tmp_old (MAILSTREAM * const stream) | |
+{ | |
+ char file_tmp[MAILTMPLEN]; | |
+ const int dir_len = | |
+ snprintf(file_tmp, sizeof(file_tmp), "%s/../tmp", LOCAL->dir); | |
+ size_t file_len; | |
+ struct dirent *d; | |
+ struct stat sbuf; | |
+ DIR *dir; | |
+ const time_t cutoff = time(NULL) - 129600; /* (129600 secs == 36 hours) */ | |
+ | |
+ if (dir_len >= sizeof(file_tmp) | |
+ || stat (file_tmp,&sbuf) < 0 || !S_ISDIR(sbuf.st_mode) | |
+ || !(dir = opendir (file_tmp))) { | |
+ snprintf (file_tmp, sizeof(file_tmp), "Unable to open maildir: %s", | |
+ strerror (errno)); | |
+ mm_log (file_tmp,ERROR); | |
+ return; | |
+ } | |
+ | |
+ file_tmp[dir_len] = '/'; | |
+ | |
+ while ((d = readdir (dir))) { | |
+ /* select only maildir message filename format, regular file, | |
+ skip filenames too long (+1 for "/" between dir and filenames), | |
+ unlink files older than 36 hours */ | |
+ if (maildir_select(d) | |
+ && dir_len + (file_len = strlen(d->d_name)) + 1 < sizeof(file_tmp) | |
+ && (memcpy (file_tmp+dir_len,d->d_name,file_len+1), | |
+ stat(file_tmp, &sbuf) == 0) | |
+ && S_ISREG (sbuf.st_mode) | |
+ && sbuf.st_mtime < cutoff) | |
+ unlink (file_tmp); | |
+ } | |
+ | |
+ closedir (dir); | |
+} | |
+ | |
+ | |
+/* move new messages from new/ to cur/ */ | |
+static long | |
+maildir_move_new (MAILSTREAM * const stream) | |
+{ | |
+ char file_new[MAILTMPLEN],file_cur[MAILTMPLEN],*f; | |
+ const int dir_len = | |
+ snprintf(file_new, sizeof(file_new), "%s/../new", LOCAL->dir); | |
+ size_t file_len; | |
+ struct dirent *d; | |
+ struct stat sbuf; | |
+ DIR *dir; | |
+ long recent = 0; | |
+ | |
+ /* return if new/ has not changed since last scan, else process new/ dir */ | |
+ if (dir_len >= sizeof(file_new) | |
+ || stat (file_new,&sbuf) < 0 || !S_ISDIR(sbuf.st_mode)) { | |
+ snprintf (file_new, sizeof(file_new), "Unable to open maildir: %s", | |
+ strerror (errno)); | |
+ mm_log (file_new,ERROR); | |
+ return 0; | |
+ } | |
+ /*(requires that mail be checked whenever cur/ LOCAL->scantime is changed)*/ | |
+ if (sbuf.st_mtime < LOCAL->scantime | |
+ || !(dir = opendir (file_new))) | |
+ return 0; | |
+ | |
+ file_new[dir_len] = '/'; | |
+ memcpy(file_cur, file_new, dir_len-6); /* (-6 to skip "../new") */ | |
+ | |
+ while ((d = readdir (dir))) { | |
+ /* select only maildir message filename format, regular file, | |
+ * and skip filenames too long (+3 for ":2,"; +13 for ",S=9999999999"; | |
+ * -6 for removed "../new"; ==> 10) | |
+ * (skip adding ,S=<size> if size is >= 4 GiB) */ | |
+ if (maildir_select(d) | |
+ && dir_len + (file_len = strlen(d->d_name)) + 10 < sizeof(file_new) | |
+ && (memcpy (file_new+dir_len+1,d->d_name,file_len+1), | |
+ stat(file_new, &sbuf) == 0) | |
+ && S_ISREG (sbuf.st_mode) ) { | |
+ | |
+ /* generate target filename, append maildir flags if not present */ | |
+ if (!strstr(d->d_name, ",S=") | |
+ && sbuf.st_size <= 4294967295U) { /*(space checked above)*/ | |
+ if ((f = strrchr(d->d_name, ':')) | |
+ && (f[1] == '2' || f[1] == '3') && f[2] == ',') { | |
+ if (f - d->d_name) | |
+ memcpy(file_cur+dir_len-6, d->d_name, f - d->d_name); | |
+ snprintf(file_cur+dir_len-6+(f - d->d_name), | |
+ file_len - (f - d->d_name) + 14, | |
+ ",S=%lu%s", sbuf.st_size, f); | |
+ } | |
+ else | |
+ snprintf(file_cur+dir_len-6, file_len + 17, | |
+ "%s,S=%lu:2,", d->d_name, sbuf.st_size); | |
+ } | |
+ else { | |
+ memcpy(file_cur+dir_len-6, d->d_name, file_len+1); | |
+ if (!((f = strrchr(d->d_name, ':')) | |
+ && (f[1] == '2' || f[1] == '3') && f[2] == ',')) | |
+ memcpy(file_cur+dir_len-6+file_len, ":2,", 4); | |
+ } | |
+ | |
+ /* move new message from new/ to cur/ */ | |
+ if (rename (file_new,file_cur) == 0) { | |
+ recent++; | |
+ } | |
+ else | |
+ mm_log("Unable to read new mail!",WARN); | |
+ } | |
+ } | |
+ | |
+ closedir (dir); | |
+ return recent; | |
+} | |
+ | |
+ | |
+static __inline__ void | |
+maildir_free_elt (MAILSTREAM * const stream, MESSAGECACHE * const elt) | |
+{ | |
+ if (elt->maildirp) | |
+ fs_give ((void **) &elt->maildirp); | |
+ mail_expunged (stream,elt->msgno); | |
+} | |
+ | |
+ | |
+ | |
+ | |
+/* check validity of maildir */ | |
+DRIVER * | |
+maildir_valid (char *mailbox) | |
+{ | |
+ return maildir_isvalid(mailbox,T) ? &maildirdriver : NIL; | |
+} | |
+ | |
+ | |
+/* open maildir */ | |
+MAILSTREAM * | |
+maildir_open (MAILSTREAM *stream) | |
+{ | |
+ if (!stream) | |
+ return &maildirproto; | |
+ | |
+ /* recycle stream if previously initialized */ | |
+ if (LOCAL) { /* ((LOCAL) is a macro for stream->local with a cast) */ | |
+ maildir_close (stream, 0); | |
+ stream->dtb = &maildirdriver; | |
+ mail_free_cache (stream); | |
+ } | |
+ | |
+ stream->sequence++; | |
+ stream->uid_last = 0; | |
+ stream->uid_validity = 0; | |
+ stream->nmsgs = stream->recent = 0; | |
+ stream->local = fs_get (sizeof (MAILDIRLOCAL)); | |
+ /*stream->inbox = is_md_inbox(stream->mailbox);*//* currently unused */ | |
+ | |
+ LOCAL->mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL); | |
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1); | |
+ LOCAL->dir = cpystr(maildir_path(LOCAL->buf,LOCAL->buflen,stream->mailbox)); | |
+ LOCAL->scantime = 0; | |
+ LOCAL->last_refresh = 0; | |
+ | |
+ LOCAL->svc = (const char *) mail_parameters (NIL,GET_SERVICENAME,NIL); | |
+ if (LOCAL->svc && memcmp(LOCAL->svc, "unknown", 8) == 0) | |
+ LOCAL->svc = NULL; | |
+ /* (LOCAL->svc is NULL for PINE client, "imap" for imapd, "pop" for popd) */ | |
+ | |
+ maildir_remove_tmp_old (stream); | |
+ maildir_ping (stream); | |
+ | |
+ return stream; | |
+} | |
+ | |
+ | |
+/* close maildir */ | |
+void | |
+maildir_close (MAILSTREAM *stream, long options) | |
+{ | |
+ MESSAGECACHE *elt; | |
+ size_t i; | |
+ mailcache_t const mc = LOCAL->mc; | |
+ const int expunge = (options & CL_EXPUNGE); | |
+ size_t file_len; | |
+ char * const buf = LOCAL->buf; | |
+ const size_t dir_len = strlen(LOCAL->dir)+1; | |
+ const size_t buf_len = LOCAL->buflen; | |
+ const int silent_val = stream->silent; | |
+ | |
+ stream->silent = T; | |
+ | |
+ if (dir_len < buf_len) { | |
+ memcpy(buf, LOCAL->dir, dir_len-1); | |
+ buf[dir_len-1] = '/'; | |
+ } | |
+ | |
+ for (i = stream->nmsgs; i != 0; i--) { | |
+ if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))) { | |
+ /*(code here is similar to maildir_expunge(); keep in sync)*/ | |
+ if (expunge && elt->deleted | |
+ && dir_len+(file_len=strlen(elt->maildirp)) < buf_len | |
+ && (memcpy(buf+dir_len, elt->maildirp, file_len+1), | |
+ unlink(buf) == 0)) { | |
+ } | |
+ maildir_free_elt (stream,elt); | |
+ } | |
+ } | |
+ | |
+ fs_give ((void **) &LOCAL->dir); | |
+ fs_give ((void **) &LOCAL->buf); | |
+ fs_give ((void **) &stream->local); /* nuke the LOCAL data */ | |
+ stream->dtb = NIL; /* log out the DTB */ | |
+ | |
+ stream->silent = silent_val; | |
+} | |
+ | |
+ | |
+/* (aims to be more memory-efficient than strcrlfcpy(), though slightly less | |
+ * flexible, as it requires *dst to be non-NULL, and does not NIL terminate | |
+ * the *dst string (useful for incremental block expansions rather than | |
+ * all-at-once)) | |
+ * (d_size must be at least one larger than s_size or dst will immediately | |
+ * be reallocated; there typically exists at least one newline in s (src)) | |
+ */ | |
+static size_t | |
+copy_add_CRs (char ** const dst, size_t * const dst_size, size_t offset, | |
+ char * restrict s, size_t s_size) | |
+{ | |
+ char *d,*p; | |
+ size_t d_size = *dst_size - offset; | |
+ | |
+ d = p = *dst+offset; | |
+ | |
+ if (offset == 0 && s_size && *s == '\012' && d_size > 1) { | |
+ s_size--; | |
+ s++; | |
+ *p++ = '\015'; | |
+ *p++ = '\012'; | |
+ d_size -= 2; | |
+ } | |
+ | |
+ while (s_size && s_size < d_size && (d=memccpy(p,s,'\012',s_size)) != NULL){ | |
+ s_size -= d - p; | |
+ s += d - p; | |
+ if (*(s-2) != '\015') { | |
+ *(d-1) = '\015'; | |
+ *d++ = '\012'; | |
+ } | |
+ d_size -= d - p; | |
+ p = d; | |
+ } | |
+ | |
+ if (s_size == 0 || d == NULL) | |
+ return p - *dst + s_size; | |
+ else if ((offset += (d - *dst)) < SSIZE_MAX && s_size < (SSIZE_MAX>>1)) { | |
+ /* (if buffer not large enough, recurse once with oversized buffer) */ | |
+ char *tmp = *dst; | |
+ *dst = (char *)fs_get((*dst_size=offset+(s_size*2))+1); | |
+ if (offset) | |
+ memcpy(*dst,tmp,offset); | |
+ fs_give ((void **) &tmp); | |
+ return copy_add_CRs(dst, dst_size, offset, s, s_size); | |
+ } | |
+ else { | |
+ mm_fatal("Message way too large. Aborting."); | |
+ abort(); | |
+ } | |
+} | |
+ | |
+ | |
+static size_t | |
+copy_without_CRs (char * restrict d, char * restrict s, size_t size) | |
+{ | |
+ char * const orig = d; | |
+ char *p = d; | |
+ long len = 0; /* (would prefer ptrdiff_t, but long will do fine) */ | |
+ while ((size -= len) && (d = memccpy(p, s+=len, '\015', size)) != NULL) { | |
+ len = d - p; | |
+ p = d - 1; | |
+ } | |
+ return p - orig + size; | |
+} | |
+ | |
+ | |
+static size_t | |
+rm_CRs (char * const orig) | |
+{ | |
+ char *p,*q,*s; | |
+ long len; /* (would prefer ptrdiff_t, but long will do fine) */ | |
+ if ((p = strchr(orig, '\015')) == NULL) | |
+ return strlen(orig); | |
+ for (s = p++; (q = strchr(p,'\015')) != NULL; p = q+1) { | |
+ if ((len = q - p) != 0) { | |
+ memmove(s, p, len); | |
+ s += len; | |
+ } | |
+ } | |
+ if ((len = strlen(p)) != 0) | |
+ memmove(s, p, len+1); | |
+ return s - orig + len; | |
+} | |
+ | |
+ | |
+/* fetch requested maildir message parts */ | |
+static int | |
+maildir_fetch_msg_core (MAILSTREAM * const stream, MESSAGECACHE * const elt, | |
+ const long flags, const int fetch_body) | |
+{ | |
+ char * const buf = LOCAL->buf; | |
+ const size_t buflen = LOCAL->buflen; | |
+ size_t i, rd; | |
+ ssize_t r; | |
+ MESSAGE * const restrict msg = &elt->private.msg; | |
+ int fd = -1; | |
+ struct stat sbuf; | |
+ | |
+ if (snprintf(buf,buflen,"%s/%s",LOCAL->dir,elt->maildirp) < buflen | |
+ && (fd = open(buf, O_RDONLY)) >= 0 | |
+ && fstat (fd, &sbuf) == 0 && !S_ISDIR(sbuf.st_mode)) { | |
+ | |
+ /* message arrival time (local time of delivery) is mtime of file */ | |
+ /* (this info is for the benefit of PINE mail index sorting code) */ | |
+ ((SORTCACHE *)(*LOCAL->mc)(stream,elt->msgno,CH_SORTCACHE))->arrival = | |
+ (unsigned long) sbuf.st_mtime; | |
+ | |
+ i = sbuf.st_size < buflen ? sbuf.st_size : buflen-1; | |
+ rd = 0; | |
+ do { | |
+ r = read(fd, buf+rd, i); | |
+ } while (r > 0 && (rd += r, i -= r)); /*(allow signals to interrupt)*/ | |
+ /*while (r > 0 ? (rd += r, i -= r) : errno == EINTR); */ | |
+ if (i) { | |
+ close(fd); | |
+ return NIL; | |
+ } | |
+ buf[rd] = '\0'; | |
+ | |
+ /* find end of message headers, counting newlines along the way */ | |
+ for (i=0, r=0; | |
+ buf[i] != '\0' && (buf[i] != '\n' || !++r || buf[i+1] != '\n'); | |
+ i++) | |
+ ; | |
+ if (buf[i] != '\0') { | |
+ i += 2; | |
+ r++; | |
+ } | |
+ else { | |
+ r++; /*(allocate space for at least one newline; none might exist)*/ | |
+ /* should we return NIL? | |
+ * warn that headers were too long or were incomplete? | |
+ */ | |
+ } | |
+ | |
+ if (!msg->header.text.data) { | |
+ const struct tm * const tm = gmtime (&sbuf.st_mtime); | |
+ | |
+ /* make plausible IMAPish date string */ | |
+ elt->day = tm->tm_mday; | |
+ elt->month = tm->tm_mon + 1; | |
+ elt->year = tm->tm_year + 1900 - BASEYEAR; | |
+ elt->hours = tm->tm_hour; | |
+ elt->minutes = tm->tm_min; | |
+ elt->seconds = tm->tm_sec; | |
+ elt->zhours = 0; | |
+ elt->zminutes= 0; | |
+ | |
+ /* store inode # as unique ID | |
+ * (assumes all messages in the same folder are from the same mount) | |
+ * (not used by maildir driver; no maildir_uid() function defined) | |
+ * (private.uid is unsigned long and so if large file support makes | |
+ * st_ino a 64-bit quantity, unsigned long better be 64-bits, too) | |
+ * (Using inode number, there is the possibility that someone else | |
+ * might delete a message file out from under us, and a new message | |
+ * file might be created with the same inode number. If this did | |
+ * happen, the original message should be detected as deleted when | |
+ * rescanning the directory to initially find the new message that | |
+ * happened to have the matching inode number. Anyway, since this | |
+ * maildir driver does not use the unique IDs, this is unlikely to | |
+ * be a problem) | |
+ * [should probably add compile-time test and issue warning] | |
+ * (This code was written before I found out the UIDs are for IMAP. | |
+ * Using inode is insufficient because it is not necessarily | |
+ * unique across sessions (the original message could be deleted | |
+ * and a new message might reuse the same inode). For use with | |
+ * IMAP, new code should be written that stores UID mapping info | |
+ * in the filename (before the ':' for flags), or in a static file | |
+ * or directory, mapping filename (without flags) -- which is | |
+ * guaranteed to be unique because of the Maildir spec -- to UID. | |
+ * Simple.) | |
+ * (For now, this maildir driver is defined with DR_NOSTICKY to | |
+ * inform the main program that UIDs from this driver are not | |
+ * persistent across sessions) | |
+ */ | |
+ elt->private.uid = sbuf.st_ino; | |
+ | |
+ /* cache message headers */ | |
+ if (flags & FT_INTERNAL) { | |
+ (msg->header.text.data = fs_get (i+1))[i] = '\0'; | |
+ memcpy(msg->header.text.data, buf, i); | |
+ msg->header.text.size = i; | |
+ } | |
+ else { | |
+ r += i; | |
+ msg->header.text.data = fs_get (r+1); | |
+ msg->header.text.size = | |
+ copy_add_CRs((char **) &msg->header.text.data,&r,0,buf,i); | |
+ msg->header.text.data[msg->header.text.size] = '\0'; | |
+ } | |
+ } | |
+ | |
+ if (fetch_body) { | |
+ /* allocate size for body including carriage returns | |
+ * (generously approx one LF per 16 chars, and then allow for | |
+ * as many additional LFs as there were chars in original header) | |
+ */ | |
+ size_t * const dst_size = (size_t *)&msg->text.text.size; | |
+ char ** const restrict dst = (char **)&msg->text.text.data; | |
+ | |
+ if (flags & FT_INTERNAL) { | |
+ *dst_size = sbuf.st_size - i; | |
+ (*dst = (char *)fs_get(*dst_size+1))[*dst_size] = '\0'; | |
+ if ((r = rd = rd - i)) | |
+ memcpy(*dst, buf+i, r); | |
+ if ((i = *dst_size - r)) { | |
+ do { | |
+ r = read (fd, *dst+rd, i); | |
+ } while (r > 0 && (rd += r, i -= r)); /*(allow interrupt)*/ | |
+ /* while (r > 0 ? (rd += r, i -= r) : errno == EINTR); */ | |
+ } | |
+ } | |
+ else { | |
+ /*(allocate +1 greater than *dst_size so room for last NIL)*/ | |
+ if (sbuf.st_size < SSIZE_MAX) | |
+ *dst = (char *) | |
+ fs_get((*dst_size=sbuf.st_size+(sbuf.st_size>>4))+1); | |
+ else { | |
+ close(fd); | |
+ return NIL; | |
+ } | |
+ | |
+ /* expand portion of body in buf that remained after headers */ | |
+ rd = ((r = rd - i)) ? copy_add_CRs(dst,dst_size,0,buf+i,r) : 0; | |
+ | |
+ /* read message body in blocks, expand, and loop */ | |
+ r++; /*(to get through loop start (r>0), add one to r and i)*/ | |
+ i = sbuf.st_size - i + 1; | |
+ | |
+ /* while (r > 0 ? (i -= r) : errno == EINTR) */ | |
+ while (r > 0 && (i -= r)) { /*(allows signals to interrupt)*/ | |
+ if ((r = read (fd, buf, i > buflen ? buflen : i)) > 0) { | |
+ if (i > buflen && (rd+i+(i >> 4)) > *dst_size) { | |
+ /* attempt to avoid some worst-case allocation by | |
+ * copy_add_CRs() since it will reallocate as | |
+ * necessary, but only guaranteed large enough for | |
+ * block in scope | |
+ */ | |
+ char *b = *dst; | |
+ *dst = memcpy(fs_get((*dst_size+=(i*2))+1),b,rd); | |
+ fs_give((void **) &b); | |
+ } | |
+ rd = copy_add_CRs(dst,dst_size,rd,buf,r); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (i == 0) { | |
+ (*dst)[rd] = '\0'; | |
+ *dst_size = rd; | |
+ elt->rfc822_size = msg->header.text.size + rd; | |
+ /* size of entire message in RFC822 CRLF form */ | |
+ } | |
+ else { | |
+ fs_give((void **) dst); | |
+ *dst = NULL; | |
+ *dst_size = 0; | |
+ close(fd); | |
+ return NIL; | |
+ } | |
+ } | |
+ else /* headers only */ | |
+ /* approximate RFC822 size; will be updated when body is read */ | |
+ elt->rfc822_size = msg->header.text.size + sbuf.st_size - i; | |
+ | |
+ close(fd); | |
+ return T; | |
+ } | |
+ else { | |
+ if (fd != -1) | |
+ close (fd); | |
+ else if (errno == ENOENT && maildir_refresh_elt(stream, elt)) | |
+ return maildir_fetch_msg_core(stream, elt, flags, fetch_body); | |
+ return NIL; | |
+ } | |
+} | |
+ | |
+ | |
+/* fetch maildir message headers */ | |
+char * | |
+maildir_fetchheader (MAILSTREAM *stream, unsigned long msgno, | |
+ unsigned long *length, long flags) | |
+{ | |
+ MESSAGECACHE * const elt =(MESSAGECACHE *)(*LOCAL->mc)(stream,msgno,CH_ELT); | |
+ | |
+ /*(ignore FT_UID; would have to match against all elts->private.uid first)*/ | |
+ if (elt && !(flags & FT_UID)) { | |
+ | |
+ SIZEDTEXT * const st = &elt->private.msg.header.text; | |
+ | |
+ if (st->data | |
+ || (maildir_fetch_msg_core(stream,elt,flags,0) && st->data)) { | |
+ *length = st->size; | |
+ return (char *) st->data; | |
+ } | |
+ } | |
+ *length = 0; | |
+ return ""; | |
+} | |
+ | |
+ | |
+/* fetch maildir message body */ | |
+long | |
+maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) | |
+{ | |
+ MESSAGECACHE * const elt =(MESSAGECACHE *)(*LOCAL->mc)(stream,msgno,CH_ELT); | |
+ | |
+ /*(ignore FT_UID; would have to match against all elts->private.uid first)*/ | |
+ if (elt && !(flags & FT_UID)) { | |
+ | |
+ SIZEDTEXT * const st = &elt->private.msg.text.text; | |
+ | |
+ if (st->data | |
+ || (maildir_fetch_msg_core(stream,elt,flags,1) && st->data)) { | |
+ /* mark as seen unless only peeking */ | |
+ if (!(flags & FT_PEEK) && !elt->seen) { | |
+ elt->seen = T; | |
+ maildir_flagmsg (stream, elt); | |
+ MM_FLAGS (stream, msgno); | |
+ } | |
+ INIT (bs,mail_string,st->data,st->size); | |
+ return T; | |
+ } | |
+ elt->rfc822_size = 0; | |
+ INIT (bs,mail_string,"",0); | |
+ } | |
+ return NIL; | |
+} | |
+ | |
+ | |
+#if 0 | |
+/* The maildir driver uses DR_NOFAST flag to indicate that it does not | |
+ * implement driver_fetchfast. The following is a possible implementation, but | |
+ * is slow since it requires opening every single file (one message per file). | |
+ * If we could obtain all the information from Maildir+ format filenames, then | |
+ * all of this information could be obtained during maildir_ping() and just | |
+ * parsed out into appropriate fields here without the need to hit the file | |
+ * system. | |
+ */ | |
+void | |
+maildir_fetchfast (MAILSTREAM *stream, char *sequence, long flags) | |
+{ | |
+ unsigned long i; | |
+ MESSAGECACHE *elt; | |
+ mailcache_t const mc = LOCAL->mc; | |
+ | |
+ if ((flags & FT_UID) | |
+ ? mail_uid_sequence (stream,sequence) | |
+ : mail_sequence (stream,sequence)) { | |
+ for (i = stream->nmsgs; i != 0; i--) { | |
+ if ((elt = (MESSAGECACHE *)(*mc)(stream,i,CH_ELT)) && elt->sequence) | |
+ maildir_fetch_msg_core (stream,elt,flags,0); | |
+ } | |
+ } | |
+} | |
+#endif | |
+ | |
+ | |
+/* mail_fetch_structure() does not check return values, so we can not rescan | |
+ * and update the Maildir at this time. Just attempt to recover if the flags | |
+ * on a single message were changed. Code flow is copied (yuck) and modified | |
+ * from maildir_refresh_cur(). | |
+ */ | |
+static int | |
+maildir_refresh_elt (MAILSTREAM * const stream, MESSAGECACHE * const elt) | |
+{ | |
+ long i; | |
+ struct dirent *d; | |
+ DIR * const dirp = opendir(LOCAL->dir); | |
+ char * const s = strrchr(elt->maildirp,':'); | |
+ const size_t len = s ? (size_t)(s - elt->maildirp) : strlen(elt->maildirp); | |
+ if (!dirp) { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, | |
+ "Unable to scan maildir: %s", strerror (errno)); | |
+ mm_log (LOCAL->buf,ERROR); | |
+ return NIL; | |
+ } | |
+ while ((d = readdir(dirp))) { | |
+ if (memcmp(elt->maildirp,d->d_name,len) == 0 && d->d_name[len] == ':') { | |
+ /* (if used with an IMAP server which does not use flag | |
+ * markers ":2," (or ":3,"), then PINE and the IMAP server | |
+ * may compete back and forth with file renames! If both | |
+ * sides just append ":2," to the filename, eventually it | |
+ * will hit NAME_MAX and the file may not be deletable! | |
+ * In such a case (heretofore not know to this coder), use | |
+ * PINE in IMAP mode, rather than this maildir driver) | |
+ */ | |
+ maildir_flagmsg_init (stream,elt,d->d_name); | |
+ MM_FLAGS (stream,elt->msgno); | |
+ closedir(dirp); | |
+ return T; | |
+ } | |
+ } | |
+ closedir(dirp); | |
+ return NIL; | |
+} | |
+ | |
+ | |
+static int | |
+maildir_refresh_cur (MAILSTREAM * const stream) | |
+{ | |
+ long i; | |
+ struct dirent **names = NULL; | |
+ long nfiles = | |
+ (long) scandir(LOCAL->dir,&names,maildir_select,maildir_namesort); | |
+ MESSAGECACHE *elt; | |
+ mailcache_t const mc = LOCAL->mc; | |
+ const int silent = stream->silent; | |
+ long recent = stream->recent; | |
+ int status = 0; | |
+ char *s; | |
+ | |
+ if (nfiles == -1) { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, | |
+ "Unable to process maildir: %s", strerror (errno)); | |
+ mm_log (LOCAL->buf,ERROR); | |
+ return NIL; | |
+ } | |
+ if (nfiles > MAXMESSAGES) { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, | |
+ "Mailbox has more messages (%lu) exist than maximum (%lu). " | |
+ "Not all shown.", nfiles, MAXMESSAGES); | |
+ mm_log (LOCAL->buf,ERROR); | |
+ /* free *names[] above MAXMESSAGES */ | |
+ for (i = MAXMESSAGES; i < nfiles; i++) | |
+ fs_give ((void **) &names[i]); | |
+ nfiles = MAXMESSAGES; | |
+ } | |
+ | |
+ if (stream->lock) | |
+ return NIL; | |
+ mail_lock (stream); /* locking stream is probably unnecessary */ | |
+ | |
+ LOCAL->last_refresh = time(0); | |
+ | |
+ /* check that cached filenames match sorted directory contents */ | |
+ i = 0; | |
+ while (i < stream->nmsgs && i < nfiles) { | |
+ if ((elt = (MESSAGECACHE *)(*mc)(stream,i+1,CH_ELT))) { | |
+ if (strcmp(elt->maildirp,names[i]->d_name) == 0) | |
+ i++; /* matched */ | |
+ else { | |
+ if ((s = strrchr(elt->maildirp,':')) | |
+ && memcmp(elt->maildirp, names[i]->d_name, | |
+ s - elt->maildirp + 1) == 0) { | |
+ /* (if used with an IMAP server which does not use flag | |
+ * markers ":2," (or ":3,"), then PINE and the IMAP server | |
+ * may compete back and forth with file renames! If both | |
+ * sides just append ":2," to the filename, eventually it | |
+ * will hit NAME_MAX and the file may not be deletable! | |
+ * In such a case (heretofore not know to this coder), use | |
+ * PINE in IMAP mode, rather than this maildir driver) | |
+ */ | |
+ status |= 1; /* matched, but flags were modified */ | |
+ maildir_flagmsg_init (stream,elt,names[i]->d_name); | |
+ MM_FLAGS (stream,i+1); | |
+ i++; | |
+ } | |
+ else { | |
+ status |= 2; /* mismatch */ | |
+ maildir_free_elt (stream, elt); | |
+ #ifdef PINE_HACK_EXPUNGE | |
+ if (!stream->silent && LOCAL->svc == NULL) | |
+ ((PER_STREAM_S *)stream->sparep)->expunge_count--; | |
+ #endif | |
+ } | |
+ } | |
+ } | |
+ else { /* create and release this element to force array renumbering */ | |
+ stream->silent = T; | |
+ (elt = mail_elt (stream, i+1))->maildirp = NULL; | |
+ maildir_free_elt (stream, elt); | |
+ stream->silent = silent; | |
+ } | |
+ } | |
+ while (i < stream->nmsgs) { | |
+ if ((elt = (MESSAGECACHE *)(*mc)(stream,i+1,CH_ELT))) { | |
+ status |= 2; /* mismatch */ | |
+ maildir_free_elt (stream, elt); | |
+ #ifdef PINE_HACK_EXPUNGE | |
+ if (!stream->silent && LOCAL->svc == NULL) | |
+ ((PER_STREAM_S *)stream->sparep)->expunge_count--; | |
+ #endif | |
+ } | |
+ else { /*create and release this element to force array renumbering*/ | |
+ stream->silent = T; | |
+ (elt = mail_elt (stream, i+1))->maildirp = NULL; | |
+ maildir_free_elt (stream, elt); | |
+ stream->silent = silent; | |
+ } | |
+ } | |
+ | |
+ switch (status) { | |
+ case 0: | |
+ break; | |
+ case 1: | |
+ #if 0 /* uncomment to send message when flags modified externally */ | |
+ mm_log ("Warning: Message flags have been modified externally.", WARN); | |
+ #endif | |
+ break; | |
+ case 2: case 3: default: | |
+ mm_log ("Warning: Mailbox changed unexpectedly. Reloading.", WARN); | |
+ break; | |
+ } | |
+ | |
+ i = stream->nmsgs; | |
+ stream->nmsgs = nfiles; | |
+ for (; i < nfiles; i++) { /* if newly seen, add to list */ | |
+ mail_exists(stream, i+1); | |
+ (elt = mail_elt(stream, i+1))->maildirp = NULL; | |
+ maildir_flagmsg_init (stream, elt, names[i]->d_name); /*fills maildirp*/ | |
+ elt->valid = T; | |
+ if (!elt->seen) { | |
+ elt->recent = T; | |
+ recent++; | |
+ } | |
+ | |
+ /* cache message headers so that message sorting by arrival time works | |
+ * in PINE (This is probably wasteful for other c-client consumers) | |
+ * Skip if stream->silent is set because it probably means this routine | |
+ * is being called forward-looking and only the summary results will be | |
+ * preserved. Specifically, this is skipped for mail_status_default(). | |
+ */ | |
+ if (!silent) | |
+ maildir_fetch_msg_core (stream, elt, 0L, 0); | |
+ else if ((s = strstr(elt->maildirp, ",S="))) | |
+ /* maildir_flagmsg_init() modifies elt->maildirp to add file size. | |
+ * Use that size here as approximate size of the message. It is not | |
+ * the size of the message in CRLF form as PINE expects, but will be | |
+ * corrected when the message is read into memory. (calling | |
+ * maildir_fetch_msg_core() above fills approx elt->rfc822_size) | |
+ */ | |
+ elt->rfc822_size = strtoul(s+3, (char **)NULL, 10); | |
+ } | |
+ | |
+ mail_unlock (stream); | |
+ | |
+ for (i = 0; i < nfiles; i++) /* free the names stuff */ | |
+ fs_give ((void **) &names[i]); | |
+ if (names) { | |
+ struct dirent *** const namestmp = &names; | |
+ fs_give ((void **) namestmp); | |
+ } | |
+ | |
+ mail_exists (stream,nfiles); | |
+ mail_recent (stream,recent); | |
+ | |
+ return T; | |
+} | |
+ | |
+ | |
+static int | |
+maildir_stat_cur (MAILSTREAM * const stream, struct stat * const sbuf) | |
+{ | |
+ /*get last modify/change times (also quick sanity check for valid maildir)*/ | |
+ if (stat (LOCAL->dir,sbuf) == 0 && S_ISDIR (sbuf->st_mode)) | |
+ return T; | |
+ else { | |
+ char tmp[MAILTMPLEN]; | |
+ snprintf (tmp,sizeof(tmp),"Unable to open maildir: %s",strerror(errno)); | |
+ mm_log (tmp,ERROR); | |
+ return NIL; | |
+ } | |
+} | |
+ | |
+ | |
+static int | |
+maildir_ping_cur (MAILSTREAM * const stream, const long recent) | |
+{ | |
+ struct stat st; | |
+ | |
+ if (!maildir_stat_cur (stream,&st)) | |
+ return NIL; | |
+ | |
+ if (LOCAL->scantime < st.st_mtime | |
+ || recent | |
+ || LOCAL->last_refresh + 900 < time(0)) { /* (refresh if > 15 mins) */ | |
+ if (maildir_refresh_cur (stream)) | |
+ LOCAL->scantime = st.st_mtime; | |
+ else | |
+ return NIL; | |
+ } | |
+ return T; | |
+ | |
+} | |
+ | |
+ | |
+long | |
+maildir_ping (MAILSTREAM *stream) | |
+{ | |
+ /* (similar to maildir_check() but with additional check for new mail) */ | |
+ return maildir_ping_cur (stream, maildir_move_new (stream)); | |
+} | |
+ | |
+ | |
+void | |
+maildir_check (MAILSTREAM *stream) | |
+{ | |
+ /* (similar to maildir_ping(), but without new mail check) */ | |
+ if (maildir_ping_cur (stream, 0)) | |
+ mm_log ("Check completed",(long) NIL); | |
+} | |
+ | |
+ | |
+void | |
+maildir_flagmsg (MAILSTREAM *stream, MESSAGECACHE *elt) | |
+{ | |
+ if (stream->silent) | |
+ maildir_flagmsg_rename (stream, elt); | |
+ else { | |
+ struct stat st; | |
+ int renamed; | |
+ long recent = NIL; | |
+ | |
+ if (!maildir_stat_cur (stream,&st)) | |
+ st.st_mtime = 0; /* ignore error */ | |
+ | |
+ if ((renamed = maildir_flagmsg_rename (stream, elt))) | |
+ recent = maildir_move_new (stream); | |
+ /* LOCAL->scantime (last scantime of cur/) is also compared to new/, | |
+ * and so new mail must be checked whenever LOCAL->scantime is updated*/ | |
+ | |
+ if (recent || LOCAL->scantime < st.st_mtime) { | |
+ if (maildir_refresh_cur (stream)) | |
+ LOCAL->scantime = renamed ? time(0) : st.st_mtime; | |
+ } | |
+ else if (renamed) | |
+ LOCAL->scantime = time(0); | |
+ } | |
+} | |
+ | |
+ | |
+long | |
+maildir_create (MAILSTREAM *stream, char *mailbox) | |
+{ | |
+ char path[MAILTMPLEN]; | |
+ char *s; | |
+ size_t len; | |
+ | |
+ maildir_path (path,sizeof(path),mailbox); | |
+ | |
+ if (access (path,F_OK) == 0) { | |
+ snprintf (path, sizeof(path), | |
+ "Can't create mailbox %s: mailbox already exists", mailbox); | |
+ mm_log (path,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ /* remove trailing "/cur" from maildir directory path */ | |
+ len = strlen (path) - 4; | |
+ path[len] = '\0'; | |
+ | |
+ /* check that parent directory exists */ | |
+ if ((s = strrchr(path, '/'))) { | |
+ *s = '\0'; | |
+ if (access (path,W_OK) == 0) | |
+ *s = '/'; | |
+ else { | |
+ *s = '/'; | |
+ /* create nested hierarchy of dirs (brute force; could be better) */ | |
+ for (s = path; (s = strchr(s, '/')); s++) { | |
+ *s = '\0'; | |
+ if (mkdir (path, (DIR_MASK)) != 0 && errno != EEXIST) { | |
+ char err[MAILTMPLEN]; | |
+ snprintf (err, sizeof(err), | |
+ "Can't create mailbox %s: %s (%s)", | |
+ mailbox, path, strerror (errno)); | |
+ mm_log (err,ERROR); | |
+ return NIL; | |
+ } | |
+ *s = '/'; | |
+ } | |
+ } | |
+ } | |
+ | |
+ /* create maildir and its subdirectories */ | |
+ if (mkdir (path, (DIR_MASK)) == 0 | |
+ && (memcpy (path+len, "/tmp", 5), mkdir (path, (DIR_MASK)) == 0) | |
+ && (memcpy (path+len, "/new", 5), mkdir (path, (DIR_MASK)) == 0) | |
+ && (memcpy (path+len, "/cur", 5), mkdir (path, (DIR_MASK)) == 0)) | |
+ return T; | |
+ else { | |
+ char err[MAILTMPLEN]; | |
+ snprintf (err, sizeof(err), "Can't create mailbox %s: %s (%s)", | |
+ mailbox, path, strerror (errno)); | |
+ mm_log (err,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ return T; | |
+} | |
+ | |
+ | |
+void | |
+maildir_expunge (MAILSTREAM *stream) | |
+{ | |
+ MESSAGECACHE *elt; | |
+ char * const buf = LOCAL->buf; | |
+ unsigned long i, n = 0, recent = stream->recent; | |
+ mailcache_t const mc = LOCAL->mc; | |
+ size_t file_len; | |
+ const size_t dir_len = strlen(LOCAL->dir)+1; | |
+ const size_t buf_len = LOCAL->buflen; | |
+ struct stat st; | |
+ | |
+ if (dir_len < buf_len) { | |
+ memcpy(buf, LOCAL->dir, dir_len-1); | |
+ buf[dir_len-1] = '/'; | |
+ } | |
+ else | |
+ return; | |
+ | |
+ if (!maildir_stat_cur (stream,&st)) | |
+ st.st_mtime = 0; /* ignore error */ | |
+ | |
+ for (i = stream->nmsgs; i != 0; i--) { | |
+ if (!(elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) || !elt->deleted) | |
+ continue; | |
+ /*(code here is similar to expunge in maildir_close(); keep in sync)*/ | |
+ if (dir_len+(file_len=strlen(elt->maildirp)) < buf_len | |
+ && (memcpy(buf+dir_len, elt->maildirp, file_len+1), | |
+ unlink(buf) == 0)) { | |
+ if (elt->recent) | |
+ --recent; /* if recent, one less recent message */ | |
+ n++; /* count up one more expunged message */ | |
+ maildir_free_elt (stream,elt); | |
+ } | |
+ else { | |
+ snprintf (buf, buf_len,"Expunge of message %ld failed, aborted: %s", | |
+ i, strerror (errno)); | |
+ mm_log (buf,WARN); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (n) { | |
+ /* notify upper level of new mailbox size and send message to user */ | |
+ mail_exists (stream,stream->nmsgs); | |
+ mail_recent (stream,recent); | |
+ snprintf (LOCAL->buf, LOCAL->buflen, "Expunged %ld messages", n); | |
+ mm_log (LOCAL->buf,(long) NIL); | |
+ | |
+ if (!stream->silent) { | |
+ /* unlink() causes directory st_mtime to be updated | |
+ * LOCAL->scantime (last scantime of cur/) is also compared to new/, | |
+ * and so new mail must be checked when LOCAL->scantime is updated*/ | |
+ if (maildir_move_new (stream) || LOCAL->scantime < st.st_mtime) { | |
+ if (maildir_refresh_cur (stream)) | |
+ LOCAL->scantime = time(0); | |
+ } | |
+ else | |
+ LOCAL->scantime = time(0); | |
+ } | |
+ } | |
+ else | |
+ mm_log ("No messages deleted, so no update needed",(long) NIL); | |
+} | |
+ | |
+ | |
+static void | |
+maildir_getflags(MESSAGECACHE * const elt, const char * const flags) | |
+{ | |
+ char *t,tmp[MAILTMPLEN]; | |
+ size_t n; | |
+ | |
+ elt->seen = elt->answered = elt->flagged = elt->deleted = elt->draft = NIL; | |
+ | |
+ /* check for valid flags string */ | |
+ if (flags == NULL || (n = strlen(flags)) == 0 || n > sizeof(tmp)-2) | |
+ return; | |
+ if (*flags == '(') { | |
+ if (flags[n-1] != ')') { | |
+ mm_log ("Bad flag list",ERROR); | |
+ return; | |
+ } | |
+ if ((n -= 2) == 0) | |
+ return; | |
+ memcpy(tmp, flags+1, n); | |
+ } | |
+ else | |
+ memcpy(tmp, flags, n); | |
+ /* add trailing space, terminate, uppercase string (for simple comparison)*/ | |
+ tmp[n] = ' '; | |
+ tmp[n+1] = '\0'; | |
+ t = ucase (tmp); | |
+ | |
+ /* parse flag strings, e.g. '\Seen \Flagged ...' or '(\Seen \Flagged ...)'*/ | |
+ while (*t) { | |
+ if (*t == '\\') { | |
+ switch (*++t) { | |
+ case 'S': | |
+ if (0==memcmp(t,"SEEN ", 5)) {elt->seen = T; t+=5;} break; | |
+ case 'A': | |
+ if (0==memcmp(t,"ANSWERED ",9)) {elt->answered= T; t+=9;} break; | |
+ case 'F': | |
+ if (0==memcmp(t,"FLAGGED ", 8)) {elt->flagged = T; t+=8;} break; | |
+ case 'D': | |
+ if (*(t+1) == 'E' && | |
+ 0==memcmp(t,"DELETED ", 8)) {elt->deleted = T; t+=8;} | |
+ else if (0==memcmp(t,"DRAFT ",6)){elt->draft = T; t+=6;} | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ } | |
+ else { | |
+ char err[MAILTMPLEN]; | |
+ n = strcspn(t, " "); | |
+ snprintf (err,sizeof(err),"Unknown flag: %.*s",(n < 80 ? n : 80),t); | |
+ mm_log (err,ERROR); | |
+ t += n + 1; | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+static long | |
+maildir_append_msg (MAILSTREAM * const stream, | |
+ char * const restrict mailbox, | |
+ MESSAGECACHE * const elt, /* contains message flags */ | |
+ const time_t unix_time, | |
+ char * restrict message, | |
+ size_t size) | |
+{ | |
+ /*(Note: MAILSTREAM *stream might not be an open stream! (uninitialized!))*/ | |
+ ssize_t w = 0; | |
+ int fd = -1; | |
+ char path_tmp[MAILTMPLEN],path_cur[MAILTMPLEN],*m; | |
+ const size_t dir_len = | |
+ strlen(maildir_path(path_tmp, sizeof(path_tmp), mailbox)); | |
+ struct timeval tv; | |
+ struct stat st; | |
+ struct utimbuf utbuf; | |
+ static size_t transact = 0; /* intentionally 'static' */ | |
+ | |
+ (void)gettimeofday(&tv, NULL); | |
+ utbuf.actime = utbuf.modtime = unix_time; | |
+ | |
+ if (dir_len == 0 | |
+ || snprintf(path_tmp+dir_len, sizeof(path_tmp) - dir_len, | |
+ "/../tmp/%lu.M%luP%lu_%07lu.%s,S=%lu", | |
+ (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec, | |
+ (unsigned long) getpid (), (unsigned long) transact++, | |
+ mylocalhost (), (unsigned long) size) | |
+ >= sizeof(path_tmp) - dir_len) | |
+ return NIL; | |
+ memcpy(path_cur,path_tmp,dir_len); | |
+ | |
+ if ((fd = open (path_tmp,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,(FILE_MASK))) >= 0 | |
+ && fstat (fd,&st) == 0 | |
+ && (w = snprintf(path_cur+dir_len, sizeof(path_cur) - dir_len, | |
+ "/%010lu.M%06luP%luV%lxI%lx_%07lu.%s,S=%lu:2,", | |
+ (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec, | |
+ (unsigned long) getpid (), (unsigned long) st.st_dev, | |
+ (unsigned long) st.st_ino, (unsigned long) transact-1, | |
+ mylocalhost (), (unsigned long) size)) | |
+ < sizeof(path_cur) - dir_len - MAILDIR_MAX_FLAGS) { | |
+ | |
+ if (elt->maildirp | |
+ && (m = strrchr(elt->maildirp,':')) | |
+ && (m[1] == '2' || m[1] == '3') && m[2] == ',') | |
+ m += 3; | |
+ else | |
+ m = NULL; | |
+ maildir_add_flags(path_cur+dir_len+w, MAILDIR_MAX_FLAGS+1, elt, m); | |
+ /* remove the trashed 'T' flag if present */ | |
+ if ((m = strchr(path_cur+dir_len+w,'T'))) { | |
+ if (*(m+1)) | |
+ memmove(m,m+1,strlen(m)); /*(also moves terminating NIL)*/ | |
+ else | |
+ *m = '\0'; | |
+ } | |
+ | |
+ do { | |
+ w = write (fd, message, size); | |
+ } while (w > 0 ? (message += w, size -= w) : errno == EINTR); | |
+ | |
+ if (size == 0 && fsync(fd) == 0 && close(fd) == 0 | |
+ && (fd = -1, utime(path_tmp,&utbuf) == 0) | |
+ && link(path_tmp,path_cur) == 0) { | |
+ unlink (path_tmp); | |
+ return LONGT; /* success */ | |
+ } | |
+ else { | |
+ if (fd != -1) | |
+ close(fd); | |
+ unlink (path_tmp); | |
+ snprintf (path_tmp, sizeof(path_tmp), "Message append failed: %s", | |
+ strerror (errno)); | |
+ mm_log (path_tmp,ERROR); | |
+ return NIL; | |
+ } | |
+ } | |
+ else { | |
+ if (fd != -1) { | |
+ close(fd); | |
+ unlink (path_tmp); | |
+ if (w >= sizeof(path_cur) - dir_len - MAILDIR_MAX_FLAGS) | |
+ return NIL; /* filename too long */ | |
+ } | |
+ else if (errno == EEXIST) { | |
+ sleep(2); | |
+ return maildir_append_msg (stream, mailbox, elt, unix_time, | |
+ message, size); | |
+ } | |
+ snprintf (path_tmp, sizeof(path_tmp), "Can't open append mailbox: %s", | |
+ strerror (errno)); | |
+ mm_log (path_tmp,ERROR); | |
+ return NIL; | |
+ } | |
+} | |
+ | |
+ | |
+long | |
+maildir_append (MAILSTREAM *stream, char *mailbox, append_t af, void *data) | |
+{ | |
+ /*(Note: MAILSTREAM *stream is not an open stream! It is uninitialized!)*/ | |
+ char *message,*flags,*date,tmp[MAILTMPLEN]; | |
+ size_t size = 0; | |
+ STRING *s; | |
+ char ** const msgtmp = &message; /*(avoid fs_give() compiler -Wall warn)*/ | |
+ time_t unix_time; | |
+ MESSAGECACHE elt = {0}; /* initializes all elements to 0, incl maildirp */ | |
+ long status = T; | |
+ | |
+ if (!maildir_isvalid (mailbox, NIL)) { | |
+ snprintf (tmp, sizeof(tmp), "Not a valid Maildir mailbox: %s", mailbox); | |
+ mm_log (tmp,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ /*(MM_APPEND(af)(...) is callback function to retrieve next message)*/ | |
+ while (status && (status=(MM_APPEND(af)(stream,data,&flags,&date,&s)))&& s){ | |
+ | |
+ maildir_getflags(&elt, flags); | |
+ | |
+ /* (if PINE provided an interface to the SORTCACHE s->arrival time | |
+ * of the originating message, that time would be used instead) */ | |
+ /* wasteful, but uses existing routines */ | |
+ if (!date) | |
+ rfc822_date ((date = tmp)); | |
+ if (mail_parse_date (&elt,date)) | |
+ unix_time = mail_longdate(&elt); | |
+ else { | |
+ char err[MAILTMPLEN]; | |
+ snprintf(err, sizeof(err), "Bad date in append: %.80s", date); | |
+ mm_log (err,ERROR); | |
+ unix_time = time(0); /* just use current time */ | |
+ } | |
+ | |
+ /* copy data without carriage returns */ | |
+ size = SIZE (s); | |
+ (message = (char *) fs_get (size+1))[size] = '\0'; | |
+ size = copy_without_CRs(message, s->curpos, size); | |
+ | |
+ status = maildir_append_msg(stream,mailbox,&elt,unix_time,message,size); | |
+ | |
+ fs_give ((void **) msgtmp); /* release the buffer */ | |
+ } | |
+ | |
+ return status; | |
+} | |
+ | |
+ | |
+long | |
+maildir_copy (MAILSTREAM *stream, char *sequence, char *mailbox, long options) | |
+{ | |
+ char *message; | |
+ struct stat sbuf; | |
+ MESSAGECACHE *elt; | |
+ mailcache_t const mc = LOCAL->mc; | |
+ char ** const msgtmp = &message; /*(avoid fs_give() compiler -Wall warn)*/ | |
+ int fd = -1; | |
+ long i,status = T; | |
+ size_t size; | |
+ | |
+ if (!maildir_isvalid (mailbox, NIL)) { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, "Not a valid Maildir mailbox: %s", | |
+ mailbox); | |
+ mm_log (LOCAL->buf,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ if (!((options & CP_UID) | |
+ ? mail_uid_sequence (stream, sequence) | |
+ : mail_sequence (stream,sequence) )) | |
+ return NIL; | |
+ | |
+ for (i = stream->nmsgs; i != 0; i--) { | |
+ /* marked for copy? or else continue to next message */ | |
+ if (!(elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) || !elt->sequence) | |
+ continue; | |
+ | |
+ if (snprintf (LOCAL->buf,LOCAL->buflen,"%s/%s",LOCAL->dir, | |
+ elt->maildirp) < LOCAL->buflen | |
+ && (fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0 | |
+ && fstat (fd,&sbuf) == 0) { | |
+ | |
+ /* maildir_copy() does not appear to be used by current applications | |
+ * based on the c-client library. If it were, some possible | |
+ * performance enhancements to this routine might be worthwhile: | |
+ * Best, we could try hard linking the files between folders, and | |
+ * could use same filename and intentionally skip if it already | |
+ * exists (to prevent duplicating message into target mailbox), and | |
+ * could fall back to other methods if folders were located on | |
+ * different filesystems. Failing that, we could check to see if | |
+ * we already have the message headers and body in memory | |
+ * (elt->private.msg.{header,text}.text.data) and could copy that | |
+ * and then rm_CRs() on the copy. And if we did not have the | |
+ * message in memory, we could alternatively use FT_INTERNAL flag | |
+ * with maildir_fetchtext(), but then would have to free it because | |
+ * the rest of PINE expects the data in CRLF format */ | |
+ | |
+ ssize_t r; /* slurp message */ | |
+ size = 0; | |
+ (message = (char *) fs_get (sbuf.st_size+1))[sbuf.st_size] = '\0'; | |
+ do { | |
+ r = read (fd, message+size, sbuf.st_size - size); | |
+ } while (r > 0 ? (size += r) != sbuf.st_size : errno == EINTR); | |
+ | |
+ /* no need to check elt->valid to verify that flags are valid | |
+ * because if we have a maildir elt, then elt->valid and flags | |
+ * were set from the filename when the elt was created. */ | |
+ | |
+ if (size == sbuf.st_size) { | |
+ close (fd); | |
+ size = rm_CRs(message); /* remove carriage returns */ | |
+ /* add message to maildir */ | |
+ status = maildir_append_msg (stream,mailbox,elt,sbuf.st_mtime, | |
+ message,size); | |
+ fs_give ((void **) msgtmp); | |
+ if (!status) | |
+ break; | |
+ } | |
+ else { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, | |
+ "Skipping message %ld; copy failed: %s", | |
+ i, strerror (errno)); | |
+ mm_log (LOCAL->buf,WARN); | |
+ fs_give ((void **) msgtmp); | |
+ close (fd); /* close after strerror(errno)*/ | |
+ } | |
+ } | |
+ else { | |
+ snprintf (LOCAL->buf, LOCAL->buflen, | |
+ "Skipping message %ld; copy failed: %s", | |
+ i, strerror (errno)); | |
+ mm_log (LOCAL->buf,WARN); | |
+ if (fd != -1) | |
+ close(fd); | |
+ } | |
+ } | |
+ | |
+ return status; | |
+} | |
+ | |
+ | |
+long | |
+maildir_delete (MAILSTREAM *stream, char *mailbox) | |
+{ | |
+ char *name,tmp[MAILTMPLEN]; | |
+ size_t dir_len,file_len; | |
+ struct dirent *d; | |
+ DIR *dirp; | |
+ char *subdir_names[] = {"cur","new","tmp",NULL}; | |
+ int i; | |
+ | |
+ if (!maildir_isvalid (mailbox,NIL)) { | |
+ snprintf (tmp, sizeof(tmp), "Can't delete mailbox %s: no such mailbox", | |
+ mailbox); | |
+ mm_log (tmp,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ dir_len = strlen (maildir_path (tmp,sizeof(tmp),mailbox)) - 3; | |
+ if (*tmp == '\0') | |
+ return NIL; | |
+ if (stream && LOCAL && strcmp (tmp, LOCAL->dir) == 0) | |
+ maildir_close(stream, 0); | |
+ | |
+ for (i = 0; subdir_names[i]; i++) { | |
+ memcpy(tmp+dir_len, subdir_names[i], 4); | |
+ if ((dirp = opendir (tmp))) { | |
+ tmp[dir_len+3] = '/'; | |
+ while ((d = readdir (dirp))) { | |
+ file_len = strlen((name = d->d_name)); | |
+ if ((file_len > 2 /* skip "." and ".." dirs */ | |
+ || !(name[0] == '.' | |
+ && (name[1]=='\0' ||(name[1]=='.' && name[2]=='\0')))) | |
+ && dir_len + 4 + file_len < sizeof(tmp)) { | |
+ memcpy(tmp+dir_len+4, name, file_len+1); | |
+ unlink (tmp); | |
+ } | |
+ } | |
+ closedir (dirp); | |
+ } | |
+ tmp[dir_len+3] = '\0'; | |
+ rmdir (tmp); | |
+ } | |
+ tmp[dir_len-1] = '\0'; | |
+ if (rmdir (tmp) == 0) | |
+ return T; | |
+ else { | |
+ snprintf (tmp, sizeof(tmp), "Can't delete mailbox %s, errors " | |
+ "encountered removing files", mailbox); | |
+ mm_log (tmp,ERROR); | |
+ return NIL; | |
+ } | |
+} | |
+ | |
+ | |
+long | |
+maildir_rename (MAILSTREAM *stream, char *oldname, char *newname) | |
+{ | |
+ char new_path[MAILTMPLEN],old_path[MAILTMPLEN],*cur; | |
+ | |
+ /* old mailbox must be valid, and new mailbox must not already exist */ | |
+ if (!maildir_isvalid (oldname,NIL)) { | |
+ snprintf (new_path, sizeof(new_path), | |
+ "Can't rename mailbox %s: no such mailbox", oldname); | |
+ mm_log (new_path,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ maildir_path (new_path,sizeof(new_path),newname); | |
+ if ((cur = strrchr(new_path,'/')) && memcmp(cur,"/cur",5) == 0) | |
+ *cur = '\0'; | |
+ | |
+ if (access (new_path,F_OK) == 0) { | |
+ snprintf (new_path, sizeof(new_path), "Can't rename to mailbox %s: " | |
+ "destination already exists", newname); | |
+ mm_log (new_path,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ maildir_path (old_path,sizeof(old_path),oldname); | |
+ if ((cur = strrchr(old_path,'/')) && memcmp(cur,"/cur",5) == 0) | |
+ *cur = '\0'; | |
+ | |
+ if (rename (old_path, new_path) != 0) { | |
+ snprintf (new_path,sizeof(new_path),"Can't rename mailbox %s to %s: %s", | |
+ oldname, newname, strerror (errno)); | |
+ mm_log (new_path,ERROR); | |
+ return NIL; | |
+ } | |
+ | |
+ if (stream && LOCAL) { | |
+ *cur = '/'; | |
+ if (strcmp (old_path, LOCAL->dir) == 0) { | |
+ fs_give ((void **) &LOCAL->dir); | |
+ LOCAL->dir = cpystr (old_path); | |
+ } | |
+ } | |
+ | |
+ return T; | |
+} | |
+ | |
+ | |
+#if 0 | |
+long | |
+maildir_sub (MAILSTREAM *stream, char *mailbox) | |
+{ | |
+ return sm_subscribe (mailbox); | |
+} | |
+ | |
+ | |
+long | |
+maildir_unsub (MAILSTREAM *stream, char *mailbox) | |
+{ | |
+ return sm_unsubscribe (mailbox); | |
+} | |
+#endif | |
+ | |
+ | |
+/* | |
+ * XXX: maildir_lsub() and maildir_list() are incomplete | |
+ * See http://www.math.washington.edu/~chappa/pine/info/maildir.html patch | |
+ * or similar code in mh.c, but do not copy sloppy coding (e.g. strcpy()) | |
+ */ | |
+ | |
+void | |
+maildir_lsub (MAILSTREAM *stream, char *ref, char *pat) | |
+{ | |
+ void *sdb = NIL; | |
+ char *s; | |
+ | |
+ /* cycle through subscriptions and match against canonical form of name */ | |
+ while ((s = sm_read (&sdb))) { | |
+ if (pmatch_full (s,pat,'/')) | |
+ mm_lsub (stream,'/',s,NIL); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+maildir_list (MAILSTREAM *stream,char *ref, char *pat) | |
+{ | |
+ if (pat && is_md_inbox(pat)) { | |
+ mm_list (stream,NIL,"INBOX",LATT_NOINFERIORS); | |
+ } | |
+} | |
+ | |
+ | |
+void * | |
+maildir_parameters (long function,void *value) | |
+{ | |
+ return NULL; | |
+} | |
--- /dev/null | |
+++ alpine-1.0+dfsg/imap/src/osdep/unix/maildir.h | |
@@ -0,0 +1,51 @@ | |
+/* | |
+ * Please read maildir.c for license and information | |
+ * | |
+ */ | |
+#ifndef C_CLIENT_MAILDIR_H | |
+#define C_CLIENT_MAILDIR_H | |
+ | |
+ | |
+#define MAILDIRPATH "Maildir" | |
+ | |
+typedef struct maildir_local { | |
+ mailcache_t mc; /* mailcache function */ | |
+ const char *svc; /* servicename of program using c-client lib */ | |
+ char *dir; /* mail directory name */ | |
+ char *buf; /* temporary buffer */ | |
+ unsigned long buflen; /* current size of temporary buffer */ | |
+ time_t scantime; /* last time directory scanned */ | |
+ time_t last_refresh; /* last time directory actually refreshed */ | |
+} MAILDIRLOCAL; | |
+ | |
+/* Convenient access to local data */ | |
+ | |
+#define LOCAL ((MAILDIRLOCAL *) stream->local) | |
+ | |
+/* Function prototypes */ | |
+ | |
+DRIVER *maildir_valid (char *mailbox); | |
+MAILSTREAM *maildir_open (MAILSTREAM *stream); | |
+void maildir_gc (MAILSTREAM *stream,long gcflags); | |
+void maildir_close (MAILSTREAM *stream, long options); | |
+long maildir_ping (MAILSTREAM *stream); | |
+void maildir_check (MAILSTREAM *stream); | |
+long maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); | |
+char *maildir_fetchheader (MAILSTREAM *stream,unsigned long msgno, | |
+ unsigned long *length, long flags); | |
+void maildir_fetchfast (MAILSTREAM *stream,char *sequence,long flags); | |
+void maildir_list (MAILSTREAM *stream,char *ref,char *pat); | |
+void *maildir_parameters (long function,void *value); | |
+long maildir_create (MAILSTREAM *stream,char *mailbox); | |
+void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); | |
+void maildir_expunge (MAILSTREAM *stream); | |
+long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); | |
+long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); | |
+long maildir_delete (MAILSTREAM *stream,char *mailbox); | |
+long maildir_rename (MAILSTREAM *stream,char *oldname,char *newname); | |
+long maildir_sub (MAILSTREAM *stream,char *mailbox); | |
+long maildir_unsub (MAILSTREAM *stream,char *mailbox); | |
+void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat); | |
+ | |
+ | |
+#endif /* C_CLIENT_MAILDIR_H */ | |
--- alpine-1.0+dfsg.orig/imap/src/osdep/unix/Makefile | |
+++ alpine-1.0+dfsg/imap/src/osdep/unix/Makefile | |
@@ -27,7 +27,7 @@ | |
# Command line build parameters | |
EXTRAAUTHENTICATORS= | |
-EXTRADRIVERS=mbox | |
+EXTRADRIVERS=maildir mbox | |
PASSWDTYPE=std | |
SSLTYPE=nopwd | |
IP=4 | |
@@ -144,7 +144,7 @@ | |
# However, mh needs to be before any sysinbox formats (such as mmdf or unix) | |
# since otherwise INBOX won't work correctly when mh_allow_inbox is set. | |
# | |
-DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile | |
+DEFAULTDRIVERS=maildir imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile | |
CHUNKSIZE=65536 | |
# Normally no need to change any of these | |
@@ -153,7 +153,7 @@ | |
BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \ | |
dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ | |
rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ | |
- unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o | |
+ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o maildir.o | |
CFLAGS=-g | |
CAT=cat | |
@@ -892,6 +892,7 @@ | |
unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h | |
utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c | |
utf8aux.o: mail.h misc.h osdep.h utf8.h | |
+maildir.o: mail.h misc.h osdep.h maildir.h dummy.h | |
# OS-dependent | |
--- alpine-1.0+dfsg.orig/pith/sort.c | |
+++ alpine-1.0+dfsg/pith/sort.c | |
@@ -128,7 +128,9 @@ | |
raw_current = mn_m2raw(msgmap, mn_get_cur(msgmap)); | |
- if(new_sort == SortArrival){ | |
+ /*(do not bypass SortArrival sorting; needed for maildir arrival sorting) */ | |
+ /* if(new_sort == SortArrival){ */ | |
+ if(0){ | |
/* | |
* NOTE: RE c-client sorting, our idea of arrival is really | |
* just the natural sequence order. C-client, and probably |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment