main.c 20 KB

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