main.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. /*
  2. * mbsync - mailbox synchronizer
  3. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  4. * Copyright (C) 2002-2006,2010-2017 Oswald Buddenhagen <ossi@users.sf.net>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * As a special exception, mbsync may be linked with the OpenSSL library,
  20. * despite that library's more restrictive license.
  21. */
  22. #include "sync.h"
  23. #include <stdlib.h>
  24. #include <stddef.h>
  25. #include <unistd.h>
  26. #include <string.h>
  27. #include <fcntl.h>
  28. #include <signal.h>
  29. #include <time.h>
  30. #include <sys/wait.h>
  31. int DFlags;
  32. int UseFSync = 1;
  33. #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__)
  34. char FieldDelimiter = ';';
  35. #else
  36. char FieldDelimiter = ':';
  37. #endif
  38. int Pid; /* for maildir and imap */
  39. char Hostname[256]; /* for maildir */
  40. const char *Home; /* for config */
  41. int BufferLimit = 10 * 1024 * 1024;
  42. int chans_total, chans_done;
  43. int boxes_total, boxes_done;
  44. int new_total[2], new_done[2];
  45. int flags_total[2], flags_done[2];
  46. int trash_total[2], trash_done[2];
  47. static void
  48. version( void )
  49. {
  50. puts( PACKAGE " " VERSION );
  51. exit( 0 );
  52. }
  53. static void
  54. usage( int code )
  55. {
  56. fputs(
  57. PACKAGE " " VERSION " - mailbox synchronizer\n"
  58. "Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
  59. "Copyright (C) 2002-2006,2008,2010-2017 Oswald Buddenhagen <ossi@users.sf.net>\n"
  60. "Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
  61. "usage:\n"
  62. " " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n"
  63. " -a, --all operate on all defined channels\n"
  64. " -l, --list list mailboxes instead of syncing them\n"
  65. " -n, --new propagate new messages\n"
  66. " -d, --delete propagate message deletions\n"
  67. " -f, --flags propagate message flag changes\n"
  68. " -N, --renew propagate previously not propagated new messages\n"
  69. " -L, --pull propagate from master to slave\n"
  70. " -H, --push propagate from slave to master\n"
  71. " -C, --create create mailboxes if nonexistent\n"
  72. " -X, --expunge expunge deleted messages\n"
  73. " -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
  74. " -D, --debug debugging modes (see manual)\n"
  75. " -V, --verbose display what is happening\n"
  76. " -q, --quiet don't display progress counters\n"
  77. " -v, --version display version\n"
  78. " -h, --help display this help message\n"
  79. "\nIf neither --pull nor --push are specified, both are active.\n"
  80. "If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
  81. "Direction and operation can be concatenated like --pull-new, etc.\n"
  82. "--create and --expunge can be suffixed with -master/-slave. Read the man page.\n"
  83. "\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
  84. "\nCompile time options:\n"
  85. #ifdef HAVE_LIBSSL
  86. " +HAVE_LIBSSL"
  87. #else
  88. " -HAVE_LIBSSL"
  89. #endif
  90. #ifdef HAVE_LIBSASL
  91. " +HAVE_LIBSASL"
  92. #else
  93. " -HAVE_LIBSASL"
  94. #endif
  95. #ifdef HAVE_LIBZ
  96. " +HAVE_LIBZ"
  97. #else
  98. " -HAVE_LIBZ"
  99. #endif
  100. #ifdef USE_DB
  101. " +USE_DB"
  102. #else
  103. " -USE_DB"
  104. #endif
  105. #ifdef HAVE_IPV6
  106. " +HAVE_IPV6\n"
  107. #else
  108. " -HAVE_IPV6\n"
  109. #endif
  110. , code ? stderr : stdout );
  111. exit( code );
  112. }
  113. static void ATTR_PRINTFLIKE(1, 2)
  114. debug( const char *msg, ... )
  115. {
  116. va_list va;
  117. va_start( va, msg );
  118. vdebug( DEBUG_MAIN, msg, va );
  119. va_end( va );
  120. }
  121. #ifdef __linux__
  122. static void
  123. crashHandler( int n )
  124. {
  125. int dpid;
  126. char pbuf[10], pabuf[20];
  127. close( 0 );
  128. open( "/dev/tty", O_RDWR );
  129. dup2( 0, 1 );
  130. dup2( 0, 2 );
  131. error( "*** " EXE " caught signal %d. Starting debugger ...\n", n );
  132. switch ((dpid = fork())) {
  133. case -1:
  134. perror( "fork()" );
  135. break;
  136. case 0:
  137. sprintf( pbuf, "%d", Pid );
  138. sprintf( pabuf, "/proc/%d/exe", Pid );
  139. execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 );
  140. perror( "execlp()" );
  141. _exit( 1 );
  142. default:
  143. waitpid( dpid, 0, 0 );
  144. break;
  145. }
  146. exit( 3 );
  147. }
  148. #endif
  149. void
  150. stats( void )
  151. {
  152. char buf[3][64];
  153. char *cs;
  154. int t, l, ll, cls;
  155. static int cols = -1;
  156. if (!(DFlags & PROGRESS))
  157. return;
  158. if (cols < 0 && (!(cs = getenv( "COLUMNS" )) || !(cols = atoi( cs ))))
  159. cols = 80;
  160. ll = sprintf( buf[2], "C: %d/%d B: %d/%d", chans_done, chans_total, boxes_done, boxes_total );
  161. cls = (cols - ll - 10) / 2;
  162. for (t = 0; t < 2; t++) {
  163. l = sprintf( buf[t], "+%d/%d *%d/%d #%d/%d",
  164. new_done[t], new_total[t],
  165. flags_done[t], flags_total[t],
  166. trash_done[t], trash_total[t] );
  167. if (l > cls)
  168. buf[t][cls - 1] = '~';
  169. }
  170. progress( "\r%s M: %.*s S: %.*s", buf[2], cls, buf[0], cls, buf[1] );
  171. }
  172. static int
  173. matches( const char *t, const char *p )
  174. {
  175. for (;;) {
  176. if (!*p)
  177. return !*t;
  178. if (*p == '*') {
  179. p++;
  180. do {
  181. if (matches( t, p ))
  182. return 1;
  183. } while (*t++);
  184. return 0;
  185. } else if (*p == '%') {
  186. p++;
  187. do {
  188. if (*t == '/')
  189. return 0;
  190. if (matches( t, p ))
  191. return 1;
  192. } while (*t++);
  193. return 0;
  194. } else {
  195. if (*p != *t)
  196. return 0;
  197. p++, t++;
  198. }
  199. }
  200. }
  201. static int
  202. is_inbox( const char *name )
  203. {
  204. return starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/');
  205. }
  206. static int
  207. cmp_box_names( const void *a, const void *b )
  208. {
  209. const char *as = *(const char **)a;
  210. const char *bs = *(const char **)b;
  211. int ai = is_inbox( as );
  212. int bi = is_inbox( bs );
  213. int di = bi - ai;
  214. if (di)
  215. return di;
  216. return strcmp( as, bs );
  217. }
  218. static char **
  219. filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns )
  220. {
  221. string_list_t *cpat;
  222. char **boxarr = 0;
  223. const char *ps;
  224. int not, fnot, pfxl, num = 0, rnum = 0;
  225. pfxl = prefix ? strlen( prefix ) : 0;
  226. for (; boxes; boxes = boxes->next) {
  227. if (!starts_with( boxes->string, -1, prefix, pfxl ))
  228. continue;
  229. fnot = 1;
  230. for (cpat = patterns; cpat; cpat = cpat->next) {
  231. ps = cpat->string;
  232. if (*ps == '!') {
  233. ps++;
  234. not = 1;
  235. } else
  236. not = 0;
  237. if (matches( boxes->string + pfxl, ps )) {
  238. fnot = not;
  239. break;
  240. }
  241. }
  242. if (!fnot) {
  243. if (num + 1 >= rnum)
  244. boxarr = nfrealloc( boxarr, (rnum = (rnum + 10) * 2) * sizeof(*boxarr) );
  245. boxarr[num++] = nfstrdup( boxes->string + pfxl );
  246. boxarr[num] = 0;
  247. }
  248. }
  249. qsort( boxarr, num, sizeof(*boxarr), cmp_box_names );
  250. return boxarr;
  251. }
  252. static void
  253. merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
  254. {
  255. if (ops[M] & have) {
  256. chan->ops[M] &= ~mask;
  257. chan->ops[M] |= ops[M] & mask;
  258. chan->ops[S] &= ~mask;
  259. chan->ops[S] |= ops[S] & mask;
  260. } else if (!(chan->ops[M] & have)) {
  261. if (global_conf.ops[M] & have) {
  262. chan->ops[M] |= global_conf.ops[M] & mask;
  263. chan->ops[S] |= global_conf.ops[S] & mask;
  264. } else {
  265. chan->ops[M] |= def;
  266. chan->ops[S] |= def;
  267. }
  268. }
  269. }
  270. typedef struct box_ent {
  271. struct box_ent *next;
  272. char *name;
  273. int present[2];
  274. } box_ent_t;
  275. typedef struct chan_ent {
  276. struct chan_ent *next;
  277. channel_conf_t *conf;
  278. box_ent_t *boxes;
  279. char boxlist;
  280. } chan_ent_t;
  281. static chan_ent_t *
  282. add_channel( chan_ent_t ***chanapp, channel_conf_t *chan, int ops[] )
  283. {
  284. chan_ent_t *ce = nfcalloc( sizeof(*ce) );
  285. ce->conf = chan;
  286. merge_actions( chan, ops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_MASK_TYPE );
  287. merge_actions( chan, ops, XOP_HAVE_CREATE, OP_CREATE, 0 );
  288. merge_actions( chan, ops, XOP_HAVE_REMOVE, OP_REMOVE, 0 );
  289. merge_actions( chan, ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
  290. **chanapp = ce;
  291. *chanapp = &ce->next;
  292. chans_total++;
  293. return ce;
  294. }
  295. static chan_ent_t *
  296. add_named_channel( chan_ent_t ***chanapp, char *channame, int ops[] )
  297. {
  298. channel_conf_t *chan;
  299. chan_ent_t *ce;
  300. box_ent_t *boxes = 0, **mboxapp = &boxes, *mbox;
  301. char *boxp, *nboxp;
  302. int boxl, boxlist = 0;
  303. if ((boxp = strchr( channame, ':' )))
  304. *boxp++ = 0;
  305. for (chan = channels; chan; chan = chan->next)
  306. if (!strcmp( chan->name, channame ))
  307. goto gotchan;
  308. error( "No channel or group named '%s' defined.\n", channame );
  309. return 0;
  310. gotchan:
  311. if (boxp) {
  312. if (!chan->patterns) {
  313. error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame );
  314. return 0;
  315. }
  316. boxlist = 1;
  317. do {
  318. nboxp = strpbrk( boxp, ",\n" );
  319. if (nboxp) {
  320. boxl = nboxp - boxp;
  321. *nboxp++ = 0;
  322. } else {
  323. boxl = strlen( boxp );
  324. }
  325. mbox = nfmalloc( sizeof(*mbox) );
  326. if (boxl)
  327. mbox->name = nfstrndup( boxp, boxl );
  328. else
  329. mbox->name = nfstrndup( "INBOX", 5 );
  330. mbox->present[M] = mbox->present[S] = BOX_POSSIBLE;
  331. mbox->next = 0;
  332. *mboxapp = mbox;
  333. mboxapp = &mbox->next;
  334. boxes_total++;
  335. boxp = nboxp;
  336. } while (boxp);
  337. } else {
  338. if (!chan->patterns)
  339. boxes_total++;
  340. }
  341. ce = add_channel( chanapp, chan, ops );
  342. ce->boxes = boxes;
  343. ce->boxlist = boxlist;
  344. return ce;
  345. }
  346. typedef struct {
  347. int t[2];
  348. channel_conf_t *chan;
  349. driver_t *drv[2];
  350. store_t *ctx[2];
  351. chan_ent_t *chanptr;
  352. box_ent_t *boxptr;
  353. char *names[2];
  354. int ret, all, list, state[2];
  355. char done, skip, cben;
  356. } main_vars_t;
  357. #define AUX &mvars->t[t]
  358. #define MVARS(aux) \
  359. int t = *(int *)aux; \
  360. main_vars_t *mvars = (main_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(main_vars_t, t));
  361. #define E_START 0
  362. #define E_OPEN 1
  363. #define E_SYNC 2
  364. static void sync_chans( main_vars_t *mvars, int ent );
  365. int
  366. main( int argc, char **argv )
  367. {
  368. main_vars_t mvars[1];
  369. chan_ent_t *chans = 0, **chanapp = &chans;
  370. group_conf_t *group;
  371. channel_conf_t *chan;
  372. string_list_t *channame;
  373. char *config = 0, *opt, *ochar;
  374. int oind, cops = 0, op, ops[2] = { 0, 0 }, pseudo = 0;
  375. tzset();
  376. gethostname( Hostname, sizeof(Hostname) );
  377. if ((ochar = strchr( Hostname, '.' )))
  378. *ochar = 0;
  379. Pid = getpid();
  380. if (!(Home = getenv("HOME"))) {
  381. fputs( "Fatal: $HOME not set\n", stderr );
  382. return 1;
  383. }
  384. arc4_init();
  385. memset( mvars, 0, sizeof(*mvars) );
  386. mvars->t[1] = 1;
  387. for (oind = 1, ochar = 0; ; ) {
  388. if (!ochar || !*ochar) {
  389. if (oind >= argc)
  390. break;
  391. if (argv[oind][0] != '-')
  392. break;
  393. if (argv[oind][1] == '-') {
  394. opt = argv[oind++] + 2;
  395. if (!*opt)
  396. break;
  397. if (!strcmp( opt, "config" )) {
  398. if (oind >= argc) {
  399. error( "--config requires an argument.\n" );
  400. return 1;
  401. }
  402. config = argv[oind++];
  403. } else if (starts_with( opt, -1, "config=", 7 ))
  404. config = opt + 7;
  405. else if (!strcmp( opt, "all" ))
  406. mvars->all = 1;
  407. else if (!strcmp( opt, "list" ))
  408. mvars->list = 1;
  409. else if (!strcmp( opt, "help" ))
  410. usage( 0 );
  411. else if (!strcmp( opt, "version" ))
  412. version();
  413. else if (!strcmp( opt, "quiet" )) {
  414. if (DFlags & QUIET)
  415. DFlags |= VERYQUIET;
  416. else
  417. DFlags |= QUIET;
  418. } else if (!strcmp( opt, "verbose" )) {
  419. DFlags |= VERBOSE;
  420. } else if (starts_with( opt, -1, "debug", 5 )) {
  421. opt += 5;
  422. if (!*opt)
  423. op = VERBOSE | DEBUG_ALL;
  424. else if (!strcmp( opt, "-crash" ))
  425. op = DEBUG_CRASH;
  426. else if (!strcmp( opt, "-maildir" ))
  427. op = VERBOSE | DEBUG_MAILDIR;
  428. else if (!strcmp( opt, "-main" ))
  429. op = VERBOSE | DEBUG_MAIN;
  430. else if (!strcmp( opt, "-net" ))
  431. op = VERBOSE | DEBUG_NET;
  432. else if (!strcmp( opt, "-net-all" ))
  433. op = VERBOSE | DEBUG_NET | DEBUG_NET_ALL;
  434. else if (!strcmp( opt, "-sync" ))
  435. op = VERBOSE | DEBUG_SYNC;
  436. else
  437. goto badopt;
  438. DFlags |= op;
  439. } else if (!strcmp( opt, "pull" ))
  440. cops |= XOP_PULL, ops[M] |= XOP_HAVE_TYPE;
  441. else if (!strcmp( opt, "push" ))
  442. cops |= XOP_PUSH, ops[M] |= XOP_HAVE_TYPE;
  443. else if (starts_with( opt, -1, "create", 6 )) {
  444. opt += 6;
  445. op = OP_CREATE|XOP_HAVE_CREATE;
  446. lcop:
  447. if (!*opt)
  448. cops |= op;
  449. else if (!strcmp( opt, "-master" ))
  450. ops[M] |= op;
  451. else if (!strcmp( opt, "-slave" ))
  452. ops[S] |= op;
  453. else
  454. goto badopt;
  455. ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
  456. } else if (starts_with( opt, -1, "remove", 6 )) {
  457. opt += 6;
  458. op = OP_REMOVE|XOP_HAVE_REMOVE;
  459. goto lcop;
  460. } else if (starts_with( opt, -1, "expunge", 7 )) {
  461. opt += 7;
  462. op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
  463. goto lcop;
  464. } else if (!strcmp( opt, "no-expunge" ))
  465. ops[M] |= XOP_HAVE_EXPUNGE;
  466. else if (!strcmp( opt, "no-create" ))
  467. ops[M] |= XOP_HAVE_CREATE;
  468. else if (!strcmp( opt, "no-remove" ))
  469. ops[M] |= XOP_HAVE_REMOVE;
  470. else if (!strcmp( opt, "full" ))
  471. ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
  472. else if (!strcmp( opt, "noop" ))
  473. ops[M] |= XOP_HAVE_TYPE;
  474. else if (starts_with( opt, -1, "pull", 4 )) {
  475. op = XOP_PULL;
  476. lcac:
  477. opt += 4;
  478. if (!*opt)
  479. cops |= op;
  480. else if (*opt == '-') {
  481. opt++;
  482. goto rlcac;
  483. } else
  484. goto badopt;
  485. } else if (starts_with( opt, -1, "push", 4 )) {
  486. op = XOP_PUSH;
  487. goto lcac;
  488. } else {
  489. op = 0;
  490. rlcac:
  491. if (!strcmp( opt, "new" ))
  492. op |= OP_NEW;
  493. else if (!strcmp( opt, "renew" ))
  494. op |= OP_RENEW;
  495. else if (!strcmp( opt, "delete" ))
  496. op |= OP_DELETE;
  497. else if (!strcmp( opt, "flags" ))
  498. op |= OP_FLAGS;
  499. else {
  500. badopt:
  501. error( "Unknown option '%s'\n", argv[oind - 1] );
  502. return 1;
  503. }
  504. switch (op & XOP_MASK_DIR) {
  505. case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
  506. case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
  507. default: cops |= op; break;
  508. }
  509. ops[M] |= XOP_HAVE_TYPE;
  510. }
  511. continue;
  512. }
  513. ochar = argv[oind++] + 1;
  514. if (!*ochar) {
  515. error( "Invalid option '-'\n" );
  516. return 1;
  517. }
  518. }
  519. switch (*ochar++) {
  520. case 'a':
  521. mvars->all = 1;
  522. break;
  523. case 'l':
  524. mvars->list = 1;
  525. break;
  526. case 'c':
  527. if (*ochar == 'T') {
  528. ochar++;
  529. pseudo = 1;
  530. }
  531. if (oind >= argc) {
  532. error( "-c requires an argument.\n" );
  533. return 1;
  534. }
  535. config = argv[oind++];
  536. break;
  537. case 'C':
  538. op = OP_CREATE|XOP_HAVE_CREATE;
  539. cop:
  540. if (*ochar == 'm')
  541. ops[M] |= op, ochar++;
  542. else if (*ochar == 's')
  543. ops[S] |= op, ochar++;
  544. else if (*ochar == '-')
  545. ochar++;
  546. else
  547. cops |= op;
  548. ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_REMOVE|XOP_HAVE_EXPUNGE);
  549. break;
  550. case 'R':
  551. op = OP_REMOVE|XOP_HAVE_REMOVE;
  552. goto cop;
  553. case 'X':
  554. op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
  555. goto cop;
  556. case 'F':
  557. cops |= XOP_PULL|XOP_PUSH;
  558. /* fallthrough */
  559. case '0':
  560. ops[M] |= XOP_HAVE_TYPE;
  561. break;
  562. case 'n':
  563. case 'd':
  564. case 'f':
  565. case 'N':
  566. --ochar;
  567. op = 0;
  568. cac:
  569. for (;; ochar++) {
  570. if (*ochar == 'n')
  571. op |= OP_NEW;
  572. else if (*ochar == 'd')
  573. op |= OP_DELETE;
  574. else if (*ochar == 'f')
  575. op |= OP_FLAGS;
  576. else if (*ochar == 'N')
  577. op |= OP_RENEW;
  578. else
  579. break;
  580. }
  581. if (op & OP_MASK_TYPE)
  582. switch (op & XOP_MASK_DIR) {
  583. case XOP_PULL: ops[S] |= op & OP_MASK_TYPE; break;
  584. case XOP_PUSH: ops[M] |= op & OP_MASK_TYPE; break;
  585. default: cops |= op; break;
  586. }
  587. else
  588. cops |= op;
  589. ops[M] |= XOP_HAVE_TYPE;
  590. break;
  591. case 'L':
  592. op = XOP_PULL;
  593. goto cac;
  594. case 'H':
  595. op = XOP_PUSH;
  596. goto cac;
  597. case 'q':
  598. if (DFlags & QUIET)
  599. DFlags |= VERYQUIET;
  600. else
  601. DFlags |= QUIET;
  602. break;
  603. case 'V':
  604. DFlags |= VERBOSE;
  605. break;
  606. case 'D':
  607. for (op = 0; *ochar; ochar++) {
  608. switch (*ochar) {
  609. case 'C':
  610. op |= DEBUG_CRASH;
  611. break;
  612. case 'm':
  613. op |= DEBUG_MAILDIR | VERBOSE;
  614. break;
  615. case 'M':
  616. op |= DEBUG_MAIN | VERBOSE;
  617. break;
  618. case 'n':
  619. op |= DEBUG_NET | VERBOSE;
  620. break;
  621. case 'N':
  622. op |= DEBUG_NET | DEBUG_NET_ALL | VERBOSE;
  623. break;
  624. case 's':
  625. op |= DEBUG_SYNC | VERBOSE;
  626. break;
  627. default:
  628. error( "Unknown -D flag '%c'\n", *ochar );
  629. return 1;
  630. }
  631. }
  632. if (!op)
  633. op = DEBUG_ALL | VERBOSE;
  634. DFlags |= op;
  635. break;
  636. case 'J':
  637. DFlags |= KEEPJOURNAL;
  638. break;
  639. case 'Z':
  640. DFlags |= ZERODELAY;
  641. break;
  642. case 'v':
  643. version();
  644. case 'h':
  645. usage( 0 );
  646. default:
  647. error( "Unknown option '-%c'\n", *(ochar - 1) );
  648. return 1;
  649. }
  650. }
  651. if (!(DFlags & (QUIET | DEBUG_ALL)) && isatty( 1 ))
  652. DFlags |= PROGRESS;
  653. #ifdef __linux__
  654. if (DFlags & DEBUG_CRASH) {
  655. signal( SIGSEGV, crashHandler );
  656. signal( SIGBUS, crashHandler );
  657. signal( SIGILL, crashHandler );
  658. }
  659. #endif
  660. if (merge_ops( cops, ops ))
  661. return 1;
  662. if (load_config( config, pseudo ))
  663. return 1;
  664. if (!channels) {
  665. fputs( "No channels defined. Try 'man " EXE "'\n", stderr );
  666. return 1;
  667. }
  668. if (mvars->all) {
  669. for (chan = channels; chan; chan = chan->next) {
  670. add_channel( &chanapp, chan, ops );
  671. if (!chan->patterns)
  672. boxes_total++;
  673. }
  674. } else {
  675. for (; argv[oind]; oind++) {
  676. for (group = groups; group; group = group->next) {
  677. if (!strcmp( group->name, argv[oind] )) {
  678. for (channame = group->channels; channame; channame = channame->next)
  679. if (!add_named_channel( &chanapp, channame->string, ops ))
  680. mvars->ret = 1;
  681. goto gotgrp;
  682. }
  683. }
  684. if (!add_named_channel( &chanapp, argv[oind], ops ))
  685. mvars->ret = 1;
  686. gotgrp: ;
  687. }
  688. }
  689. if (!chans) {
  690. fputs( "No channel specified. Try '" EXE " -h'\n", stderr );
  691. return 1;
  692. }
  693. mvars->chanptr = chans;
  694. if (!mvars->list)
  695. stats();
  696. mvars->cben = 1;
  697. sync_chans( mvars, E_START );
  698. main_loop();
  699. if (!mvars->list)
  700. flushn();
  701. return mvars->ret;
  702. }
  703. #define ST_FRESH 0
  704. #define ST_CONNECTED 1
  705. #define ST_OPEN 2
  706. #define ST_CANCELING 3
  707. #define ST_CLOSED 4
  708. static void
  709. cancel_prep_done( void *aux )
  710. {
  711. MVARS(aux)
  712. mvars->drv[t]->free_store( mvars->ctx[t] );
  713. mvars->state[t] = ST_CLOSED;
  714. sync_chans( mvars, E_OPEN );
  715. }
  716. static void
  717. store_bad( void *aux )
  718. {
  719. MVARS(aux)
  720. mvars->drv[t]->cancel_store( mvars->ctx[t] );
  721. mvars->state[t] = ST_CLOSED;
  722. mvars->ret = mvars->skip = 1;
  723. sync_chans( mvars, E_OPEN );
  724. }
  725. static void store_connected( int sts, void *aux );
  726. static void store_listed( int sts, void *aux );
  727. static int sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox );
  728. static void done_sync_2_dyn( int sts, void *aux );
  729. static void done_sync( int sts, void *aux );
  730. #define nz(a,b) ((a)?(a):(b))
  731. static void
  732. sync_chans( main_vars_t *mvars, int ent )
  733. {
  734. box_ent_t *mbox, *nmbox, **mboxapp;
  735. char **boxes[2];
  736. const char *labels[2];
  737. int t, mb, sb, cmp;
  738. if (!mvars->cben)
  739. return;
  740. switch (ent) {
  741. case E_OPEN: goto opened;
  742. case E_SYNC: goto syncone;
  743. }
  744. do {
  745. mvars->chan = mvars->chanptr->conf;
  746. info( "Channel %s\n", mvars->chan->name );
  747. mvars->skip = mvars->cben = 0;
  748. for (t = 0; t < 2; t++) {
  749. int st = mvars->chan->stores[t]->driver->fail_state( mvars->chan->stores[t] );
  750. if (st != FAIL_TEMP) {
  751. info( "Skipping due to %sfailed %s store %s.\n",
  752. (st == FAIL_WAIT) ? "temporarily " : "", str_ms[t], mvars->chan->stores[t]->name );
  753. mvars->skip = 1;
  754. }
  755. }
  756. if (mvars->skip)
  757. goto next2;
  758. mvars->state[M] = mvars->state[S] = ST_FRESH;
  759. if (mvars->chan->stores[M]->driver->flags & mvars->chan->stores[S]->driver->flags & DRV_VERBOSE)
  760. labels[M] = "M: ", labels[S] = "S: ";
  761. else
  762. labels[M] = labels[S] = "";
  763. for (t = 0; t < 2; t++) {
  764. mvars->drv[t] = mvars->chan->stores[t]->driver;
  765. mvars->ctx[t] = mvars->drv[t]->alloc_store( mvars->chan->stores[t], labels[t] );
  766. set_bad_callback( mvars->ctx[t], store_bad, AUX );
  767. }
  768. for (t = 0; ; t++) {
  769. info( "Opening %s store %s...\n", str_ms[t], mvars->chan->stores[t]->name );
  770. mvars->drv[t]->connect_store( mvars->ctx[t], store_connected, AUX );
  771. if (t || mvars->skip)
  772. break;
  773. }
  774. mvars->cben = 1;
  775. opened:
  776. if (mvars->skip)
  777. goto next;
  778. if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
  779. return;
  780. if (!mvars->chanptr->boxlist && mvars->chan->patterns) {
  781. mvars->chanptr->boxlist = 2;
  782. boxes[M] = filter_boxes( mvars->ctx[M]->boxes, mvars->chan->boxes[M], mvars->chan->patterns );
  783. boxes[S] = filter_boxes( mvars->ctx[S]->boxes, mvars->chan->boxes[S], mvars->chan->patterns );
  784. mboxapp = &mvars->chanptr->boxes;
  785. for (mb = sb = 0; ; ) {
  786. char *mname = boxes[M] ? boxes[M][mb] : 0;
  787. char *sname = boxes[S] ? boxes[S][sb] : 0;
  788. if (!mname && !sname)
  789. break;
  790. mbox = nfmalloc( sizeof(*mbox) );
  791. if (!(cmp = !mname - !sname) && !(cmp = cmp_box_names( &mname, &sname ))) {
  792. mbox->name = mname;
  793. free( sname );
  794. mbox->present[M] = mbox->present[S] = BOX_PRESENT;
  795. mb++;
  796. sb++;
  797. } else if (cmp < 0) {
  798. mbox->name = mname;
  799. mbox->present[M] = BOX_PRESENT;
  800. mbox->present[S] = (!mb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
  801. mb++;
  802. } else {
  803. mbox->name = sname;
  804. mbox->present[M] = (!sb && !strcmp( mbox->name, "INBOX" )) ? BOX_PRESENT : BOX_ABSENT;
  805. mbox->present[S] = BOX_PRESENT;
  806. sb++;
  807. }
  808. mbox->next = 0;
  809. *mboxapp = mbox;
  810. mboxapp = &mbox->next;
  811. boxes_total++;
  812. }
  813. free( boxes[M] );
  814. free( boxes[S] );
  815. if (!mvars->list)
  816. stats();
  817. }
  818. mvars->boxptr = mvars->chanptr->boxes;
  819. if (mvars->list && chans_total > 1)
  820. printf( "%s:\n", mvars->chan->name );
  821. syncml:
  822. mvars->done = mvars->cben = 0;
  823. if (mvars->chanptr->boxlist) {
  824. while ((mbox = mvars->boxptr)) {
  825. mvars->boxptr = mbox->next;
  826. if (sync_listed_boxes( mvars, mbox ))
  827. goto syncw;
  828. }
  829. } else {
  830. if (!mvars->list) {
  831. int present[] = { BOX_POSSIBLE, BOX_POSSIBLE };
  832. sync_boxes( mvars->ctx, mvars->chan->boxes, present, mvars->chan, done_sync, mvars );
  833. mvars->skip = 1;
  834. syncw:
  835. mvars->cben = 1;
  836. if (!mvars->done)
  837. return;
  838. syncone:
  839. if (!mvars->skip)
  840. goto syncml;
  841. } else
  842. printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
  843. }
  844. next:
  845. mvars->cben = 0;
  846. for (t = 0; t < 2; t++)
  847. if (mvars->state[t] == ST_FRESH) {
  848. /* An unconnected store may be only cancelled. */
  849. mvars->state[t] = ST_CLOSED;
  850. mvars->drv[t]->cancel_store( mvars->ctx[t] );
  851. } else if (mvars->state[t] == ST_CONNECTED || mvars->state[t] == ST_OPEN) {
  852. mvars->state[t] = ST_CANCELING;
  853. mvars->drv[t]->cancel_cmds( mvars->ctx[t], cancel_prep_done, AUX );
  854. }
  855. mvars->cben = 1;
  856. if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
  857. mvars->skip = 1;
  858. return;
  859. }
  860. if (mvars->chanptr->boxlist == 2) {
  861. for (nmbox = mvars->chanptr->boxes; (mbox = nmbox); ) {
  862. nmbox = mbox->next;
  863. free( mbox->name );
  864. free( mbox );
  865. }
  866. mvars->chanptr->boxes = 0;
  867. mvars->chanptr->boxlist = 0;
  868. }
  869. next2:
  870. if (!mvars->list) {
  871. chans_done++;
  872. stats();
  873. }
  874. } while ((mvars->chanptr = mvars->chanptr->next));
  875. for (t = 0; t < N_DRIVERS; t++)
  876. drivers[t]->cleanup();
  877. }
  878. static void
  879. store_connected( int sts, void *aux )
  880. {
  881. MVARS(aux)
  882. string_list_t *cpat;
  883. int cflags;
  884. switch (sts) {
  885. case DRV_CANCELED:
  886. return;
  887. case DRV_OK:
  888. if (!mvars->skip && !mvars->chanptr->boxlist && mvars->chan->patterns && !mvars->ctx[t]->listed) {
  889. for (cflags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
  890. const char *pat = cpat->string;
  891. if (*pat != '!') {
  892. char buf[8];
  893. int bufl = snprintf( buf, sizeof(buf), "%s%s", nz( mvars->chan->boxes[t], "" ), pat );
  894. int flags = 0;
  895. /* Partial matches like "INB*" or even "*" are not considered,
  896. * except implicity when the INBOX lives under Path. */
  897. if (starts_with( buf, bufl, "INBOX", 5 )) {
  898. char c = buf[5];
  899. if (!c) {
  900. /* User really wants the INBOX. */
  901. flags |= LIST_INBOX;
  902. } else if (c == '/') {
  903. /* Flattened sub-folders of INBOX actually end up in Path. */
  904. if (mvars->ctx[t]->conf->flat_delim)
  905. flags |= LIST_PATH;
  906. else
  907. flags |= LIST_INBOX;
  908. } else if (c == '*' || c == '%') {
  909. /* It can be both INBOX and Path, but don't require Path to be configured. */
  910. flags |= LIST_INBOX | LIST_PATH_MAYBE;
  911. } else {
  912. /* It's definitely not the INBOX. */
  913. flags |= LIST_PATH;
  914. }
  915. } else {
  916. flags |= LIST_PATH;
  917. }
  918. debug( "pattern '%s' (effective '%s'): %sPath, %sINBOX\n",
  919. pat, buf, (flags & LIST_PATH) ? "" : "no ", (flags & LIST_INBOX) ? "" : "no ");
  920. cflags |= flags;
  921. }
  922. }
  923. mvars->state[t] = ST_CONNECTED;
  924. mvars->drv[t]->list_store( mvars->ctx[t], cflags, store_listed, AUX );
  925. return;
  926. }
  927. mvars->state[t] = ST_OPEN;
  928. break;
  929. default:
  930. mvars->ret = mvars->skip = 1;
  931. mvars->state[t] = ST_OPEN;
  932. break;
  933. }
  934. sync_chans( mvars, E_OPEN );
  935. }
  936. static void
  937. store_listed( int sts, void *aux )
  938. {
  939. MVARS(aux)
  940. string_list_t **box, *bx;
  941. switch (sts) {
  942. case DRV_CANCELED:
  943. return;
  944. case DRV_OK:
  945. mvars->ctx[t]->listed = 1;
  946. if (DFlags & DEBUG_MAIN) {
  947. debug( "got mailbox list from %s:\n", str_ms[t] );
  948. for (bx = mvars->ctx[t]->boxes; bx; bx = bx->next)
  949. debug( " %s\n", bx->string );
  950. }
  951. if (mvars->ctx[t]->conf->flat_delim) {
  952. for (box = &mvars->ctx[t]->boxes; *box; box = &(*box)->next) {
  953. string_list_t *nbox;
  954. if (map_name( (*box)->string, (char **)&nbox, offsetof(string_list_t, string), mvars->ctx[t]->conf->flat_delim, "/" ) < 0) {
  955. error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", (*box)->string );
  956. mvars->ret = mvars->skip = 1;
  957. } else {
  958. nbox->next = (*box)->next;
  959. free( *box );
  960. *box = nbox;
  961. }
  962. }
  963. }
  964. if (mvars->ctx[t]->conf->map_inbox) {
  965. debug( "adding mapped inbox to %s: %s\n", str_ms[t], mvars->ctx[t]->conf->map_inbox );
  966. add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
  967. }
  968. break;
  969. default:
  970. mvars->ret = mvars->skip = 1;
  971. break;
  972. }
  973. mvars->state[t] = ST_OPEN;
  974. sync_chans( mvars, E_OPEN );
  975. }
  976. static int
  977. sync_listed_boxes( main_vars_t *mvars, box_ent_t *mbox )
  978. {
  979. if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
  980. const char *mpfx = nz( mvars->chan->boxes[M], "" );
  981. const char *spfx = nz( mvars->chan->boxes[S], "" );
  982. if (!mvars->list) {
  983. nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->name );
  984. nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->name );
  985. sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync_2_dyn, mvars );
  986. return 1;
  987. }
  988. printf( "%s%s <=> %s%s\n", mpfx, mbox->name, spfx, mbox->name );
  989. } else {
  990. if (!mvars->list) {
  991. mvars->names[M] = mvars->names[S] = mbox->name;
  992. sync_boxes( mvars->ctx, (const char **)mvars->names, mbox->present, mvars->chan, done_sync, mvars );
  993. return 1;
  994. }
  995. puts( mbox->name );
  996. }
  997. return 0;
  998. }
  999. static void
  1000. done_sync_2_dyn( int sts, void *aux )
  1001. {
  1002. main_vars_t *mvars = (main_vars_t *)aux;
  1003. free( mvars->names[M] );
  1004. free( mvars->names[S] );
  1005. done_sync( sts, aux );
  1006. }
  1007. static void
  1008. done_sync( int sts, void *aux )
  1009. {
  1010. main_vars_t *mvars = (main_vars_t *)aux;
  1011. mvars->done = 1;
  1012. boxes_done++;
  1013. stats();
  1014. if (sts) {
  1015. mvars->ret = 1;
  1016. if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
  1017. if (sts & SYNC_BAD(M))
  1018. mvars->state[M] = ST_CLOSED;
  1019. if (sts & SYNC_BAD(S))
  1020. mvars->state[S] = ST_CLOSED;
  1021. mvars->skip = 1;
  1022. }
  1023. }
  1024. sync_chans( mvars, E_SYNC );
  1025. }