drv_maildir.c 52 KB

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