sync.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  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
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Flags[] = { 'D', 'F', 'R', 'S', 'T' };
  34. static int
  35. parse_flags( const char *buf )
  36. {
  37. unsigned flags, i, d;
  38. for (flags = i = d = 0; i < as(Flags); i++)
  39. if (buf[d] == Flags[i]) {
  40. flags |= (1 << i);
  41. d++;
  42. }
  43. return flags;
  44. }
  45. static int
  46. make_flags( int flags, char *buf )
  47. {
  48. unsigned i, d;
  49. for (i = d = 0; i < as(Flags); i++)
  50. if (flags & (1 << i))
  51. buf[d++] = Flags[i];
  52. buf[d] = 0;
  53. return d;
  54. }
  55. static void
  56. makeopts( int dops, store_conf_t *dconf, int *dopts,
  57. store_conf_t *sconf, int *sopts )
  58. {
  59. if (dops & (OP_DELETE|OP_FLAGS)) {
  60. *dopts |= OPEN_SETFLAGS;
  61. *sopts |= OPEN_OLD;
  62. if (dops & OP_FLAGS)
  63. *sopts |= OPEN_FLAGS;
  64. }
  65. if (dops & (OP_NEW|OP_RENEW)) {
  66. *dopts |= OPEN_APPEND;
  67. if (dops & OP_RENEW)
  68. *sopts |= OPEN_OLD;
  69. if (dops & OP_NEW)
  70. *sopts |= OPEN_NEW;
  71. if (dops & OP_EXPUNGE)
  72. *sopts |= OPEN_FLAGS;
  73. if (dconf->max_size)
  74. *sopts |= OPEN_SIZE;
  75. }
  76. if (dops & OP_EXPUNGE) {
  77. *dopts |= OPEN_EXPUNGE;
  78. if (dconf->trash) {
  79. if (!dconf->trash_only_new)
  80. *dopts |= OPEN_OLD;
  81. *dopts |= OPEN_NEW|OPEN_FLAGS;
  82. } else if (sconf->trash && sconf->trash_remote_new)
  83. *dopts |= OPEN_NEW|OPEN_FLAGS;
  84. }
  85. if (dops & OP_CREATE)
  86. *dopts |= OPEN_CREATE;
  87. }
  88. static void
  89. dump_box( store_t *ctx )
  90. {
  91. message_t *msg;
  92. char fbuf[16]; /* enlarge when support for keywords is added */
  93. if (Debug)
  94. for (msg = ctx->msgs; msg; msg = msg->next) {
  95. make_flags( msg->flags, fbuf );
  96. printf( " message %d, %s, %d\n", msg->uid, fbuf, msg->size );
  97. }
  98. }
  99. static message_t *
  100. findmsg( store_t *ctx, int uid, message_t **nmsg, const char *who )
  101. {
  102. message_t *msg;
  103. if (uid > 0) {
  104. if (*nmsg && (*nmsg)->uid == uid) {
  105. debug( " %s came in sequence\n", who );
  106. msg = *nmsg;
  107. found:
  108. *nmsg = msg->next;
  109. if (!(msg->status & M_DEAD)) {
  110. msg->status |= M_PROCESSED;
  111. return msg;
  112. }
  113. debug( " ... but it vanished under our feet!\n" );
  114. } else {
  115. for (msg = ctx->msgs; msg; msg = msg->next)
  116. if (msg->uid == uid) {
  117. debug( " %s came out of sequence\n", who );
  118. goto found;
  119. }
  120. debug( " %s not present\n", who );
  121. }
  122. } else
  123. debug( " no %s expected\n", who );
  124. return 0;
  125. }
  126. #define S_DEAD (1<<0)
  127. #define S_EXPIRED (1<<1)
  128. #define S_DEL_MASTER (1<<2)
  129. #define S_DEL_SLAVE (1<<3)
  130. #define S_EXP_SLAVE (1<<4)
  131. typedef struct sync_rec {
  132. struct sync_rec *next;
  133. /* string_list_t *keywords; */
  134. int muid, suid;
  135. unsigned char flags, status;
  136. } sync_rec_t;
  137. #define EX_OK 0
  138. #define EX_FAIL 1
  139. #define EX_STORE_BAD 2
  140. #define EX_RSTORE_BAD 3
  141. static int
  142. expunge( store_t *ctx, store_t *rctx )
  143. {
  144. driver_t *driver = ctx->conf->driver, *rdriver = rctx->conf->driver;
  145. message_t *msg;
  146. msg_data_t msgdata;
  147. for (msg = ctx->msgs; msg; msg = msg->next)
  148. if (msg->flags & F_DELETED) {
  149. if (ctx->conf->trash) {
  150. if (!ctx->conf->trash_only_new || (msg->status & M_NOT_SYNCED)) {
  151. debug( " trashing message %d\n", msg->uid );
  152. switch (driver->trash_msg( ctx, msg )) {
  153. case DRV_STORE_BAD: return EX_STORE_BAD;
  154. default: return EX_FAIL;
  155. case DRV_OK: break;
  156. }
  157. } else
  158. debug( " not trashing message %d - not new\n", msg->uid );
  159. } else if (rctx->conf->trash && rctx->conf->trash_remote_new) {
  160. if (msg->status & M_NOT_SYNCED) {
  161. if (!rctx->conf->max_size || msg->size <= rctx->conf->max_size) {
  162. debug( " remote trashing message %d\n", msg->uid );
  163. msgdata.flags = msg->flags;
  164. switch (driver->fetch_msg( ctx, msg, &msgdata )) {
  165. case DRV_STORE_BAD: return EX_STORE_BAD;
  166. default: return EX_FAIL;
  167. case DRV_OK: break;
  168. }
  169. switch (rdriver->store_msg( rctx, &msgdata, 0 )) {
  170. case DRV_STORE_BAD: return EX_RSTORE_BAD;
  171. default: return EX_FAIL;
  172. case DRV_OK: break;
  173. }
  174. } else
  175. debug( " not remote trashing message %d - too big\n", msg->uid );
  176. } else
  177. debug( " not remote trashing message %d - not new\n", msg->uid );
  178. }
  179. }
  180. switch (driver->close( ctx )) {
  181. case DRV_STORE_BAD: return EX_STORE_BAD;
  182. default: return EX_FAIL;
  183. case DRV_OK: return EX_OK;;
  184. }
  185. }
  186. /* cases:
  187. a) both non-null
  188. b) only master null
  189. b.1) muid 0
  190. b.2) muid -1
  191. b.3) master not scanned
  192. b.4) master gone
  193. c) only slave null
  194. c.1) suid 0
  195. c.2) suid -1
  196. c.3) slave not scanned
  197. c.4) slave gone
  198. d) both null
  199. d.1) both gone
  200. d.2) muid 0, slave not scanned
  201. d.3) muid -1, slave not scanned
  202. d.4) master gone, slave not scanned
  203. d.5) muid 0, slave gone
  204. d.6) muid -1, slave gone
  205. d.7) suid 0, master not scanned
  206. d.8) suid -1, master not scanned
  207. d.9) slave gone, master not scanned
  208. d.10) suid 0, master gone
  209. d.11) suid -1, master gone
  210. impossible cases: both muid & suid 0 or -1, both not scanned
  211. */
  212. static int
  213. sync_old( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, int pull,
  214. unsigned char *nflags, sync_rec_t *srec, message_t *smsg, message_t *tmsg, int dels, int delt )
  215. {
  216. driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver;
  217. int uid, tuid, unex;
  218. unsigned char sflags, aflags, dflags, rflags;
  219. msg_data_t msgdata;
  220. /* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */
  221. tuid = pull ? srec->suid : srec->muid;
  222. if (!tuid) {
  223. /* b.1) / c.1) */
  224. debug( pull ? " no more slave\n" : " no more master\n" );
  225. } else if (dels) {
  226. /* c.4) d.9) / b.4) d.4) */
  227. debug( pull ? " master vanished\n" : " slave vanished\n" );
  228. if (tmsg && tmsg->flags != *nflags)
  229. info( "Info: conflicting changes in (%d,%d)\n", srec->muid, srec->suid );
  230. if (tops & OP_DELETE) {
  231. debug( pull ? " -> pulling delete\n" : " -> pushing delete\n" );
  232. switch (tdriver->set_flags( tctx, tmsg, tuid, F_DELETED, 0 )) {
  233. case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
  234. case DRV_BOX_BAD: return SYNC_FAIL;
  235. default: /* ok */ break;
  236. case DRV_OK:
  237. if (pull) {
  238. fprintf( jfp, "< %d %d 0\n", srec->muid, srec->suid );
  239. srec->muid = 0;
  240. } else {
  241. fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
  242. srec->suid = 0;
  243. }
  244. }
  245. }
  246. } else if (!smsg)
  247. /* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */
  248. ;
  249. else if (tuid < 0) {
  250. /* b.2) / c.2) */
  251. debug( pull ? " no slave yet\n" : " no master yet\n" );
  252. if (tops & OP_RENEW) {
  253. if ((tops & OP_EXPUNGE) && (smsg->flags & F_DELETED)) {
  254. debug( pull ? " -> not pulling - would be expunged anyway\n" : " -> not pushing - would be expunged anyway\n" );
  255. smsg->status |= M_NOT_SYNCED;
  256. } else {
  257. if ((smsg->flags & F_FLAGGED) || !tconf->max_size || smsg->size <= tconf->max_size) {
  258. debug( pull ? " -> pulling it\n" : " -> pushing it\n" );
  259. msgdata.flags = smsg->flags;
  260. switch (sdriver->fetch_msg( sctx, smsg, &msgdata )) {
  261. case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD;
  262. case DRV_BOX_BAD: return SYNC_FAIL;
  263. default: /* ok */ smsg->status |= M_NOT_SYNCED; break;
  264. case DRV_OK:
  265. smsg->flags = msgdata.flags;
  266. switch (tdriver->store_msg( tctx, &msgdata, &uid )) {
  267. case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
  268. default: return SYNC_FAIL;
  269. case DRV_OK:
  270. if (pull) {
  271. srec->suid = uid;
  272. fprintf( jfp, "> %d -1 %d\n", srec->muid, srec->suid );
  273. } else {
  274. srec->muid = uid;
  275. fprintf( jfp, "< -1 %d %d\n", srec->suid, srec->muid );
  276. }
  277. *nflags = smsg->flags;
  278. }
  279. }
  280. } else {
  281. debug( pull ? " -> not pulling - still too big\n" : " -> not pushing - still too big\n" );
  282. smsg->status |= M_NOT_SYNCED;
  283. }
  284. }
  285. } else
  286. smsg->status |= M_NOT_SYNCED;
  287. } else if (!delt) {
  288. /* a) & b.3) / c.3) */
  289. debug( pull ? " may pull\n" : " may push\n" );
  290. if (tops & OP_FLAGS) {
  291. debug( pull ? " -> pulling flags\n" : " -> pushing flags\n" );
  292. sflags = smsg->flags;
  293. aflags = sflags & ~*nflags;
  294. dflags = ~sflags & *nflags;
  295. unex = 0;
  296. if (srec->status & S_EXPIRED) {
  297. if (!pull) {
  298. if (sflags & F_DELETED) {
  299. if (!(sflags & F_FLAGGED))
  300. aflags &= ~F_DELETED;
  301. } else
  302. unex = 1;
  303. } else {
  304. if ((sflags & F_FLAGGED) && !(sflags & F_DELETED)) {
  305. unex = 1;
  306. dflags |= F_DELETED;
  307. }
  308. }
  309. }
  310. rflags = (*nflags | aflags) & ~dflags;
  311. if ((tops & OP_EXPUNGE) && (rflags & F_DELETED) &&
  312. (!tctx->conf->trash || tctx->conf->trash_only_new))
  313. {
  314. aflags &= F_DELETED;
  315. dflags = 0;
  316. }
  317. switch (tdriver->set_flags( tctx, tmsg, tuid, aflags, dflags )) {
  318. case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
  319. case DRV_BOX_BAD: return SYNC_FAIL;
  320. default: /* ok */ break;
  321. case DRV_OK:
  322. *nflags = rflags;
  323. if (unex) {
  324. debug( "unexpiring pair(%d,%d)\n", srec->muid, srec->suid );
  325. /* log last, so deletion can't be misinterpreted! */
  326. fprintf( jfp, "~ %d %d 0\n", srec->muid, srec->suid );
  327. srec->status &= ~S_EXPIRED;
  328. }
  329. }
  330. }
  331. } /* else b.4) / c.4) */
  332. return SYNC_OK;
  333. }
  334. static int
  335. sync_new( int tops, store_t *sctx, store_t *tctx, store_conf_t *tconf, FILE *jfp, sync_rec_t ***srecadd, int pull, int *smaxuid )
  336. {
  337. driver_t *tdriver = tctx->conf->driver, *sdriver = sctx->conf->driver;
  338. sync_rec_t *srec;
  339. message_t *msg;
  340. int nmsgs, uid;
  341. msg_data_t msgdata;
  342. for (nmsgs = 0, msg = sctx->msgs; msg; msg = msg->next)
  343. if (!(msg->status & M_PROCESSED)) {
  344. if (tops & OP_NEW) {
  345. debug( pull ? "new message %d on master\n" : "new message %d on slave\n", msg->uid );
  346. if ((tops & OP_EXPUNGE) && (msg->flags & F_DELETED)) {
  347. debug( pull ? " not pulling - would be expunged anyway\n" : " not pushing - would be expunged anyway\n" );
  348. msg->status |= M_NOT_SYNCED;
  349. } else {
  350. if ((msg->flags & F_FLAGGED) || !tconf->max_size || msg->size <= tconf->max_size) {
  351. debug( pull ? " pulling it\n" : " pushing it\n" );
  352. if (!nmsgs)
  353. info( pull ? "Pulling new messages..." : "Pushing new messages..." );
  354. else
  355. infoc( '.' );
  356. nmsgs++;
  357. msgdata.flags = msg->flags;
  358. switch (sdriver->fetch_msg( sctx, msg, &msgdata )) {
  359. case DRV_STORE_BAD: return pull ? SYNC_MASTER_BAD : SYNC_SLAVE_BAD;
  360. case DRV_BOX_BAD: return SYNC_FAIL;
  361. case DRV_MSG_BAD: /* ok */ msg->status |= M_NOT_SYNCED; continue;
  362. }
  363. msg->flags = msgdata.flags;
  364. switch (tdriver->store_msg( tctx, &msgdata, &uid )) {
  365. case DRV_STORE_BAD: return pull ? SYNC_SLAVE_BAD : SYNC_MASTER_BAD;
  366. default: return SYNC_FAIL;
  367. case DRV_OK: break;
  368. }
  369. } else {
  370. debug( pull ? " not pulling - too big\n" : " not pushing - too big\n" );
  371. msg->status |= M_NOT_SYNCED;
  372. uid = -1;
  373. }
  374. srec = nfmalloc( sizeof(*srec) );
  375. if (pull) {
  376. srec->muid = msg->uid;
  377. srec->suid = uid;
  378. } else {
  379. srec->muid = uid;
  380. srec->suid = msg->uid;
  381. }
  382. srec->flags = msg->flags;
  383. srec->status = 0;
  384. srec->next = 0;
  385. **srecadd = srec;
  386. *srecadd = &srec->next;
  387. fprintf( jfp, "+ %d %d %u\n", srec->muid, srec->suid, srec->flags );
  388. if (*smaxuid < msg->uid) {
  389. *smaxuid = msg->uid;
  390. fprintf( jfp, pull ? "( %d\n" : ") %d\n", msg->uid );
  391. }
  392. }
  393. } else
  394. msg->status |= M_NOT_SYNCED;
  395. }
  396. if (nmsgs)
  397. info( " %d messages\n", nmsgs );
  398. return SYNC_OK;
  399. }
  400. static char *
  401. clean_strdup( const char *s )
  402. {
  403. char *cs;
  404. int i;
  405. cs = nfstrdup( s );
  406. for (i = 0; cs[i]; i++)
  407. if (cs[i] == '/')
  408. cs[i] = '!';
  409. return cs;
  410. }
  411. int
  412. sync_boxes( store_t *mctx, const char *mname,
  413. store_t *sctx, const char *sname,
  414. channel_conf_t *chan )
  415. {
  416. driver_t *mdriver = mctx->conf->driver, *sdriver = sctx->conf->driver;
  417. message_t *mmsg, *smsg, *nmmsg, *nsmsg;
  418. sync_rec_t *recs, *srec, **srecadd, *nsrec;
  419. char *dname, *jname, *nname, *lname, *s, *cmname, *csname;
  420. FILE *dfp, *jfp, *nfp;
  421. int mopts, sopts;
  422. int nom, nos, delm, dels, mex, sex;
  423. int muidval, suidval, smaxxuid, mmaxuid, smaxuid, minwuid, maxwuid;
  424. int t1, t2, t3;
  425. int lfd, ret, line, todel, delt, i, *mexcs, nmexcs, rmexcs;
  426. unsigned char nflags;
  427. struct stat st;
  428. struct flock lck;
  429. char fbuf[16]; /* enlarge when support for keywords is added */
  430. char buf[64];
  431. ret = SYNC_OK;
  432. recs = 0, srecadd = &recs;
  433. nmmsg = nsmsg = 0;
  434. mctx->uidvalidity = sctx->uidvalidity = 0;
  435. mopts = sopts = 0;
  436. makeopts( chan->sops, chan->slave, &sopts, chan->master, &mopts );
  437. makeopts( chan->mops, chan->master, &mopts, chan->slave, &sopts );
  438. if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages)
  439. sopts |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS;
  440. if (!mname || (mctx->conf->map_inbox && !strcmp( mctx->conf->map_inbox, mname )))
  441. mname = "INBOX";
  442. mctx->name = mname;
  443. mdriver->prepare( mctx, mopts );
  444. if (!sname || (sctx->conf->map_inbox && !strcmp( sctx->conf->map_inbox, sname )))
  445. sname = "INBOX";
  446. sctx->name = sname;
  447. sdriver->prepare( sctx, sopts );
  448. if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) {
  449. if (!sctx->path) {
  450. fprintf( stderr, "Error: store '%s' does not support in-box sync state\n", chan->slave->name );
  451. return SYNC_SLAVE_BAD;
  452. }
  453. nfasprintf( &dname, "%s/." EXE "state", sctx->path );
  454. } else {
  455. csname = clean_strdup( sname );
  456. if (chan->sync_state)
  457. nfasprintf( &dname, "%s%s", chan->sync_state, csname );
  458. else {
  459. cmname = clean_strdup( mname );
  460. nfasprintf( &dname, "%s:%s:%s_:%s:%s", global_sync_state,
  461. chan->master->name, cmname, chan->slave->name, csname );
  462. free( cmname );
  463. }
  464. free( csname );
  465. }
  466. nfasprintf( &jname, "%s.journal", dname );
  467. nfasprintf( &nname, "%s.new", dname );
  468. nfasprintf( &lname, "%s.lock", dname );
  469. muidval = suidval = smaxxuid = mmaxuid = smaxuid = 0;
  470. memset( &lck, 0, sizeof(lck) );
  471. #if SEEK_SET != 0
  472. lck.l_whence = SEEK_SET;
  473. #endif
  474. #if F_WRLCK != 0
  475. lck.l_type = F_WRLCK;
  476. #endif
  477. if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
  478. if (errno != ENOENT) {
  479. lferr:
  480. fprintf( stderr, "Error: cannot create lock file %s: %s\n", lname, strerror(errno) );
  481. ret = SYNC_FAIL;
  482. goto bail2;
  483. }
  484. goto skiprd;
  485. }
  486. if (fcntl( lfd, F_SETLK, &lck )) {
  487. lckerr:
  488. fprintf( stderr, "Error: channel :%s:%s-:%s:%s is locked\n",
  489. chan->master->name, mname, chan->slave->name, sname );
  490. ret = SYNC_FAIL;
  491. goto bail1;
  492. }
  493. if ((dfp = fopen( dname, "r" ))) {
  494. debug( "reading sync state %s ...\n", dname );
  495. if (fscanf( dfp, "%d:%d %d:%d:%d\n", &muidval, &mmaxuid, &suidval, &smaxxuid, &smaxuid) != 5) {
  496. fprintf( stderr, "Error: invalid sync state header in %s\n", dname );
  497. fclose( dfp );
  498. ret = SYNC_FAIL;
  499. goto bail;
  500. }
  501. line = 1;
  502. while (fgets( buf, sizeof(buf), dfp )) {
  503. line++;
  504. fbuf[0] = 0;
  505. if (sscanf( buf, "%d %d %15s\n", &t1, &t2, fbuf ) < 2) {
  506. fprintf( stderr, "Error: invalid sync state entry at %s:%d\n", dname, line );
  507. fclose( dfp );
  508. ret = SYNC_FAIL;
  509. goto bail;
  510. }
  511. srec = nfmalloc( sizeof(*srec) );
  512. srec->muid = t1;
  513. srec->suid = t2;
  514. s = fbuf;
  515. if (*s == 'X') {
  516. s++;
  517. srec->status = S_EXPIRED;
  518. } else
  519. srec->status = 0;
  520. srec->flags = parse_flags( s );
  521. debug( " entry (%d,%d,%u,%s)\n", srec->muid, srec->suid, srec->flags, srec->status & S_EXPIRED ? "X" : "" );
  522. srec->next = 0;
  523. *srecadd = srec;
  524. srecadd = &srec->next;
  525. }
  526. fclose( dfp );
  527. } else {
  528. if (errno != ENOENT) {
  529. fprintf( stderr, "Error: cannot read sync state %s\n", dname );
  530. ret = SYNC_FAIL;
  531. goto bail;
  532. }
  533. }
  534. if ((jfp = fopen( jname, "r" ))) {
  535. if (!stat( nname, &st )) {
  536. debug( "recovering journal ...\n" );
  537. line = 0;
  538. srec = recs;
  539. while (fgets( buf, sizeof(buf), jfp )) {
  540. line++;
  541. if (buf[0] == '^')
  542. srec = recs;
  543. else {
  544. if (buf[0] == '(' || buf[0] == ')' ?
  545. (sscanf( buf + 2, "%d\n", &t1 ) != 1) :
  546. buf[0] == '-' || buf[0] == '|' ?
  547. (sscanf( buf + 2, "%d %d\n", &t1, &t2 ) != 2) :
  548. (sscanf( buf + 2, "%d %d %d\n", &t1, &t2, &t3 ) != 3))
  549. {
  550. fprintf( stderr, "Error: malformed journal entry at %s:%d\n", jname, line );
  551. fclose( jfp );
  552. ret = SYNC_FAIL;
  553. goto bail;
  554. }
  555. if (buf[0] == '(')
  556. mmaxuid = t1;
  557. else if (buf[0] == ')')
  558. smaxuid = t1;
  559. else if (buf[0] == '|') {
  560. muidval = t1;
  561. suidval = t2;
  562. } else if (buf[0] == '+') {
  563. srec = nfmalloc( sizeof(*srec) );
  564. srec->muid = t1;
  565. srec->suid = t2;
  566. srec->flags = t3;
  567. debug( " new entry(%d,%d,%u)\n", t1, t2, t3 );
  568. srec->status = 0;
  569. srec->next = 0;
  570. *srecadd = srec;
  571. srecadd = &srec->next;
  572. } else {
  573. for (; srec; srec = srec->next)
  574. if (srec->muid == t1 && srec->suid == t2)
  575. goto syncfnd;
  576. fprintf( stderr, "Error: journal entry at %s:%d refers to non-existing sync state entry\n", jname, line );
  577. fclose( jfp );
  578. ret = SYNC_FAIL;
  579. goto bail;
  580. syncfnd:
  581. debug( " entry(%d,%d,%u) ", srec->muid, srec->suid, srec->flags );
  582. switch (buf[0]) {
  583. case '-':
  584. debug( "killed\n" );
  585. srec->status = S_DEAD;
  586. break;
  587. case '<':
  588. debug( "master now %d\n", t3 );
  589. srec->muid = t3;
  590. break;
  591. case '>':
  592. debug( "slave now %d\n", t3 );
  593. srec->suid = t3;
  594. break;
  595. case '*':
  596. debug( "flags now %d\n", t3 );
  597. srec->flags = t3;
  598. break;
  599. case '~':
  600. debug( "expired now %d\n", t3 );
  601. if (t3) {
  602. if (smaxxuid < t2)
  603. smaxxuid = t2;
  604. srec->status |= S_EXPIRED;
  605. } else
  606. srec->status &= ~S_EXPIRED;
  607. break;
  608. default:
  609. fprintf( stderr, "Error: unrecognized journal entry at %s:%d\n", jname, line );
  610. fclose( jfp );
  611. ret = SYNC_FAIL;
  612. goto bail;
  613. }
  614. }
  615. }
  616. }
  617. }
  618. fclose( jfp );
  619. } else {
  620. if (errno != ENOENT) {
  621. fprintf( stderr, "Error: cannot read journal %s\n", jname );
  622. ret = SYNC_FAIL;
  623. goto bail;
  624. }
  625. }
  626. skiprd:
  627. if (sctx->opts & OPEN_NEW)
  628. maxwuid = INT_MAX;
  629. else if (sctx->opts & OPEN_OLD) {
  630. maxwuid = 0;
  631. for (srec = recs; srec; srec = srec->next)
  632. if (!(srec->status & S_DEAD) && srec->suid > maxwuid)
  633. maxwuid = srec->suid;
  634. } else
  635. maxwuid = 0;
  636. info( "Selecting slave... " );
  637. debug( "selecting slave [1,%d]\n", maxwuid );
  638. switch (sdriver->select( sctx, (sctx->opts & OPEN_OLD) ? 1 : smaxuid + 1, maxwuid, 0, 0 )) {
  639. case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto bail;
  640. case DRV_BOX_BAD: ret = SYNC_FAIL; goto bail;
  641. }
  642. info( "%d messages, %d recent\n", sctx->count, sctx->recent );
  643. dump_box( sctx );
  644. if (suidval && suidval != sctx->uidvalidity) {
  645. fprintf( stderr, "Error: UIDVALIDITY of slave changed\n" );
  646. ret = SYNC_FAIL;
  647. goto bail;
  648. }
  649. s = strrchr( dname, '/' );
  650. *s = 0;
  651. mkdir( dname, 0700 );
  652. *s = '/';
  653. if (lfd < 0) {
  654. if ((lfd = open( lname, O_WRONLY|O_CREAT, 0666 )) < 0)
  655. goto lferr;
  656. if (fcntl( lfd, F_SETLK, &lck ))
  657. goto lckerr;
  658. }
  659. if (!(nfp = fopen( nname, "w" ))) {
  660. fprintf( stderr, "Error: cannot write new sync state %s\n", nname );
  661. ret = SYNC_FAIL;
  662. goto bail;
  663. }
  664. if (!(jfp = fopen( jname, "a" ))) {
  665. fprintf( stderr, "Error: cannot write journal %s\n", jname );
  666. fclose( nfp );
  667. ret = SYNC_FAIL;
  668. goto bail;
  669. }
  670. setlinebuf( jfp );
  671. mexcs = 0;
  672. nmexcs = rmexcs = 0;
  673. minwuid = INT_MAX;
  674. if (smaxxuid) {
  675. debug( "preparing master selection - max expired slave uid is %d\n", smaxxuid );
  676. for (srec = recs; srec; srec = srec->next) {
  677. if (srec->status & S_DEAD)
  678. continue;
  679. if (srec->status & S_EXPIRED) {
  680. if (!srec->suid || ((sctx->opts & OPEN_OLD) && !findmsg( sctx, srec->suid, &nsmsg, "slave" )))
  681. srec->status |= S_EXP_SLAVE;
  682. else if (minwuid > srec->muid)
  683. minwuid = srec->muid;
  684. } else if (smaxxuid < srec->suid && minwuid > srec->muid)
  685. minwuid = srec->muid;
  686. }
  687. debug( " min non-orphaned master uid is %d\n", minwuid );
  688. fprintf( jfp, "^\n" ); /* if any S_EXP_SLAVE */
  689. for (srec = recs; srec; srec = srec->next) {
  690. if (srec->status & S_DEAD)
  691. continue;
  692. if (srec->status & S_EXP_SLAVE) {
  693. if (minwuid > srec->muid && mmaxuid >= srec->muid) {
  694. debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
  695. srec->status = S_DEAD;
  696. fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
  697. } else if (srec->suid) {
  698. debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid );
  699. fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
  700. srec->suid = 0;
  701. }
  702. } else if (minwuid > srec->muid) {
  703. if (srec->suid < 0) {
  704. if (mmaxuid >= srec->muid) {
  705. debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
  706. srec->status = S_DEAD;
  707. fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
  708. }
  709. } else if (srec->muid > 0 && srec->suid && (mctx->opts & OPEN_OLD) &&
  710. (!(mctx->opts & OPEN_NEW) || mmaxuid >= srec->muid)) {
  711. if (nmexcs == rmexcs) {
  712. rmexcs = rmexcs * 2 + 100;
  713. mexcs = nfrealloc( mexcs, rmexcs * sizeof(int) );
  714. }
  715. mexcs[nmexcs++] = srec->muid;
  716. }
  717. }
  718. }
  719. debug( " exception list is:" );
  720. for (i = 0; i < nmexcs; i++)
  721. debug( " %d", mexcs[i] );
  722. debug( "\n" );
  723. } else if (mctx->opts & OPEN_OLD)
  724. minwuid = 1;
  725. if (mctx->opts & OPEN_NEW) {
  726. if (minwuid > mmaxuid + 1)
  727. minwuid = mmaxuid + 1;
  728. maxwuid = INT_MAX;
  729. } else if (mctx->opts & OPEN_OLD) {
  730. maxwuid = 0;
  731. for (srec = recs; srec; srec = srec->next)
  732. if (!(srec->status & S_DEAD) && srec->muid > maxwuid)
  733. maxwuid = srec->muid;
  734. } else
  735. maxwuid = 0;
  736. info( "Selecting master... " );
  737. debug( "selecting master [%d,%d]\n", minwuid, maxwuid );
  738. switch (mdriver->select( mctx, minwuid, maxwuid, mexcs, nmexcs )) {
  739. case DRV_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
  740. case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
  741. }
  742. info( "%d messages, %d recent\n", mctx->count, mctx->recent );
  743. dump_box( mctx );
  744. if (muidval && muidval != mctx->uidvalidity) {
  745. fprintf( stderr, "Error: UIDVALIDITY of master changed\n" );
  746. ret = SYNC_FAIL;
  747. goto finish;
  748. }
  749. if (!muidval || !suidval) {
  750. muidval = mctx->uidvalidity;
  751. suidval = sctx->uidvalidity;
  752. fprintf( jfp, "| %d %d\n", muidval, suidval );
  753. }
  754. info( "Synchronizing\n" );
  755. debug( "synchronizing old entries\n" );
  756. fprintf( jfp, "^\n" );
  757. for (srec = recs; srec; srec = srec->next) {
  758. if (srec->status & S_DEAD)
  759. continue;
  760. debug( "pair (%d,%d)\n", srec->muid, srec->suid );
  761. mmsg = findmsg( mctx, srec->muid, &nmmsg, "master" );
  762. smsg = (srec->status & S_EXP_SLAVE) ? 0 : findmsg( sctx, srec->suid, &nsmsg, "slave" );
  763. nom = !mmsg && (mctx->opts & OPEN_OLD);
  764. nos = !smsg && (sctx->opts & OPEN_OLD);
  765. if (nom && nos) {
  766. debug( " vanished\n" );
  767. /* d.1) d.5) d.6) d.10) d.11) */
  768. srec->status = S_DEAD;
  769. fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
  770. } else {
  771. delm = nom && (srec->muid > 0);
  772. dels = nos && (srec->suid > 0);
  773. nflags = srec->flags;
  774. if ((ret = sync_old( chan->mops, sctx, mctx, chan->master, jfp, 0, &nflags, srec, smsg, mmsg, dels, delm )) != SYNC_OK ||
  775. (ret = sync_old( chan->sops, mctx, sctx, chan->slave, jfp, 1, &nflags, srec, mmsg, smsg, delm, dels )) != SYNC_OK)
  776. goto finish;
  777. if (srec->flags != nflags) {
  778. debug( " updating flags (%u -> %u)\n", srec->flags, nflags );
  779. srec->flags = nflags;
  780. fprintf( jfp, "* %d %d %u\n", srec->muid, srec->suid, nflags );
  781. }
  782. if (mmsg && (mmsg->flags & F_DELETED))
  783. srec->status |= S_DEL_MASTER;
  784. if (smsg && (smsg->flags & F_DELETED))
  785. srec->status |= S_DEL_SLAVE;
  786. }
  787. }
  788. debug( "synchronizing new entries\n" );
  789. if ((ret = sync_new( chan->mops, sctx, mctx, chan->master, jfp, &srecadd, 0, &smaxuid )) != SYNC_OK ||
  790. (ret = sync_new( chan->sops, mctx, sctx, chan->slave, jfp, &srecadd, 1, &mmaxuid )) != SYNC_OK)
  791. goto finish;
  792. if ((chan->sops & (OP_NEW|OP_RENEW)) && chan->max_messages) {
  793. debug( "expiring excessive entries\n" );
  794. todel = sctx->count - chan->max_messages;
  795. for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next)
  796. if (!(smsg->status & M_DEAD) && (smsg->flags & F_DELETED))
  797. todel--;
  798. delt = 0;
  799. for (smsg = sctx->msgs; smsg && todel > 0; smsg = smsg->next) {
  800. if ((smsg->status & M_DEAD) || (smsg->flags & F_DELETED))
  801. continue;
  802. if ((smsg->flags & F_FLAGGED) || (smsg->status & M_NOT_SYNCED)) /* add M_DESYNCED? */
  803. todel--;
  804. else if (!(smsg->status & M_RECENT)) {
  805. smsg->status |= M_EXPIRED;
  806. delt++;
  807. todel--;
  808. }
  809. }
  810. if (delt) {
  811. fprintf( jfp, "^\n" );
  812. for (srec = recs; srec; srec = srec->next) {
  813. if (srec->status & (S_DEAD|S_EXPIRED))
  814. continue;
  815. smsg = findmsg( sctx, srec->suid, &nsmsg, "slave" );
  816. if (smsg && (smsg->status & M_EXPIRED)) {
  817. debug( " expiring pair(%d,%d)\n", srec->muid, srec->suid );
  818. /* log first, so deletion can't be misinterpreted! */
  819. fprintf( jfp, "~ %d %d 1\n", srec->muid, srec->suid );
  820. if (smaxxuid < srec->suid)
  821. smaxxuid = srec->suid;
  822. srec->status |= S_EXPIRED;
  823. switch (sdriver->set_flags( sctx, smsg, 0, F_DELETED, 0 )) {
  824. case DRV_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
  825. case DRV_BOX_BAD: ret = SYNC_FAIL; goto finish;
  826. default: /* ok */ break;
  827. case DRV_OK: srec->status |= S_DEL_SLAVE;
  828. }
  829. }
  830. }
  831. }
  832. }
  833. /* Doing CLOSE here instead of EXPUNGE above saves network traffic.
  834. But it costs more server power for single-file formats. And it
  835. makes disk-full/quota-exceeded more probable. */
  836. mex = sex = 0;
  837. if (chan->mops & OP_EXPUNGE) {
  838. info( "Expunging master\n" );
  839. debug( "expunging master\n" );
  840. switch (expunge( mctx, sctx )) {
  841. case EX_STORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
  842. case EX_RSTORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
  843. default: ret = SYNC_FAIL; break;
  844. case EX_OK: mex = 1;
  845. }
  846. }
  847. if (chan->sops & OP_EXPUNGE) {
  848. info( "Expunging slave\n" );
  849. debug( "expunging slave\n" );
  850. switch (expunge( sctx, mctx )) {
  851. case EX_STORE_BAD: ret = SYNC_SLAVE_BAD; goto finish;
  852. case EX_RSTORE_BAD: ret = SYNC_MASTER_BAD; goto finish;
  853. default: ret = SYNC_FAIL; break;
  854. case EX_OK: mex = 1;
  855. }
  856. }
  857. if (mex || sex) {
  858. /* This cleanup is not strictly necessary, as the next full sync
  859. would throw out the dead entries anyway. But ... */
  860. minwuid = INT_MAX;
  861. if (smaxxuid) {
  862. debug( "preparing entry purge - max expired slave uid is %d\n", smaxxuid );
  863. for (srec = recs; srec; srec = srec->next) {
  864. if (srec->status & S_DEAD)
  865. continue;
  866. if (!((srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) &&
  867. (srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex) || (srec->status & S_EXPIRED))) &&
  868. smaxxuid < srec->suid && minwuid > srec->muid)
  869. minwuid = srec->muid;
  870. }
  871. debug( " min non-orphaned master uid is %d\n", minwuid );
  872. }
  873. fprintf( jfp, "^\n" );
  874. for (srec = recs; srec; srec = srec->next) {
  875. if (srec->status & S_DEAD)
  876. continue;
  877. if (srec->suid <= 0 || ((srec->status & S_DEL_SLAVE) && sex)) {
  878. if (srec->muid <= 0 || ((srec->status & S_DEL_MASTER) && mex)) {
  879. debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
  880. srec->status = S_DEAD;
  881. fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
  882. } else if (srec->status & S_EXPIRED) {
  883. if (mmaxuid >= srec->muid && minwuid > srec->muid) {
  884. debug( " -> killing (%d,%d)\n", srec->muid, srec->suid );
  885. srec->status = S_DEAD;
  886. fprintf( jfp, "- %d %d\n", srec->muid, srec->suid );
  887. } else if (srec->suid) {
  888. debug( " -> orphaning (%d,[%d])\n", srec->muid, srec->suid );
  889. fprintf( jfp, "> %d %d 0\n", srec->muid, srec->suid );
  890. srec->suid = 0;
  891. }
  892. }
  893. }
  894. }
  895. }
  896. finish:
  897. fprintf( nfp, "%d:%d %d:%d:%d\n", muidval, mmaxuid, suidval, smaxxuid, smaxuid );
  898. for (srec = recs; srec; srec = srec->next) {
  899. if (srec->status & S_DEAD)
  900. continue;
  901. make_flags( srec->flags, fbuf );
  902. fprintf( nfp, "%d %d %s%s\n", srec->muid, srec->suid,
  903. srec->status & S_EXPIRED ? "X" : "", fbuf );
  904. }
  905. fclose( nfp );
  906. fclose( jfp );
  907. /* order is important! */
  908. rename( nname, dname );
  909. unlink( jname );
  910. bail:
  911. for (srec = recs; srec; srec = nsrec) {
  912. nsrec = srec->next;
  913. free( srec );
  914. }
  915. unlink( lname );
  916. bail1:
  917. close( lfd );
  918. bail2:
  919. free( lname );
  920. free( nname );
  921. free( jname );
  922. free( dname );
  923. return ret;
  924. }