1 /*
2 * mbsync - mailbox synchronizer
3 * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 2002-2006,2010-2013 Oswald Buddenhagen <ossi@users.sf.net>
5 * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * As a special exception, mbsync may be linked with the OpenSSL library,
21 * despite that library's more restrictive license.
22 */
23
24 #include "driver.h"
25
26 #include <assert.h>
27 #include <limits.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <utime.h>
39
40 #if !defined(_POSIX_SYNCHRONIZED_IO) || _POSIX_SYNCHRONIZED_IO <= 0
41 # define fdatasync fsync
42 #endif
43
44 #ifdef USE_DB
45 #include <db.h>
46 #endif /* USE_DB */
47
48 #define SUB_UNSET 0
49 #define SUB_VERBATIM 1
50 #define SUB_MAILDIRPP 2
51 #define SUB_LEGACY 3
52
53 typedef union maildir_store_conf {
54 store_conf_t gen;
55 struct {
56 STORE_CONF
57 char *path;
58 char *inbox;
59 #ifdef USE_DB
60 int alt_map;
61 #endif /* USE_DB */
62 char info_delimiter;
63 char sub_style;
64 char failed;
65 char *info_prefix, *info_stop; /* precalculated from info_delimiter */
66 };
67 } maildir_store_conf_t;
68
69 typedef union maildir_message {
70 message_t gen;
71 struct {
72 MESSAGE(union maildir_message)
73 char *base;
74 };
75 } maildir_message_t;
76
77 typedef union maildir_store {
78 store_t gen;
79 struct {
80 STORE(union maildir_store)
81 int uvfd, uvok, is_inbox, fresh[3];
82 uint opts, minuid, maxuid, finduid, pairuid, newuid, uidvalidity, nuid;
83 uint_array_t excs;
84 char *path; // own
85 char *trash;
86 #ifdef USE_DB
87 DB *db;
88 char *usedb;
89 #endif /* USE_DB */
90 string_list_t *boxes; // _list results
91 char listed; // was _list already run with these flags?
92 // note that the message counts do _not_ reflect stats from msgs,
93 // but mailbox totals. also, don't trust them beyond the initial load.
94 int total_msgs, recent_msgs;
95 maildir_message_t *msgs;
96 wakeup_t lcktmr;
97
98 void (*bad_callback)( void *aux );
99 void *bad_callback_aux;
100 };
101 } maildir_store_t;
102
103 #ifdef USE_DB
104 static DBT key, value; /* no need to be reentrant, and this saves lots of memset()s */
105 #endif /* USE_DB */
106 static struct flock lck;
107
108 static int MaildirCount;
109
110 static void ATTR_PRINTFLIKE(1, 2)
debug(const char * msg,...)111 debug( const char *msg, ... )
112 {
113 va_list va;
114
115 va_start( va, msg );
116 vdebug( DEBUG_SYNC, msg, va );
117 va_end( va );
118 }
119
120 /* Keep the mailbox driver flag definitions in sync: */
121 /* grep for MAILBOX_DRIVER_FLAG */
122 /* The order is according to alphabetical maildir flag sort */
123 static const char Flags[] = { 'D', 'F', 'P', 'R', 'S', 'T' };
124
125 static uchar
maildir_parse_flags(const char * info_prefix,const char * base)126 maildir_parse_flags( const char *info_prefix, const char *base )
127 {
128 const char *s;
129 uint i;
130 uchar flags;
131
132 flags = 0;
133 if ((s = strstr( base, info_prefix )))
134 for (s += 3, i = 0; i < as(Flags); i++)
135 if (strchr( s, Flags[i] ))
136 flags |= (1 << i);
137 return flags;
138 }
139
140 static int
maildir_ensure_path(maildir_store_conf_t * conf)141 maildir_ensure_path( maildir_store_conf_t *conf )
142 {
143 if (!conf->path) {
144 error( "Maildir error: store '%s' has no Path\n", conf->name );
145 conf->failed = FAIL_FINAL;
146 return -1;
147 }
148 return 0;
149 }
150
151 /* Subdirs of INBOX include a leading slash. */
152 /* Path includes a trailing slash, Inbox does not. */
153 static char *
maildir_join_path(maildir_store_conf_t * conf,int in_inbox,const char * box)154 maildir_join_path( maildir_store_conf_t *conf, int in_inbox, const char *box )
155 {
156 char *out, *p;
157 const char *prefix;
158 uint pl, bl, n;
159 char c;
160
161 if (in_inbox || conf->sub_style == SUB_MAILDIRPP) {
162 prefix = conf->inbox;
163 } else {
164 if (maildir_ensure_path( conf ) < 0)
165 return NULL;
166 prefix = conf->path;
167 }
168 pl = strlen( prefix );
169 for (bl = 0, n = 0; (c = box[bl]); bl++)
170 if (c == '/') {
171 if (conf->sub_style == SUB_UNSET) {
172 error( "Maildir error: accessing subfolder '%s', but store '%s' does not specify SubFolders style\n",
173 box, conf->name );
174 return NULL;
175 }
176 n++;
177 } else if (c == '.' && conf->sub_style == SUB_MAILDIRPP) {
178 error( "Maildir error: store '%s', folder '%s': SubFolders style Maildir++ does not support dots in mailbox names\n",
179 conf->name, box );
180 return NULL;
181 }
182 switch (conf->sub_style) {
183 case SUB_VERBATIM:
184 n = 0;
185 break;
186 case SUB_MAILDIRPP:
187 n = 2;
188 break;
189 default: /* SUB_LEGACY and SUB_UNSET */
190 break;
191 }
192 out = nfmalloc( pl + bl + n + 1 );
193 memcpy( out, prefix, pl );
194 p = out + pl;
195 if (conf->sub_style == SUB_MAILDIRPP) {
196 *p++ = '/';
197 *p++ = '.';
198 }
199 while ((c = *box++)) {
200 if (c == '/') {
201 switch (conf->sub_style) {
202 case SUB_VERBATIM:
203 *p++ = c;
204 break;
205 case SUB_LEGACY:
206 *p++ = c;
207 FALLTHROUGH
208 default: /* SUB_MAILDIRPP */
209 *p++ = '.';
210 break;
211 }
212 } else {
213 *p++ = c;
214 }
215 }
216 *p = 0;
217 return out;
218 }
219
220 static int
maildir_validate_path(maildir_store_conf_t * conf)221 maildir_validate_path( maildir_store_conf_t *conf )
222 {
223 struct stat st;
224
225 if (stat( conf->path, &st ) || !S_ISDIR(st.st_mode)) {
226 error( "Maildir error: cannot open store '%s'\n", conf->path );
227 conf->failed = FAIL_FINAL;
228 return -1;
229 }
230 return 0;
231 }
232
233 static void lcktmr_timeout( void *aux );
234
235 static store_t *
maildir_alloc_store(store_conf_t * gconf,const char * label ATTR_UNUSED)236 maildir_alloc_store( store_conf_t *gconf, const char *label ATTR_UNUSED )
237 {
238 maildir_store_t *ctx;
239
240 ctx = nfcalloc( sizeof(*ctx) );
241 ctx->driver = &maildir_driver;
242 ctx->gen.conf = gconf;
243 ctx->uvfd = -1;
244 init_wakeup( &ctx->lcktmr, lcktmr_timeout, ctx );
245 return &ctx->gen;
246 }
247
248 static void
maildir_connect_store(store_t * gctx,void (* cb)(int sts,void * aux),void * aux)249 maildir_connect_store( store_t *gctx,
250 void (*cb)( int sts, void *aux ), void *aux )
251 {
252 maildir_store_t *ctx = (maildir_store_t *)gctx;
253 maildir_store_conf_t *conf = ctx->conf;
254
255 if (conf->path && maildir_validate_path( conf ) < 0) {
256 cb( DRV_STORE_BAD, aux );
257 return;
258 }
259 if (conf->trash && !(ctx->trash = maildir_join_path( conf, 0, conf->trash ))) {
260 cb( DRV_STORE_BAD, aux );
261 return;
262 }
263 cb( DRV_OK, aux );
264 }
265
266 static void
free_maildir_messages(maildir_message_t * msg)267 free_maildir_messages( maildir_message_t *msg )
268 {
269 for (maildir_message_t *tmsg; (tmsg = msg); msg = tmsg) {
270 tmsg = msg->next;
271 free( msg->base );
272 free( msg );
273 }
274 }
275
276 static void
maildir_cleanup(store_t * gctx)277 maildir_cleanup( store_t *gctx )
278 {
279 maildir_store_t *ctx = (maildir_store_t *)gctx;
280
281 free_maildir_messages( ctx->msgs );
282 #ifdef USE_DB
283 if (ctx->db)
284 ctx->db->close( ctx->db, 0 );
285 free( ctx->usedb );
286 #endif /* USE_DB */
287 free( ctx->path );
288 free( ctx->excs.data );
289 if (ctx->uvfd >= 0)
290 close( ctx->uvfd );
291 conf_wakeup( &ctx->lcktmr, -1 );
292 }
293
294 static void
maildir_free_store(store_t * gctx)295 maildir_free_store( store_t *gctx )
296 {
297 maildir_store_t *ctx = (maildir_store_t *)gctx;
298
299 maildir_cleanup( gctx );
300 wipe_wakeup( &ctx->lcktmr );
301 free( ctx->trash );
302 free_string_list( ctx->boxes );
303 free( gctx );
304 }
305
306 static void
maildir_cleanup_drv(void)307 maildir_cleanup_drv( void )
308 {
309 }
310
311 static void
maildir_set_bad_callback(store_t * gctx,void (* cb)(void * aux),void * aux)312 maildir_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
313 {
314 maildir_store_t *ctx = (maildir_store_t *)gctx;
315
316 ctx->bad_callback = cb;
317 ctx->bad_callback_aux = aux;
318 }
319
320 static void
maildir_invoke_bad_callback(maildir_store_t * ctx)321 maildir_invoke_bad_callback( maildir_store_t *ctx )
322 {
323 ctx->bad_callback( ctx->bad_callback_aux );
324 }
325
326 static int
maildir_list_maildirpp(maildir_store_t * ctx,int flags,const char * inbox)327 maildir_list_maildirpp( maildir_store_t *ctx, int flags, const char *inbox )
328 {
329 DIR *dir;
330 struct dirent *de;
331 int warned = 0;
332 struct stat st;
333
334 if (ctx->listed & LIST_PATH) // Implies LIST_INBOX
335 return 0;
336
337 if (!(ctx->listed & LIST_INBOX))
338 add_string_list( &ctx->boxes, "INBOX" );
339
340 char path[_POSIX_PATH_MAX];
341 int pathLen = nfsnprintf( path, _POSIX_PATH_MAX, "%s/", inbox );
342 if (!(dir = opendir( path ))) {
343 if (errno == ENOENT || errno == ENOTDIR)
344 return 0;
345 sys_error( "Maildir error: cannot list %s", path );
346 return -1;
347 }
348 while ((de = readdir( dir ))) {
349 const char *ent = de->d_name;
350 if (*ent++ != '.' || !*ent)
351 continue;
352 char name[_POSIX_PATH_MAX];
353 char *effName = name;
354 if (*ent == '.') {
355 if (ctx->listed & LIST_INBOX)
356 continue;
357 if (!*++ent)
358 continue;
359 // The Maildir++ Inbox is technically not under Path (as there is none), so
360 // "*" would never match INBOX*, which is rather unintuitive. Matching INBOX*
361 // implicitly instead makes it consistent with an IMAP Store with an empty Path.
362 } else {
363 if (!(flags & (LIST_PATH | LIST_PATH_MAYBE)))
364 continue;
365 // Explained in maildir_list_recurse().
366 if (starts_with( ent, -1, "INBOX", 5 ) && (!ent[5] || ent[5] == '.')) {
367 if (!warned) {
368 warned = 1;
369 path[pathLen] = 0;
370 warn( "Maildir warning: ignoring INBOX in %s\n", path );
371 }
372 continue;
373 }
374 effName += 6;
375 }
376 nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s/cur", de->d_name );
377 if (!stat( path, &st ) && S_ISDIR(st.st_mode)) {
378 int nl = nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/%s", ent );
379 for (int i = 6; i < nl; i++) {
380 if (name[i] == '.')
381 name[i] = '/';
382 }
383 add_string_list( &ctx->boxes, effName );
384 }
385 }
386 closedir (dir);
387
388 if (flags & (LIST_PATH | LIST_PATH_MAYBE))
389 ctx->listed |= LIST_PATH;
390 ctx->listed |= LIST_INBOX;
391 return 0;
392 }
393
394 static int maildir_list_inbox( maildir_store_t *ctx, int flags, const char *basePath );
395 static int maildir_list_path( maildir_store_t *ctx, int flags, const char *inbox );
396
397 static int
maildir_list_recurse(maildir_store_t * ctx,int isBox,int flags,int depth,const char * inbox,uint inboxLen,const char * basePath,uint basePathLen,char * path,int pathLen,char * name,int nameLen)398 maildir_list_recurse( maildir_store_t *ctx, int isBox, int flags, int depth,
399 const char *inbox, uint inboxLen, const char *basePath, uint basePathLen,
400 char *path, int pathLen, char *name, int nameLen )
401 {
402 DIR *dir;
403 int style = ctx->conf->sub_style;
404 int pl, nl;
405 struct dirent *de;
406 struct stat st;
407
408 if (!(dir = opendir( path ))) {
409 if (isBox && (errno == ENOENT || errno == ENOTDIR))
410 return 0;
411 sys_error( "Maildir error: cannot list %s", path );
412 return -1;
413 }
414 if (isBox > 1 && style == SUB_UNSET) {
415 error( "Maildir error: found subfolder '%.*s', but store '%s' does not specify SubFolders style\n",
416 nameLen - 1, name, ctx->conf->name );
417 closedir( dir );
418 return -1;
419 }
420 if (++depth > 10) {
421 // We do the other checks first to avoid confusing error messages for files.
422 error( "Maildir error: path %s is too deeply nested. Symlink loop?\n", path );
423 closedir( dir );
424 return -1;
425 }
426 while ((de = readdir( dir ))) {
427 const char *ent = de->d_name;
428 if (ent[0] == '.' && (!ent[1] || (ent[1] == '.' && !ent[2])))
429 continue;
430 pl = nfsnprintf( path + pathLen, _POSIX_PATH_MAX - pathLen, "%s", ent );
431 if (pl == 3 && (!memcmp( ent, "cur", 3 ) || !memcmp( ent, "new", 3 ) || !memcmp( ent, "tmp", 3 )))
432 continue;
433 pl += pathLen;
434 if (inbox && equals( path, pl, inbox, inboxLen )) {
435 // Inbox nested into Path.
436 if (maildir_list_inbox( ctx, flags, NULL ) < 0) {
437 closedir( dir );
438 return -1;
439 }
440 } else if (basePath && equals( path, pl, basePath, basePathLen )) {
441 // Path nested into Inbox.
442 if (maildir_list_path( ctx, flags, NULL ) < 0) {
443 closedir( dir );
444 return -1;
445 }
446 } else {
447 if (style == SUB_LEGACY) {
448 if (*ent == '.') {
449 if (!isBox)
450 continue;
451 ent++;
452 } else {
453 if (isBox)
454 continue;
455 }
456 }
457 // A folder named "INBOX" would be indistinguishable from the
458 // actual INBOX after prefix stripping, so drop it. This applies
459 // only to the fully uppercased spelling, as our canonical box
460 // names are case-sensitive (unlike IMAP's INBOX).
461 if (!nameLen && equals( ent, -1, "INBOX", 5 )) {
462 path[pathLen] = 0;
463 warn( "Maildir warning: ignoring INBOX in %s\n", path );
464 continue;
465 }
466 nl = nameLen + nfsnprintf( name + nameLen, _POSIX_PATH_MAX - nameLen, "%s", ent );
467 path[pl++] = '/';
468 nfsnprintf( path + pl, _POSIX_PATH_MAX - pl, "cur" );
469 if (!stat( path, &st ) && S_ISDIR(st.st_mode))
470 add_string_list( &ctx->boxes, name );
471 path[pl] = 0;
472 name[nl++] = '/';
473 if (maildir_list_recurse( ctx, isBox + 1, flags, depth, inbox, inboxLen, basePath, basePathLen, path, pl, name, nl ) < 0) {
474 closedir( dir );
475 return -1;
476 }
477 }
478 }
479 closedir (dir);
480 return 0;
481 }
482
483 static int
maildir_list_inbox(maildir_store_t * ctx,int flags,const char * basePath)484 maildir_list_inbox( maildir_store_t *ctx, int flags, const char *basePath )
485 {
486 char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
487
488 if (ctx->listed & LIST_INBOX)
489 return 0;
490 ctx->listed |= LIST_INBOX;
491
492 add_string_list( &ctx->boxes, "INBOX" );
493 return maildir_list_recurse(
494 ctx, 1, flags, 0, NULL, 0, basePath, basePath ? strlen( basePath ) - 1 : 0,
495 path, nfsnprintf( path, _POSIX_PATH_MAX, "%s/", ctx->conf->inbox ),
496 name, nfsnprintf( name, _POSIX_PATH_MAX, "INBOX/" ) );
497 }
498
499 static int
maildir_list_path(maildir_store_t * ctx,int flags,const char * inbox)500 maildir_list_path( maildir_store_t *ctx, int flags, const char *inbox )
501 {
502 char path[_POSIX_PATH_MAX], name[_POSIX_PATH_MAX];
503
504 if (ctx->listed & LIST_PATH)
505 return 0;
506 ctx->listed |= LIST_PATH;
507
508 if (maildir_ensure_path( ctx->conf ) < 0)
509 return -1;
510 return maildir_list_recurse(
511 ctx, 0, flags, 0, inbox, inbox ? strlen( inbox ) : 0, NULL, 0,
512 path, nfsnprintf( path, _POSIX_PATH_MAX, "%s", ctx->conf->path ),
513 name, 0 );
514 }
515
516 static void
maildir_list_store(store_t * gctx,int flags,void (* cb)(int sts,string_list_t * boxes,void * aux),void * aux)517 maildir_list_store( store_t *gctx, int flags,
518 void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux )
519 {
520 maildir_store_t *ctx = (maildir_store_t *)gctx;
521 maildir_store_conf_t *conf = ctx->conf;
522
523 if (conf->sub_style == SUB_MAILDIRPP
524 ? maildir_list_maildirpp( ctx, flags, conf->inbox ) < 0
525 : ((((flags & LIST_PATH) || ((flags & LIST_PATH_MAYBE) && conf->path))
526 && maildir_list_path( ctx, flags, conf->inbox ) < 0) ||
527 ((flags & LIST_INBOX)
528 && maildir_list_inbox( ctx, flags, conf->path ) < 0))) {
529 maildir_invoke_bad_callback( ctx );
530 cb( DRV_CANCELED, NULL, aux );
531 } else {
532 cb( DRV_OK, ctx->boxes, aux );
533 }
534 }
535
536 static const char *subdirs[] = { "cur", "new", "tmp" };
537
538 typedef struct {
539 char *base;
540 char *msgid;
541 uint size;
542 uint uid;
543 uchar recent;
544 char tuid[TUIDL];
545 } msg_t;
546
DEFINE_ARRAY_TYPE(msg_t)547 DEFINE_ARRAY_TYPE(msg_t)
548
549 static void
550 maildir_free_scan( msg_t_array_alloc_t *msglist )
551 {
552 uint i;
553
554 if (msglist->array.data) {
555 for (i = 0; i < msglist->array.size; i++)
556 free( msglist->array.data[i].base );
557 free( msglist->array.data );
558 }
559 }
560
561 #define _24_HOURS (3600 * 24)
562
563 static int
maildir_clear_tmp(char * buf,int bufsz,int bl)564 maildir_clear_tmp( char *buf, int bufsz, int bl )
565 {
566 DIR *dirp;
567 struct dirent *entry;
568 time_t now;
569 struct stat st;
570
571 memcpy( buf + bl, "tmp/", 5 );
572 bl += 4;
573 if (!(dirp = opendir( buf ))) {
574 sys_error( "Maildir error: cannot list %s", buf );
575 return DRV_BOX_BAD;
576 }
577 time( &now );
578 while ((entry = readdir( dirp ))) {
579 nfsnprintf( buf + bl, bufsz - bl, "%s", entry->d_name );
580 if (stat( buf, &st )) {
581 if (errno != ENOENT)
582 sys_error( "Maildir error: cannot access %s", buf );
583 } else if (S_ISREG(st.st_mode) && now - st.st_ctime >= _24_HOURS) {
584 /* This should happen infrequently enough that it won't be
585 * bothersome to the user to display when it occurs.
586 */
587 notice( "Maildir notice: removing stale file %s\n", buf );
588 if (unlink( buf ) && errno != ENOENT)
589 sys_error( "Maildir error: cannot remove %s", buf );
590 }
591 }
592 closedir( dirp );
593 return DRV_OK;
594 }
595
596 static int
make_box_dir(char * buf,int bl)597 make_box_dir( char *buf, int bl )
598 {
599 char *p;
600
601 if (!mkdir( buf, 0700 ) || errno == EEXIST)
602 return 0;
603 p = memrchr( buf, '/', (size_t)bl - 1 );
604 *p = 0;
605 if (make_box_dir( buf, (int)(p - buf) ))
606 return -1;
607 *p = '/';
608 return mkdir( buf, 0700 );
609 }
610
611 static int
maildir_validate(const char * box,int create,maildir_store_t * ctx)612 maildir_validate( const char *box, int create, maildir_store_t *ctx )
613 {
614 int i, bl, ret;
615 struct stat st;
616 char buf[_POSIX_PATH_MAX];
617
618 bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box );
619 if (stat( buf, &st )) {
620 if (errno != ENOENT) {
621 sys_error( "Maildir error: cannot access mailbox '%s'", box );
622 return DRV_BOX_BAD;
623 }
624 if (!create)
625 return DRV_BOX_BAD;
626 if (make_box_dir( buf, bl )) {
627 sys_error( "Maildir error: cannot create mailbox '%s'", box );
628 ctx->conf->failed = FAIL_FINAL;
629 maildir_invoke_bad_callback( ctx );
630 return DRV_CANCELED;
631 }
632 } else if (!S_ISDIR(st.st_mode)) {
633 notdir:
634 error( "Maildir error: '%s' is no valid mailbox\n", box );
635 return DRV_BOX_BAD;
636 }
637 for (i = 0; i < 3; i++) {
638 memcpy( buf + bl, subdirs[i], 4 );
639 if (stat( buf, &st )) {
640 /* We always create new/ and tmp/ if they are missing. cur/ is the presence indicator. */
641 if (!i && !create)
642 return DRV_BOX_BAD;
643 if (mkdir( buf, 0700 )) {
644 sys_error( "Maildir error: cannot create directory %s", buf );
645 return DRV_BOX_BAD;
646 }
647 ctx->fresh[i] = 1;
648 } else if (!S_ISDIR(st.st_mode)) {
649 goto notdir;
650 } else {
651 if (i == 2) {
652 if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) != DRV_OK)
653 return ret;
654 }
655 }
656 }
657 return DRV_OK;
658 }
659
660 #ifdef USE_DB
661 static void
make_key(const char * info_stop,DBT * tkey,const char * name)662 make_key( const char *info_stop, DBT *tkey, const char *name )
663 {
664 char *u = strpbrk( name, info_stop );
665 DIAG_PUSH
666 DIAG_DISABLE("-Wcast-qual") // C has no const_cast<> ...
667 tkey->data = (char *)name;
668 DIAG_POP
669 tkey->size = u ? (size_t)(u - name) : strlen( name );
670 }
671 #endif /* USE_DB */
672
673 static int
maildir_store_uidval(maildir_store_t * ctx)674 maildir_store_uidval( maildir_store_t *ctx )
675 {
676 int n;
677 #ifdef USE_DB
678 int ret;
679 uint uv[2];
680 #endif
681 char buf[128];
682
683 #ifdef USE_DB
684 if (ctx->db) {
685 key.data = (void *)"UIDVALIDITY";
686 key.size = 11;
687 uv[0] = ctx->uidvalidity;
688 uv[1] = ctx->nuid;
689 value.data = uv;
690 value.size = sizeof(uv);
691 if ((ret = ctx->db->put( ctx->db, NULL, &key, &value, 0 ))) {
692 ctx->db->err( ctx->db, ret, "Maildir error: db->put()" );
693 return DRV_BOX_BAD;
694 }
695 if ((ret = ctx->db->sync( ctx->db, 0 ))) {
696 ctx->db->err( ctx->db, ret, "Maildir error: db->sync()" );
697 return DRV_BOX_BAD;
698 }
699 } else
700 #endif /* USE_DB */
701 {
702 n = sprintf( buf, "%u\n%u\n", ctx->uidvalidity, ctx->nuid );
703 lseek( ctx->uvfd, 0, SEEK_SET );
704 if (write( ctx->uvfd, buf, (uint)n ) != n || ftruncate( ctx->uvfd, n ) || (UseFSync && fdatasync( ctx->uvfd ))) {
705 error( "Maildir error: cannot write UIDVALIDITY.\n" );
706 return DRV_BOX_BAD;
707 }
708 }
709 conf_wakeup( &ctx->lcktmr, 2 );
710 return DRV_OK;
711 }
712
713 static int
maildir_init_uidval(maildir_store_t * ctx)714 maildir_init_uidval( maildir_store_t *ctx )
715 {
716 ctx->uidvalidity = (uint)time( NULL );
717 ctx->nuid = 0;
718 ctx->uvok = 0;
719 #ifdef USE_DB
720 if (ctx->db) {
721 u_int32_t count;
722 ctx->db->truncate( ctx->db, NULL, &count, 0 );
723 }
724 #endif /* USE_DB */
725 return maildir_store_uidval( ctx );
726 }
727
728 static int
maildir_init_uidval_new(maildir_store_t * ctx)729 maildir_init_uidval_new( maildir_store_t *ctx )
730 {
731 notice( "Maildir notice: no UIDVALIDITY, creating new.\n" );
732 return maildir_init_uidval( ctx );
733 }
734
735 static int
maildir_uidval_lock(maildir_store_t * ctx)736 maildir_uidval_lock( maildir_store_t *ctx )
737 {
738 int n;
739 #ifdef USE_DB
740 int ret;
741 struct stat st;
742 #endif
743 char buf[128];
744
745 if (pending_wakeup( &ctx->lcktmr )) {
746 /* The unlock timer is active, so we are obviously already locked. */
747 return DRV_OK;
748 }
749 /* This (theoretically) works over NFS. Let's hope nobody else did
750 the same in the opposite order, as we'd deadlock then. */
751 #if SEEK_SET != 0
752 lck.l_whence = SEEK_SET;
753 #endif
754 lck.l_type = F_WRLCK;
755 if (fcntl( ctx->uvfd, F_SETLKW, &lck )) {
756 error( "Maildir error: cannot fcntl lock UIDVALIDITY.\n" );
757 return DRV_BOX_BAD;
758 }
759
760 #ifdef USE_DB
761 if (ctx->usedb) {
762 if (fstat( ctx->uvfd, &st )) {
763 sys_error( "Maildir error: cannot fstat UID database" );
764 return DRV_BOX_BAD;
765 }
766 if (db_create( &ctx->db, NULL, 0 )) {
767 fputs( "Maildir error: db_create() failed\n", stderr );
768 return DRV_BOX_BAD;
769 }
770 if ((ret = (ctx->db->open)( ctx->db, NULL, ctx->usedb, NULL, DB_HASH,
771 st.st_size ? 0 : DB_CREATE | DB_TRUNCATE, 0 ))) {
772 ctx->db->err( ctx->db, ret, "Maildir error: db->open(%s)", ctx->usedb );
773 return DRV_BOX_BAD;
774 }
775 key.data = (void *)"UIDVALIDITY";
776 key.size = 11;
777 if ((ret = ctx->db->get( ctx->db, NULL, &key, &value, 0 ))) {
778 if (ret != DB_NOTFOUND) {
779 ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
780 return DRV_BOX_BAD;
781 }
782 return maildir_init_uidval_new( ctx );
783 }
784 ctx->uidvalidity = ((uint *)value.data)[0];
785 ctx->nuid = ((uint *)value.data)[1];
786 } else
787 #endif
788 {
789 lseek( ctx->uvfd, 0, SEEK_SET );
790 if ((n = read( ctx->uvfd, buf, sizeof(buf) - 1 )) <= 0 ||
791 (buf[n] = 0, sscanf( buf, "%u\n%u", &ctx->uidvalidity, &ctx->nuid ) != 2)) {
792 #if 1
793 /* In a generic driver, resetting the UID validity would be the right thing.
794 * But this would mess up the sync state completely. So better bail out and
795 * give the user a chance to fix the mailbox. */
796 if (n) {
797 error( "Maildir error: cannot read UIDVALIDITY.\n" );
798 return DRV_BOX_BAD;
799 }
800 #endif
801 return maildir_init_uidval_new( ctx );
802 }
803 }
804 ctx->uvok = 1;
805 conf_wakeup( &ctx->lcktmr, 2 );
806 return DRV_OK;
807 }
808
809 static void
maildir_uidval_unlock(maildir_store_t * ctx)810 maildir_uidval_unlock( maildir_store_t *ctx )
811 {
812 #ifdef USE_DB
813 if (ctx->db) {
814 ctx->db->close( ctx->db, 0 );
815 ctx->db = NULL;
816 }
817 #endif /* USE_DB */
818 lck.l_type = F_UNLCK;
819 fcntl( ctx->uvfd, F_SETLK, &lck );
820 }
821
822 static void
lcktmr_timeout(void * aux)823 lcktmr_timeout( void *aux )
824 {
825 maildir_uidval_unlock( (maildir_store_t *)aux );
826 }
827
828 static int
maildir_obtain_uid(maildir_store_t * ctx,uint * uid)829 maildir_obtain_uid( maildir_store_t *ctx, uint *uid )
830 {
831 int ret;
832
833 if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
834 return ret;
835 *uid = ++ctx->nuid;
836 return maildir_store_uidval( ctx );
837 }
838
839 #ifdef USE_DB
840 static int
maildir_set_uid(maildir_store_t * ctx,const char * name,uint * uid)841 maildir_set_uid( maildir_store_t *ctx, const char *name, uint *uid )
842 {
843 int ret;
844
845 if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
846 return ret;
847 *uid = ++ctx->nuid;
848
849 make_key( ctx->conf->info_stop, &key, name );
850 value.data = uid;
851 value.size = sizeof(*uid);
852 if ((ret = ctx->db->put( ctx->db, NULL, &key, &value, 0 ))) {
853 ctx->db->err( ctx->db, ret, "Maildir error: db->put()" );
854 return DRV_BOX_BAD;
855 }
856 return maildir_store_uidval( ctx );
857 }
858 #endif
859
860 static int
maildir_compare(const void * l,const void * r)861 maildir_compare( const void *l, const void *r )
862 {
863 const msg_t *lm = (const msg_t *)l, *rm = (const msg_t *)r;
864 char *ldot, *rdot, *ldot2, *rdot2, *lseq, *rseq;
865 uint llen, rlen;
866 int ret;
867
868 if (lm->uid != rm->uid) // Can't subtract, the result might not fit into signed int.
869 return lm->uid > rm->uid ? 1 : -1;
870
871 /* No UID, so sort by arrival date. We should not do this, but we rely
872 on the suggested unique file name scheme - we have no choice. */
873 /* The first field are always the seconds. Alphabetical sort should be
874 faster than numeric. */
875 if (!(ldot = strchr( lm->base, '.' )) || !(rdot = strchr( rm->base, '.' )))
876 goto stronly; /* Should never happen ... */
877 llen = (uint)(ldot - lm->base), rlen = (uint)(rdot - rm->base);
878 /* The shorter number is smaller. Really. This won't trigger with any
879 mail created after Sep 9 2001 anyway. */
880 if ((ret = (int)llen - (int)rlen))
881 return ret;
882 if ((ret = memcmp( lm->base, rm->base, llen )))
883 return ret;
884
885 ldot++, rdot++;
886
887 if ((llen = strtoul( ldot, &ldot2, 10 ))) {
888 if (!(rlen = strtoul( rdot, &rdot2, 10 )))
889 goto stronly; /* Comparing apples to oranges ... */
890 /* Classical PID specs */
891 if ((ret = (int)llen - (int)rlen)) {
892 retpid:
893 /* Handle PID wraparound. This works only on systems
894 where PIDs are not reused too fast */
895 if (ret > 20000 || ret < -20000)
896 ret = -ret;
897 return ret;
898 }
899 return (*ldot2 != '_' ? 0 : atoi( ldot2 + 1 )) -
900 (*rdot2 != '_' ? 0 : atoi( rdot2 + 1 ));
901 }
902
903 if (!(ldot2 = strchr( ldot, '.' )) || !(rdot2 = strchr( rdot, '.' )))
904 goto stronly; /* Should never happen ... */
905 llen = (uint)(ldot2 - ldot), rlen = (uint)(rdot2 - rdot);
906
907 if (((lseq = memchr( ldot, '#', llen )) && (rseq = memchr( rdot, '#', rlen ))) ||
908 ((lseq = memchr( ldot, 'M', llen )) && (rseq = memchr( rdot, 'M', rlen ))))
909 return atoi( lseq + 1 ) - atoi( rseq + 1 );
910
911 if ((lseq = memchr( ldot, 'P', llen )) && (rseq = memchr( rdot, 'P', rlen ))) {
912 if ((ret = atoi( lseq + 1 ) - atoi( rseq + 1 )))
913 goto retpid;
914 if ((lseq = memchr( ldot, 'Q', llen )) && (rseq = memchr( rdot, 'Q', rlen )))
915 return atoi( lseq + 1 ) - atoi( rseq + 1 );
916 }
917
918 stronly:
919 /* Fall-back, so the sort order is defined at all */
920 return strcmp( lm->base, rm->base );
921 }
922
923 static int
maildir_scan(maildir_store_t * ctx,msg_t_array_alloc_t * msglist)924 maildir_scan( maildir_store_t *ctx, msg_t_array_alloc_t *msglist )
925 {
926 maildir_store_conf_t *conf = ctx->conf;
927 DIR *d;
928 FILE *f;
929 struct dirent *e;
930 const char *u, *ru;
931 #ifdef USE_DB
932 DB *tdb;
933 DBC *dbc;
934 #endif /* USE_DB */
935 msg_t *entry;
936 uint i;
937 int bl, fnl, ret;
938 uint uid;
939 time_t now, stamps[2];
940 struct stat st;
941 char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX];
942
943 again:
944 ARRAY_INIT( msglist );
945 ctx->total_msgs = ctx->recent_msgs = 0;
946 if (ctx->uvok || ctx->maxuid == UINT_MAX) {
947 #ifdef USE_DB
948 if (ctx->usedb) {
949 if (db_create( &tdb, NULL, 0 )) {
950 fputs( "Maildir error: db_create() failed\n", stderr );
951 return DRV_BOX_BAD;
952 }
953 if ((tdb->open)( tdb, NULL, NULL, NULL, DB_HASH, DB_CREATE, 0 )) {
954 fputs( "Maildir error: tdb->open() failed\n", stderr );
955 bork:
956 tdb->close( tdb, 0 );
957 return DRV_BOX_BAD;
958 }
959 }
960 #endif /* USE_DB */
961 bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path );
962 restat:
963 now = time( NULL );
964 for (i = 0; i < 2; i++) {
965 memcpy( buf + bl, subdirs[i], 4 );
966 if (stat( buf, &st )) {
967 sys_error( "Maildir error: cannot stat %s", buf );
968 goto dfail;
969 }
970 if (st.st_mtime == now && !(DFlags & ZERODELAY) && !ctx->fresh[i]) {
971 /* If the modification happened during this second, we wouldn't be able to
972 * tell if there were further modifications during this second. So wait.
973 * This has the nice side effect that we wait for "batches" of changes to
974 * complete. On the downside, it can potentially block indefinitely. */
975 notice( "Maildir notice: sleeping due to recent directory modification.\n" );
976 sleep( 1 ); /* FIXME: should make this async */
977 goto restat;
978 }
979 stamps[i] = st.st_mtime;
980 }
981 for (i = 0; i < 2; i++) {
982 memcpy( buf + bl, subdirs[i], 4 );
983 if (!(d = opendir( buf ))) {
984 sys_error( "Maildir error: cannot list %s", buf );
985 rfail:
986 maildir_free_scan( msglist );
987 dfail:
988 #ifdef USE_DB
989 if (ctx->usedb)
990 tdb->close( tdb, 0 );
991 #endif /* USE_DB */
992 return DRV_BOX_BAD;
993 }
994 while ((e = readdir( d ))) {
995 if (*e->d_name == '.')
996 continue;
997 ctx->total_msgs++;
998 ctx->recent_msgs += i;
999 #ifdef USE_DB
1000 if (ctx->usedb) {
1001 if (maildir_uidval_lock( ctx ) != DRV_OK)
1002 goto mbork;
1003 make_key( conf->info_stop, &key, e->d_name );
1004 if ((ret = ctx->db->get( ctx->db, NULL, &key, &value, 0 ))) {
1005 if (ret != DB_NOTFOUND) {
1006 ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
1007 mbork:
1008 maildir_free_scan( msglist );
1009 closedir( d );
1010 goto bork;
1011 }
1012 uid = UINT_MAX;
1013 } else {
1014 value.size = 0;
1015 if ((ret = tdb->put( tdb, NULL, &key, &value, 0 ))) {
1016 tdb->err( tdb, ret, "Maildir error: tdb->put()" );
1017 goto mbork;
1018 }
1019 uid = *(uint *)value.data;
1020 }
1021 } else
1022 #endif /* USE_DB */
1023 {
1024 uid = (ctx->uvok && (u = strstr( e->d_name, ",U=" ))) ? strtoul( u + 3, NULL, 10 ) : 0;
1025 if (!uid)
1026 uid = UINT_MAX;
1027 }
1028 if (uid <= ctx->maxuid) {
1029 if (uid < ctx->minuid && !find_uint_array( ctx->excs, uid ))
1030 continue;
1031 entry = msg_t_array_append( msglist );
1032 entry->base = nfstrdup( e->d_name );
1033 entry->msgid = NULL;
1034 entry->uid = uid;
1035 entry->recent = (uchar)i;
1036 entry->size = 0;
1037 entry->tuid[0] = 0;
1038 }
1039 }
1040 closedir( d );
1041 }
1042 for (i = 0; i < 2; i++) {
1043 memcpy( buf + bl, subdirs[i], 4 );
1044 if (stat( buf, &st )) {
1045 sys_error( "Maildir error: cannot re-stat %s", buf );
1046 goto rfail;
1047 }
1048 if (st.st_mtime != stamps[i]) {
1049 /* Somebody messed with the mailbox since we started listing it. */
1050 #ifdef USE_DB
1051 if (ctx->usedb)
1052 tdb->close( tdb, 0 );
1053 #endif /* USE_DB */
1054 maildir_free_scan( msglist );
1055 goto again;
1056 }
1057 }
1058 #ifdef USE_DB
1059 if (ctx->usedb) {
1060 if (maildir_uidval_lock( ctx ) != DRV_OK)
1061 ;
1062 else if ((ret = ctx->db->cursor( ctx->db, NULL, &dbc, 0 )))
1063 ctx->db->err( ctx->db, ret, "Maildir error: db->cursor()" );
1064 else {
1065 for (;;) {
1066 if ((ret = dbc->c_get( dbc, &key, &value, DB_NEXT ))) {
1067 if (ret != DB_NOTFOUND)
1068 ctx->db->err( ctx->db, ret, "Maildir error: db->c_get()" );
1069 break;
1070 }
1071 if (!equals( key.data, (int)key.size, "UIDVALIDITY", 11 ) &&
1072 (ret = tdb->get( tdb, NULL, &key, &value, 0 ))) {
1073 if (ret != DB_NOTFOUND) {
1074 tdb->err( tdb, ret, "Maildir error: tdb->get()" );
1075 break;
1076 }
1077 if ((ret = dbc->c_del( dbc, 0 ))) {
1078 ctx->db->err( ctx->db, ret, "Maildir error: db->c_del()" );
1079 break;
1080 }
1081 }
1082 }
1083 dbc->c_close( dbc );
1084 }
1085 tdb->close( tdb, 0 );
1086 }
1087 #endif /* USE_DB */
1088 qsort( msglist->array.data, msglist->array.size, sizeof(msg_t), maildir_compare );
1089 for (uid = i = 0; i < msglist->array.size; i++) {
1090 entry = &msglist->array.data[i];
1091 if (entry->uid != UINT_MAX) {
1092 if (uid == entry->uid) {
1093 #if 1
1094 /* See comment in maildir_uidval_lock() why this is fatal. */
1095 error( "Maildir error: duplicate UID %u.\n", uid );
1096 maildir_free_scan( msglist );
1097 return DRV_BOX_BAD;
1098 #else
1099 notice( "Maildir notice: duplicate UID; changing UIDVALIDITY.\n");
1100 if ((ret = maildir_init_uid( ctx )) != DRV_OK) {
1101 maildir_free_scan( msglist );
1102 return ret;
1103 }
1104 maildir_free_scan( msglist );
1105 goto again;
1106 #endif
1107 }
1108 uid = entry->uid;
1109 if (uid > ctx->nuid) {
1110 /* In principle, we could just warn and top up nuid. However, getting into this
1111 * situation might indicate some serious trouble, so let's not make it worse. */
1112 error( "Maildir error: UID %u is beyond highest assigned UID %u.\n", uid, ctx->nuid );
1113 maildir_free_scan( msglist );
1114 return DRV_BOX_BAD;
1115 }
1116 fnl = 0;
1117 #ifdef USE_DB
1118 } else if (ctx->usedb) {
1119 if ((ret = maildir_set_uid( ctx, entry->base, &uid )) != DRV_OK) {
1120 maildir_free_scan( msglist );
1121 return ret;
1122 }
1123 entry->uid = uid;
1124 fnl = 0;
1125 #endif /* USE_DB */
1126 } else {
1127 if ((ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) {
1128 maildir_free_scan( msglist );
1129 return ret;
1130 }
1131 entry->uid = uid;
1132 if ((u = strstr( entry->base, ",U=" )))
1133 for (ru = u + 3; isdigit( (uchar)*ru ); ru++);
1134 else
1135 u = ru = strchr( entry->base, conf->info_delimiter );
1136 fnl = (u ?
1137 nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%.*s,U=%u%s", subdirs[entry->recent], (int)(u - entry->base), entry->base, uid, ru ) :
1138 nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%s,U=%u", subdirs[entry->recent], entry->base, uid ))
1139 - 4;
1140 memcpy( nbuf, buf, (size_t)(bl + 4) );
1141 nfsnprintf( nbuf + bl + 4, _POSIX_PATH_MAX - bl - 4, "%s", entry->base );
1142 if (rename( nbuf, buf )) {
1143 if (errno != ENOENT) {
1144 sys_error( "Maildir error: cannot rename %s to %s", nbuf, buf );
1145 fail:
1146 maildir_free_scan( msglist );
1147 return DRV_BOX_BAD;
1148 }
1149 retry:
1150 maildir_free_scan( msglist );
1151 goto again;
1152 }
1153 free( entry->base );
1154 entry->base = nfstrndup( buf + bl + 4, (size_t)fnl );
1155 }
1156 int want_size = ((ctx->opts & OPEN_NEW_SIZE) && uid > ctx->newuid);
1157 int want_tuid = ((ctx->opts & OPEN_FIND) && uid >= ctx->finduid);
1158 int want_msgid = ((ctx->opts & OPEN_OLD_IDS) && uid <= ctx->pairuid);
1159 if (!want_size && !want_tuid && !want_msgid)
1160 continue;
1161 if (!fnl)
1162 nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, "%s/%s", subdirs[entry->recent], entry->base );
1163 if (want_size) {
1164 if (stat( buf, &st )) {
1165 if (errno != ENOENT) {
1166 sys_error( "Maildir error: cannot stat %s", buf );
1167 goto fail;
1168 }
1169 goto retry;
1170 }
1171 // The clipped value is good enough for MaxSize comparisons.
1172 entry->size = st.st_size > UINT_MAX ? UINT_MAX : (uint)st.st_size;
1173 }
1174 if (want_tuid || want_msgid) {
1175 if (!(f = fopen( buf, "r" ))) {
1176 if (errno != ENOENT) {
1177 sys_error( "Maildir error: cannot open %s", buf );
1178 goto fail;
1179 }
1180 goto retry;
1181 }
1182 int off, in_msgid = 0;
1183 char lnbuf[1000]; // Says RFC2822
1184 while ((want_tuid || want_msgid) && fgets( lnbuf, sizeof(lnbuf), f )) {
1185 int bufl = strlen( lnbuf );
1186 if (bufl && lnbuf[bufl - 1] == '\n')
1187 --bufl;
1188 if (bufl && lnbuf[bufl - 1] == '\r')
1189 --bufl;
1190 if (!bufl)
1191 break;
1192 if (want_tuid && starts_with( lnbuf, bufl, "X-TUID: ", 8 )) {
1193 if (bufl < 8 + TUIDL) {
1194 error( "Maildir error: malformed X-TUID header (UID %u)\n", uid );
1195 continue;
1196 }
1197 memcpy( entry->tuid, lnbuf + 8, TUIDL );
1198 want_tuid = 0;
1199 in_msgid = 0;
1200 continue;
1201 }
1202 if (want_msgid && starts_with_upper( lnbuf, bufl, "MESSAGE-ID:", 11 )) {
1203 off = 11;
1204 } else if (in_msgid) {
1205 if (!isspace( lnbuf[0] )) {
1206 in_msgid = 0;
1207 continue;
1208 }
1209 off = 1;
1210 } else {
1211 continue;
1212 }
1213 while (off < bufl && isspace( lnbuf[off] ))
1214 off++;
1215 if (off == bufl) {
1216 in_msgid = 1;
1217 continue;
1218 }
1219 entry->msgid = nfstrndup( lnbuf + off, (size_t)(bufl - off) );
1220 want_msgid = 0;
1221 in_msgid = 0;
1222 }
1223 fclose( f );
1224 }
1225 }
1226 ctx->uvok = 1;
1227 }
1228 return DRV_OK;
1229 }
1230
1231 static void
maildir_init_msg(maildir_store_t * ctx,maildir_message_t * msg,msg_t * entry)1232 maildir_init_msg( maildir_store_t *ctx, maildir_message_t *msg, msg_t *entry )
1233 {
1234 msg->base = entry->base;
1235 entry->base = NULL; /* prevent deletion */
1236 msg->msgid = entry->msgid;
1237 entry->msgid = NULL; /* prevent deletion */
1238 msg->size = entry->size;
1239 msg->srec = NULL;
1240 memcpy( msg->tuid, entry->tuid, TUIDL );
1241 if (entry->recent)
1242 msg->status |= M_RECENT;
1243 if (ctx->opts & OPEN_FLAGS) {
1244 msg->status |= M_FLAGS;
1245 msg->flags = maildir_parse_flags( ctx->conf->info_prefix, msg->base );
1246 } else
1247 msg->flags = 0;
1248 }
1249
1250 static void
maildir_app_msg(maildir_store_t * ctx,maildir_message_t *** msgapp,msg_t * entry)1251 maildir_app_msg( maildir_store_t *ctx, maildir_message_t ***msgapp, msg_t *entry )
1252 {
1253 maildir_message_t *msg = nfmalloc( sizeof(*msg) );
1254 msg->next = **msgapp;
1255 **msgapp = msg;
1256 *msgapp = &msg->next;
1257 msg->uid = entry->uid;
1258 msg->status = 0;
1259 maildir_init_msg( ctx, msg, entry );
1260 }
1261
1262 static int
maildir_select_box(store_t * gctx,const char * name)1263 maildir_select_box( store_t *gctx, const char *name )
1264 {
1265 maildir_store_t *ctx = (maildir_store_t *)gctx;
1266 maildir_store_conf_t *conf = ctx->conf;
1267
1268 maildir_cleanup( gctx );
1269 ctx->msgs = NULL;
1270 ctx->excs.data = NULL;
1271 ctx->uvfd = -1;
1272 #ifdef USE_DB
1273 ctx->db = NULL;
1274 ctx->usedb = NULL;
1275 #endif /* USE_DB */
1276 ctx->fresh[0] = ctx->fresh[1] = 0;
1277 if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) {
1278 if (!name[5]) {
1279 ctx->path = nfstrdup( conf->inbox );
1280 ctx->is_inbox = 1;
1281 } else {
1282 ctx->path = maildir_join_path( conf, 1, name + 5 );
1283 ctx->is_inbox = 0;
1284 }
1285 } else {
1286 if (!(ctx->path = maildir_join_path( conf, 0, name )))
1287 return DRV_STORE_BAD;
1288 ctx->is_inbox = 0;
1289 }
1290 return ctx->path ? DRV_OK : DRV_BOX_BAD;
1291 }
1292
1293 static const char *
maildir_get_box_path(store_t * gctx)1294 maildir_get_box_path( store_t *gctx )
1295 {
1296 return ((maildir_store_t *)gctx)->path;
1297 }
1298
1299 static void
maildir_open_box(store_t * gctx,void (* cb)(int sts,uint uidvalidity,void * aux),void * aux)1300 maildir_open_box( store_t *gctx,
1301 void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux )
1302 {
1303 maildir_store_t *ctx = (maildir_store_t *)gctx;
1304 int ret;
1305 char uvpath[_POSIX_PATH_MAX];
1306
1307 if ((ret = maildir_validate( ctx->path, ctx->is_inbox, ctx )) != DRV_OK)
1308 goto bail;
1309
1310 nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", ctx->path );
1311 #ifndef USE_DB
1312 if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) < 0) {
1313 sys_error( "Maildir error: cannot write %s", uvpath );
1314 cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
1315 return;
1316 }
1317 #else
1318 ctx->usedb = NULL;
1319 if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
1320 nfsnprintf( uvpath, sizeof(uvpath), "%s/.isyncuidmap.db", ctx->path );
1321 if ((ctx->uvfd = open( uvpath, O_RDWR, 0600 )) < 0) {
1322 if (ctx->conf->alt_map) {
1323 if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) >= 0)
1324 goto dbok;
1325 } else {
1326 nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", ctx->path );
1327 if ((ctx->uvfd = open( uvpath, O_RDWR|O_CREAT, 0600 )) >= 0)
1328 goto fnok;
1329 }
1330 sys_error( "Maildir error: cannot write %s", uvpath );
1331 cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
1332 return;
1333 } else {
1334 dbok:
1335 ctx->usedb = nfstrdup( uvpath );
1336 }
1337 }
1338 fnok:
1339 #endif /* USE_DB */
1340 ret = maildir_uidval_lock( ctx );
1341
1342 bail:
1343 cb( ret, ctx->uidvalidity, aux );
1344 }
1345
1346 static uint
maildir_get_uidnext(store_t * gctx)1347 maildir_get_uidnext( store_t *gctx )
1348 {
1349 maildir_store_t *ctx = (maildir_store_t *)gctx;
1350
1351 return ctx->nuid;
1352 }
1353
1354 static xint
maildir_get_supported_flags(store_t * gctx ATTR_UNUSED)1355 maildir_get_supported_flags( store_t *gctx ATTR_UNUSED )
1356 {
1357 return 255;
1358 }
1359
1360 static void
maildir_create_box(store_t * gctx,void (* cb)(int sts,void * aux),void * aux)1361 maildir_create_box( store_t *gctx,
1362 void (*cb)( int sts, void *aux ), void *aux )
1363 {
1364 maildir_store_t *ctx = (maildir_store_t *)gctx;
1365
1366 cb( maildir_validate( ctx->path, 1, ctx ), aux );
1367 }
1368
1369 static int
maildir_confirm_box_empty(store_t * gctx)1370 maildir_confirm_box_empty( store_t *gctx )
1371 {
1372 maildir_store_t *ctx = (maildir_store_t *)gctx;
1373 msg_t_array_alloc_t msglist;
1374
1375 ctx->excs.size = ctx->minuid = ctx->maxuid = ctx->finduid = 0;
1376
1377 if (maildir_scan( ctx, &msglist ) != DRV_OK)
1378 return DRV_BOX_BAD;
1379 maildir_free_scan( &msglist );
1380 return ctx->total_msgs ? DRV_BOX_BAD : DRV_OK;
1381 }
1382
1383 static void
maildir_delete_box(store_t * gctx,void (* cb)(int sts,void * aux),void * aux)1384 maildir_delete_box( store_t *gctx,
1385 void (*cb)( int sts, void *aux ), void *aux )
1386 {
1387 maildir_store_t *ctx = (maildir_store_t *)gctx;
1388 int i, bl, ret = DRV_OK;
1389 struct stat st;
1390 char buf[_POSIX_PATH_MAX];
1391
1392 bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", ctx->path );
1393 if (stat( buf, &st )) {
1394 if (errno != ENOENT) {
1395 sys_error( "Maildir error: cannot access mailbox '%s'", ctx->path );
1396 ret = DRV_BOX_BAD;
1397 }
1398 } else if (!S_ISDIR(st.st_mode)) {
1399 error( "Maildir error: '%s' is no valid mailbox\n", ctx->path );
1400 ret = DRV_BOX_BAD;
1401 } else if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) == DRV_OK) {
1402 nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, ".uidvalidity" );
1403 if (unlink( buf ) && errno != ENOENT)
1404 goto badrm;
1405 #ifdef USE_DB
1406 nfsnprintf( buf + bl, _POSIX_PATH_MAX - bl, ".isyncuidmap.db" );
1407 if (unlink( buf ) && errno != ENOENT)
1408 goto badrm;
1409 #endif
1410 /* We delete cur/ last, as it is the indicator for a present mailbox.
1411 * That way an interrupted operation can be resumed. */
1412 for (i = 3; --i >= 0; ) {
1413 memcpy( buf + bl, subdirs[i], 4 );
1414 if (rmdir( buf ) && errno != ENOENT) {
1415 badrm:
1416 sys_error( "Maildir error: cannot remove '%s'", buf );
1417 ret = DRV_BOX_BAD;
1418 break;
1419 }
1420 }
1421 }
1422 cb( ret, aux );
1423 }
1424
1425 static int
maildir_finish_delete_box(store_t * gctx)1426 maildir_finish_delete_box( store_t *gctx )
1427 {
1428 maildir_store_t *ctx = (maildir_store_t *)gctx;
1429
1430 /* Subfolders are not deleted; the deleted folder is only "stripped of its mailboxness".
1431 * Consequently, the rmdir may legitimately fail. This behavior follows the IMAP spec. */
1432 if (rmdir( ctx->path ) && errno != ENOENT && errno != ENOTEMPTY) {
1433 sys_error( "Maildir warning: cannot remove '%s'", ctx->path );
1434 return DRV_BOX_BAD;
1435 }
1436 return DRV_OK;
1437 }
1438
1439 static uint
maildir_prepare_load_box(store_t * gctx,uint opts)1440 maildir_prepare_load_box( store_t *gctx, uint opts )
1441 {
1442 maildir_store_t *ctx = (maildir_store_t *)gctx;
1443
1444 if (opts & OPEN_SETFLAGS)
1445 opts |= OPEN_OLD;
1446 if (opts & OPEN_EXPUNGE)
1447 opts |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS;
1448 ctx->opts = opts;
1449 return opts;
1450 }
1451
1452 static void
maildir_load_box(store_t * gctx,uint minuid,uint maxuid,uint finduid,uint pairuid,uint newuid,uint_array_t excs,void (* cb)(int sts,message_t * msgs,int total_msgs,int recent_msgs,void * aux),void * aux)1453 maildir_load_box( store_t *gctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs,
1454 void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux )
1455 {
1456 maildir_store_t *ctx = (maildir_store_t *)gctx;
1457 maildir_message_t **msgapp;
1458 msg_t_array_alloc_t msglist;
1459 uint i;
1460
1461 ctx->minuid = minuid;
1462 ctx->maxuid = maxuid;
1463 ctx->finduid = finduid;
1464 ctx->pairuid = pairuid;
1465 ctx->newuid = newuid;
1466 ARRAY_SQUEEZE( &excs );
1467 ctx->excs = excs;
1468
1469 if (maildir_scan( ctx, &msglist ) != DRV_OK) {
1470 cb( DRV_BOX_BAD, NULL, 0, 0, aux );
1471 return;
1472 }
1473 msgapp = &ctx->msgs;
1474 for (i = 0; i < msglist.array.size; i++)
1475 maildir_app_msg( ctx, &msgapp, msglist.array.data + i );
1476 maildir_free_scan( &msglist );
1477
1478 cb( DRV_OK, &ctx->msgs->gen, ctx->total_msgs, ctx->recent_msgs, aux );
1479 }
1480
1481 static int
maildir_rescan(maildir_store_t * ctx)1482 maildir_rescan( maildir_store_t *ctx )
1483 {
1484 maildir_message_t **msgapp, *msg;
1485 msg_t_array_alloc_t msglist;
1486 uint i;
1487
1488 ctx->fresh[0] = ctx->fresh[1] = 0;
1489 if (maildir_scan( ctx, &msglist ) != DRV_OK)
1490 return DRV_BOX_BAD;
1491 for (msgapp = &ctx->msgs, i = 0; (msg = *msgapp) || i < msglist.array.size; ) {
1492 if (!msg) {
1493 #if 0
1494 debug( "adding new message %u\n", msglist.array.data[i].uid );
1495 maildir_app_msg( ctx, &msgapp, msglist.array.data + i );
1496 #else
1497 debug( "ignoring new message %u\n", msglist.array.data[i].uid );
1498 #endif
1499 i++;
1500 } else if (i >= msglist.array.size) {
1501 debug( "purging deleted message %u\n", msg->uid );
1502 msg->status = M_DEAD;
1503 msgapp = &msg->next;
1504 } else if (msglist.array.data[i].uid < msg->uid) {
1505 /* this should not happen, actually */
1506 #if 0
1507 debug( "adding new message %u\n", msglist.array.data[i].uid );
1508 maildir_app_msg( ctx, &msgapp, msglist.array.data + i );
1509 #else
1510 debug( "ignoring new message %u\n", msglist.array.data[i].uid );
1511 #endif
1512 i++;
1513 } else if (msglist.array.data[i].uid > msg->uid) {
1514 debug( "purging deleted message %u\n", msg->uid );
1515 msg->status = M_DEAD;
1516 msgapp = &msg->next;
1517 } else {
1518 debug( "updating message %u\n", msg->uid );
1519 msg->status &= ~(M_FLAGS|M_RECENT);
1520 free( msg->base );
1521 free( msg->msgid );
1522 maildir_init_msg( ctx, msg, msglist.array.data + i );
1523 i++, msgapp = &msg->next;
1524 }
1525 }
1526 maildir_free_scan( &msglist );
1527 return DRV_OK;
1528 }
1529
1530 static int ATTR_PRINTFLIKE(3, 0)
maildir_again(maildir_store_t * ctx,maildir_message_t * msg,const char * err,...)1531 maildir_again( maildir_store_t *ctx, maildir_message_t *msg, const char *err, ... )
1532 {
1533 int ret;
1534
1535 if (errno != ENOENT) {
1536 va_list va;
1537 va_start( va, err );
1538 vsys_error( err, va );
1539 va_end( va );
1540 return DRV_BOX_BAD;
1541 }
1542 if ((ret = maildir_rescan( ctx )) != DRV_OK)
1543 return ret;
1544 return (msg->status & M_DEAD) ? DRV_MSG_BAD : DRV_OK;
1545 }
1546
1547 static void
maildir_fetch_msg(store_t * gctx,message_t * gmsg,msg_data_t * data,int minimal ATTR_UNUSED,void (* cb)(int sts,void * aux),void * aux)1548 maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, int minimal ATTR_UNUSED,
1549 void (*cb)( int sts, void *aux ), void *aux )
1550 {
1551 maildir_store_t *ctx = (maildir_store_t *)gctx;
1552 maildir_message_t *msg = (maildir_message_t *)gmsg;
1553 int fd, ret;
1554 struct stat st;
1555 char buf[_POSIX_PATH_MAX];
1556
1557 for (;;) {
1558 nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->path, subdirs[gmsg->status & M_RECENT], msg->base );
1559 if ((fd = open( buf, O_RDONLY )) >= 0)
1560 break;
1561 if ((ret = maildir_again( ctx, msg, "Cannot open %s", buf )) != DRV_OK) {
1562 cb( ret, aux );
1563 return;
1564 }
1565 }
1566 fstat( fd, &st );
1567 if (st.st_size > INT_MAX) {
1568 error( "Maildir error: %s is too big", buf );
1569 goto mbad;
1570 }
1571 data->len = st.st_size;
1572 if (data->date == -1)
1573 data->date = st.st_mtime;
1574 data->data = nfmalloc( data->len );
1575 if (read( fd, data->data, data->len ) != data->len) {
1576 sys_error( "Maildir error: cannot read %s", buf );
1577 mbad:
1578 close( fd );
1579 cb( DRV_MSG_BAD, aux );
1580 return;
1581 }
1582 close( fd );
1583 if (!(gmsg->status & M_FLAGS))
1584 data->flags = maildir_parse_flags( ctx->conf->info_prefix, msg->base );
1585 cb( DRV_OK, aux );
1586 }
1587
1588 static int
maildir_make_flags(char info_delimiter,uchar flags,char * buf)1589 maildir_make_flags( char info_delimiter, uchar flags, char *buf )
1590 {
1591 int i, d;
1592
1593 buf[0] = info_delimiter;
1594 buf[1] = '2';
1595 buf[2] = ',';
1596 for (d = 3, i = 0; i < (int)as(Flags); i++)
1597 if (flags & (1 << i))
1598 buf[d++] = Flags[i];
1599 buf[d] = 0;
1600 return d;
1601 }
1602
1603 static void
maildir_store_msg(store_t * gctx,msg_data_t * data,int to_trash,void (* cb)(int sts,uint uid,void * aux),void * aux)1604 maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
1605 void (*cb)( int sts, uint uid, void *aux ), void *aux )
1606 {
1607 maildir_store_t *ctx = (maildir_store_t *)gctx;
1608 const char *box;
1609 int ret, fd, bl;
1610 uint uid;
1611 char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX], fbuf[NUM_FLAGS + 3], base[128];
1612
1613 bl = nfsnprintf( base, sizeof(base), "%lld.%d_%d.%s", (long long)time( NULL ), Pid, ++MaildirCount, Hostname );
1614 if (!to_trash) {
1615 #ifdef USE_DB
1616 if (ctx->usedb) {
1617 if ((ret = maildir_set_uid( ctx, base, &uid )) != DRV_OK) {
1618 free( data->data );
1619 cb( ret, 0, aux );
1620 return;
1621 }
1622 } else
1623 #endif /* USE_DB */
1624 {
1625 if ((ret = maildir_obtain_uid( ctx, &uid )) != DRV_OK) {
1626 free( data->data );
1627 cb( ret, 0, aux );
1628 return;
1629 }
1630 nfsnprintf( base + bl, (int)sizeof(base) - bl, ",U=%u", uid );
1631 }
1632 box = ctx->path;
1633 } else {
1634 uid = 0;
1635 box = ctx->trash;
1636 }
1637
1638 maildir_make_flags( ctx->conf->info_delimiter, data->flags, fbuf );
1639 nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", box, base, fbuf );
1640 if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
1641 if (errno != ENOENT || !to_trash) {
1642 sys_error( "Maildir error: cannot create %s", buf );
1643 free( data->data );
1644 cb( DRV_BOX_BAD, 0, aux );
1645 return;
1646 }
1647 if ((ret = maildir_validate( box, 1, ctx )) != DRV_OK) {
1648 free( data->data );
1649 cb( ret, 0, aux );
1650 return;
1651 }
1652 if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
1653 sys_error( "Maildir error: cannot create %s", buf );
1654 free( data->data );
1655 cb( DRV_BOX_BAD, 0, aux );
1656 return;
1657 }
1658 }
1659 ret = write( fd, data->data, data->len );
1660 free( data->data );
1661 if (ret != (int)data->len || (UseFSync && (ret = fsync( fd )))) {
1662 if (ret < 0)
1663 sys_error( "Maildir error: cannot write %s", buf );
1664 else
1665 error( "Maildir error: cannot write %s. Disk full?\n", buf );
1666 close( fd );
1667 cb( DRV_BOX_BAD, 0, aux );
1668 return;
1669 }
1670 if (close( fd ) < 0) {
1671 /* Quota exceeded may cause this. */
1672 sys_error( "Maildir error: cannot write %s", buf );
1673 cb( DRV_BOX_BAD, 0, aux );
1674 return;
1675 }
1676
1677 if (data->date) {
1678 /* Set atime and mtime according to INTERNALDATE or mtime of source message */
1679 struct utimbuf utimebuf;
1680 utimebuf.actime = utimebuf.modtime = data->date;
1681 if (utime( buf, &utimebuf ) < 0) {
1682 sys_error( "Maildir error: cannot set times for %s", buf );
1683 cb( DRV_BOX_BAD, 0, aux );
1684 return;
1685 }
1686 }
1687
1688 /* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */
1689 nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%s%s", box, subdirs[!(data->flags & F_SEEN)], base, fbuf );
1690 if (rename( buf, nbuf )) {
1691 sys_error( "Maildir error: cannot rename %s to %s", buf, nbuf );
1692 cb( DRV_BOX_BAD, 0, aux );
1693 return;
1694 }
1695 cb( DRV_OK, uid, aux );
1696 }
1697
1698 static void
maildir_set_msg_flags(store_t * gctx,message_t * gmsg,uint uid ATTR_UNUSED,int add,int del,void (* cb)(int sts,void * aux),void * aux)1699 maildir_set_msg_flags( store_t *gctx, message_t *gmsg, uint uid ATTR_UNUSED, int add, int del,
1700 void (*cb)( int sts, void *aux ), void *aux )
1701 {
1702 maildir_store_t *ctx = (maildir_store_t *)gctx;
1703 maildir_store_conf_t *conf = ctx->conf;
1704 maildir_message_t *msg = (maildir_message_t *)gmsg;
1705 char *s, *p;
1706 uint i;
1707 int j, ret, ol, fl, bbl, bl, tl;
1708 char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX];
1709
1710 bbl = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path );
1711 memcpy( nbuf, ctx->path, (size_t)bbl - 1 );
1712 memcpy( nbuf + bbl - 1, "/cur/", 5 );
1713 for (;;) {
1714 bl = bbl + nfsnprintf( buf + bbl, _POSIX_PATH_MAX - bbl, "%s/", subdirs[gmsg->status & M_RECENT] );
1715 ol = strlen( msg->base );
1716 if (_POSIX_PATH_MAX - bl < ol + 3 + NUM_FLAGS)
1717 oob();
1718 memcpy( buf + bl, msg->base, (size_t)ol + 1 );
1719 memcpy( nbuf + bl, msg->base, (size_t)ol + 1 );
1720 if ((s = strstr( nbuf + bl, conf->info_prefix ))) {
1721 s += 3;
1722 fl = ol - (s - (nbuf + bl));
1723 for (i = 0; i < as(Flags); i++) {
1724 if ((p = strchr( s, Flags[i] ))) {
1725 if (del & (1 << i)) {
1726 memmove( p, p + 1, (size_t)fl - (size_t)(p - s) );
1727 fl--;
1728 }
1729 } else if (add & (1 << i)) {
1730 for (j = 0; j < fl && Flags[i] > s[j]; j++);
1731 fl++;
1732 memmove( s + j + 1, s + j, (size_t)(fl - j) );
1733 s[j] = Flags[i];
1734 }
1735 }
1736 tl = ol + 3 + fl;
1737 } else {
1738 tl = ol + maildir_make_flags( conf->info_delimiter, (uchar)add, nbuf + bl + ol );
1739 }
1740 if (!rename( buf, nbuf ))
1741 break;
1742 if ((ret = maildir_again( ctx, msg, "Maildir error: cannot rename %s to %s", buf, nbuf )) != DRV_OK) {
1743 cb( ret, aux );
1744 return;
1745 }
1746 }
1747 free( msg->base );
1748 msg->base = nfstrndup( nbuf + bl, (size_t)tl );
1749 msg->flags |= add;
1750 msg->flags &= ~del;
1751 gmsg->status &= ~M_RECENT;
1752
1753 cb( DRV_OK, aux );
1754 }
1755
1756 #ifdef USE_DB
1757 static int
maildir_purge_msg(maildir_store_t * ctx,const char * name)1758 maildir_purge_msg( maildir_store_t *ctx, const char *name )
1759 {
1760 int ret;
1761
1762 if ((ret = maildir_uidval_lock( ctx )) != DRV_OK)
1763 return ret;
1764 make_key( ctx->conf->info_stop, &key, name );
1765 if ((ret = ctx->db->del( ctx->db, NULL, &key, 0 ))) {
1766 ctx->db->err( ctx->db, ret, "Maildir error: db->del()" );
1767 return DRV_BOX_BAD;
1768 }
1769 return DRV_OK;
1770 }
1771 #endif /* USE_DB */
1772
1773 static void
maildir_trash_msg(store_t * gctx,message_t * gmsg,void (* cb)(int sts,void * aux),void * aux)1774 maildir_trash_msg( store_t *gctx, message_t *gmsg,
1775 void (*cb)( int sts, void *aux ), void *aux )
1776 {
1777 maildir_store_t *ctx = (maildir_store_t *)gctx;
1778 maildir_message_t *msg = (maildir_message_t *)gmsg;
1779 char *s;
1780 int ret;
1781 struct stat st;
1782 char buf[_POSIX_PATH_MAX], nbuf[_POSIX_PATH_MAX];
1783
1784 for (;;) {
1785 nfsnprintf( buf, sizeof(buf), "%s/%s/%s", ctx->path, subdirs[gmsg->status & M_RECENT], msg->base );
1786 s = strstr( msg->base, ctx->conf->info_prefix );
1787 nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%lld.%d_%d.%s%s", ctx->trash,
1788 subdirs[gmsg->status & M_RECENT], (long long)time( NULL ), Pid, ++MaildirCount, Hostname, s ? s : "" );
1789 if (!rename( buf, nbuf ))
1790 break;
1791 if (!stat( buf, &st )) {
1792 if ((ret = maildir_validate( ctx->trash, 1, ctx )) != DRV_OK) {
1793 cb( ret, aux );
1794 return;
1795 }
1796 if (!rename( buf, nbuf ))
1797 break;
1798 if (errno != ENOENT) {
1799 sys_error( "Maildir error: cannot move %s to %s", buf, nbuf );
1800 cb( DRV_BOX_BAD, aux );
1801 return;
1802 }
1803 }
1804 if ((ret = maildir_again( ctx, msg, "Maildir error: cannot move %s to %s", buf, nbuf )) != DRV_OK) {
1805 cb( ret, aux );
1806 return;
1807 }
1808 }
1809 gmsg->status |= M_DEAD;
1810 ctx->total_msgs--;
1811
1812 #ifdef USE_DB
1813 if (ctx->usedb) {
1814 cb( maildir_purge_msg( ctx, msg->base ), aux );
1815 return;
1816 }
1817 #endif /* USE_DB */
1818 cb( DRV_OK, aux );
1819 }
1820
1821 static void
maildir_close_box(store_t * gctx,void (* cb)(int sts,void * aux),void * aux)1822 maildir_close_box( store_t *gctx,
1823 void (*cb)( int sts, void *aux ), void *aux )
1824 {
1825 maildir_store_t *ctx = (maildir_store_t *)gctx;
1826 maildir_message_t *msg;
1827 int basel, retry, ret;
1828 char buf[_POSIX_PATH_MAX];
1829
1830 for (;;) {
1831 retry = 0;
1832 basel = nfsnprintf( buf, sizeof(buf), "%s/", ctx->path );
1833 for (msg = ctx->msgs; msg; msg = msg->next)
1834 if (!(msg->status & M_DEAD) && (msg->flags & F_DELETED)) {
1835 nfsnprintf( buf + basel, _POSIX_PATH_MAX - basel, "%s/%s", subdirs[msg->status & M_RECENT], msg->base );
1836 if (unlink( buf )) {
1837 if (errno == ENOENT)
1838 retry = 1;
1839 else
1840 sys_error( "Maildir error: cannot remove %s", buf );
1841 } else {
1842 msg->status |= M_DEAD;
1843 ctx->total_msgs--;
1844 #ifdef USE_DB
1845 if (ctx->db && (ret = maildir_purge_msg( ctx, msg->base )) != DRV_OK) {
1846 cb( ret, aux );
1847 return;
1848 }
1849 #endif /* USE_DB */
1850 }
1851 }
1852 if (!retry) {
1853 cb( DRV_OK, aux );
1854 return;
1855 }
1856 if ((ret = maildir_rescan( (maildir_store_t *)gctx )) != DRV_OK) {
1857 cb( ret, aux );
1858 return;
1859 }
1860 }
1861 }
1862
1863 static void
maildir_cancel_cmds(store_t * gctx ATTR_UNUSED,void (* cb)(void * aux),void * aux)1864 maildir_cancel_cmds( store_t *gctx ATTR_UNUSED,
1865 void (*cb)( void *aux ), void *aux )
1866 {
1867 cb( aux );
1868 }
1869
1870 static void
maildir_commit_cmds(store_t * gctx)1871 maildir_commit_cmds( store_t *gctx )
1872 {
1873 (void) gctx;
1874 }
1875
1876 static uint
maildir_get_memory_usage(store_t * gctx ATTR_UNUSED)1877 maildir_get_memory_usage( store_t *gctx ATTR_UNUSED )
1878 {
1879 return 0;
1880 }
1881
1882 static int
maildir_get_fail_state(store_conf_t * gconf)1883 maildir_get_fail_state( store_conf_t *gconf )
1884 {
1885 return ((maildir_store_conf_t *)gconf)->failed;
1886 }
1887
1888 static int
maildir_parse_store(conffile_t * cfg,store_conf_t ** storep)1889 maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
1890 {
1891 maildir_store_conf_t *store;
1892
1893 if (strcasecmp( "MaildirStore", cfg->cmd ))
1894 return 0;
1895 store = nfcalloc( sizeof(*store) );
1896 store->info_delimiter = FieldDelimiter;
1897 store->driver = &maildir_driver;
1898 store->name = nfstrdup( cfg->val );
1899
1900 while (getcline( cfg ) && cfg->cmd)
1901 if (!strcasecmp( "Inbox", cfg->cmd ))
1902 store->inbox = expand_strdup( cfg->val );
1903 else if (!strcasecmp( "Path", cfg->cmd ))
1904 store->path = expand_strdup( cfg->val );
1905 #ifdef USE_DB
1906 else if (!strcasecmp( "AltMap", cfg->cmd ))
1907 store->alt_map = parse_bool( cfg );
1908 #endif /* USE_DB */
1909 else if (!strcasecmp( "InfoDelimiter", cfg->cmd )) {
1910 if (strlen( cfg->val ) != 1) {
1911 error( "%s:%d: Info delimiter must be exactly one character long\n", cfg->file, cfg->line );
1912 cfg->err = 1;
1913 continue;
1914 }
1915 store->info_delimiter = cfg->val[0];
1916 if (!ispunct( store->info_delimiter )) {
1917 error( "%s:%d: Info delimiter must be a punctuation character\n", cfg->file, cfg->line );
1918 cfg->err = 1;
1919 continue;
1920 }
1921 } else if (!strcasecmp( "SubFolders", cfg->cmd )) {
1922 if (!strcasecmp( "Verbatim", cfg->val )) {
1923 store->sub_style = SUB_VERBATIM;
1924 } else if (!strcasecmp( "Maildir++", cfg->val )) {
1925 store->sub_style = SUB_MAILDIRPP;
1926 } else if (!strcasecmp( "Legacy", cfg->val )) {
1927 store->sub_style = SUB_LEGACY;
1928 } else {
1929 error( "%s:%d: Unrecognized SubFolders style\n", cfg->file, cfg->line );
1930 cfg->err = 1;
1931 }
1932 } else
1933 parse_generic_store( &store->gen, cfg, "MaildirStore" );
1934 if (!store->inbox)
1935 store->inbox = expand_strdup( "~/Maildir" );
1936 if (store->sub_style == SUB_MAILDIRPP && store->path) {
1937 error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
1938 cfg->err = 1;
1939 }
1940 nfasprintf( &store->info_prefix, "%c2,", store->info_delimiter );
1941 nfasprintf( &store->info_stop, "%c,", store->info_delimiter );
1942 *storep = &store->gen;
1943 return 1;
1944 }
1945
1946 static uint
maildir_get_caps(store_t * gctx ATTR_UNUSED)1947 maildir_get_caps( store_t *gctx ATTR_UNUSED )
1948 {
1949 return 0; /* XXX DRV_CRLF? */
1950 }
1951
1952 struct driver maildir_driver = {
1953 maildir_get_caps,
1954 maildir_parse_store,
1955 maildir_cleanup_drv,
1956 maildir_alloc_store,
1957 maildir_set_bad_callback,
1958 maildir_connect_store,
1959 maildir_free_store,
1960 maildir_free_store, /* _cancel_, but it's the same */
1961 maildir_list_store,
1962 maildir_select_box,
1963 maildir_get_box_path,
1964 maildir_create_box,
1965 maildir_open_box,
1966 maildir_get_uidnext,
1967 maildir_get_supported_flags,
1968 maildir_confirm_box_empty,
1969 maildir_delete_box,
1970 maildir_finish_delete_box,
1971 maildir_prepare_load_box,
1972 maildir_load_box,
1973 maildir_fetch_msg,
1974 maildir_store_msg,
1975 NULL, // find_new_msgs
1976 maildir_set_msg_flags,
1977 maildir_trash_msg,
1978 maildir_close_box,
1979 maildir_cancel_cmds,
1980 maildir_commit_cmds,
1981 maildir_get_memory_usage,
1982 maildir_get_fail_state,
1983 };
1984