main.c 20 KB

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