sync.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. /*
  2. * mbsync - mailbox synchronizer
  3. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  4. * Copyright (C) 2002-2004 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, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  19. *
  20. * As a special exception, mbsync may be linked with the OpenSSL library,
  21. * despite that library's more restrictive license.
  22. */
  23. #include "isync.h"
  24. #include <stdio.h>
  25. #include <limits.h>
  26. #include <stdlib.h>
  27. #include <unistd.h>
  28. #include <time.h>
  29. #include <fcntl.h>
  30. #include <string.h>
  31. #include <errno.h>
  32. #include <sys/stat.h>
  33. static const char *str_ms[] = { "master", "slave" }, *str_hl[] = { "push", "pull" };
  34. void
  35. Fprintf( FILE *f, const char *msg, ... )
  36. {
  37. int r;
  38. va_list va;
  39. va_start( va, msg );
  40. r = vfprintf( f, msg, va );
  41. va_end( va );
  42. if (r < 0) {
  43. perror( "cannot write file" );
  44. exit( 1 );
  45. }
  46. }
  47. static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
  48. static int
  49. parse_flags( const char *buf )
  50. {
  51. unsigned flags, i, d;
  52. for (flags = i = d = 0; i < as(Flags); i++)
  53. if (buf[d] == Flags[i]) {
  54. flags |= (1 << i);
  55. d++;
  56. }
  57. return flags;
  58. }
  59. static int
  60. make_flags( int flags, char *buf )
  61. {
  62. unsigned i, d;
  63. for (i = d = 0; i < as(Flags); i++)
  64. if (flags & (1 << i))
  65. buf[d++] = Flags[i];
  66. buf[d] = 0;
  67. return d;
  68. }
  69. #define S_DEAD (1<<0)
  70. #define S_DONE (1<<1)
  71. #define S_DEL(ms) (1<<(2+(ms)))
  72. #define S_EXPIRED (1<<4)
  73. #define S_EXPIRE (1<<5)
  74. #define S_NEXPIRE (1<<6)
  75. #define S_EXP_S (1<<7)
  76. #define mvBit(in,ib,ob) ((unsigned char)(((unsigned)in) * (ob) / (ib)))
  77. typedef struct sync_rec {
  78. struct sync_rec *next;
  79. /* string_list_t *keywords; */
  80. int uid[2];
  81. message_t *msg[2];
  82. unsigned char status, flags, aflags[2], dflags[2];
  83. char tuid[TUIDL];
  84. } sync_rec_t;
  85. static int
  86. findmsgs( sync_rec_t *srecs, store_t *ctx[], int t, FILE *jfp )
  87. {
  88. sync_rec_t *srec, *nsrec = 0;
  89. message_t *msg;
  90. const char *diag;
  91. int uid;
  92. char fbuf[16]; /* enlarge when support for keywords is added */
  93. if (jfp) {
  94. /*
  95. * Alternatively, the TUIDs could be fetched into the messages and
  96. * looked up here. This would make the search faster (probably) and
  97. * save roundtrips. On the downside, quite some additional data would
  98. * have to be fetched for every message and the IMAP driver would be
  99. * more complicated. This is a corner case anyway, so why bother.
  100. */
  101. debug( "finding previously copied messages\n" );
  102. for (srec = srecs; srec; srec = srec->next) {
  103. if (srec->status & S_DEAD)
  104. continue;
  105. if (srec->uid[t] == -2 && srec->tuid[0]) {
  106. debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
  107. switch (ctx[t]->conf->driver->find_msg( ctx[t], srec->tuid, &uid )) {
  108. case DRV_STORE_BAD: return SYNC_BAD(t);
  109. case DRV_OK:
  110. debug( " -> new UID %d\n", uid );
  111. Fprintf( jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
  112. srec->uid[t] = uid;
  113. srec->tuid[0] = 0;
  114. break;
  115. default:
  116. debug( " -> TUID lost\n" );
  117. Fprintf( jfp, "& %d %d\n", srec->uid[M], srec->uid[S] );
  118. srec->flags = 0;
  119. srec->tuid[0] = 0;
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. /*
  126. * Mapping msg -> srec (this variant) is dog slow for new messages.
  127. * Mapping srec -> msg is dog slow for deleted messages.
  128. * One solution would be using binary search on an index array.
  129. * msgs are already sorted by UID, srecs would have to be sorted by uid[t].
  130. */
  131. debug( "matching messages against sync records\n" );
  132. for (msg = ctx[t]->msgs; msg; msg = msg->next) {
  133. uid = msg->uid;
  134. if (DFlags & DEBUG) {
  135. make_flags( msg->flags, fbuf );
  136. printf( ctx[t]->opts & OPEN_SIZE ? " message %5d, %-4s, %6d: " : " message %5d, %-4s: ", uid, fbuf, msg->size );
  137. }
  138. for (srec = nsrec; srec; srec = srec->next) {
  139. if (srec->status & S_DEAD)
  140. continue;
  141. if (srec->uid[t] == uid) {
  142. diag = srec == nsrec ? "adjacently" : "after gap";
  143. goto found;
  144. }
  145. }
  146. for (srec = srecs; srec != nsrec; srec = srec->next) {
  147. if (srec->status & S_DEAD)
  148. continue;
  149. if (srec->uid[t] == uid) {
  150. diag = "after reset";
  151. goto found;
  152. }
  153. }
  154. msg->srec = 0;
  155. debug( "new\n" );
  156. continue;
  157. found:
  158. msg->srec = srec;
  159. srec->msg[t] = msg;
  160. nsrec = srec->next;
  161. debug( "pairs %5d %s\n", srec->uid[1-t], diag );
  162. }
  163. return SYNC_OK;
  164. }
  165. static int
  166. copy_msg( store_t *ctx[], int t, message_t *tmsg, const char *tuid, int *uid )
  167. {
  168. msg_data_t msgdata;
  169. char *fmap, *buf;
  170. int i, len, extra, cra, crd, scr, tcr;
  171. int start, sbreak = 0, ebreak = 0;
  172. char c;
  173. msgdata.flags = tmsg->flags;
  174. switch (ctx[1-t]->conf->driver->fetch_msg( ctx[1-t], tmsg, &msgdata )) {
  175. case DRV_STORE_BAD: return SYNC_BAD(1-t);
  176. case DRV_BOX_BAD: return SYNC_FAIL;
  177. case DRV_MSG_BAD: return SYNC_NOGOOD;
  178. }
  179. tmsg->flags = msgdata.flags;
  180. scr = (ctx[1-t]->conf->driver->flags / DRV_CRLF) & 1;
  181. tcr = (ctx[t]->conf->driver->flags / DRV_CRLF) & 1;
  182. if (tuid || scr != tcr) {
  183. fmap = msgdata.data;
  184. len = msgdata.len;
  185. cra = crd = 0;
  186. if (scr > tcr)
  187. crd = -1;
  188. else if (scr < tcr)
  189. crd = 1;
  190. extra = 0, i = 0;
  191. if (tuid) {
  192. extra += 8 + TUIDL + 1 + tcr;
  193. nloop:
  194. start = i;
  195. while (i < len) {
  196. c = fmap[i++];
  197. if (c == '\r')
  198. extra += crd;
  199. else if (c == '\n') {
  200. extra += cra;
  201. if (i - 2 + !scr == start) {
  202. sbreak = ebreak = i - 2 + !scr; // precalc this!
  203. goto oke;
  204. }
  205. if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
  206. extra -= (ebreak = i) - (sbreak = start);
  207. goto oke;
  208. }
  209. goto nloop;
  210. }
  211. }
  212. /* invalid message */
  213. free( fmap );
  214. return SYNC_NOGOOD;
  215. }
  216. oke:
  217. if (cra || crd)
  218. for (; i < len; i++) {
  219. c = fmap[i];
  220. if (c == '\r')
  221. extra += crd;
  222. else if (c == '\n')
  223. extra += cra;
  224. }
  225. msgdata.len = len + extra;
  226. buf = msgdata.data = nfmalloc( msgdata.len );
  227. i = 0;
  228. if (tuid) {
  229. if (cra) {
  230. for (; i < sbreak; i++) {
  231. if (fmap[i] == '\n')
  232. *buf++ = '\r';
  233. *buf++ = fmap[i];
  234. }
  235. } else if (crd) {
  236. for (; i < sbreak; i++)
  237. if (fmap[i] != '\r')
  238. *buf++ = fmap[i];
  239. } else {
  240. memcpy( buf, fmap, sbreak );
  241. buf += sbreak;
  242. }
  243. memcpy( buf, "X-TUID: ", 8 );
  244. buf += 8;
  245. memcpy( buf, tuid, TUIDL );
  246. buf += TUIDL;
  247. if (tcr)
  248. *buf++ = '\r';
  249. *buf++ = '\n';
  250. i = ebreak;
  251. }
  252. if (cra) {
  253. for (; i < len; i++) {
  254. if (fmap[i] == '\n')
  255. *buf++ = '\r';
  256. *buf++ = fmap[i];
  257. }
  258. } else if (crd) {
  259. for (; i < len; i++)
  260. if (fmap[i] != '\r')
  261. *buf++ = fmap[i];
  262. } else
  263. memcpy( buf, fmap + i, len - i );
  264. free( fmap );
  265. }
  266. switch (ctx[t]->conf->driver->store_msg( ctx[t], &msgdata, uid )) {
  267. case DRV_STORE_BAD: return SYNC_BAD(t);
  268. case DRV_OK: return SYNC_OK;
  269. default: return SYNC_FAIL;
  270. }
  271. }
  272. /* cases:
  273. a) both non-null
  274. b) only master null
  275. b.1) uid[M] 0
  276. b.2) uid[M] -1
  277. b.3) master not scanned
  278. b.4) master gone
  279. c) only slave null
  280. c.1) uid[S] 0
  281. c.2) uid[S] -1
  282. c.3) slave not scanned
  283. c.4) slave gone
  284. d) both null
  285. d.1) both gone
  286. d.2) uid[M] 0, slave not scanned
  287. d.3) uid[M] -1, slave not scanned
  288. d.4) master gone, slave not scanned
  289. d.5) uid[M] 0, slave gone
  290. d.6) uid[M] -1, slave gone
  291. d.7) uid[S] 0, master not scanned
  292. d.8) uid[S] -1, master not scanned
  293. d.9) slave gone, master not scanned
  294. d.10) uid[S] 0, master gone
  295. d.11) uid[S] -1, master gone
  296. impossible cases: both uid[M] & uid[S] 0 or -1, both not scanned
  297. */
  298. static char *
  299. clean_strdup( const char *s )
  300. {
  301. char *cs;
  302. int i;
  303. cs = nfstrdup( s );
  304. for (i = 0; cs[i]; i++)
  305. if (cs[i] == '/')
  306. cs[i] = '!';
  307. return cs;
  308. }
  309. #define JOURNAL_VERSION "2"
  310. int
  311. sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan )
  312. {
  313. driver_t *driver[2];
  314. message_t *tmsg;
  315. sync_rec_t *recs, *srec, **srecadd, *nsrec, **osrecadd;
  316. char *dname, *jname, *nname, *lname, *s, *cmname, *csname;
  317. FILE *dfp, *jfp, *nfp;
  318. int opts[2];
  319. int nom, nos, del[2], ex[2], nex;
  320. int muidval, suidval, smaxxuid, maxuid[2], minwuid, maxwuid;
  321. int t1, t2, t3, t, uid, nmsgs;
  322. int lfd, ret, line, sline, todel, *mexcs, nmexcs, rmexcs;
  323. unsigned char nflags, sflags, aflags, dflags;
  324. struct stat st;
  325. struct flock lck;
  326. char fbuf[16]; /* enlarge when support for keywords is added */
  327. char buf[64];
  328. ret = SYNC_OK;
  329. recs = 0, srecadd = &recs;
  330. for (t = 0; t < 2; t++) {
  331. ctx[t]->name =
  332. (!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ?
  333. "INBOX" : names[t];
  334. ctx[t]->uidvalidity = 0;
  335. driver[t] = ctx[t]->conf->driver;
  336. driver[t]->prepare_paths( ctx[t] );
  337. }
  338. if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) {
  339. if (!ctx[S]->path) {
  340. fprintf( stderr, "Error: store '%s' does not support in-box sync state\n", chan->stores[S]->name );
  341. return SYNC_BAD(S);
  342. }
  343. nfasprintf( &dname, "%s/." EXE "state", ctx[S]->path );
  344. } else {
  345. csname = clean_strdup( ctx[S]->name );
  346. if (chan->sync_state)
  347. nfasprintf( &dname, "%s%s", chan->sync_state, csname );
  348. else {
  349. cmname = clean_strdup( ctx[M]->name );
  350. nfasprintf( &dname, "%s:%s:%s_:%s:%s", global_sync_state,
  351. chan->stores[M]->name, cmname, chan->stores[S]->name, csname );
  352. free( cmname );
  353. }
  354. free( csname );
  355. }
  356. nfasprintf( &jname, "%s.journal", dname );
  357. nfasprintf( &nname, "%s.new", dname );
  358. nfasprintf( &lname, "%s.lock", dname );
  359. muidval = suidval = smaxxuid = maxuid[M] = maxuid[S] = 0;
  360. memset( &lck, 0, sizeof(lck) );
  361. #if SEEK_SET != 0
  362. lck.l_whence = SEEK_SET;
  363. #endif
  364. #if F_WRLCK != 0
  365. lck.l_type = F_WRLCK;
  366. #endif
  367. line = 0;
  368. if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
  369. if (errno != ENOENT) {
  370. lferr:
  371. fprintf( stderr, "Error: cannot create lock file %s: %s\n", lname, strerror(errno) );
  372. ret = SYNC_FAIL;
  373. goto bail2;
  374. }
  375. goto skiprd;
  376. }
  377. if (fcntl( lfd, F_SETLK, &lck )) {
  378. lckerr:
  379. fprintf( stderr, "Error: channel :%s:%s-:%s:%s is locked\n",
  380. chan->stores[M]->name, ctx[M]->name, chan->stores[S]->name, ctx[S]->name );
  381. ret = SYNC_FAIL;
  382. goto bail1;
  383. }
  384. if ((dfp = fopen( dname, "r" ))) {
  385. debug( "reading sync state %s ...\n", dname );
  386. if (!fgets( buf, sizeof(buf), dfp ) || !(t = strlen( buf )) || buf[t - 1] != '\n') {
  387. fprintf( stderr, "Error: incomplete sync state header in %s\n", dname );
  388. fclose( dfp );
  389. ret = SYNC_FAIL;
  390. goto bail;
  391. }
  392. if (sscanf( buf, "%d:%d %d:%d:%d", &muidval, &maxuid[M], &suidval, &smaxxuid, &maxuid[S]) != 5) {
  393. fprintf( stderr, "Error: invalid sync state header in %s\n", dname );
  394. fclose( dfp );
  395. ret = SYNC_FAIL;
  396. goto bail;
  397. }
  398. sline = 1;
  399. while (fgets( buf, sizeof(buf), dfp )) {
  400. sline++;
  401. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  402. fprintf( stderr, "Error: incomplete sync state entry at %s:%d\n", dname, sline );
  403. fclose( dfp );
  404. ret = SYNC_FAIL;
  405. goto bail;
  406. }
  407. fbuf[0] = 0;
  408. if (sscanf( buf, "%d %d %15s", &t1, &t2, fbuf ) < 2) {
  409. fprintf( stderr, "Error: invalid sync state entry at %s:%d\n", dname, sline );
  410. fclose( dfp );
  411. ret = SYNC_FAIL;
  412. goto bail;
  413. }
  414. srec = nfmalloc( sizeof(*srec) );
  415. srec->uid[M] = t1;
  416. srec->uid[S] = t2;
  417. s = fbuf;
  418. if (*s == 'X') {
  419. s++;
  420. srec->status = S_EXPIRE | S_EXPIRED;
  421. } else
  422. srec->status = 0;
  423. srec->flags = parse_flags( s );
  424. debug( " entry (%d,%d,%u,%s)\n", srec->uid[M], srec->uid[S], srec->flags, srec->status & S_EXPIRED ? "X" : "" );
  425. srec->msg[M] = srec->msg[S] = 0;
  426. srec->tuid[0] = 0;
  427. srec->next = 0;
  428. *srecadd = srec;
  429. srecadd = &srec->next;
  430. }
  431. fclose( dfp );
  432. } else {
  433. if (errno != ENOENT) {
  434. fprintf( stderr, "Error: cannot read sync state %s\n", dname );
  435. ret = SYNC_FAIL;
  436. goto bail;
  437. }
  438. }
  439. if ((jfp = fopen( jname, "r" ))) {
  440. if (!stat( nname, &st ) && fgets( buf, sizeof(buf), jfp )) {
  441. debug( "recovering journal ...\n" );
  442. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  443. fprintf( stderr, "Error: incomplete journal header in %s\n", jname );
  444. fclose( jfp );
  445. ret = SYNC_FAIL;
  446. goto bail;
  447. }
  448. if (memcmp( buf, JOURNAL_VERSION "\n", strlen(JOURNAL_VERSION) + 1 )) {
  449. fprintf( stderr, "Error: incompatible journal version "
  450. "(got %.*s, expected " JOURNAL_VERSION ")\n", t - 1, buf );
  451. fclose( jfp );
  452. ret = SYNC_FAIL;
  453. goto bail;
  454. }
  455. srec = 0;
  456. line = 1;
  457. while (fgets( buf, sizeof(buf), jfp )) {
  458. line++;
  459. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  460. fprintf( stderr, "Error: incomplete journal entry at %s:%d\n", jname, line );
  461. fclose( jfp );
  462. ret = SYNC_FAIL;
  463. goto bail;
  464. }
  465. if (buf[0] == '#' ?
  466. (t3 = 0, (sscanf( buf + 2, "%d %d %n", &t1, &t2, &t3 ) < 2) || !t3 || (t - t3 != TUIDL + 3)) :
  467. buf[0] == '(' || buf[0] == ')' ?
  468. (sscanf( buf + 2, "%d", &t1 ) != 1) :
  469. buf[0] == '+' || buf[0] == '&' || buf[0] == '-' || buf[0] == '|' || buf[0] == '/' || buf[0] == '\\' ?
  470. (sscanf( buf + 2, "%d %d", &t1, &t2 ) != 2) :
  471. (sscanf( buf + 2, "%d %d %d", &t1, &t2, &t3 ) != 3))
  472. {
  473. fprintf( stderr, "Error: malformed journal entry at %s:%d\n", jname, line );
  474. fclose( jfp );
  475. ret = SYNC_FAIL;
  476. goto bail;
  477. }
  478. if (buf[0] == '(')
  479. maxuid[M] = t1;
  480. else if (buf[0] == ')')
  481. maxuid[S] = t1;
  482. else if (buf[0] == '|') {
  483. muidval = t1;
  484. suidval = t2;
  485. } else if (buf[0] == '+') {
  486. srec = nfmalloc( sizeof(*srec) );
  487. srec->uid[M] = t1;
  488. srec->uid[S] = t2;
  489. debug( " new entry(%d,%d)\n", t1, t2 );
  490. srec->msg[M] = srec->msg[S] = 0;
  491. srec->status = 0;
  492. srec->flags = 0;
  493. srec->tuid[0] = 0;
  494. srec->next = 0;
  495. *srecadd = srec;
  496. srecadd = &srec->next;
  497. } else {
  498. for (nsrec = srec; srec; srec = srec->next)
  499. if (srec->uid[M] == t1 && srec->uid[S] == t2)
  500. goto syncfnd;
  501. for (srec = recs; srec != nsrec; srec = srec->next)
  502. if (srec->uid[M] == t1 && srec->uid[S] == t2)
  503. goto syncfnd;
  504. fprintf( stderr, "Error: journal entry at %s:%d refers to non-existing sync state entry\n", jname, line );
  505. fclose( jfp );
  506. ret = SYNC_FAIL;
  507. goto bail;
  508. syncfnd:
  509. debug( " entry(%d,%d,%u) ", srec->uid[M], srec->uid[S], srec->flags );
  510. switch (buf[0]) {
  511. case '-':
  512. debug( "killed\n" );
  513. srec->status = S_DEAD;
  514. break;
  515. case '#':
  516. debug( "TUID now %." stringify(TUIDL) "s\n", buf + t3 + 2 );
  517. memcpy( srec->tuid, buf + t3 + 2, TUIDL );
  518. break;
  519. case '&':
  520. debug( "TUID %." stringify(TUIDL) "s lost\n", srec->tuid );
  521. srec->flags = 0;
  522. srec->tuid[0] = 0;
  523. break;
  524. case '<':
  525. debug( "master now %d\n", t3 );
  526. srec->uid[M] = t3;
  527. srec->tuid[0] = 0;
  528. break;
  529. case '>':
  530. debug( "slave now %d\n", t3 );
  531. srec->uid[S] = t3;
  532. srec->tuid[0] = 0;
  533. break;
  534. case '*':
  535. debug( "flags now %d\n", t3 );
  536. srec->flags = t3;
  537. break;
  538. case '~':
  539. debug( "expire now %d\n", t3 );
  540. if (t3)
  541. srec->status |= S_EXPIRE;
  542. else
  543. srec->status &= ~S_EXPIRE;
  544. break;
  545. case '\\':
  546. t3 = (srec->status & S_EXPIRED);
  547. debug( "expire back to %d\n", t3 / S_EXPIRED );
  548. if (t3)
  549. srec->status |= S_EXPIRE;
  550. else
  551. srec->status &= ~S_EXPIRE;
  552. break;
  553. case '/':
  554. t3 = (srec->status & S_EXPIRE);
  555. debug( "expired now %d\n", t3 / S_EXPIRE );
  556. if (t3) {
  557. if (smaxxuid < srec->uid[S])
  558. smaxxuid = srec->uid[S];
  559. srec->status |= S_EXPIRED;
  560. } else
  561. srec->status &= ~S_EXPIRED;
  562. break;
  563. default:
  564. fprintf( stderr, "Error: unrecognized journal entry at %s:%d\n", jname, line );
  565. fclose( jfp );
  566. ret = SYNC_FAIL;
  567. goto bail;
  568. }
  569. }
  570. }
  571. }
  572. fclose( jfp );
  573. } else {
  574. if (errno != ENOENT) {
  575. fprintf( stderr, "Error: cannot read journal %s\n", jname );
  576. ret = SYNC_FAIL;
  577. goto bail;
  578. }
  579. }
  580. skiprd:
  581. opts[M] = opts[S] = 0;
  582. for (t = 0; t < 2; t++) {
  583. if (chan->ops[t] & (OP_DELETE|OP_FLAGS)) {
  584. opts[t] |= OPEN_SETFLAGS;
  585. opts[1-t] |= OPEN_OLD;
  586. if (chan->ops[t] & OP_FLAGS)
  587. opts[1-t] |= OPEN_FLAGS;
  588. }
  589. if (chan->ops[t] & (OP_NEW|OP_RENEW)) {
  590. opts[t] |= OPEN_APPEND;
  591. if (chan->ops[t] & OP_RENEW)
  592. opts[1-t] |= OPEN_OLD;
  593. if (chan->ops[t] & OP_NEW)
  594. opts[1-t] |= OPEN_NEW;
  595. if (chan->ops[t] & OP_EXPUNGE)
  596. opts[1-t] |= OPEN_FLAGS;
  597. if (chan->stores[t]->max_size)
  598. opts[1-t] |= OPEN_SIZE;
  599. }
  600. if (chan->ops[t] & OP_EXPUNGE) {
  601. opts[t] |= OPEN_EXPUNGE;
  602. if (chan->stores[t]->trash) {
  603. if (!chan->stores[t]->trash_only_new)
  604. opts[t] |= OPEN_OLD;
  605. opts[t] |= OPEN_NEW|OPEN_FLAGS;
  606. } else if (chan->stores[1-t]->trash && chan->stores[1-t]->trash_remote_new)
  607. opts[t] |= OPEN_NEW|OPEN_FLAGS;
  608. }
  609. if (chan->ops[t] & OP_CREATE)
  610. opts[t] |= OPEN_CREATE;
  611. }
  612. if ((chan->ops[S] & (OP_NEW|OP_RENEW)) && chan->max_messages)
  613. opts[S] |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS;
  614. if (line)
  615. for (srec = recs; srec; srec = srec->next) {
  616. if (srec->status & S_DEAD)
  617. continue;
  618. if ((mvBit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)
  619. opts[S] |= OPEN_OLD|OPEN_FLAGS;
  620. if (srec->tuid[0]) {
  621. if (srec->uid[M] == -2)
  622. opts[M] |= OPEN_OLD|OPEN_FIND;
  623. else if (srec->uid[S] == -2)
  624. opts[S] |= OPEN_OLD|OPEN_FIND;
  625. }
  626. }
  627. driver[M]->prepare_opts( ctx[M], opts[M] );
  628. driver[S]->prepare_opts( ctx[S], opts[S] );
  629. if (ctx[S]->opts & OPEN_NEW)
  630. maxwuid = INT_MAX;
  631. else if (ctx[S]->opts & OPEN_OLD) {
  632. maxwuid = 0;
  633. for (srec = recs; srec; srec = srec->next)
  634. if (!(srec->status & S_DEAD) && srec->uid[S] > maxwuid)
  635. maxwuid = srec->uid[S];
  636. } else
  637. maxwuid = 0;
  638. info( "Selecting slave %s... ", ctx[S]->name );
  639. debug( maxwuid == INT_MAX ? "selecting slave [1,inf]\n" : "selecting slave [1,%d]\n", maxwuid );
  640. switch (driver[S]->select( ctx[S], (ctx[S]->opts & OPEN_OLD) ? 1 : maxuid[S] + 1, maxwuid, 0, 0 )) {
  641. case DRV_STORE_BAD: ret = SYNC_BAD(S); goto bail;
  642. case DRV_BOX_BAD: ret = SYNC_FAIL; goto bail;
  643. }
  644. if (suidval && suidval != ctx[S]->uidvalidity) {
  645. fprintf( stderr, "Error: UIDVALIDITY of slave changed\n" );
  646. ret = SYNC_FAIL;
  647. goto bail;
  648. }
  649. info( "%d messages, %d recent\n", ctx[S]->count, ctx[S]->recent );
  650. s = strrchr( dname, '/' );
  651. *s = 0;
  652. mkdir( dname, 0700 );
  653. *s = '/';
  654. if (lfd < 0) {
  655. if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0)
  656. goto lferr;
  657. if (fcntl( lfd, F_SETLK, &lck ))
  658. goto lckerr;
  659. }
  660. if (!(nfp = fopen( nname, "w" ))) {
  661. fprintf( stderr, "Error: cannot write new sync state %s\n", nname );
  662. ret = SYNC_FAIL;
  663. goto bail;
  664. }
  665. if (!(jfp = fopen( jname, "a" ))) {
  666. fprintf( stderr, "Error: cannot write journal %s\n", jname );
  667. fclose( nfp );
  668. ret = SYNC_FAIL;
  669. goto bail;
  670. }
  671. setlinebuf( jfp );
  672. if (!line)
  673. Fprintf( jfp, JOURNAL_VERSION "\n" );
  674. if ((ret = findmsgs( recs, ctx, S, line ? jfp : 0 )) != SYNC_OK)
  675. goto finish;
  676. mexcs = 0;
  677. nmexcs = rmexcs = 0;
  678. minwuid = INT_MAX;
  679. if (smaxxuid) {
  680. debug( "preparing master selection - max expired slave uid is %d\n", smaxxuid );
  681. for (srec = recs; srec; srec = srec->next) {
  682. if (srec->status & S_DEAD)
  683. continue;
  684. if (srec->status & S_EXPIRED) {
  685. if (!srec->uid[S] || ((ctx[S]->opts & OPEN_OLD) && !srec->msg[S])) {
  686. srec->status |= S_EXP_S;
  687. continue;
  688. }
  689. } else {
  690. if (smaxxuid >= srec->uid[S])
  691. continue;
  692. }
  693. if (minwuid > srec->uid[M])
  694. minwuid = srec->uid[M];
  695. }
  696. debug( " min non-orphaned master uid is %d\n", minwuid );
  697. for (srec = recs; srec; srec = srec->next) {
  698. if (srec->status & S_DEAD)
  699. continue;
  700. if (srec->status & S_EXP_S) {
  701. if (minwuid > srec->uid[M] && maxuid[M] >= srec->uid[M]) {
  702. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  703. srec->status = S_DEAD;
  704. Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  705. } else if (srec->uid[S]) {
  706. debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] );
  707. Fprintf( jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] );
  708. srec->uid[S] = 0;
  709. }
  710. } else if (minwuid > srec->uid[M]) {
  711. if (srec->uid[S] < 0) {
  712. if (maxuid[M] >= srec->uid[M]) {
  713. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  714. srec->status = S_DEAD;
  715. Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  716. }
  717. } else if (srec->uid[M] > 0 && srec->uid[S] && (ctx[M]->opts & OPEN_OLD) &&
  718. (!(ctx[M]->opts & OPEN_NEW) || maxuid[M] >= srec->uid[M])) {
  719. if (nmexcs == rmexcs) {
  720. rmexcs = rmexcs * 2 + 100;
  721. mexcs = nfrealloc( mexcs, rmexcs * sizeof(int) );
  722. }
  723. mexcs[nmexcs++] = srec->uid[M];
  724. }
  725. }
  726. }
  727. debug( " exception list is:" );
  728. for (t = 0; t < nmexcs; t++)
  729. debug( " %d", mexcs[t] );
  730. debug( "\n" );
  731. } else if (ctx[M]->opts & OPEN_OLD)
  732. minwuid = 1;
  733. if (ctx[M]->opts & OPEN_NEW) {
  734. if (minwuid > maxuid[M] + 1)
  735. minwuid = maxuid[M] + 1;
  736. maxwuid = INT_MAX;
  737. } else if (ctx[M]->opts & OPEN_OLD) {
  738. maxwuid = 0;
  739. for (srec = recs; srec; srec = srec->next)
  740. if (!(srec->status & S_DEAD) && srec->uid[M] > maxwuid)
  741. maxwuid = srec->uid[M];
  742. } else
  743. maxwuid = 0;
  744. info( "Selecting master %s... ", ctx[M]->name );
  745. debug( maxwuid == INT_MAX ? "selecting master [%d,inf]\n" : "selecting master [%d,%d]\n", minwuid, maxwuid );
  746. switch (driver[M]->select( ctx[M], minwuid, maxwuid, mexcs, nmexcs )) {
  747. case DRV_STORE_BAD: ret = SYNC_BAD(M); goto finish;
  748. case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
  749. }
  750. if (muidval && muidval != ctx[M]->uidvalidity) {
  751. fprintf( stderr, "Error: UIDVALIDITY of master changed\n" );
  752. ret = SYNC_FAIL;
  753. goto finish;
  754. }
  755. info( "%d messages, %d recent\n", ctx[M]->count, ctx[M]->recent );
  756. if ((ret = findmsgs( recs, ctx, M, line ? jfp : 0 )) != SYNC_OK)
  757. goto finish;
  758. if (!muidval || !suidval) {
  759. muidval = ctx[M]->uidvalidity;
  760. suidval = ctx[S]->uidvalidity;
  761. Fprintf( jfp, "| %d %d\n", muidval, suidval );
  762. }
  763. info( "Synchronizing...\n" );
  764. debug( "synchronizing new entries\n" );
  765. osrecadd = srecadd;
  766. for (t = 0; t < 2; t++) {
  767. for (nmsgs = 0, tmsg = ctx[1-t]->msgs; tmsg; tmsg = tmsg->next)
  768. if (tmsg->srec ? tmsg->srec->uid[t] < 0 && (tmsg->srec->uid[t] == -1 ? (chan->ops[t] & OP_RENEW) : (chan->ops[t] & OP_NEW)) : (chan->ops[t] & OP_NEW)) {
  769. debug( "new message %d on %s\n", tmsg->uid, str_ms[1-t] );
  770. if ((chan->ops[t] & OP_EXPUNGE) && (tmsg->flags & F_DELETED))
  771. debug( " -> not %sing - would be expunged anyway\n", str_hl[t] );
  772. else {
  773. if (tmsg->srec) {
  774. srec = tmsg->srec;
  775. srec->status |= S_DONE;
  776. debug( " -> pair(%d,%d) exists\n", srec->uid[M], srec->uid[S] );
  777. } else {
  778. srec = nfmalloc( sizeof(*srec) );
  779. srec->next = 0;
  780. *srecadd = srec;
  781. srecadd = &srec->next;
  782. srec->status = S_DONE;
  783. srec->flags = 0;
  784. srec->tuid[0] = 0;
  785. srec->uid[1-t] = tmsg->uid;
  786. srec->uid[t] = -2;
  787. Fprintf( jfp, "+ %d %d\n", srec->uid[M], srec->uid[S] );
  788. debug( " -> pair(%d,%d) created\n", srec->uid[M], srec->uid[S] );
  789. }
  790. if ((tmsg->flags & F_FLAGGED) || !chan->stores[t]->max_size || tmsg->size <= chan->stores[t]->max_size) {
  791. if (!nmsgs)
  792. info( t ? "Pulling new messages..." : "Pushing new messages..." );
  793. else
  794. infoc( '.' );
  795. nmsgs++;
  796. if (tmsg->flags) {
  797. srec->flags = tmsg->flags;
  798. Fprintf( jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], srec->flags );
  799. debug( " -> updated flags to %u\n", tmsg->flags );
  800. }
  801. for (t1 = 0; t1 < TUIDL; t1++) {
  802. t2 = arc4_getbyte() & 0x3f;
  803. srec->tuid[t1] = t2 < 26 ? t2 + 'A' : t2 < 52 ? t2 + 'a' - 26 : t2 < 62 ? t2 + '0' - 52 : t2 == 62 ? '+' : '/';
  804. }
  805. Fprintf( jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid );
  806. debug( " -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid );
  807. switch ((ret = copy_msg( ctx, t, tmsg, srec->tuid, &uid ))) {
  808. case SYNC_OK: break;
  809. case SYNC_NOGOOD:
  810. /* The error is either transient or the message is gone. */
  811. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  812. srec->status = S_DEAD;
  813. Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  814. continue;
  815. default: goto finish;
  816. }
  817. } else {
  818. if (tmsg->srec) {
  819. debug( " -> not %sing - still too big\n", str_hl[t] );
  820. continue;
  821. }
  822. debug( " -> not %sing - too big\n", str_hl[t] );
  823. uid = -1;
  824. }
  825. if (srec->uid[t] != uid) {
  826. debug( " -> new UID %d\n", uid );
  827. Fprintf( jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
  828. srec->uid[t] = uid;
  829. srec->tuid[0] = 0;
  830. }
  831. if (!tmsg->srec) {
  832. tmsg->srec = srec;
  833. if (maxuid[1-t] < tmsg->uid) {
  834. maxuid[1-t] = tmsg->uid;
  835. Fprintf( jfp, "%c %d\n", ")("[t], tmsg->uid );
  836. }
  837. }
  838. }
  839. }
  840. if (nmsgs)
  841. info( " %d messages\n", nmsgs );
  842. }
  843. debug( "finding just copied messages\n" );
  844. for (srec = recs; srec; srec = srec->next) {
  845. if (srec->status & S_DEAD)
  846. continue;
  847. if (srec->tuid[0]) {
  848. t = (srec->uid[M] == -2) ? M : S;
  849. debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
  850. switch (driver[t]->find_msg( ctx[t], srec->tuid, &uid )) {
  851. case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish;
  852. case DRV_OK:
  853. debug( " -> new UID %d\n", uid );
  854. break;
  855. default:
  856. warn( "Warning: cannot find newly stored message %." stringify(TUIDL) "s on %s.\n", srec->tuid, str_ms[t] );
  857. uid = 0;
  858. break;
  859. }
  860. Fprintf( jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
  861. srec->uid[t] = uid;
  862. srec->tuid[0] = 0;
  863. }
  864. }
  865. debug( "synchronizing old entries\n" );
  866. for (srec = recs; srec != *osrecadd; srec = srec->next) {
  867. if (srec->status & (S_DEAD|S_DONE))
  868. continue;
  869. debug( "pair (%d,%d)\n", srec->uid[M], srec->uid[S] );
  870. nom = !srec->msg[M] && (ctx[M]->opts & OPEN_OLD);
  871. nos = !srec->msg[S] && (ctx[S]->opts & OPEN_OLD);
  872. if (nom && nos) {
  873. debug( " vanished\n" );
  874. /* d.1) d.5) d.6) d.10) d.11) */
  875. srec->status = S_DEAD;
  876. Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  877. } else {
  878. del[M] = nom && (srec->uid[M] > 0);
  879. del[S] = nos && (srec->uid[S] > 0);
  880. for (t = 0; t < 2; t++) {
  881. srec->aflags[t] = srec->dflags[t] = 0;
  882. if (srec->msg[t] && (srec->msg[t]->flags & F_DELETED))
  883. srec->status |= S_DEL(t);
  884. /* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */
  885. if (!srec->uid[t]) {
  886. /* b.1) / c.1) */
  887. debug( " no more %s\n", str_ms[t] );
  888. } else if (del[1-t]) {
  889. /* c.4) d.9) / b.4) d.4) */
  890. if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS) && srec->msg[t]->flags != srec->flags)
  891. info( "Info: conflicting changes in (%d,%d)\n", srec->uid[M], srec->uid[S] );
  892. if (chan->ops[t] & OP_DELETE) {
  893. debug( " %sing delete\n", str_hl[t] );
  894. switch (driver[t]->set_flags( ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0 )) {
  895. case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish;
  896. case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
  897. default: /* ok */ break;
  898. case DRV_OK:
  899. srec->status |= S_DEL(t);
  900. Fprintf( jfp, "%c %d %d 0\n", "><"[t], srec->uid[M], srec->uid[S] );
  901. srec->uid[1-t] = 0;
  902. }
  903. } else
  904. debug( " not %sing delete\n", str_hl[t] );
  905. } else if (!srec->msg[1-t])
  906. /* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */
  907. ;
  908. else if (srec->uid[t] < 0)
  909. /* b.2) / c.2) */
  910. ; /* handled above */
  911. else if (!del[t]) {
  912. /* a) & b.3) / c.3) */
  913. if (chan->ops[t] & OP_FLAGS) {
  914. sflags = srec->msg[1-t]->flags;
  915. if ((srec->status & (S_EXPIRE|S_EXPIRED)) && !t)
  916. sflags &= ~F_DELETED;
  917. srec->aflags[t] = sflags & ~srec->flags;
  918. srec->dflags[t] = ~sflags & srec->flags;
  919. if (DFlags & DEBUG) {
  920. char afbuf[16], dfbuf[16]; /* enlarge when support for keywords is added */
  921. make_flags( srec->aflags[t], afbuf );
  922. make_flags( srec->dflags[t], dfbuf );
  923. debug( " %sing flags: +%s -%s\n", str_hl[t], afbuf, dfbuf );
  924. }
  925. } else
  926. debug( " not %sing flags\n", str_hl[t] );
  927. } /* else b.4) / c.4) */
  928. }
  929. }
  930. }
  931. if ((chan->ops[S] & (OP_NEW|OP_RENEW|OP_FLAGS)) && chan->max_messages) {
  932. /* Flagged and not yet synced messages older than the first not
  933. * expired message are not counted. */
  934. todel = ctx[S]->count - chan->max_messages;
  935. debug( "scheduling %d excess messages for expiration\n", todel );
  936. for (tmsg = ctx[S]->msgs; tmsg && todel > 0; tmsg = tmsg->next)
  937. if (!(tmsg->status & M_DEAD) && (srec = tmsg->srec) &&
  938. ((tmsg->flags | srec->aflags[S]) & ~srec->dflags[S] & F_DELETED) &&
  939. !(srec->status & (S_EXPIRE|S_EXPIRED)))
  940. todel--;
  941. debug( "%d non-deleted excess messages\n", todel );
  942. for (tmsg = ctx[S]->msgs; tmsg; tmsg = tmsg->next) {
  943. if (tmsg->status & M_DEAD)
  944. continue;
  945. if (!(srec = tmsg->srec) || srec->uid[M] <= 0)
  946. todel--;
  947. else {
  948. nflags = (tmsg->flags | srec->aflags[S]) & ~srec->dflags[S];
  949. if (!(nflags & F_DELETED) || (srec->status & (S_EXPIRE|S_EXPIRED))) {
  950. if (nflags & F_FLAGGED)
  951. todel--;
  952. else if (!(tmsg->status & M_RECENT) &&
  953. (todel > 0 ||
  954. ((srec->status & (S_EXPIRE|S_EXPIRED)) == (S_EXPIRE|S_EXPIRED)) ||
  955. ((srec->status & (S_EXPIRE|S_EXPIRED)) && (tmsg->flags & F_DELETED)))) {
  956. srec->status |= S_NEXPIRE;
  957. debug( " pair(%d,%d)\n", srec->uid[M], srec->uid[S] );
  958. todel--;
  959. }
  960. }
  961. }
  962. }
  963. debug( "%d excess messages remain\n", todel );
  964. for (srec = recs; srec; srec = srec->next) {
  965. if ((srec->status & (S_DEAD|S_DONE)) || !srec->msg[S])
  966. continue;
  967. nex = (srec->status / S_NEXPIRE) & 1;
  968. if (nex != ((srec->status / S_EXPIRED) & 1)) {
  969. if (nex != ((srec->status / S_EXPIRE) & 1)) {
  970. Fprintf( jfp, "~ %d %d %d\n", srec->uid[M], srec->uid[S], nex );
  971. debug( " pair(%d,%d): %d (pre)\n", srec->uid[M], srec->uid[S], nex );
  972. srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
  973. } else
  974. debug( " pair(%d,%d): %d (pending)\n", srec->uid[M], srec->uid[S], nex );
  975. }
  976. }
  977. }
  978. debug( "synchronizing flags\n" );
  979. for (srec = recs; srec != *osrecadd; srec = srec->next) {
  980. if (srec->status & (S_DEAD|S_DONE))
  981. continue;
  982. for (t = 0; t < 2; t++) {
  983. aflags = srec->aflags[t];
  984. dflags = srec->dflags[t];
  985. if (t && ((mvBit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)) {
  986. if (srec->status & S_NEXPIRE)
  987. aflags |= F_DELETED;
  988. else
  989. dflags |= F_DELETED;
  990. }
  991. if ((chan->ops[t] & OP_EXPUNGE) && (((srec->msg[t] ? srec->msg[t]->flags : 0) | aflags) & ~dflags & F_DELETED) &&
  992. (!ctx[t]->conf->trash || ctx[t]->conf->trash_only_new))
  993. {
  994. srec->aflags[t] &= F_DELETED;
  995. aflags &= F_DELETED;
  996. srec->dflags[t] = dflags = 0;
  997. }
  998. if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS)) {
  999. aflags &= ~srec->msg[t]->flags;
  1000. dflags &= srec->msg[t]->flags;
  1001. }
  1002. switch ((aflags | dflags) ? driver[t]->set_flags( ctx[t], srec->msg[t], srec->uid[t], aflags, dflags ) : DRV_OK) {
  1003. case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish;
  1004. case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
  1005. default: /* ok */ srec->aflags[t] = srec->dflags[t] = 0; break;
  1006. case DRV_OK:
  1007. if (aflags & F_DELETED)
  1008. srec->status |= S_DEL(t);
  1009. else if (dflags & F_DELETED)
  1010. srec->status &= ~S_DEL(t);
  1011. if (t) {
  1012. nex = (srec->status / S_NEXPIRE) & 1;
  1013. if (nex != ((srec->status / S_EXPIRED) & 1)) {
  1014. if (nex && (smaxxuid < srec->uid[S]))
  1015. smaxxuid = srec->uid[S];
  1016. Fprintf( jfp, "/ %d %d\n", srec->uid[M], srec->uid[S] );
  1017. debug( " pair(%d,%d): expired %d (commit)\n", srec->uid[M], srec->uid[S], nex );
  1018. srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED);
  1019. } else if (nex != ((srec->status / S_EXPIRE) & 1)) {
  1020. Fprintf( jfp, "\\ %d %d\n", srec->uid[M], srec->uid[S] );
  1021. debug( " pair(%d,%d): expire %d (cancel)\n", srec->uid[M], srec->uid[S], nex );
  1022. srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
  1023. }
  1024. }
  1025. }
  1026. }
  1027. nflags = (srec->flags | srec->aflags[M] | srec->aflags[S]) & ~(srec->dflags[M] | srec->dflags[S]);
  1028. if (srec->flags != nflags) {
  1029. debug( " pair(%d,%d): updating flags (%u -> %u)\n", srec->uid[M], srec->uid[S], srec->flags, nflags );
  1030. srec->flags = nflags;
  1031. Fprintf( jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], nflags );
  1032. }
  1033. }
  1034. for (t = 0; t < 2; t++) {
  1035. ex[t] = 0;
  1036. if (chan->ops[t] & OP_EXPUNGE) {
  1037. info( "Expunging %s\n", str_ms[t] );
  1038. debug( "expunging %s\n", str_ms[t] );
  1039. for (tmsg = ctx[t]->msgs; tmsg; tmsg = tmsg->next)
  1040. if (tmsg->flags & F_DELETED) {
  1041. if (ctx[t]->conf->trash) {
  1042. if (!ctx[t]->conf->trash_only_new || !tmsg->srec || tmsg->srec->uid[1-t] < 0) {
  1043. debug( " trashing message %d\n", tmsg->uid );
  1044. switch (driver[t]->trash_msg( ctx[t], tmsg )) {
  1045. case DRV_OK: break;
  1046. case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish;
  1047. default: ret = SYNC_FAIL; goto nexex;
  1048. }
  1049. } else
  1050. debug( " not trashing message %d - not new\n", tmsg->uid );
  1051. } else if (ctx[1-t]->conf->trash && ctx[1-t]->conf->trash_remote_new) {
  1052. if (!tmsg->srec || tmsg->srec->uid[1-t] < 0) {
  1053. if (!ctx[1-t]->conf->max_size || tmsg->size <= ctx[1-t]->conf->max_size) {
  1054. debug( " remote trashing message %d\n", tmsg->uid );
  1055. switch ((ret = copy_msg( ctx, 1 - t, tmsg, 0, 0 ))) {
  1056. case SYNC_OK: break;
  1057. case SYNC_NOGOOD: ret = SYNC_FAIL; goto nexex;
  1058. case SYNC_FAIL: goto nexex;
  1059. default: goto finish;
  1060. }
  1061. } else
  1062. debug( " not remote trashing message %d - too big\n", tmsg->uid );
  1063. } else
  1064. debug( " not remote trashing message %d - not new\n", tmsg->uid );
  1065. }
  1066. }
  1067. switch (driver[t]->close( ctx[t] )) {
  1068. case DRV_OK: ex[t] = 1; break;
  1069. case DRV_STORE_BAD: ret = SYNC_BAD(t); goto finish;
  1070. default: break;
  1071. }
  1072. }
  1073. nexex: ;
  1074. }
  1075. if (ex[M] || ex[S]) {
  1076. /* This cleanup is not strictly necessary, as the next full sync
  1077. would throw out the dead entries anyway. But ... */
  1078. minwuid = INT_MAX;
  1079. if (smaxxuid) {
  1080. debug( "preparing entry purge - max expired slave uid is %d\n", smaxxuid );
  1081. for (srec = recs; srec; srec = srec->next) {
  1082. if (srec->status & S_DEAD)
  1083. continue;
  1084. if (!((srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && ex[S])) &&
  1085. (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && ex[M]) || (srec->status & S_EXPIRED))) &&
  1086. smaxxuid < srec->uid[S] && minwuid > srec->uid[M])
  1087. minwuid = srec->uid[M];
  1088. }
  1089. debug( " min non-orphaned master uid is %d\n", minwuid );
  1090. }
  1091. for (srec = recs; srec; srec = srec->next) {
  1092. if (srec->status & S_DEAD)
  1093. continue;
  1094. if (srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && ex[S])) {
  1095. if (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && ex[M]) ||
  1096. ((srec->status & S_EXPIRED) && maxuid[M] >= srec->uid[M] && minwuid > srec->uid[M])) {
  1097. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1098. srec->status = S_DEAD;
  1099. Fprintf( jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  1100. } else if (srec->uid[S] > 0) {
  1101. debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] );
  1102. Fprintf( jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] );
  1103. srec->uid[S] = 0;
  1104. }
  1105. } else if (srec->uid[M] > 0 && ((srec->status & S_DEL(M)) && ex[M])) {
  1106. debug( " -> orphaning ([%d],%d)\n", srec->uid[M], srec->uid[S] );
  1107. Fprintf( jfp, "< %d %d 0\n", srec->uid[M], srec->uid[S] );
  1108. srec->uid[M] = 0;
  1109. }
  1110. }
  1111. }
  1112. finish:
  1113. Fprintf( nfp, "%d:%d %d:%d:%d\n", muidval, maxuid[M], suidval, smaxxuid, maxuid[S] );
  1114. for (srec = recs; srec; srec = srec->next) {
  1115. if (srec->status & S_DEAD)
  1116. continue;
  1117. make_flags( srec->flags, fbuf );
  1118. Fprintf( nfp, "%d %d %s%s\n", srec->uid[M], srec->uid[S],
  1119. srec->status & S_EXPIRED ? "X" : "", fbuf );
  1120. }
  1121. fclose( nfp );
  1122. fclose( jfp );
  1123. if (!(DFlags & KEEPJOURNAL)) {
  1124. /* order is important! */
  1125. rename( nname, dname );
  1126. unlink( jname );
  1127. }
  1128. bail:
  1129. for (srec = recs; srec; srec = nsrec) {
  1130. nsrec = srec->next;
  1131. free( srec );
  1132. }
  1133. unlink( lname );
  1134. bail1:
  1135. close( lfd );
  1136. bail2:
  1137. free( lname );
  1138. free( nname );
  1139. free( jname );
  1140. free( dname );
  1141. return ret;
  1142. }