sync.c 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734
  1. /*
  2. * mbsync - mailbox synchronizer
  3. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  4. * Copyright (C) 2002-2006 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 <stddef.h>
  28. #include <unistd.h>
  29. #include <time.h>
  30. #include <fcntl.h>
  31. #include <string.h>
  32. #include <errno.h>
  33. #include <sys/stat.h>
  34. const char *str_ms[] = { "master", "slave" }, *str_hl[] = { "push", "pull" };
  35. void
  36. Fclose( FILE *f )
  37. {
  38. if (fclose( f ) == EOF) {
  39. sys_error( "Error: cannot close file. Disk full?" );
  40. exit( 1 );
  41. }
  42. }
  43. void
  44. Fprintf( FILE *f, const char *msg, ... )
  45. {
  46. int r;
  47. va_list va;
  48. va_start( va, msg );
  49. r = vfprintf( f, msg, va );
  50. va_end( va );
  51. if (r < 0) {
  52. sys_error( "Error: cannot write file. Disk full?" );
  53. exit( 1 );
  54. }
  55. }
  56. static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
  57. static int
  58. parse_flags( const char *buf )
  59. {
  60. unsigned flags, i, d;
  61. for (flags = i = d = 0; i < as(Flags); i++)
  62. if (buf[d] == Flags[i]) {
  63. flags |= (1 << i);
  64. d++;
  65. }
  66. return flags;
  67. }
  68. static int
  69. make_flags( int flags, char *buf )
  70. {
  71. unsigned i, d;
  72. for (i = d = 0; i < as(Flags); i++)
  73. if (flags & (1 << i))
  74. buf[d++] = Flags[i];
  75. buf[d] = 0;
  76. return d;
  77. }
  78. #define S_DEAD (1<<0)
  79. #define S_DONE (1<<1)
  80. #define S_DEL(ms) (1<<(2+(ms)))
  81. #define S_EXPIRED (1<<4)
  82. #define S_EXPIRE (1<<5)
  83. #define S_NEXPIRE (1<<6)
  84. #define S_EXP_S (1<<7)
  85. #define S_FIND (1<<8)
  86. #define mvBit(in,ib,ob) ((unsigned char)(((unsigned)in) * (ob) / (ib)))
  87. typedef struct sync_rec {
  88. struct sync_rec *next;
  89. /* string_list_t *keywords; */
  90. int uid[2];
  91. message_t *msg[2];
  92. unsigned char status, flags, aflags[2], dflags[2];
  93. char tuid[TUIDL];
  94. } sync_rec_t;
  95. /* cases:
  96. a) both non-null
  97. b) only master null
  98. b.1) uid[M] 0
  99. b.2) uid[M] -1
  100. b.3) master not scanned
  101. b.4) master gone
  102. c) only slave null
  103. c.1) uid[S] 0
  104. c.2) uid[S] -1
  105. c.3) slave not scanned
  106. c.4) slave gone
  107. d) both null
  108. d.1) both gone
  109. d.2) uid[M] 0, slave not scanned
  110. d.3) uid[M] -1, slave not scanned
  111. d.4) master gone, slave not scanned
  112. d.5) uid[M] 0, slave gone
  113. d.6) uid[M] -1, slave gone
  114. d.7) uid[S] 0, master not scanned
  115. d.8) uid[S] -1, master not scanned
  116. d.9) slave gone, master not scanned
  117. d.10) uid[S] 0, master gone
  118. d.11) uid[S] -1, master gone
  119. impossible cases: both uid[M] & uid[S] 0 or -1, both not scanned
  120. */
  121. typedef struct {
  122. int t[2];
  123. void (*cb)( int sts, void *aux ), *aux;
  124. char *dname, *jname, *nname, *lname;
  125. FILE *jfp, *nfp;
  126. sync_rec_t *srecs, **srecadd, **osrecadd;
  127. channel_conf_t *chan;
  128. store_t *ctx[2];
  129. driver_t *drv[2];
  130. int state[2], ref_count, ret, lfd;
  131. int new_total[2], new_done[2];
  132. int flags_total[2], flags_done[2];
  133. int trash_total[2], trash_done[2];
  134. int maxuid[2]; /* highest UID that was already propagated */
  135. int uidval[2]; /* UID validity value */
  136. int uidnext[2]; /* next expected UID; TUID lookup makes sense only for lower UIDs */
  137. int smaxxuid; /* highest expired UID on slave */
  138. } sync_vars_t;
  139. static void sync_ref( sync_vars_t *svars ) { ++svars->ref_count; }
  140. static int sync_deref( sync_vars_t *svars );
  141. static int deref_check_cancel( sync_vars_t *svars );
  142. static int check_cancel( sync_vars_t *svars );
  143. #define DRIVER_CALL_RET(call) \
  144. do { \
  145. sync_ref( svars ); \
  146. svars->drv[t]->call; \
  147. return deref_check_cancel( svars ); \
  148. } while (0)
  149. #define DRIVER_CALL(call) \
  150. do { \
  151. sync_ref( svars ); \
  152. svars->drv[t]->call; \
  153. if (deref_check_cancel( svars )) \
  154. return; \
  155. } while (0)
  156. #define AUX &svars->t[t]
  157. #define DECL_SVARS \
  158. int t; \
  159. sync_vars_t *svars
  160. #define INIT_SVARS(aux) \
  161. t = *(int *)aux; \
  162. svars = (sync_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(sync_vars_t, t))
  163. #define DECL_INIT_SVARS(aux) \
  164. int t = *(int *)aux; \
  165. sync_vars_t *svars = (sync_vars_t *)(((char *)(&((int *)aux)[-t])) - offsetof(sync_vars_t, t))
  166. /* operation dependencies:
  167. select(S): -
  168. select(M): select(S) | -
  169. new(M), new(S), flags(M): select(M) & select(S)
  170. flags(S): count(new(S))
  171. find_new(x): new(x)
  172. trash(x): flags(x)
  173. close(x): trash(x) & find_new(x) // with expunge
  174. cleanup: close(M) & close(S)
  175. */
  176. #define ST_LOADED (1<<0)
  177. #define ST_SENT_NEW (1<<1)
  178. #define ST_FOUND_NEW (1<<2)
  179. #define ST_SENT_FLAGS (1<<3)
  180. #define ST_SENT_TRASH (1<<4)
  181. #define ST_CLOSED (1<<5)
  182. #define ST_SENT_CANCEL (1<<6)
  183. #define ST_CANCELED (1<<7)
  184. #define ST_SELECTED (1<<8)
  185. #define ST_DID_EXPUNGE (1<<16)
  186. static void
  187. match_tuids( sync_vars_t *svars, int t )
  188. {
  189. sync_rec_t *srec;
  190. message_t *tmsg, *ntmsg = 0;
  191. const char *diag;
  192. for (srec = svars->srecs; srec; srec = srec->next) {
  193. if (srec->status & S_DEAD)
  194. continue;
  195. if (srec->uid[t] == -2 && srec->tuid[0]) {
  196. debug( " pair(%d,%d): lookup %s, TUID %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], str_ms[t], srec->tuid );
  197. for (tmsg = ntmsg; tmsg; tmsg = tmsg->next) {
  198. if (tmsg->status & M_DEAD)
  199. continue;
  200. if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) {
  201. diag = (tmsg == ntmsg) ? "adjacently" : "after gap";
  202. goto mfound;
  203. }
  204. }
  205. for (tmsg = svars->ctx[t]->msgs; tmsg != ntmsg; tmsg = tmsg->next) {
  206. if (tmsg->status & M_DEAD)
  207. continue;
  208. if (tmsg->tuid[0] && !memcmp( tmsg->tuid, srec->tuid, TUIDL )) {
  209. diag = "after reset";
  210. goto mfound;
  211. }
  212. }
  213. debug( " -> TUID lost\n" );
  214. Fprintf( svars->jfp, "& %d %d\n", srec->uid[M], srec->uid[S] );
  215. srec->flags = 0;
  216. srec->tuid[0] = 0;
  217. continue;
  218. mfound:
  219. debug( " -> new UID %d %s\n", tmsg->uid, diag );
  220. Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], tmsg->uid );
  221. tmsg->srec = srec;
  222. ntmsg = tmsg->next;
  223. srec->uid[t] = tmsg->uid;
  224. srec->tuid[0] = 0;
  225. }
  226. }
  227. }
  228. typedef struct copy_vars {
  229. void (*cb)( int sts, int uid, struct copy_vars *vars );
  230. void *aux;
  231. sync_rec_t *srec; /* also ->tuid */
  232. message_t *msg;
  233. msg_data_t data;
  234. } copy_vars_t;
  235. static void msg_fetched( int sts, void *aux );
  236. static int
  237. copy_msg( copy_vars_t *vars )
  238. {
  239. DECL_INIT_SVARS(vars->aux);
  240. t ^= 1;
  241. vars->data.flags = vars->msg->flags;
  242. DRIVER_CALL_RET(fetch_msg( svars->ctx[t], vars->msg, &vars->data, msg_fetched, vars ));
  243. }
  244. static void msg_stored( int sts, int uid, void *aux );
  245. static void
  246. msg_fetched( int sts, void *aux )
  247. {
  248. copy_vars_t *vars = (copy_vars_t *)aux;
  249. DECL_SVARS;
  250. char *fmap, *buf;
  251. int i, len, extra, scr, tcr, lcrs, hcrs, bcrs, lines;
  252. int start, sbreak = 0, ebreak = 0;
  253. char c;
  254. switch (sts) {
  255. case DRV_OK:
  256. INIT_SVARS(vars->aux);
  257. if (check_cancel( svars )) {
  258. free( vars->data.data );
  259. vars->cb( SYNC_CANCELED, 0, vars );
  260. return;
  261. }
  262. vars->msg->flags = vars->data.flags;
  263. scr = (svars->drv[1-t]->flags / DRV_CRLF) & 1;
  264. tcr = (svars->drv[t]->flags / DRV_CRLF) & 1;
  265. if (vars->srec || scr != tcr) {
  266. fmap = vars->data.data;
  267. len = vars->data.len;
  268. extra = lines = hcrs = bcrs = i = 0;
  269. if (vars->srec) {
  270. nloop:
  271. start = i;
  272. lcrs = 0;
  273. while (i < len) {
  274. c = fmap[i++];
  275. if (c == '\r')
  276. lcrs++;
  277. else if (c == '\n') {
  278. if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
  279. extra = (sbreak = start) - (ebreak = i);
  280. goto oke;
  281. }
  282. lines++;
  283. hcrs += lcrs;
  284. if (i - lcrs - 1 == start) {
  285. sbreak = ebreak = start;
  286. goto oke;
  287. }
  288. goto nloop;
  289. }
  290. }
  291. /* invalid message */
  292. warn( "Warning: message %d from %s has incomplete header.\n",
  293. vars->msg->uid, str_ms[1-t] );
  294. free( fmap );
  295. vars->cb( SYNC_NOGOOD, 0, vars );
  296. return;
  297. oke:
  298. extra += 8 + TUIDL + 1 + (tcr && (!scr || hcrs));
  299. }
  300. if (tcr != scr) {
  301. for (; i < len; i++) {
  302. c = fmap[i];
  303. if (c == '\r')
  304. bcrs++;
  305. else if (c == '\n')
  306. lines++;
  307. }
  308. extra -= hcrs + bcrs;
  309. if (tcr)
  310. extra += lines;
  311. }
  312. vars->data.len = len + extra;
  313. buf = vars->data.data = nfmalloc( vars->data.len );
  314. i = 0;
  315. if (vars->srec) {
  316. if (tcr != scr) {
  317. if (tcr) {
  318. for (; i < sbreak; i++)
  319. if ((c = fmap[i]) != '\r') {
  320. if (c == '\n')
  321. *buf++ = '\r';
  322. *buf++ = c;
  323. }
  324. } else {
  325. for (; i < sbreak; i++)
  326. if ((c = fmap[i]) != '\r')
  327. *buf++ = c;
  328. }
  329. } else {
  330. memcpy( buf, fmap, sbreak );
  331. buf += sbreak;
  332. }
  333. memcpy( buf, "X-TUID: ", 8 );
  334. buf += 8;
  335. memcpy( buf, vars->srec->tuid, TUIDL );
  336. buf += TUIDL;
  337. if (tcr && (!scr || hcrs))
  338. *buf++ = '\r';
  339. *buf++ = '\n';
  340. i = ebreak;
  341. }
  342. if (tcr != scr) {
  343. if (tcr) {
  344. for (; i < len; i++)
  345. if ((c = fmap[i]) != '\r') {
  346. if (c == '\n')
  347. *buf++ = '\r';
  348. *buf++ = c;
  349. }
  350. } else {
  351. for (; i < len; i++)
  352. if ((c = fmap[i]) != '\r')
  353. *buf++ = c;
  354. }
  355. } else
  356. memcpy( buf, fmap + i, len - i );
  357. free( fmap );
  358. }
  359. svars->drv[t]->store_msg( svars->ctx[t], &vars->data, !vars->srec, msg_stored, vars );
  360. break;
  361. case DRV_CANCELED:
  362. vars->cb( SYNC_CANCELED, 0, vars );
  363. break;
  364. case DRV_MSG_BAD:
  365. vars->cb( SYNC_NOGOOD, 0, vars );
  366. break;
  367. default:
  368. vars->cb( SYNC_FAIL, 0, vars );
  369. break;
  370. }
  371. }
  372. static void
  373. msg_stored( int sts, int uid, void *aux )
  374. {
  375. copy_vars_t *vars = (copy_vars_t *)aux;
  376. DECL_SVARS;
  377. switch (sts) {
  378. case DRV_OK:
  379. vars->cb( SYNC_OK, uid, vars );
  380. break;
  381. case DRV_CANCELED:
  382. vars->cb( SYNC_CANCELED, 0, vars );
  383. break;
  384. case DRV_MSG_BAD:
  385. INIT_SVARS(vars->aux);
  386. (void)svars;
  387. warn( "Warning: %s refuses to store message %d from %s.\n",
  388. str_ms[t], vars->msg->uid, str_ms[1-t] );
  389. vars->cb( SYNC_NOGOOD, 0, vars );
  390. break;
  391. default:
  392. vars->cb( SYNC_FAIL, 0, vars );
  393. break;
  394. }
  395. }
  396. static void
  397. stats( sync_vars_t *svars )
  398. {
  399. char buf[2][64];
  400. char *cs;
  401. int t, l;
  402. static int cols = -1;
  403. if (cols < 0 && (!(cs = getenv( "COLUMNS" )) || !(cols = atoi( cs ) / 2)))
  404. cols = 36;
  405. if (!(DFlags & QUIET)) {
  406. for (t = 0; t < 2; t++) {
  407. l = sprintf( buf[t], "+%d/%d *%d/%d #%d/%d",
  408. svars->new_done[t], svars->new_total[t],
  409. svars->flags_done[t], svars->flags_total[t],
  410. svars->trash_done[t], svars->trash_total[t] );
  411. if (l > cols)
  412. buf[t][cols - 1] = '~';
  413. }
  414. infon( "\v\rM: %.*s S: %.*s", cols, buf[0], cols, buf[1] );
  415. }
  416. }
  417. static void sync_bail( sync_vars_t *svars );
  418. static void sync_bail1( sync_vars_t *svars );
  419. static void sync_bail2( sync_vars_t *svars );
  420. static void cancel_done( void *aux );
  421. static void
  422. cancel_sync( sync_vars_t *svars )
  423. {
  424. int t;
  425. for (t = 0; t < 2; t++) {
  426. int other_state = svars->state[1-t];
  427. if (svars->ret & SYNC_BAD(t)) {
  428. cancel_done( AUX );
  429. } else if (!(svars->state[t] & ST_SENT_CANCEL)) {
  430. /* ignore subsequent failures from in-flight commands */
  431. svars->state[t] |= ST_SENT_CANCEL;
  432. svars->drv[t]->cancel( svars->ctx[t], cancel_done, AUX );
  433. }
  434. if (other_state & ST_CANCELED)
  435. break;
  436. }
  437. }
  438. static void
  439. cancel_done( void *aux )
  440. {
  441. DECL_INIT_SVARS(aux);
  442. svars->state[t] |= ST_CANCELED;
  443. if (svars->state[1-t] & ST_CANCELED) {
  444. if (svars->lfd) {
  445. Fclose( svars->nfp );
  446. Fclose( svars->jfp );
  447. sync_bail( svars );
  448. } else {
  449. sync_bail2( svars );
  450. }
  451. }
  452. }
  453. static void
  454. store_bad( void *aux )
  455. {
  456. DECL_INIT_SVARS(aux);
  457. svars->drv[t]->cancel_store( svars->ctx[t] );
  458. svars->ret |= SYNC_BAD(t);
  459. cancel_sync( svars );
  460. }
  461. static int
  462. deref_check_cancel( sync_vars_t *svars )
  463. {
  464. if (sync_deref( svars ))
  465. return -1;
  466. return check_cancel( svars );
  467. }
  468. static int
  469. check_cancel( sync_vars_t *svars )
  470. {
  471. return (svars->state[M] | svars->state[S]) & (ST_SENT_CANCEL | ST_CANCELED);
  472. }
  473. static int
  474. check_ret( int sts, void *aux )
  475. {
  476. DECL_SVARS;
  477. if (sts == DRV_CANCELED)
  478. return 1;
  479. INIT_SVARS(aux);
  480. if (sts == DRV_BOX_BAD) {
  481. svars->ret |= SYNC_FAIL;
  482. cancel_sync( svars );
  483. return 1;
  484. }
  485. return check_cancel( svars );
  486. }
  487. #define SVARS_CHECK_RET \
  488. DECL_SVARS; \
  489. if (check_ret( sts, aux )) \
  490. return; \
  491. INIT_SVARS(aux)
  492. #define SVARS_CHECK_RET_VARS(type) \
  493. type *vars = (type *)aux; \
  494. DECL_SVARS; \
  495. if (check_ret( sts, vars->aux )) { \
  496. free( vars ); \
  497. return; \
  498. } \
  499. INIT_SVARS(vars->aux)
  500. #define SVARS_CHECK_CANCEL_RET \
  501. DECL_SVARS; \
  502. if (sts == SYNC_CANCELED) { \
  503. free( vars ); \
  504. return; \
  505. } \
  506. INIT_SVARS(vars->aux)
  507. static char *
  508. clean_strdup( const char *s )
  509. {
  510. char *cs;
  511. int i;
  512. cs = nfstrdup( s );
  513. for (i = 0; cs[i]; i++)
  514. if (cs[i] == '/')
  515. cs[i] = '!';
  516. return cs;
  517. }
  518. #define JOURNAL_VERSION "2"
  519. static void box_selected( int sts, void *aux );
  520. void
  521. sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
  522. void (*cb)( int sts, void *aux ), void *aux )
  523. {
  524. sync_vars_t *svars;
  525. int t;
  526. svars = nfcalloc( sizeof(*svars) );
  527. svars->t[1] = 1;
  528. svars->ref_count = 1;
  529. svars->cb = cb;
  530. svars->aux = aux;
  531. svars->ctx[0] = ctx[0];
  532. svars->ctx[1] = ctx[1];
  533. svars->chan = chan;
  534. svars->uidval[0] = svars->uidval[1] = -1;
  535. svars->srecadd = &svars->srecs;
  536. for (t = 0; t < 2; t++) {
  537. ctx[t]->name =
  538. (!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ?
  539. "INBOX" : names[t];
  540. ctx[t]->uidvalidity = -1;
  541. set_bad_callback( ctx[t], store_bad, AUX );
  542. svars->drv[t] = ctx[t]->conf->driver;
  543. info( "Selecting %s %s...\n", str_ms[t], ctx[t]->name );
  544. DRIVER_CALL(select( ctx[t], (chan->ops[t] & OP_CREATE) != 0, box_selected, AUX ));
  545. }
  546. }
  547. static int load_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs );
  548. static void
  549. box_selected( int sts, void *aux )
  550. {
  551. DECL_SVARS;
  552. sync_rec_t *srec, *nsrec;
  553. char *s, *cmname, *csname;
  554. store_t *ctx[2];
  555. channel_conf_t *chan;
  556. FILE *jfp;
  557. int opts[2], line, t1, t2, t3;
  558. struct stat st;
  559. struct flock lck;
  560. char fbuf[16]; /* enlarge when support for keywords is added */
  561. char buf[128], buf1[64], buf2[64];
  562. if (check_ret( sts, aux ))
  563. return;
  564. INIT_SVARS(aux);
  565. ctx[0] = svars->ctx[0];
  566. ctx[1] = svars->ctx[1];
  567. svars->state[t] |= ST_SELECTED;
  568. if (!(svars->state[1-t] & ST_SELECTED))
  569. return;
  570. chan = svars->chan;
  571. if (!strcmp( chan->sync_state ? chan->sync_state : global_sync_state, "*" )) {
  572. if (!ctx[S]->path) {
  573. error( "Error: store '%s' does not support in-box sync state\n", chan->stores[S]->name );
  574. sbail:
  575. svars->ret = SYNC_BAD(S);
  576. sync_bail2( svars );
  577. return;
  578. }
  579. nfasprintf( &svars->dname, "%s/." EXE "state", ctx[S]->path );
  580. } else {
  581. csname = clean_strdup( ctx[S]->name );
  582. if (chan->sync_state)
  583. nfasprintf( &svars->dname, "%s%s", chan->sync_state, csname );
  584. else {
  585. cmname = clean_strdup( ctx[M]->name );
  586. nfasprintf( &svars->dname, "%s:%s:%s_:%s:%s", global_sync_state,
  587. chan->stores[M]->name, cmname, chan->stores[S]->name, csname );
  588. free( cmname );
  589. }
  590. free( csname );
  591. if (!(s = strrchr( svars->dname, '/' ))) {
  592. error( "Error: invalid SyncState location '%s'\n", svars->dname );
  593. goto sbail;
  594. }
  595. *s = 0;
  596. if (mkdir( svars->dname, 0700 ) && errno != EEXIST) {
  597. sys_error( "Error: cannot create SyncState directory '%s'", svars->dname );
  598. goto sbail;
  599. }
  600. *s = '/';
  601. }
  602. nfasprintf( &svars->jname, "%s.journal", svars->dname );
  603. nfasprintf( &svars->nname, "%s.new", svars->dname );
  604. nfasprintf( &svars->lname, "%s.lock", svars->dname );
  605. memset( &lck, 0, sizeof(lck) );
  606. #if SEEK_SET != 0
  607. lck.l_whence = SEEK_SET;
  608. #endif
  609. #if F_WRLCK != 0
  610. lck.l_type = F_WRLCK;
  611. #endif
  612. if ((svars->lfd = open( svars->lname, O_WRONLY|O_CREAT, 0666 )) < 0) {
  613. sys_error( "Error: cannot create lock file %s", svars->lname );
  614. svars->ret = SYNC_FAIL;
  615. sync_bail2( svars );
  616. return;
  617. }
  618. if (fcntl( svars->lfd, F_SETLK, &lck )) {
  619. error( "Error: channel :%s:%s-:%s:%s is locked\n",
  620. chan->stores[M]->name, ctx[M]->name, chan->stores[S]->name, ctx[S]->name );
  621. svars->ret = SYNC_FAIL;
  622. sync_bail1( svars );
  623. return;
  624. }
  625. if ((jfp = fopen( svars->dname, "r" ))) {
  626. debug( "reading sync state %s ...\n", svars->dname );
  627. if (!fgets( buf, sizeof(buf), jfp ) || !(t = strlen( buf )) || buf[t - 1] != '\n') {
  628. error( "Error: incomplete sync state header in %s\n", svars->dname );
  629. jbail:
  630. fclose( jfp );
  631. bail:
  632. svars->ret = SYNC_FAIL;
  633. sync_bail( svars );
  634. return;
  635. }
  636. if (sscanf( buf, "%63s %63s", buf1, buf2 ) != 2 ||
  637. sscanf( buf1, "%d:%d:%d", &svars->uidval[M], &svars->maxuid[M], &svars->uidnext[M] ) < 2 ||
  638. sscanf( buf2, "%d:%d:%d:%d", &svars->uidval[S], &svars->smaxxuid, &svars->maxuid[S], &svars->uidnext[S] ) < 3) {
  639. error( "Error: invalid sync state header in %s\n", svars->dname );
  640. goto jbail;
  641. }
  642. line = 1;
  643. while (fgets( buf, sizeof(buf), jfp )) {
  644. line++;
  645. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  646. error( "Error: incomplete sync state entry at %s:%d\n", svars->dname, line );
  647. goto jbail;
  648. }
  649. fbuf[0] = 0;
  650. if (sscanf( buf, "%d %d %15s", &t1, &t2, fbuf ) < 2) {
  651. error( "Error: invalid sync state entry at %s:%d\n", svars->dname, line );
  652. goto jbail;
  653. }
  654. srec = nfmalloc( sizeof(*srec) );
  655. srec->uid[M] = t1;
  656. srec->uid[S] = t2;
  657. s = fbuf;
  658. if (*s == 'X') {
  659. s++;
  660. srec->status = S_EXPIRE | S_EXPIRED;
  661. } else
  662. srec->status = 0;
  663. srec->flags = parse_flags( s );
  664. debug( " entry (%d,%d,%u,%s)\n", srec->uid[M], srec->uid[S], srec->flags, srec->status & S_EXPIRED ? "X" : "" );
  665. srec->msg[M] = srec->msg[S] = 0;
  666. srec->tuid[0] = 0;
  667. srec->next = 0;
  668. *svars->srecadd = srec;
  669. svars->srecadd = &srec->next;
  670. }
  671. fclose( jfp );
  672. } else {
  673. if (errno != ENOENT) {
  674. error( "Error: cannot read sync state %s\n", svars->dname );
  675. goto bail;
  676. }
  677. }
  678. line = 0;
  679. if ((jfp = fopen( svars->jname, "r" ))) {
  680. if (!stat( svars->nname, &st ) && fgets( buf, sizeof(buf), jfp )) {
  681. debug( "recovering journal ...\n" );
  682. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  683. error( "Error: incomplete journal header in %s\n", svars->jname );
  684. goto jbail;
  685. }
  686. if (memcmp( buf, JOURNAL_VERSION "\n", strlen(JOURNAL_VERSION) + 1 )) {
  687. error( "Error: incompatible journal version "
  688. "(got %.*s, expected " JOURNAL_VERSION ")\n", t - 1, buf );
  689. goto jbail;
  690. }
  691. srec = 0;
  692. line = 1;
  693. while (fgets( buf, sizeof(buf), jfp )) {
  694. line++;
  695. if (!(t = strlen( buf )) || buf[t - 1] != '\n') {
  696. error( "Error: incomplete journal entry at %s:%d\n", svars->jname, line );
  697. goto jbail;
  698. }
  699. if (buf[0] == '#' ?
  700. (t3 = 0, (sscanf( buf + 2, "%d %d %n", &t1, &t2, &t3 ) < 2) || !t3 || (t - t3 != TUIDL + 3)) :
  701. buf[0] == '(' || buf[0] == ')' || buf[0] == '{' || buf[0] == '}' ?
  702. (sscanf( buf + 2, "%d", &t1 ) != 1) :
  703. buf[0] == '+' || buf[0] == '&' || buf[0] == '-' || buf[0] == '|' || buf[0] == '/' || buf[0] == '\\' ?
  704. (sscanf( buf + 2, "%d %d", &t1, &t2 ) != 2) :
  705. (sscanf( buf + 2, "%d %d %d", &t1, &t2, &t3 ) != 3))
  706. {
  707. error( "Error: malformed journal entry at %s:%d\n", svars->jname, line );
  708. goto jbail;
  709. }
  710. if (buf[0] == '(')
  711. svars->maxuid[M] = t1;
  712. else if (buf[0] == ')')
  713. svars->maxuid[S] = t1;
  714. else if (buf[0] == '{')
  715. svars->uidnext[M] = t1;
  716. else if (buf[0] == '}')
  717. svars->uidnext[S] = t1;
  718. else if (buf[0] == '|') {
  719. svars->uidval[M] = t1;
  720. svars->uidval[S] = t2;
  721. } else if (buf[0] == '+') {
  722. srec = nfmalloc( sizeof(*srec) );
  723. srec->uid[M] = t1;
  724. srec->uid[S] = t2;
  725. debug( " new entry(%d,%d)\n", t1, t2 );
  726. srec->msg[M] = srec->msg[S] = 0;
  727. srec->status = 0;
  728. srec->flags = 0;
  729. srec->tuid[0] = 0;
  730. srec->next = 0;
  731. *svars->srecadd = srec;
  732. svars->srecadd = &srec->next;
  733. } else {
  734. for (nsrec = srec; srec; srec = srec->next)
  735. if (srec->uid[M] == t1 && srec->uid[S] == t2)
  736. goto syncfnd;
  737. for (srec = svars->srecs; srec != nsrec; srec = srec->next)
  738. if (srec->uid[M] == t1 && srec->uid[S] == t2)
  739. goto syncfnd;
  740. error( "Error: journal entry at %s:%d refers to non-existing sync state entry\n", svars->jname, line );
  741. goto jbail;
  742. syncfnd:
  743. debugn( " entry(%d,%d,%u) ", srec->uid[M], srec->uid[S], srec->flags );
  744. switch (buf[0]) {
  745. case '-':
  746. debug( "killed\n" );
  747. srec->status = S_DEAD;
  748. break;
  749. case '#':
  750. debug( "TUID now %." stringify(TUIDL) "s\n", buf + t3 + 2 );
  751. memcpy( srec->tuid, buf + t3 + 2, TUIDL );
  752. break;
  753. case '&':
  754. debug( "TUID %." stringify(TUIDL) "s lost\n", srec->tuid );
  755. srec->flags = 0;
  756. srec->tuid[0] = 0;
  757. break;
  758. case '<':
  759. debug( "master now %d\n", t3 );
  760. srec->uid[M] = t3;
  761. srec->tuid[0] = 0;
  762. break;
  763. case '>':
  764. debug( "slave now %d\n", t3 );
  765. srec->uid[S] = t3;
  766. srec->tuid[0] = 0;
  767. break;
  768. case '*':
  769. debug( "flags now %d\n", t3 );
  770. srec->flags = t3;
  771. break;
  772. case '~':
  773. debug( "expire now %d\n", t3 );
  774. if (t3)
  775. srec->status |= S_EXPIRE;
  776. else
  777. srec->status &= ~S_EXPIRE;
  778. break;
  779. case '\\':
  780. t3 = (srec->status & S_EXPIRED);
  781. debug( "expire back to %d\n", t3 / S_EXPIRED );
  782. if (t3)
  783. srec->status |= S_EXPIRE;
  784. else
  785. srec->status &= ~S_EXPIRE;
  786. break;
  787. case '/':
  788. t3 = (srec->status & S_EXPIRE);
  789. debug( "expired now %d\n", t3 / S_EXPIRE );
  790. if (t3) {
  791. if (svars->smaxxuid < srec->uid[S])
  792. svars->smaxxuid = srec->uid[S];
  793. srec->status |= S_EXPIRED;
  794. } else
  795. srec->status &= ~S_EXPIRED;
  796. break;
  797. default:
  798. error( "Error: unrecognized journal entry at %s:%d\n", svars->jname, line );
  799. goto jbail;
  800. }
  801. }
  802. }
  803. }
  804. fclose( jfp );
  805. } else {
  806. if (errno != ENOENT) {
  807. error( "Error: cannot read journal %s\n", svars->jname );
  808. goto bail;
  809. }
  810. }
  811. t1 = 0;
  812. for (t = 0; t < 2; t++)
  813. if (svars->uidval[t] >= 0 && svars->uidval[t] != ctx[t]->uidvalidity) {
  814. error( "Error: UIDVALIDITY of %s changed (got %d, expected %d)\n",
  815. str_ms[t], ctx[t]->uidvalidity, svars->uidval[t] );
  816. t1++;
  817. }
  818. if (t1)
  819. goto bail;
  820. if (!(svars->nfp = fopen( svars->nname, "w" ))) {
  821. error( "Error: cannot write new sync state %s\n", svars->nname );
  822. goto bail;
  823. }
  824. if (!(svars->jfp = fopen( svars->jname, "a" ))) {
  825. error( "Error: cannot write journal %s\n", svars->jname );
  826. fclose( svars->nfp );
  827. goto bail;
  828. }
  829. setlinebuf( svars->jfp );
  830. if (!line)
  831. Fprintf( svars->jfp, JOURNAL_VERSION "\n" );
  832. opts[M] = opts[S] = 0;
  833. for (t = 0; t < 2; t++) {
  834. if (chan->ops[t] & (OP_DELETE|OP_FLAGS)) {
  835. opts[t] |= OPEN_SETFLAGS;
  836. opts[1-t] |= OPEN_OLD;
  837. if (chan->ops[t] & OP_FLAGS)
  838. opts[1-t] |= OPEN_FLAGS;
  839. }
  840. if (chan->ops[t] & (OP_NEW|OP_RENEW)) {
  841. opts[t] |= OPEN_APPEND;
  842. if (chan->ops[t] & OP_RENEW)
  843. opts[1-t] |= OPEN_OLD;
  844. if (chan->ops[t] & OP_NEW)
  845. opts[1-t] |= OPEN_NEW;
  846. if (chan->ops[t] & OP_EXPUNGE)
  847. opts[1-t] |= OPEN_FLAGS;
  848. if (chan->stores[t]->max_size)
  849. opts[1-t] |= OPEN_SIZE;
  850. }
  851. if (chan->ops[t] & OP_EXPUNGE) {
  852. opts[t] |= OPEN_EXPUNGE;
  853. if (chan->stores[t]->trash) {
  854. if (!chan->stores[t]->trash_only_new)
  855. opts[t] |= OPEN_OLD;
  856. opts[t] |= OPEN_NEW|OPEN_FLAGS;
  857. } else if (chan->stores[1-t]->trash && chan->stores[1-t]->trash_remote_new)
  858. opts[t] |= OPEN_NEW|OPEN_FLAGS;
  859. }
  860. }
  861. if ((chan->ops[S] & (OP_NEW|OP_RENEW)) && chan->max_messages)
  862. opts[S] |= OPEN_OLD|OPEN_NEW|OPEN_FLAGS;
  863. if (line)
  864. for (srec = svars->srecs; srec; srec = srec->next) {
  865. if (srec->status & S_DEAD)
  866. continue;
  867. if ((mvBit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)
  868. opts[S] |= OPEN_OLD|OPEN_FLAGS;
  869. if (srec->tuid[0]) {
  870. if (srec->uid[M] == -2)
  871. opts[M] |= OPEN_NEW|OPEN_FIND, svars->state[M] |= S_FIND;
  872. else if (srec->uid[S] == -2)
  873. opts[S] |= OPEN_NEW|OPEN_FIND, svars->state[S] |= S_FIND;
  874. }
  875. }
  876. svars->drv[M]->prepare_opts( ctx[M], opts[M] );
  877. svars->drv[S]->prepare_opts( ctx[S], opts[S] );
  878. if (!svars->smaxxuid && load_box( svars, M, (ctx[M]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 ))
  879. return;
  880. load_box( svars, S, (ctx[S]->opts & OPEN_OLD) ? 1 : INT_MAX, 0, 0 );
  881. }
  882. static void box_loaded( int sts, void *aux );
  883. static int
  884. load_box( sync_vars_t *svars, int t, int minwuid, int *mexcs, int nmexcs )
  885. {
  886. sync_rec_t *srec;
  887. int maxwuid;
  888. if (svars->ctx[t]->opts & OPEN_NEW) {
  889. if (minwuid > svars->maxuid[t] + 1)
  890. minwuid = svars->maxuid[t] + 1;
  891. maxwuid = INT_MAX;
  892. } else if (svars->ctx[t]->opts & OPEN_OLD) {
  893. maxwuid = 0;
  894. for (srec = svars->srecs; srec; srec = srec->next)
  895. if (!(srec->status & S_DEAD) && srec->uid[t] > maxwuid)
  896. maxwuid = srec->uid[t];
  897. } else
  898. maxwuid = 0;
  899. info( "Loading %s...\n", str_ms[t] );
  900. debug( maxwuid == INT_MAX ? "loading %s [%d,inf]\n" : "loading %s [%d,%d]\n", str_ms[t], minwuid, maxwuid );
  901. DRIVER_CALL_RET(load( svars->ctx[t], minwuid, maxwuid, svars->uidnext[t], mexcs, nmexcs, box_loaded, AUX ));
  902. }
  903. typedef struct {
  904. void *aux;
  905. sync_rec_t *srec;
  906. int aflags, dflags;
  907. } flag_vars_t;
  908. static void flags_set_del( int sts, void *aux );
  909. static void flags_set_sync( int sts, void *aux );
  910. static void flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t );
  911. static int msgs_flags_set( sync_vars_t *svars, int t );
  912. static void msg_copied( int sts, int uid, copy_vars_t *vars );
  913. static void msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int uid );
  914. static void msgs_copied( sync_vars_t *svars, int t );
  915. static void
  916. box_loaded( int sts, void *aux )
  917. {
  918. DECL_SVARS;
  919. sync_rec_t *srec, *nsrec = 0;
  920. message_t *tmsg;
  921. copy_vars_t *cv;
  922. flag_vars_t *fv;
  923. const char *diag;
  924. int uid, minwuid, *mexcs, nmexcs, rmexcs, no[2], del[2], todel, t1, t2;
  925. int sflags, nflags, aflags, dflags, nex;
  926. char fbuf[16]; /* enlarge when support for keywords is added */
  927. if (check_ret( sts, aux ))
  928. return;
  929. INIT_SVARS(aux);
  930. svars->state[t] |= ST_LOADED;
  931. info( "%s: %d messages, %d recent\n", str_ms[t], svars->ctx[t]->count, svars->ctx[t]->recent );
  932. if (svars->state[t] & S_FIND) {
  933. svars->state[t] &= ~S_FIND;
  934. debug( "matching previously copied messages on %s\n", str_ms[t] );
  935. match_tuids( svars, t );
  936. }
  937. Fprintf( svars->jfp, "%c %d\n", "{}"[t], svars->ctx[t]->uidnext );
  938. /*
  939. * Mapping tmsg -> srec (this variant) is dog slow for new messages.
  940. * Mapping srec -> tmsg is dog slow for deleted messages.
  941. * One solution would be using binary search on an index array.
  942. * msgs are already sorted by UID, srecs would have to be sorted by uid[t].
  943. */
  944. debug( "matching messages on %s against sync records\n", str_ms[t] );
  945. for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next) {
  946. if (tmsg->srec) /* found by TUID */
  947. continue;
  948. uid = tmsg->uid;
  949. if (DFlags & DEBUG) {
  950. make_flags( tmsg->flags, fbuf );
  951. printf( svars->ctx[t]->opts & OPEN_SIZE ? " message %5d, %-4s, %6lu: " : " message %5d, %-4s: ", uid, fbuf, tmsg->size );
  952. }
  953. for (srec = nsrec; srec; srec = srec->next) {
  954. if (srec->status & S_DEAD)
  955. continue;
  956. if (srec->uid[t] == uid) {
  957. diag = srec == nsrec ? "adjacently" : "after gap";
  958. goto found;
  959. }
  960. }
  961. for (srec = svars->srecs; srec != nsrec; srec = srec->next) {
  962. if (srec->status & S_DEAD)
  963. continue;
  964. if (srec->uid[t] == uid) {
  965. diag = "after reset";
  966. goto found;
  967. }
  968. }
  969. tmsg->srec = 0;
  970. debug( "new\n" );
  971. continue;
  972. found:
  973. tmsg->srec = srec;
  974. srec->msg[t] = tmsg;
  975. nsrec = srec->next;
  976. debug( "pairs %5d %s\n", srec->uid[1-t], diag );
  977. }
  978. if ((t == S) && svars->smaxxuid) {
  979. debug( "preparing master selection - max expired slave uid is %d\n", svars->smaxxuid );
  980. mexcs = 0;
  981. nmexcs = rmexcs = 0;
  982. minwuid = INT_MAX;
  983. for (srec = svars->srecs; srec; srec = srec->next) {
  984. if (srec->status & S_DEAD)
  985. continue;
  986. if (srec->status & S_EXPIRED) {
  987. if (!srec->uid[S] || ((svars->ctx[S]->opts & OPEN_OLD) && !srec->msg[S])) {
  988. srec->status |= S_EXP_S;
  989. continue;
  990. }
  991. } else {
  992. if (svars->smaxxuid >= srec->uid[S])
  993. continue;
  994. }
  995. if (minwuid > srec->uid[M])
  996. minwuid = srec->uid[M];
  997. }
  998. debug( " min non-orphaned master uid is %d\n", minwuid );
  999. for (srec = svars->srecs; srec; srec = srec->next) {
  1000. if (srec->status & S_DEAD)
  1001. continue;
  1002. if (srec->status & S_EXP_S) {
  1003. if (minwuid > srec->uid[M] && svars->maxuid[M] >= srec->uid[M]) {
  1004. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1005. srec->status = S_DEAD;
  1006. Fprintf( svars->jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  1007. } else if (srec->uid[S]) {
  1008. debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] );
  1009. Fprintf( svars->jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] );
  1010. srec->uid[S] = 0;
  1011. }
  1012. } else if (minwuid > srec->uid[M]) {
  1013. if (srec->uid[S] < 0) {
  1014. if (svars->maxuid[M] >= srec->uid[M]) {
  1015. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1016. srec->status = S_DEAD;
  1017. Fprintf( svars->jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  1018. }
  1019. } else if (srec->uid[M] > 0 && srec->uid[S] && (svars->ctx[M]->opts & OPEN_OLD) &&
  1020. (!(svars->ctx[M]->opts & OPEN_NEW) || svars->maxuid[M] >= srec->uid[M])) {
  1021. if (nmexcs == rmexcs) {
  1022. rmexcs = rmexcs * 2 + 100;
  1023. mexcs = nfrealloc( mexcs, rmexcs * sizeof(int) );
  1024. }
  1025. mexcs[nmexcs++] = srec->uid[M];
  1026. }
  1027. }
  1028. }
  1029. debugn( " exception list is:" );
  1030. for (t = 0; t < nmexcs; t++)
  1031. debugn( " %d", mexcs[t] );
  1032. debug( "\n" );
  1033. load_box( svars, M, minwuid, mexcs, nmexcs );
  1034. return;
  1035. }
  1036. if (!(svars->state[1-t] & ST_LOADED))
  1037. return;
  1038. if (svars->uidval[M] < 0 || svars->uidval[S] < 0) {
  1039. svars->uidval[M] = svars->ctx[M]->uidvalidity;
  1040. svars->uidval[S] = svars->ctx[S]->uidvalidity;
  1041. Fprintf( svars->jfp, "| %d %d\n", svars->uidval[M], svars->uidval[S] );
  1042. }
  1043. info( "Synchronizing...\n" );
  1044. debug( "synchronizing new entries\n" );
  1045. svars->osrecadd = svars->srecadd;
  1046. for (t = 0; t < 2; t++) {
  1047. for (tmsg = svars->ctx[1-t]->msgs; tmsg; tmsg = tmsg->next)
  1048. if (tmsg->srec ? tmsg->srec->uid[t] < 0 && (tmsg->srec->uid[t] == -1 ? (svars->chan->ops[t] & OP_RENEW) : (svars->chan->ops[t] & OP_NEW)) : (svars->chan->ops[t] & OP_NEW)) {
  1049. debug( "new message %d on %s\n", tmsg->uid, str_ms[1-t] );
  1050. if ((svars->chan->ops[t] & OP_EXPUNGE) && (tmsg->flags & F_DELETED))
  1051. debug( " -> not %sing - would be expunged anyway\n", str_hl[t] );
  1052. else {
  1053. if (tmsg->srec) {
  1054. srec = tmsg->srec;
  1055. srec->status |= S_DONE;
  1056. debug( " -> pair(%d,%d) exists\n", srec->uid[M], srec->uid[S] );
  1057. } else {
  1058. srec = nfmalloc( sizeof(*srec) );
  1059. srec->next = 0;
  1060. *svars->srecadd = srec;
  1061. svars->srecadd = &srec->next;
  1062. srec->status = S_DONE;
  1063. srec->flags = 0;
  1064. srec->tuid[0] = 0;
  1065. srec->uid[1-t] = tmsg->uid;
  1066. srec->uid[t] = -2;
  1067. Fprintf( svars->jfp, "+ %d %d\n", srec->uid[M], srec->uid[S] );
  1068. debug( " -> pair(%d,%d) created\n", srec->uid[M], srec->uid[S] );
  1069. }
  1070. if ((tmsg->flags & F_FLAGGED) || !svars->chan->stores[t]->max_size || tmsg->size <= svars->chan->stores[t]->max_size) {
  1071. if (tmsg->flags) {
  1072. srec->flags = tmsg->flags;
  1073. Fprintf( svars->jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], srec->flags );
  1074. debug( " -> updated flags to %u\n", tmsg->flags );
  1075. }
  1076. for (t1 = 0; t1 < TUIDL; t1++) {
  1077. t2 = arc4_getbyte() & 0x3f;
  1078. srec->tuid[t1] = t2 < 26 ? t2 + 'A' : t2 < 52 ? t2 + 'a' - 26 : t2 < 62 ? t2 + '0' - 52 : t2 == 62 ? '+' : '/';
  1079. }
  1080. svars->new_total[t]++;
  1081. stats( svars );
  1082. cv = nfmalloc( sizeof(*cv) );
  1083. cv->cb = msg_copied;
  1084. cv->aux = AUX;
  1085. cv->srec = srec;
  1086. cv->msg = tmsg;
  1087. Fprintf( svars->jfp, "# %d %d %." stringify(TUIDL) "s\n", srec->uid[M], srec->uid[S], srec->tuid );
  1088. debug( " -> %sing message, TUID %." stringify(TUIDL) "s\n", str_hl[t], srec->tuid );
  1089. if (copy_msg( cv ))
  1090. return;
  1091. } else {
  1092. if (tmsg->srec) {
  1093. debug( " -> not %sing - still too big\n", str_hl[t] );
  1094. continue;
  1095. }
  1096. debug( " -> not %sing - too big\n", str_hl[t] );
  1097. msg_copied_p2( svars, srec, t, tmsg, -1 );
  1098. }
  1099. }
  1100. }
  1101. svars->state[t] |= ST_SENT_NEW;
  1102. msgs_copied( svars, t );
  1103. }
  1104. debug( "synchronizing old entries\n" );
  1105. for (srec = svars->srecs; srec != *svars->osrecadd; srec = srec->next) {
  1106. if (srec->status & (S_DEAD|S_DONE))
  1107. continue;
  1108. debug( "pair (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1109. no[M] = !srec->msg[M] && (svars->ctx[M]->opts & OPEN_OLD);
  1110. no[S] = !srec->msg[S] && (svars->ctx[S]->opts & OPEN_OLD);
  1111. if (no[M] && no[S]) {
  1112. debug( " vanished\n" );
  1113. /* d.1) d.5) d.6) d.10) d.11) */
  1114. srec->status = S_DEAD;
  1115. Fprintf( svars->jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  1116. } else {
  1117. del[M] = no[M] && (srec->uid[M] > 0);
  1118. del[S] = no[S] && (srec->uid[S] > 0);
  1119. for (t = 0; t < 2; t++) {
  1120. srec->aflags[t] = srec->dflags[t] = 0;
  1121. if (srec->msg[t] && (srec->msg[t]->flags & F_DELETED))
  1122. srec->status |= S_DEL(t);
  1123. /* excludes (push) c.3) d.2) d.3) d.4) / (pull) b.3) d.7) d.8) d.9) */
  1124. if (!srec->uid[t]) {
  1125. /* b.1) / c.1) */
  1126. debug( " no more %s\n", str_ms[t] );
  1127. } else if (del[1-t]) {
  1128. /* c.4) d.9) / b.4) d.4) */
  1129. if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS) && srec->msg[t]->flags != srec->flags)
  1130. info( "Info: conflicting changes in (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1131. if (svars->chan->ops[t] & OP_DELETE) {
  1132. debug( " %sing delete\n", str_hl[t] );
  1133. svars->flags_total[t]++;
  1134. stats( svars );
  1135. fv = nfmalloc( sizeof(*fv) );
  1136. fv->aux = AUX;
  1137. fv->srec = srec;
  1138. DRIVER_CALL(set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], F_DELETED, 0, flags_set_del, fv ));
  1139. } else
  1140. debug( " not %sing delete\n", str_hl[t] );
  1141. } else if (!srec->msg[1-t])
  1142. /* c.1) c.2) d.7) d.8) / b.1) b.2) d.2) d.3) */
  1143. ;
  1144. else if (srec->uid[t] < 0)
  1145. /* b.2) / c.2) */
  1146. ; /* handled as new messages (sort of) */
  1147. else if (!del[t]) {
  1148. /* a) & b.3) / c.3) */
  1149. if (svars->chan->ops[t] & OP_FLAGS) {
  1150. sflags = srec->msg[1-t]->flags;
  1151. if ((srec->status & (S_EXPIRE|S_EXPIRED)) && !t)
  1152. sflags &= ~F_DELETED;
  1153. srec->aflags[t] = sflags & ~srec->flags;
  1154. srec->dflags[t] = ~sflags & srec->flags;
  1155. if (DFlags & DEBUG) {
  1156. char afbuf[16], dfbuf[16]; /* enlarge when support for keywords is added */
  1157. make_flags( srec->aflags[t], afbuf );
  1158. make_flags( srec->dflags[t], dfbuf );
  1159. debug( " %sing flags: +%s -%s\n", str_hl[t], afbuf, dfbuf );
  1160. }
  1161. } else
  1162. debug( " not %sing flags\n", str_hl[t] );
  1163. } /* else b.4) / c.4) */
  1164. }
  1165. }
  1166. }
  1167. if ((svars->chan->ops[S] & (OP_NEW|OP_RENEW|OP_FLAGS)) && svars->chan->max_messages) {
  1168. /* Flagged and not yet synced messages older than the first not
  1169. * expired message are not counted. */
  1170. todel = svars->ctx[S]->count + svars->new_total[S] - svars->chan->max_messages;
  1171. debug( "scheduling %d excess messages for expiration\n", todel );
  1172. for (tmsg = svars->ctx[S]->msgs; tmsg && todel > 0; tmsg = tmsg->next)
  1173. if (!(tmsg->status & M_DEAD) && (srec = tmsg->srec) &&
  1174. ((tmsg->flags | srec->aflags[S]) & ~srec->dflags[S] & F_DELETED) &&
  1175. !(srec->status & (S_EXPIRE|S_EXPIRED)))
  1176. todel--;
  1177. debug( "%d non-deleted excess messages\n", todel );
  1178. for (tmsg = svars->ctx[S]->msgs; tmsg; tmsg = tmsg->next) {
  1179. if (tmsg->status & M_DEAD)
  1180. continue;
  1181. if (!(srec = tmsg->srec) || srec->uid[M] <= 0)
  1182. todel--;
  1183. else {
  1184. nflags = (tmsg->flags | srec->aflags[S]) & ~srec->dflags[S];
  1185. if (!(nflags & F_DELETED) || (srec->status & (S_EXPIRE|S_EXPIRED))) {
  1186. if (nflags & F_FLAGGED)
  1187. todel--;
  1188. else if ((!(tmsg->status & M_RECENT) || (tmsg->flags & F_SEEN)) &&
  1189. (todel > 0 ||
  1190. ((srec->status & (S_EXPIRE|S_EXPIRED)) == (S_EXPIRE|S_EXPIRED)) ||
  1191. ((srec->status & (S_EXPIRE|S_EXPIRED)) && (tmsg->flags & F_DELETED)))) {
  1192. srec->status |= S_NEXPIRE;
  1193. debug( " pair(%d,%d)\n", srec->uid[M], srec->uid[S] );
  1194. todel--;
  1195. }
  1196. }
  1197. }
  1198. }
  1199. debug( "%d excess messages remain\n", todel );
  1200. for (srec = svars->srecs; srec; srec = srec->next) {
  1201. if ((srec->status & (S_DEAD|S_DONE)) || !srec->msg[S])
  1202. continue;
  1203. nex = (srec->status / S_NEXPIRE) & 1;
  1204. if (nex != ((srec->status / S_EXPIRED) & 1)) {
  1205. if (nex != ((srec->status / S_EXPIRE) & 1)) {
  1206. Fprintf( svars->jfp, "~ %d %d %d\n", srec->uid[M], srec->uid[S], nex );
  1207. debug( " pair(%d,%d): %d (pre)\n", srec->uid[M], srec->uid[S], nex );
  1208. srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
  1209. } else
  1210. debug( " pair(%d,%d): %d (pending)\n", srec->uid[M], srec->uid[S], nex );
  1211. }
  1212. }
  1213. }
  1214. debug( "synchronizing flags\n" );
  1215. for (srec = svars->srecs; srec != *svars->osrecadd; srec = srec->next) {
  1216. if (srec->status & (S_DEAD|S_DONE))
  1217. continue;
  1218. for (t = 0; t < 2; t++) {
  1219. aflags = srec->aflags[t];
  1220. dflags = srec->dflags[t];
  1221. if ((t == S) && ((mvBit(srec->status, S_EXPIRE, S_EXPIRED) ^ srec->status) & S_EXPIRED)) {
  1222. if (srec->status & S_NEXPIRE)
  1223. aflags |= F_DELETED;
  1224. else
  1225. dflags |= F_DELETED;
  1226. }
  1227. if ((svars->chan->ops[t] & OP_EXPUNGE) && (((srec->msg[t] ? srec->msg[t]->flags : 0) | aflags) & ~dflags & F_DELETED) &&
  1228. (!svars->ctx[t]->conf->trash || svars->ctx[t]->conf->trash_only_new))
  1229. {
  1230. srec->aflags[t] &= F_DELETED;
  1231. aflags &= F_DELETED;
  1232. srec->dflags[t] = dflags = 0;
  1233. }
  1234. if (srec->msg[t] && (srec->msg[t]->status & M_FLAGS)) {
  1235. aflags &= ~srec->msg[t]->flags;
  1236. dflags &= srec->msg[t]->flags;
  1237. }
  1238. if (aflags | dflags) {
  1239. svars->flags_total[t]++;
  1240. stats( svars );
  1241. fv = nfmalloc( sizeof(*fv) );
  1242. fv->aux = AUX;
  1243. fv->srec = srec;
  1244. fv->aflags = aflags;
  1245. fv->dflags = dflags;
  1246. DRIVER_CALL(set_flags( svars->ctx[t], srec->msg[t], srec->uid[t], aflags, dflags, flags_set_sync, fv ));
  1247. } else
  1248. flags_set_sync_p2( svars, srec, t );
  1249. }
  1250. }
  1251. for (t = 0; t < 2; t++) {
  1252. svars->drv[t]->commit( svars->ctx[t] );
  1253. svars->state[t] |= ST_SENT_FLAGS;
  1254. if (msgs_flags_set( svars, t ))
  1255. return;
  1256. }
  1257. }
  1258. static void
  1259. msg_copied( int sts, int uid, copy_vars_t *vars )
  1260. {
  1261. SVARS_CHECK_CANCEL_RET;
  1262. switch (sts) {
  1263. case SYNC_OK:
  1264. if (uid < 0)
  1265. svars->state[t] |= S_FIND;
  1266. msg_copied_p2( svars, vars->srec, t, vars->msg, uid );
  1267. break;
  1268. case SYNC_NOGOOD:
  1269. debug( " -> killing (%d,%d)\n", vars->srec->uid[M], vars->srec->uid[S] );
  1270. vars->srec->status = S_DEAD;
  1271. Fprintf( svars->jfp, "- %d %d\n", vars->srec->uid[M], vars->srec->uid[S] );
  1272. break;
  1273. default:
  1274. cancel_sync( svars );
  1275. free( vars );
  1276. return;
  1277. }
  1278. free( vars );
  1279. svars->new_done[t]++;
  1280. stats( svars );
  1281. msgs_copied( svars, t );
  1282. }
  1283. static void
  1284. msg_copied_p2( sync_vars_t *svars, sync_rec_t *srec, int t, message_t *tmsg, int uid )
  1285. {
  1286. if (srec->uid[t] != uid) {
  1287. debug( " -> new UID %d\n", uid );
  1288. Fprintf( svars->jfp, "%c %d %d %d\n", "<>"[t], srec->uid[M], srec->uid[S], uid );
  1289. srec->uid[t] = uid;
  1290. srec->tuid[0] = 0;
  1291. }
  1292. if (!tmsg->srec) {
  1293. tmsg->srec = srec;
  1294. if (svars->maxuid[1-t] < tmsg->uid) {
  1295. svars->maxuid[1-t] = tmsg->uid;
  1296. Fprintf( svars->jfp, "%c %d\n", ")("[t], tmsg->uid );
  1297. }
  1298. }
  1299. }
  1300. static void msgs_found_new( int sts, void *aux );
  1301. static void msgs_new_done( sync_vars_t *svars, int t );
  1302. static void sync_close( sync_vars_t *svars, int t );
  1303. static void
  1304. msgs_copied( sync_vars_t *svars, int t )
  1305. {
  1306. if (!(svars->state[t] & ST_SENT_NEW) || svars->new_done[t] < svars->new_total[t])
  1307. return;
  1308. if (svars->state[t] & S_FIND) {
  1309. debug( "finding just copied messages on %s\n", str_ms[t] );
  1310. svars->drv[t]->find_new_msgs( svars->ctx[t], msgs_found_new, AUX );
  1311. } else {
  1312. msgs_new_done( svars, t );
  1313. }
  1314. }
  1315. static void
  1316. msgs_found_new( int sts, void *aux )
  1317. {
  1318. SVARS_CHECK_RET;
  1319. switch (sts) {
  1320. case DRV_OK:
  1321. debug( "matching just copied messages on %s\n", str_ms[t] );
  1322. break;
  1323. default:
  1324. warn( "Warning: cannot find newly stored messages on %s.\n", str_ms[t] );
  1325. break;
  1326. }
  1327. match_tuids( svars, t );
  1328. msgs_new_done( svars, t );
  1329. }
  1330. static void
  1331. msgs_new_done( sync_vars_t *svars, int t )
  1332. {
  1333. Fprintf( svars->jfp, "%c %d\n", "{}"[t], svars->ctx[t]->uidnext );
  1334. svars->state[t] |= ST_FOUND_NEW;
  1335. sync_close( svars, t );
  1336. }
  1337. static void
  1338. flags_set_del( int sts, void *aux )
  1339. {
  1340. SVARS_CHECK_RET_VARS(flag_vars_t);
  1341. switch (sts) {
  1342. case DRV_OK:
  1343. vars->srec->status |= S_DEL(t);
  1344. Fprintf( svars->jfp, "%c %d %d 0\n", "><"[t], vars->srec->uid[M], vars->srec->uid[S] );
  1345. vars->srec->uid[1-t] = 0;
  1346. break;
  1347. }
  1348. free( vars );
  1349. svars->flags_done[t]++;
  1350. stats( svars );
  1351. msgs_flags_set( svars, t );
  1352. }
  1353. static void
  1354. flags_set_sync( int sts, void *aux )
  1355. {
  1356. SVARS_CHECK_RET_VARS(flag_vars_t);
  1357. switch (sts) {
  1358. case DRV_OK:
  1359. if (vars->aflags & F_DELETED)
  1360. vars->srec->status |= S_DEL(t);
  1361. else if (vars->dflags & F_DELETED)
  1362. vars->srec->status &= ~S_DEL(t);
  1363. flags_set_sync_p2( svars, vars->srec, t );
  1364. break;
  1365. }
  1366. free( vars );
  1367. svars->flags_done[t]++;
  1368. stats( svars );
  1369. msgs_flags_set( svars, t );
  1370. }
  1371. static void
  1372. flags_set_sync_p2( sync_vars_t *svars, sync_rec_t *srec, int t )
  1373. {
  1374. int nflags, nex;
  1375. nflags = (srec->flags | srec->aflags[t]) & ~srec->dflags[t];
  1376. if (srec->flags != nflags) {
  1377. debug( " pair(%d,%d): updating flags (%u -> %u)\n", srec->uid[M], srec->uid[S], srec->flags, nflags );
  1378. srec->flags = nflags;
  1379. Fprintf( svars->jfp, "* %d %d %u\n", srec->uid[M], srec->uid[S], nflags );
  1380. }
  1381. if (t == S) {
  1382. nex = (srec->status / S_NEXPIRE) & 1;
  1383. if (nex != ((srec->status / S_EXPIRED) & 1)) {
  1384. if (nex && (svars->smaxxuid < srec->uid[S]))
  1385. svars->smaxxuid = srec->uid[S];
  1386. Fprintf( svars->jfp, "/ %d %d\n", srec->uid[M], srec->uid[S] );
  1387. debug( " pair(%d,%d): expired %d (commit)\n", srec->uid[M], srec->uid[S], nex );
  1388. srec->status = (srec->status & ~S_EXPIRED) | (nex * S_EXPIRED);
  1389. } else if (nex != ((srec->status / S_EXPIRE) & 1)) {
  1390. Fprintf( svars->jfp, "\\ %d %d\n", srec->uid[M], srec->uid[S] );
  1391. debug( " pair(%d,%d): expire %d (cancel)\n", srec->uid[M], srec->uid[S], nex );
  1392. srec->status = (srec->status & ~S_EXPIRE) | (nex * S_EXPIRE);
  1393. }
  1394. }
  1395. }
  1396. static void msg_trashed( int sts, void *aux );
  1397. static void msg_rtrashed( int sts, int uid, copy_vars_t *vars );
  1398. static int
  1399. msgs_flags_set( sync_vars_t *svars, int t )
  1400. {
  1401. message_t *tmsg;
  1402. copy_vars_t *cv;
  1403. if (!(svars->state[t] & ST_SENT_FLAGS) || svars->flags_done[t] < svars->flags_total[t])
  1404. return 0;
  1405. if ((svars->chan->ops[t] & OP_EXPUNGE) &&
  1406. (svars->ctx[t]->conf->trash || (svars->ctx[1-t]->conf->trash && svars->ctx[1-t]->conf->trash_remote_new))) {
  1407. debug( "trashing in %s\n", str_ms[t] );
  1408. for (tmsg = svars->ctx[t]->msgs; tmsg; tmsg = tmsg->next)
  1409. if (tmsg->flags & F_DELETED) {
  1410. if (svars->ctx[t]->conf->trash) {
  1411. if (!svars->ctx[t]->conf->trash_only_new || !tmsg->srec || tmsg->srec->uid[1-t] < 0) {
  1412. debug( "%s: trashing message %d\n", str_ms[t], tmsg->uid );
  1413. svars->trash_total[t]++;
  1414. stats( svars );
  1415. sync_ref( svars );
  1416. svars->drv[t]->trash_msg( svars->ctx[t], tmsg, msg_trashed, AUX );
  1417. if (deref_check_cancel( svars ))
  1418. return -1;
  1419. } else
  1420. debug( "%s: not trashing message %d - not new\n", str_ms[t], tmsg->uid );
  1421. } else {
  1422. if (!tmsg->srec || tmsg->srec->uid[1-t] < 0) {
  1423. if (!svars->ctx[1-t]->conf->max_size || tmsg->size <= svars->ctx[1-t]->conf->max_size) {
  1424. debug( "%s: remote trashing message %d\n", str_ms[t], tmsg->uid );
  1425. svars->trash_total[t]++;
  1426. stats( svars );
  1427. cv = nfmalloc( sizeof(*cv) );
  1428. cv->cb = msg_rtrashed;
  1429. cv->aux = AUX;
  1430. cv->srec = 0;
  1431. cv->msg = tmsg;
  1432. if (copy_msg( cv ))
  1433. return -1;
  1434. } else
  1435. debug( "%s: not remote trashing message %d - too big\n", str_ms[t], tmsg->uid );
  1436. } else
  1437. debug( "%s: not remote trashing message %d - not new\n", str_ms[t], tmsg->uid );
  1438. }
  1439. }
  1440. }
  1441. svars->state[t] |= ST_SENT_TRASH;
  1442. sync_close( svars, t );
  1443. return 0;
  1444. }
  1445. static void
  1446. msg_trashed( int sts, void *aux )
  1447. {
  1448. DECL_SVARS;
  1449. if (sts == DRV_MSG_BAD)
  1450. sts = DRV_BOX_BAD;
  1451. if (check_ret( sts, aux ))
  1452. return;
  1453. INIT_SVARS(aux);
  1454. svars->trash_done[t]++;
  1455. stats( svars );
  1456. sync_close( svars, t );
  1457. }
  1458. static void
  1459. msg_rtrashed( int sts, int uid, copy_vars_t *vars )
  1460. {
  1461. SVARS_CHECK_CANCEL_RET;
  1462. (void)uid;
  1463. switch (sts) {
  1464. case SYNC_OK:
  1465. case SYNC_NOGOOD: /* the message is gone or heavily busted */
  1466. break;
  1467. default:
  1468. cancel_sync( svars );
  1469. free( vars );
  1470. return;
  1471. }
  1472. free( vars );
  1473. svars->trash_done[t]++;
  1474. stats( svars );
  1475. sync_close( svars, t );
  1476. }
  1477. static void box_closed( int sts, void *aux );
  1478. static void box_closed_p2( sync_vars_t *svars, int t );
  1479. static void
  1480. sync_close( sync_vars_t *svars, int t )
  1481. {
  1482. if ((~svars->state[t] & (ST_FOUND_NEW|ST_SENT_TRASH)) ||
  1483. svars->trash_done[t] < svars->trash_total[t])
  1484. return;
  1485. if ((svars->chan->ops[t] & OP_EXPUNGE) /*&& !(svars->state[t] & ST_TRASH_BAD)*/) {
  1486. debug( "expunging %s\n", str_ms[t] );
  1487. svars->drv[t]->close( svars->ctx[t], box_closed, AUX );
  1488. } else {
  1489. box_closed_p2( svars, t );
  1490. }
  1491. }
  1492. static void
  1493. box_closed( int sts, void *aux )
  1494. {
  1495. SVARS_CHECK_RET;
  1496. svars->state[t] |= ST_DID_EXPUNGE;
  1497. box_closed_p2( svars, t );
  1498. }
  1499. static void
  1500. box_closed_p2( sync_vars_t *svars, int t )
  1501. {
  1502. sync_rec_t *srec;
  1503. int minwuid;
  1504. char fbuf[16]; /* enlarge when support for keywords is added */
  1505. svars->state[t] |= ST_CLOSED;
  1506. if (!(svars->state[1-t] & ST_CLOSED))
  1507. return;
  1508. if ((svars->state[M] | svars->state[S]) & ST_DID_EXPUNGE) {
  1509. /* This cleanup is not strictly necessary, as the next full sync
  1510. would throw out the dead entries anyway. But ... */
  1511. minwuid = INT_MAX;
  1512. if (svars->smaxxuid) {
  1513. debug( "preparing entry purge - max expired slave uid is %d\n", svars->smaxxuid );
  1514. for (srec = svars->srecs; srec; srec = srec->next) {
  1515. if (srec->status & S_DEAD)
  1516. continue;
  1517. if (!((srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && (svars->state[S] & ST_DID_EXPUNGE))) &&
  1518. (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && (svars->state[M] & ST_DID_EXPUNGE)) || (srec->status & S_EXPIRED))) &&
  1519. svars->smaxxuid < srec->uid[S] && minwuid > srec->uid[M])
  1520. minwuid = srec->uid[M];
  1521. }
  1522. debug( " min non-orphaned master uid is %d\n", minwuid );
  1523. }
  1524. for (srec = svars->srecs; srec; srec = srec->next) {
  1525. if (srec->status & S_DEAD)
  1526. continue;
  1527. if (srec->uid[S] <= 0 || ((srec->status & S_DEL(S)) && (svars->state[S] & ST_DID_EXPUNGE))) {
  1528. if (srec->uid[M] <= 0 || ((srec->status & S_DEL(M)) && (svars->state[M] & ST_DID_EXPUNGE)) ||
  1529. ((srec->status & S_EXPIRED) && svars->maxuid[M] >= srec->uid[M] && minwuid > srec->uid[M])) {
  1530. debug( " -> killing (%d,%d)\n", srec->uid[M], srec->uid[S] );
  1531. srec->status = S_DEAD;
  1532. Fprintf( svars->jfp, "- %d %d\n", srec->uid[M], srec->uid[S] );
  1533. } else if (srec->uid[S] > 0) {
  1534. debug( " -> orphaning (%d,[%d])\n", srec->uid[M], srec->uid[S] );
  1535. Fprintf( svars->jfp, "> %d %d 0\n", srec->uid[M], srec->uid[S] );
  1536. srec->uid[S] = 0;
  1537. }
  1538. } else if (srec->uid[M] > 0 && ((srec->status & S_DEL(M)) && (svars->state[M] & ST_DID_EXPUNGE))) {
  1539. debug( " -> orphaning ([%d],%d)\n", srec->uid[M], srec->uid[S] );
  1540. Fprintf( svars->jfp, "< %d %d 0\n", srec->uid[M], srec->uid[S] );
  1541. srec->uid[M] = 0;
  1542. }
  1543. }
  1544. }
  1545. Fprintf( svars->nfp, "%d:%d:%d %d:%d:%d:%d\n",
  1546. svars->uidval[M], svars->maxuid[M], svars->ctx[M]->uidnext,
  1547. svars->uidval[S], svars->smaxxuid, svars->maxuid[S], svars->ctx[S]->uidnext );
  1548. for (srec = svars->srecs; srec; srec = srec->next) {
  1549. if (srec->status & S_DEAD)
  1550. continue;
  1551. make_flags( srec->flags, fbuf );
  1552. Fprintf( svars->nfp, "%d %d %s%s\n", srec->uid[M], srec->uid[S],
  1553. srec->status & S_EXPIRED ? "X" : "", fbuf );
  1554. }
  1555. Fclose( svars->nfp );
  1556. Fclose( svars->jfp );
  1557. if (!(DFlags & KEEPJOURNAL)) {
  1558. /* order is important! */
  1559. rename( svars->nname, svars->dname );
  1560. unlink( svars->jname );
  1561. }
  1562. sync_bail( svars );
  1563. }
  1564. static void
  1565. sync_bail( sync_vars_t *svars )
  1566. {
  1567. sync_rec_t *srec, *nsrec;
  1568. for (srec = svars->srecs; srec; srec = nsrec) {
  1569. nsrec = srec->next;
  1570. free( srec );
  1571. }
  1572. unlink( svars->lname );
  1573. sync_bail1( svars );
  1574. }
  1575. static void
  1576. sync_bail1( sync_vars_t *svars )
  1577. {
  1578. close( svars->lfd );
  1579. sync_bail2( svars );
  1580. }
  1581. static void
  1582. sync_bail2( sync_vars_t *svars )
  1583. {
  1584. free( svars->lname );
  1585. free( svars->nname );
  1586. free( svars->jname );
  1587. free( svars->dname );
  1588. flushn();
  1589. sync_deref( svars );
  1590. }
  1591. static int sync_deref( sync_vars_t *svars )
  1592. {
  1593. if (!--svars->ref_count) {
  1594. void (*cb)( int sts, void *aux ) = svars->cb;
  1595. void *aux = svars->aux;
  1596. int ret = svars->ret;
  1597. free( svars );
  1598. cb( ret, aux );
  1599. return -1;
  1600. }
  1601. return 0;
  1602. }