main.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. /*
  2. * mbsync - mailbox synchronizer
  3. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  4. * Copyright (C) 2002-2006,2010-2012 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. int Pid; /* for maildir and imap */
  34. char Hostname[256]; /* for maildir */
  35. const char *Home; /* for config */
  36. static void
  37. version( void )
  38. {
  39. puts( PACKAGE " " VERSION );
  40. exit( 0 );
  41. }
  42. static void
  43. usage( int code )
  44. {
  45. fputs(
  46. PACKAGE " " VERSION " - mailbox synchronizer\n"
  47. "Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
  48. "Copyright (C) 2002-2006,2008,2010-2012 Oswald Buddenhagen <ossi@users.sf.net>\n"
  49. "Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
  50. "usage:\n"
  51. " " EXE " [flags] {{channel[:box,...]|group} ...|-a}\n"
  52. " -a, --all operate on all defined channels\n"
  53. " -l, --list list mailboxes instead of syncing them\n"
  54. " -n, --new propagate new messages\n"
  55. " -d, --delete propagate message deletions\n"
  56. " -f, --flags propagate message flag changes\n"
  57. " -N, --renew propagate previously not propagated new messages\n"
  58. " -L, --pull propagate from master to slave\n"
  59. " -H, --push propagate from slave to master\n"
  60. " -C, --create create mailboxes if nonexistent\n"
  61. " -X, --expunge expunge deleted messages\n"
  62. " -c, --config CONFIG read an alternate config file (default: ~/." EXE "rc)\n"
  63. " -D, --debug print debugging messages\n"
  64. " -V, --verbose verbose mode (display network traffic)\n"
  65. " -q, --quiet don't display progress info\n"
  66. " -v, --version display version\n"
  67. " -h, --help display this help message\n"
  68. "\nIf neither --pull nor --push are specified, both are active.\n"
  69. "If neither --new, --delete, --flags nor --renew are specified, all are active.\n"
  70. "Direction and operation can be concatenated like --pull-new, etc.\n"
  71. "--create and --expunge can be suffixed with -master/-slave. Read the man page.\n"
  72. "\nSupported mailbox formats are: IMAP4rev1, Maildir\n"
  73. "\nCompile time options:\n"
  74. #ifdef HAVE_LIBSSL
  75. " +HAVE_LIBSSL\n"
  76. #else
  77. " -HAVE_LIBSSL\n"
  78. #endif
  79. , code ? stderr : stdout );
  80. exit( code );
  81. }
  82. #ifdef __linux__
  83. static void
  84. crashHandler( int n )
  85. {
  86. int dpid;
  87. char pbuf[10], pabuf[20];
  88. close( 0 );
  89. open( "/dev/tty", O_RDWR );
  90. dup2( 0, 1 );
  91. dup2( 0, 2 );
  92. error( "*** " EXE " caught signal %d. Starting debugger ...\n", n );
  93. switch ((dpid = fork())) {
  94. case -1:
  95. perror( "fork()" );
  96. break;
  97. case 0:
  98. sprintf( pbuf, "%d", Pid );
  99. sprintf( pabuf, "/proc/%d/exe", Pid );
  100. execlp( "gdb", "gdb", pabuf, pbuf, (char *)0 );
  101. perror( "execlp()" );
  102. _exit( 1 );
  103. default:
  104. waitpid( dpid, 0, 0 );
  105. break;
  106. }
  107. exit( 3 );
  108. }
  109. #endif
  110. static int
  111. matches( const char *t, const char *p )
  112. {
  113. for (;;) {
  114. if (!*p)
  115. return !*t;
  116. if (*p == '*') {
  117. p++;
  118. do {
  119. if (matches( t, p ))
  120. return 1;
  121. } while (*t++);
  122. return 0;
  123. } else if (*p == '%') {
  124. p++;
  125. do {
  126. if (*t == '/')
  127. return 0;
  128. if (matches( t, p ))
  129. return 1;
  130. } while (*t++);
  131. return 0;
  132. } else {
  133. if (*p != *t)
  134. return 0;
  135. p++, t++;
  136. }
  137. }
  138. }
  139. static string_list_t *
  140. filter_boxes( string_list_t *boxes, const char *prefix, string_list_t *patterns )
  141. {
  142. string_list_t *nboxes = 0, *cpat;
  143. const char *ps;
  144. int not, fnot, pfxl;
  145. pfxl = prefix ? strlen( prefix ) : 0;
  146. for (; boxes; boxes = boxes->next) {
  147. if (memcmp( boxes->string, prefix, pfxl ))
  148. continue;
  149. fnot = 1;
  150. for (cpat = patterns; cpat; cpat = cpat->next) {
  151. ps = cpat->string;
  152. if (*ps == '!') {
  153. ps++;
  154. not = 1;
  155. } else
  156. not = 0;
  157. if (matches( boxes->string + pfxl, ps )) {
  158. fnot = not;
  159. break;
  160. }
  161. }
  162. if (!fnot)
  163. add_string_list( &nboxes, boxes->string + pfxl );
  164. }
  165. return nboxes;
  166. }
  167. static void
  168. merge_actions( channel_conf_t *chan, int ops[], int have, int mask, int def )
  169. {
  170. if (ops[M] & have) {
  171. chan->ops[M] &= ~mask;
  172. chan->ops[M] |= ops[M] & mask;
  173. chan->ops[S] &= ~mask;
  174. chan->ops[S] |= ops[S] & mask;
  175. } else if (!(chan->ops[M] & have)) {
  176. if (global_conf.ops[M] & have) {
  177. chan->ops[M] |= global_conf.ops[M] & mask;
  178. chan->ops[S] |= global_conf.ops[S] & mask;
  179. } else {
  180. chan->ops[M] |= def;
  181. chan->ops[S] |= def;
  182. }
  183. }
  184. }
  185. typedef struct {
  186. int t[2];
  187. channel_conf_t *chan;
  188. driver_t *drv[2];
  189. store_t *ctx[2];
  190. string_list_t *boxes[2], *cboxes, *chanptr;
  191. char *names[2];
  192. char **argv;
  193. int oind, ret, multiple, all, list, ops[2], state[2];
  194. char done, skip, cben, boxlist;
  195. } main_vars_t;
  196. #define AUX &mvars->t[t]
  197. #define MVARS(aux) \
  198. int t = *(int *)aux; \
  199. main_vars_t *mvars = (main_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(main_vars_t, t));
  200. #define E_START 0
  201. #define E_OPEN 1
  202. #define E_SYNC 2
  203. static void sync_chans( main_vars_t *mvars, int ent );
  204. int
  205. main( int argc, char **argv )
  206. {
  207. main_vars_t mvars[1];
  208. group_conf_t *group;
  209. char *config = 0, *opt, *ochar;
  210. int cops = 0, op, pseudo = 0;
  211. tzset();
  212. gethostname( Hostname, sizeof(Hostname) );
  213. if ((ochar = strchr( Hostname, '.' )))
  214. *ochar = 0;
  215. Pid = getpid();
  216. if (!(Home = getenv("HOME"))) {
  217. fputs( "Fatal: $HOME not set\n", stderr );
  218. return 1;
  219. }
  220. arc4_init();
  221. memset( mvars, 0, sizeof(*mvars) );
  222. mvars->t[1] = 1;
  223. for (mvars->oind = 1, ochar = 0; ; ) {
  224. if (!ochar || !*ochar) {
  225. if (mvars->oind >= argc)
  226. break;
  227. if (argv[mvars->oind][0] != '-')
  228. break;
  229. if (argv[mvars->oind][1] == '-') {
  230. opt = argv[mvars->oind++] + 2;
  231. if (!*opt)
  232. break;
  233. if (!strcmp( opt, "config" )) {
  234. if (mvars->oind >= argc) {
  235. error( "--config requires an argument.\n" );
  236. return 1;
  237. }
  238. config = argv[mvars->oind++];
  239. } else if (!memcmp( opt, "config=", 7 ))
  240. config = opt + 7;
  241. else if (!strcmp( opt, "all" ))
  242. mvars->all = 1;
  243. else if (!strcmp( opt, "list" ))
  244. mvars->list = 1;
  245. else if (!strcmp( opt, "help" ))
  246. usage( 0 );
  247. else if (!strcmp( opt, "version" ))
  248. version();
  249. else if (!strcmp( opt, "quiet" )) {
  250. if (DFlags & QUIET)
  251. DFlags |= VERYQUIET;
  252. else
  253. DFlags |= QUIET;
  254. } else if (!strcmp( opt, "verbose" )) {
  255. if (DFlags & VERBOSE)
  256. DFlags |= XVERBOSE;
  257. else
  258. DFlags |= VERBOSE | QUIET;
  259. } else if (!strcmp( opt, "debug" ))
  260. DFlags |= DEBUG | QUIET;
  261. else if (!strcmp( opt, "pull" ))
  262. cops |= XOP_PULL, mvars->ops[M] |= XOP_HAVE_TYPE;
  263. else if (!strcmp( opt, "push" ))
  264. cops |= XOP_PUSH, mvars->ops[M] |= XOP_HAVE_TYPE;
  265. else if (!memcmp( opt, "create", 6 )) {
  266. opt += 6;
  267. op = OP_CREATE|XOP_HAVE_CREATE;
  268. lcop:
  269. if (!*opt)
  270. cops |= op;
  271. else if (!strcmp( opt, "-master" ))
  272. mvars->ops[M] |= op, ochar++;
  273. else if (!strcmp( opt, "-slave" ))
  274. mvars->ops[S] |= op, ochar++;
  275. else
  276. goto badopt;
  277. mvars->ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE);
  278. } else if (!memcmp( opt, "expunge", 7 )) {
  279. opt += 7;
  280. op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
  281. goto lcop;
  282. } else if (!strcmp( opt, "no-expunge" ))
  283. mvars->ops[M] |= XOP_HAVE_EXPUNGE;
  284. else if (!strcmp( opt, "no-create" ))
  285. mvars->ops[M] |= XOP_HAVE_CREATE;
  286. else if (!strcmp( opt, "full" ))
  287. mvars->ops[M] |= XOP_HAVE_TYPE|XOP_PULL|XOP_PUSH;
  288. else if (!strcmp( opt, "noop" ))
  289. mvars->ops[M] |= XOP_HAVE_TYPE;
  290. else if (!memcmp( opt, "pull", 4 )) {
  291. op = XOP_PULL;
  292. lcac:
  293. opt += 4;
  294. if (!*opt)
  295. cops |= op;
  296. else if (*opt == '-') {
  297. opt++;
  298. goto rlcac;
  299. } else
  300. goto badopt;
  301. } else if (!memcmp( opt, "push", 4 )) {
  302. op = XOP_PUSH;
  303. goto lcac;
  304. } else {
  305. op = 0;
  306. rlcac:
  307. if (!strcmp( opt, "new" ))
  308. op |= OP_NEW;
  309. else if (!strcmp( opt, "renew" ))
  310. op |= OP_RENEW;
  311. else if (!strcmp( opt, "delete" ))
  312. op |= OP_DELETE;
  313. else if (!strcmp( opt, "flags" ))
  314. op |= OP_FLAGS;
  315. else {
  316. badopt:
  317. error( "Unknown option '%s'\n", argv[mvars->oind - 1] );
  318. return 1;
  319. }
  320. switch (op & XOP_MASK_DIR) {
  321. case XOP_PULL: mvars->ops[S] |= op & OP_MASK_TYPE; break;
  322. case XOP_PUSH: mvars->ops[M] |= op & OP_MASK_TYPE; break;
  323. default: cops |= op; break;
  324. }
  325. mvars->ops[M] |= XOP_HAVE_TYPE;
  326. }
  327. continue;
  328. }
  329. ochar = argv[mvars->oind++] + 1;
  330. if (!*ochar) {
  331. error( "Invalid option '-'\n" );
  332. return 1;
  333. }
  334. }
  335. switch (*ochar++) {
  336. case 'a':
  337. mvars->all = 1;
  338. break;
  339. case 'l':
  340. mvars->list = 1;
  341. break;
  342. case 'c':
  343. if (*ochar == 'T') {
  344. ochar++;
  345. pseudo = 1;
  346. }
  347. if (mvars->oind >= argc) {
  348. error( "-c requires an argument.\n" );
  349. return 1;
  350. }
  351. config = argv[mvars->oind++];
  352. break;
  353. case 'C':
  354. op = OP_CREATE|XOP_HAVE_CREATE;
  355. cop:
  356. if (*ochar == 'm')
  357. mvars->ops[M] |= op, ochar++;
  358. else if (*ochar == 's')
  359. mvars->ops[S] |= op, ochar++;
  360. else if (*ochar == '-')
  361. ochar++;
  362. else
  363. cops |= op;
  364. mvars->ops[M] |= op & (XOP_HAVE_CREATE|XOP_HAVE_EXPUNGE);
  365. break;
  366. case 'X':
  367. op = OP_EXPUNGE|XOP_HAVE_EXPUNGE;
  368. goto cop;
  369. case 'F':
  370. cops |= XOP_PULL|XOP_PUSH;
  371. /* fallthrough */
  372. case '0':
  373. mvars->ops[M] |= XOP_HAVE_TYPE;
  374. break;
  375. case 'n':
  376. case 'd':
  377. case 'f':
  378. case 'N':
  379. --ochar;
  380. op = 0;
  381. cac:
  382. for (;; ochar++) {
  383. if (*ochar == 'n')
  384. op |= OP_NEW;
  385. else if (*ochar == 'd')
  386. op |= OP_DELETE;
  387. else if (*ochar == 'f')
  388. op |= OP_FLAGS;
  389. else if (*ochar == 'N')
  390. op |= OP_RENEW;
  391. else
  392. break;
  393. }
  394. if (op & OP_MASK_TYPE)
  395. switch (op & XOP_MASK_DIR) {
  396. case XOP_PULL: mvars->ops[S] |= op & OP_MASK_TYPE; break;
  397. case XOP_PUSH: mvars->ops[M] |= op & OP_MASK_TYPE; break;
  398. default: cops |= op; break;
  399. }
  400. else
  401. cops |= op;
  402. mvars->ops[M] |= XOP_HAVE_TYPE;
  403. break;
  404. case 'L':
  405. op = XOP_PULL;
  406. goto cac;
  407. case 'H':
  408. op = XOP_PUSH;
  409. goto cac;
  410. case 'q':
  411. if (DFlags & QUIET)
  412. DFlags |= VERYQUIET;
  413. else
  414. DFlags |= QUIET;
  415. break;
  416. case 'V':
  417. if (DFlags & VERBOSE)
  418. DFlags |= XVERBOSE;
  419. else
  420. DFlags |= VERBOSE | QUIET;
  421. break;
  422. case 'D':
  423. if (*ochar == 'C')
  424. DFlags |= CRASHDEBUG, ochar++;
  425. else
  426. DFlags |= CRASHDEBUG | DEBUG | QUIET;
  427. break;
  428. case 'J':
  429. DFlags |= KEEPJOURNAL;
  430. break;
  431. case 'Z':
  432. DFlags |= ZERODELAY;
  433. break;
  434. case 'v':
  435. version();
  436. case 'h':
  437. usage( 0 );
  438. default:
  439. error( "Unknown option '-%c'\n", *(ochar - 1) );
  440. return 1;
  441. }
  442. }
  443. #ifdef __linux__
  444. if (DFlags & CRASHDEBUG) {
  445. signal( SIGSEGV, crashHandler );
  446. signal( SIGBUS, crashHandler );
  447. signal( SIGILL, crashHandler );
  448. }
  449. #endif
  450. if (merge_ops( cops, mvars->ops ))
  451. return 1;
  452. if (load_config( config, pseudo ))
  453. return 1;
  454. if (!mvars->all && !argv[mvars->oind]) {
  455. fputs( "No channel specified. Try '" EXE " -h'\n", stderr );
  456. return 1;
  457. }
  458. if (!channels) {
  459. fputs( "No channels defined. Try 'man " EXE "'\n", stderr );
  460. return 1;
  461. }
  462. mvars->chan = channels;
  463. if (mvars->all)
  464. mvars->multiple = channels->next != 0;
  465. else if (argv[mvars->oind + 1])
  466. mvars->multiple = 1;
  467. else
  468. for (group = groups; group; group = group->next)
  469. if (!strcmp( group->name, argv[mvars->oind] )) {
  470. mvars->multiple = 1;
  471. break;
  472. }
  473. mvars->argv = argv;
  474. mvars->cben = 1;
  475. sync_chans( mvars, E_START );
  476. main_loop();
  477. return mvars->ret;
  478. }
  479. #define ST_FRESH 0
  480. #define ST_OPEN 1
  481. #define ST_CLOSED 2
  482. static void store_opened( store_t *ctx, void *aux );
  483. static void store_listed( int sts, void *aux );
  484. static int sync_listed_boxes( main_vars_t *mvars, string_list_t *mbox );
  485. static void done_sync_dyn( int sts, void *aux );
  486. static void done_sync_2_dyn( int sts, void *aux );
  487. static void done_sync( int sts, void *aux );
  488. #define nz(a,b) ((a)?(a):(b))
  489. static void
  490. sync_chans( main_vars_t *mvars, int ent )
  491. {
  492. group_conf_t *group;
  493. channel_conf_t *chan;
  494. string_list_t *mbox, *sbox, **mboxp, **sboxp;
  495. char *channame, *boxp, *nboxp;
  496. const char *labels[2];
  497. int t;
  498. if (!mvars->cben)
  499. return;
  500. switch (ent) {
  501. case E_OPEN: goto opened;
  502. case E_SYNC: goto syncone;
  503. }
  504. for (;;) {
  505. mvars->boxlist = 0;
  506. mvars->boxes[M] = mvars->boxes[S] = mvars->cboxes = 0;
  507. if (!mvars->all) {
  508. if (mvars->chanptr)
  509. channame = mvars->chanptr->string;
  510. else {
  511. for (group = groups; group; group = group->next)
  512. if (!strcmp( group->name, mvars->argv[mvars->oind] )) {
  513. mvars->chanptr = group->channels;
  514. channame = mvars->chanptr->string;
  515. goto gotgrp;
  516. }
  517. channame = mvars->argv[mvars->oind];
  518. gotgrp: ;
  519. }
  520. if ((boxp = strchr( channame, ':' )))
  521. *boxp++ = 0;
  522. for (chan = channels; chan; chan = chan->next)
  523. if (!strcmp( chan->name, channame ))
  524. goto gotchan;
  525. error( "No channel or group named '%s' defined.\n", channame );
  526. mvars->ret = 1;
  527. goto gotnone;
  528. gotchan:
  529. mvars->chan = chan;
  530. if (boxp) {
  531. if (!chan->patterns) {
  532. error( "Cannot override mailbox in channel '%s' - no Patterns.\n", channame );
  533. mvars->ret = 1;
  534. goto gotnone;
  535. }
  536. mvars->boxlist = 1;
  537. for (;;) {
  538. nboxp = strpbrk( boxp, ",\n" );
  539. if (nboxp) {
  540. t = nboxp - boxp;
  541. *nboxp++ = 0;
  542. } else {
  543. t = strlen( boxp );
  544. }
  545. if (t)
  546. add_string_list_n( &mvars->cboxes, boxp, t );
  547. else
  548. add_string_list_n( &mvars->cboxes, "INBOX", 5 );
  549. if (!nboxp)
  550. break;
  551. boxp = nboxp;
  552. }
  553. }
  554. }
  555. merge_actions( mvars->chan, mvars->ops, XOP_HAVE_TYPE, OP_MASK_TYPE, OP_MASK_TYPE );
  556. merge_actions( mvars->chan, mvars->ops, XOP_HAVE_CREATE, OP_CREATE, 0 );
  557. merge_actions( mvars->chan, mvars->ops, XOP_HAVE_EXPUNGE, OP_EXPUNGE, 0 );
  558. mvars->state[M] = mvars->state[S] = ST_FRESH;
  559. info( "Channel %s\n", mvars->chan->name );
  560. mvars->skip = mvars->cben = 0;
  561. if (mvars->chan->stores[M]->driver->flags & mvars->chan->stores[S]->driver->flags & DRV_VERBOSE)
  562. labels[M] = "M: ", labels[S] = "S: ";
  563. else
  564. labels[M] = labels[S] = "";
  565. for (t = 0; t < 2; t++) {
  566. info( "Opening %s %s...\n", str_ms[t], mvars->chan->stores[t]->name );
  567. mvars->drv[t] = mvars->chan->stores[t]->driver;
  568. mvars->drv[t]->open_store( mvars->chan->stores[t], labels[t], store_opened, AUX );
  569. if (mvars->skip)
  570. break;
  571. }
  572. mvars->cben = 1;
  573. opened:
  574. if (mvars->skip)
  575. goto next;
  576. if (mvars->state[M] != ST_OPEN || mvars->state[S] != ST_OPEN)
  577. return;
  578. if (!mvars->boxlist && mvars->chan->patterns) {
  579. mvars->boxlist = 1;
  580. mvars->boxes[M] = filter_boxes( mvars->ctx[M]->boxes, mvars->chan->boxes[M], mvars->chan->patterns );
  581. mvars->boxes[S] = filter_boxes( mvars->ctx[S]->boxes, mvars->chan->boxes[S], mvars->chan->patterns );
  582. for (mboxp = &mvars->boxes[M]; (mbox = *mboxp); ) {
  583. for (sboxp = &mvars->boxes[S]; (sbox = *sboxp); sboxp = &sbox->next)
  584. if (!strcmp( sbox->string, mbox->string )) {
  585. *sboxp = sbox->next;
  586. free( sbox );
  587. *mboxp = mbox->next;
  588. mbox->next = mvars->cboxes;
  589. mvars->cboxes = mbox;
  590. goto gotdupe;
  591. }
  592. mboxp = &mbox->next;
  593. gotdupe: ;
  594. }
  595. }
  596. if (mvars->list && mvars->multiple)
  597. printf( "%s:\n", mvars->chan->name );
  598. syncml:
  599. mvars->done = mvars->cben = 0;
  600. if (mvars->boxlist) {
  601. while ((mbox = mvars->cboxes)) {
  602. mvars->cboxes = mbox->next;
  603. if (sync_listed_boxes( mvars, mbox ))
  604. goto syncw;
  605. }
  606. for (t = 0; t < 2; t++)
  607. while ((mbox = mvars->boxes[t])) {
  608. mvars->boxes[t] = mbox->next;
  609. if ((mvars->chan->ops[1-t] & OP_MASK_TYPE) && (mvars->chan->ops[1-t] & OP_CREATE)) {
  610. if (sync_listed_boxes( mvars, mbox ))
  611. goto syncw;
  612. } else {
  613. free( mbox );
  614. }
  615. }
  616. } else {
  617. if (!mvars->list) {
  618. sync_boxes( mvars->ctx, mvars->chan->boxes, mvars->chan, done_sync, mvars );
  619. mvars->skip = 1;
  620. syncw:
  621. mvars->cben = 1;
  622. if (!mvars->done)
  623. return;
  624. syncone:
  625. if (!mvars->skip)
  626. goto syncml;
  627. } else
  628. printf( "%s <=> %s\n", nz( mvars->chan->boxes[M], "INBOX" ), nz( mvars->chan->boxes[S], "INBOX" ) );
  629. }
  630. next:
  631. for (t = 0; t < 2; t++)
  632. if (mvars->state[t] == ST_OPEN) {
  633. mvars->drv[t]->disown_store( mvars->ctx[t] );
  634. mvars->state[t] = ST_CLOSED;
  635. }
  636. if (mvars->state[M] != ST_CLOSED || mvars->state[S] != ST_CLOSED) {
  637. mvars->skip = mvars->cben = 1;
  638. return;
  639. }
  640. free_string_list( mvars->cboxes );
  641. free_string_list( mvars->boxes[M] );
  642. free_string_list( mvars->boxes[S] );
  643. if (mvars->all) {
  644. if (!(mvars->chan = mvars->chan->next))
  645. break;
  646. } else {
  647. if (mvars->chanptr && (mvars->chanptr = mvars->chanptr->next))
  648. continue;
  649. gotnone:
  650. if (!mvars->argv[++mvars->oind])
  651. break;
  652. }
  653. }
  654. for (t = 0; t < N_DRIVERS; t++)
  655. drivers[t]->cleanup();
  656. }
  657. static void
  658. store_bad( void *aux )
  659. {
  660. MVARS(aux)
  661. mvars->drv[t]->cancel_store( mvars->ctx[t] );
  662. mvars->ret = mvars->skip = 1;
  663. mvars->state[t] = ST_CLOSED;
  664. sync_chans( mvars, E_OPEN );
  665. }
  666. static void
  667. store_opened( store_t *ctx, void *aux )
  668. {
  669. MVARS(aux)
  670. string_list_t *cpat;
  671. int flags;
  672. if (!ctx) {
  673. mvars->ret = mvars->skip = 1;
  674. mvars->state[t] = ST_CLOSED;
  675. sync_chans( mvars, E_OPEN );
  676. return;
  677. }
  678. mvars->ctx[t] = ctx;
  679. if (!mvars->skip && !mvars->boxlist && mvars->chan->patterns && !ctx->listed) {
  680. for (flags = 0, cpat = mvars->chan->patterns; cpat; cpat = cpat->next) {
  681. const char *pat = cpat->string;
  682. if (*pat != '!') {
  683. char buf[8];
  684. snprintf( buf, sizeof(buf), "%s%s", mvars->chan->boxes[t], pat );
  685. /* Partial matches like "INB*" or even "*" are not considered,
  686. * except implicity when the INBOX lives under Path. */
  687. if (!memcmp( buf, "INBOX", 5 )) {
  688. char c = buf[5];
  689. if (!c) {
  690. /* User really wants the INBOX. */
  691. flags |= LIST_INBOX;
  692. } else if (c == '/') {
  693. /* Flattened sub-folders of INBOX actually end up in Path. */
  694. if (ctx->conf->flat_delim)
  695. flags |= LIST_PATH;
  696. else
  697. flags |= LIST_INBOX;
  698. } else {
  699. /* User may not want the INBOX after all ... */
  700. flags |= LIST_PATH;
  701. /* ... but maybe he does.
  702. * The flattened sub-folder case is implicitly covered by the previous line. */
  703. if (c == '*' || c == '%')
  704. flags |= LIST_INBOX;
  705. }
  706. } else {
  707. flags |= LIST_PATH;
  708. }
  709. }
  710. }
  711. set_bad_callback( ctx, store_bad, AUX );
  712. mvars->drv[t]->list( ctx, flags, store_listed, AUX );
  713. } else {
  714. mvars->state[t] = ST_OPEN;
  715. sync_chans( mvars, E_OPEN );
  716. }
  717. }
  718. static void
  719. store_listed( int sts, void *aux )
  720. {
  721. MVARS(aux)
  722. string_list_t **box;
  723. switch (sts) {
  724. case DRV_CANCELED:
  725. return;
  726. case DRV_OK:
  727. mvars->ctx[t]->listed = 1;
  728. if (mvars->ctx[t]->conf->flat_delim) {
  729. for (box = &mvars->ctx[t]->boxes; *box; box = &(*box)->next) {
  730. string_list_t *nbox;
  731. if (map_name( (*box)->string, (char **)&nbox, offsetof(string_list_t, string), mvars->ctx[t]->conf->flat_delim, "/" ) < 0) {
  732. error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", (*box)->string );
  733. mvars->ret = mvars->skip = 1;
  734. } else {
  735. nbox->next = (*box)->next;
  736. free( *box );
  737. *box = nbox;
  738. }
  739. }
  740. }
  741. if (mvars->ctx[t]->conf->map_inbox)
  742. add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
  743. break;
  744. default:
  745. mvars->ret = mvars->skip = 1;
  746. break;
  747. }
  748. mvars->state[t] = ST_OPEN;
  749. sync_chans( mvars, E_OPEN );
  750. }
  751. static int
  752. sync_listed_boxes( main_vars_t *mvars, string_list_t *mbox )
  753. {
  754. if (mvars->chan->boxes[M] || mvars->chan->boxes[S]) {
  755. const char *mpfx = nz( mvars->chan->boxes[M], "" );
  756. const char *spfx = nz( mvars->chan->boxes[S], "" );
  757. if (!mvars->list) {
  758. nfasprintf( &mvars->names[M], "%s%s", mpfx, mbox->string );
  759. nfasprintf( &mvars->names[S], "%s%s", spfx, mbox->string );
  760. free( mbox );
  761. sync_boxes( mvars->ctx, (const char **)mvars->names, mvars->chan, done_sync_2_dyn, mvars );
  762. return 1;
  763. }
  764. printf( "%s%s <=> %s%s\n", mpfx, mbox->string, spfx, mbox->string );
  765. } else {
  766. if (!mvars->list) {
  767. mvars->names[M] = mvars->names[S] = mbox->string;
  768. sync_boxes( mvars->ctx, (const char **)mvars->names, mvars->chan, done_sync_dyn, mvars );
  769. return 1;
  770. }
  771. puts( mbox->string );
  772. }
  773. free( mbox );
  774. return 0;
  775. }
  776. static void
  777. done_sync_dyn( int sts, void *aux )
  778. {
  779. main_vars_t *mvars = (main_vars_t *)aux;
  780. free( ((char *)mvars->names[S]) - offsetof(string_list_t, string) );
  781. done_sync( sts, aux );
  782. }
  783. static void
  784. done_sync_2_dyn( int sts, void *aux )
  785. {
  786. main_vars_t *mvars = (main_vars_t *)aux;
  787. free( mvars->names[M] );
  788. free( mvars->names[S] );
  789. done_sync( sts, aux );
  790. }
  791. static void
  792. done_sync( int sts, void *aux )
  793. {
  794. main_vars_t *mvars = (main_vars_t *)aux;
  795. mvars->done = 1;
  796. if (sts) {
  797. mvars->ret = 1;
  798. if (sts & (SYNC_BAD(M) | SYNC_BAD(S))) {
  799. if (sts & SYNC_BAD(M))
  800. mvars->state[M] = ST_CLOSED;
  801. if (sts & SYNC_BAD(S))
  802. mvars->state[S] = ST_CLOSED;
  803. mvars->skip = 1;
  804. } else if (sts & SYNC_FAIL_ALL) {
  805. mvars->skip = 1;
  806. }
  807. }
  808. sync_chans( mvars, E_SYNC );
  809. }