main.c 27 KB

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