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