If *record* is set, avoid writing dead content twice..
[s-mailx.git] / cache.c
blob9cf75874a2a082aceed5433427c712a839440358
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 2004
8 * Gunnar Ritter. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gunnar Ritter
21 * and his contributors.
22 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)cache.c 1.61 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
45 #include "config.h"
47 #ifdef HAVE_SOCKETS
49 #include "rcv.h"
50 #include "extern.h"
51 #include <errno.h>
52 #include <sys/stat.h>
53 #include <unistd.h>
54 #include <time.h>
55 #include <dirent.h>
56 #include <fcntl.h>
58 #include <termios.h>
61 * Mail -- a mail program
63 * A cache for IMAP.
66 static char *encname(struct mailbox *mp, const char *name, int same,
67 const char *box);
68 static char *encuid(struct mailbox *mp, unsigned long uid);
69 static FILE *clean(struct mailbox *mp, struct cw *cw);
70 static unsigned long *builds(long *contentelem);
71 static void purge(struct mailbox *mp, struct message *m, long mc,
72 struct cw *cw, const char *name);
73 static int longlt(const void *a, const void *b);
74 static void remve(unsigned long n);
75 static FILE *cache_queue1(struct mailbox *mp, char *mode, char **xname);
76 static enum okay dequeue1(struct mailbox *mp);
78 static const char infofmt[] = "%c %lu %u %lu %lu";
79 #define INITSKIP 128L
80 #define USEBITS(f) \
81 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
83 static const char README1[] = "\
84 This is a cache directory maintained by mailx(1). You should not change any\n\
85 files within. Nevertheless, the structure is as follows: Each subdirectory\n\
86 of the current directory represents an IMAP account, and each subdirectory\n\
87 below that represents a mailbox. Each mailbox directory contains a file\n\
88 named UIDVALIDITY which describes the validity in relation to the version\n\
89 on the server. Other files have names corresponding to their IMAP UID.\n";
90 static const char README2[] = "\n\
91 The first 128 bytes of these files are used to store message attributes; the\n\
92 following data is equivalent to compress(1) output. So if you have to save a\n\
93 message by hand because of an emergency, throw away the first 128 bytes and\n\
94 decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n";
95 static const char README3[] = "\n\
96 Files named QUEUE contain data that will be sent do the IMAP server next\n\
97 time a connection is made in online mode.\n";
98 static const char README4[] = "\n\
99 You can safely delete any file or directory here, unless it contains a QUEUE\n\
100 file that is not empty; mailx(1) will download the data again and will also\n\
101 write new cache entries if configured in this way. If you do not wish to use\n\
102 the cache anymore, delete the entire directory and unset the 'imap-cache'\n\
103 variable in mailx(1).\n";
104 static const char README5[] = "\n\
105 For more information about mailx(1), visit\n\
106 <http://heirloom.sourceforge.net/mailx.html>.\n";
108 static char *
109 encname(struct mailbox *mp, const char *name, int same, const char *box)
111 char *cachedir, *eaccount, *emailbox, *ename, *res;
112 int resz;
114 ename = strenc(name);
115 if (mp->mb_cache_directory && same && box == NULL) {
116 res = salloc(resz = strlen(mp->mb_cache_directory) +
117 strlen(ename) + 2);
118 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
119 *ename ? "/" : "", ename);
120 } else {
121 if ((cachedir = value("imap-cache")) == NULL)
122 return NULL;
123 cachedir = expand(cachedir);
124 eaccount = strenc(mp->mb_imap_account);
125 if (box)
126 emailbox = strenc(box);
127 else if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
128 emailbox = strenc(mp->mb_imap_mailbox);
129 else
130 emailbox = "INBOX";
131 res = salloc(resz = strlen(cachedir) + strlen(eaccount) +
132 strlen(emailbox) + strlen(ename) + 4);
133 snprintf(res, resz, "%s/%s/%s%s%s",
134 cachedir, eaccount, emailbox,
135 *ename ? "/" : "", ename);
137 return res;
140 static char *
141 encuid(struct mailbox *mp, unsigned long uid)
143 char buf[30];
145 snprintf(buf, sizeof buf, "%lu", uid);
146 return encname(mp, buf, 1, NULL);
149 enum okay
150 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
151 int setflags)
153 FILE *fp;
154 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
155 int lastc = EOF, i, xflag, inheader = 1;
156 char b, iob[32768];
157 off_t offset;
158 void *zp;
160 if (setflags == 0 && ((mp->mb_type != MB_IMAP &&
161 mp->mb_type != MB_CACHE) ||
162 m->m_uid == 0))
163 return STOP;
164 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
165 return STOP;
166 fcntl_lock(fileno(fp), F_RDLCK);
167 if (fscanf(fp, infofmt, &b, &xsize, &xflag, &xtime, &xlines) < 4)
168 goto fail;
169 if (need != NEED_UNSPEC) {
170 switch (b) {
171 case 'H':
172 if (need == NEED_HEADER)
173 goto success;
174 goto fail;
175 case 'B':
176 if (need == NEED_HEADER || need == NEED_BODY)
177 goto success;
178 goto fail;
179 default:
180 goto fail;
183 success:
184 if (b == 'N')
185 goto flags;
186 fseek(fp, INITSKIP, SEEK_SET);
187 zp = zalloc(fp);
188 fseek(mp->mb_otf, 0L, SEEK_END);
189 offset = ftell(mp->mb_otf);
190 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
191 size += n;
192 for (i = 0; i < n; i++) {
193 if (iob[i] == '\n') {
194 lines++;
195 if (lastc == '\n')
196 inheader = 0;
198 lastc = iob[i]&0377;
200 fwrite(iob, 1, n, mp->mb_otf);
202 if (n > 0 && need == NEED_BODY) {
203 while ((n = zread(zp, iob, sizeof iob)) > 0) {
204 size += n;
205 for (i = 0; i < n; i++)
206 if (iob[i] == '\n')
207 lines++;
208 fwrite(iob, 1, n, mp->mb_otf);
211 fflush(mp->mb_otf);
212 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
213 goto fail;
214 m->m_size = size;
215 m->m_lines = lines;
216 m->m_block = mailx_blockof(offset);
217 m->m_offset = mailx_offsetof(offset);
218 flags: if (setflags) {
219 m->m_xsize = xsize;
220 m->m_time = xtime;
221 if (setflags & 2) {
222 m->m_flag = xflag | MNOFROM;
223 if (b != 'B')
224 m->m_flag |= MHIDDEN;
227 if (xlines > 0 && m->m_xlines <= 0)
228 m->m_xlines = xlines;
229 switch (b) {
230 case 'B':
231 m->m_xsize = xsize;
232 if (xflag == MREAD && xlines > 0)
233 m->m_flag |= MFULLYCACHED;
234 if (need == NEED_BODY) {
235 m->m_have |= HAVE_HEADER|HAVE_BODY;
236 if (m->m_lines > 0)
237 m->m_xlines = m->m_lines;
238 break;
240 /*FALLTHRU*/
241 case 'H':
242 m->m_have |= HAVE_HEADER;
243 break;
244 case 'N':
245 break;
247 Fclose(fp);
248 return OKAY;
249 fail:
250 Fclose(fp);
251 return STOP;
254 enum okay
255 getcache(struct mailbox *mp, struct message *m, enum needspec need)
257 return getcache1(mp, m, need, 0);
260 void
261 putcache(struct mailbox *mp, struct message *m)
263 FILE *ibuf, *obuf;
264 char *name, ob;
265 int c, oflag;
266 long n, count, oldoffset, osize, otime, olines = -1;
267 char iob[32768];
268 void *zp;
270 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
271 m->m_uid == 0 || m->m_time == 0 ||
272 (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
273 return;
274 if (m->m_have & HAVE_BODY)
275 c = 'B';
276 else if (m->m_have & HAVE_HEADER)
277 c = 'H';
278 else if (m->m_have == HAVE_NOTHING)
279 c = 'N';
280 else
281 return;
282 oldoffset = ftell(mp->mb_itf);
283 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
284 if ((obuf = Fopen(name, "w")) == NULL)
285 return;
286 fcntl_lock(fileno(obuf), F_WRLCK);
287 } else {
288 fcntl_lock(fileno(obuf), F_WRLCK);
289 if (fscanf(obuf, infofmt, &ob, &osize, &oflag, &otime,
290 &olines) >= 4 && ob != '\0' &&
291 (ob == 'B' || (ob == 'H' && c != 'B'))) {
292 if (m->m_xlines <= 0 && olines > 0)
293 m->m_xlines = olines;
294 if ((c != 'N' && osize != m->m_xsize) ||
295 oflag != USEBITS(m->m_flag) ||
296 otime != m->m_time ||
297 (m->m_xlines > 0 &&
298 olines != m->m_xlines)) {
299 fflush(obuf);
300 rewind(obuf);
301 fprintf(obuf, infofmt, ob,
302 (long)m->m_xsize,
303 USEBITS(m->m_flag),
304 (long)m->m_time,
305 m->m_xlines);
306 putc('\n', obuf);
308 Fclose(obuf);
309 return;
311 fflush(obuf);
312 rewind(obuf);
313 ftruncate(fileno(obuf), 0);
315 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
316 Fclose(obuf);
317 return;
319 if (c == 'N')
320 goto done;
321 fseek(obuf, INITSKIP, SEEK_SET);
322 zp = zalloc(obuf);
323 count = m->m_size;
324 while (count > 0) {
325 n = count > sizeof iob ? sizeof iob : count;
326 count -= n;
327 if (fread(iob, 1, n, ibuf) != n || zwrite(zp, iob, n) != n) {
328 unlink(name);
329 zfree(zp);
330 goto out;
333 if (zfree(zp) < 0) {
334 unlink(name);
335 goto out;
337 done: rewind(obuf);
338 fprintf(obuf, infofmt, c, (long)m->m_xsize,
339 USEBITS(m->m_flag),
340 (long)m->m_time,
341 m->m_xlines);
342 putc('\n', obuf);
343 if (ferror(obuf)) {
344 unlink(name);
345 goto out;
347 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
348 m->m_flag |= MFULLYCACHED;
349 out: if (Fclose(obuf) != 0) {
350 m->m_flag &= ~MFULLYCACHED;
351 unlink(name);
353 fseek(mp->mb_itf, oldoffset, SEEK_SET);
356 void
357 initcache(struct mailbox *mp)
359 char *name, *uvname;
360 FILE *uvfp;
361 unsigned long uv;
362 struct cw cw;
364 free(mp->mb_cache_directory);
365 mp->mb_cache_directory = NULL;
366 if ((name = encname(mp, "", 1, NULL)) == NULL)
367 return;
368 mp->mb_cache_directory = sstrdup(name);
369 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
370 return;
371 if (cwget(&cw) == STOP)
372 return;
373 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
374 (fcntl_lock(fileno(uvfp), F_RDLCK), 0) ||
375 fscanf(uvfp, "%lu", &uv) != 1 ||
376 uv != mp->mb_uidvalidity) {
377 if ((uvfp = clean(mp, &cw)) == NULL)
378 goto out;
379 } else {
380 fflush(uvfp);
381 rewind(uvfp);
383 fcntl_lock(fileno(uvfp), F_WRLCK);
384 fprintf(uvfp, "%lu\n", mp->mb_uidvalidity);
385 if (ferror(uvfp) || Fclose(uvfp) != 0) {
386 unlink(uvname);
387 mp->mb_uidvalidity = 0;
389 out: cwrelse(&cw);
392 void
393 purgecache(struct mailbox *mp, struct message *m, long mc)
395 char *name;
396 struct cw cw;
398 if ((name = encname(mp, "", 1, NULL)) == NULL)
399 return;
400 if (cwget(&cw) == STOP)
401 return;
402 purge(mp, m, mc, &cw, name);
403 cwrelse(&cw);
406 static FILE *
407 clean(struct mailbox *mp, struct cw *cw)
409 char *cachedir, *eaccount, *emailbox, *buf;
410 int bufsz;
411 DIR *dirfd;
412 struct dirent *dp;
413 FILE *fp = NULL;
415 if ((cachedir = value("imap-cache")) == NULL)
416 return NULL;
417 cachedir = expand(cachedir);
418 eaccount = strenc(mp->mb_imap_account);
419 if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
420 emailbox = strenc(mp->mb_imap_mailbox);
421 else
422 emailbox = "INBOX";
423 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
424 strlen(emailbox) + 40);
425 if (makedir(cachedir) != OKAY)
426 return NULL;
427 snprintf(buf, bufsz, "%s/README", cachedir);
428 if ((fp = Fopen(buf, "wx")) != NULL) {
429 fputs(README1, fp);
430 fputs(README2, fp);
431 fputs(README3, fp);
432 fputs(README4, fp);
433 fputs(README5, fp);
434 Fclose(fp);
436 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
437 if (makedir(buf) != OKAY)
438 return NULL;
439 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
440 if (makedir(buf) != OKAY)
441 return NULL;
442 if (chdir(buf) < 0)
443 return NULL;
444 if ((dirfd = opendir(".")) == NULL)
445 goto out;
446 while ((dp = readdir(dirfd)) != NULL) {
447 if (dp->d_name[0] == '.' &&
448 (dp->d_name[1] == '\0' ||
449 (dp->d_name[1] == '.' &&
450 dp->d_name[2] == '\0')))
451 continue;
452 unlink(dp->d_name);
454 closedir(dirfd);
455 fp = Fopen("UIDVALIDITY", "w");
456 out: if (cwret(cw) == STOP) {
457 fputs("Fatal: Cannot change back to current directory.\n",
458 stderr);
459 abort();
461 return fp;
464 static unsigned long *
465 builds(long *contentelem)
467 unsigned long n, *contents = NULL;
468 long contentalloc = 0;
469 char *x;
470 DIR *dirfd;
471 struct dirent *dp;
473 *contentelem = 0;
474 if ((dirfd = opendir(".")) == NULL)
475 return NULL;
476 while ((dp = readdir(dirfd)) != NULL) {
477 if (dp->d_name[0] == '.' &&
478 (dp->d_name[1] == '\0' ||
479 (dp->d_name[1] == '.' &&
480 dp->d_name[2] == '\0')))
481 continue;
482 n = strtoul(dp->d_name, &x, 10);
483 if (*x != '\0')
484 continue;
485 if (*contentelem >= contentalloc - 1)
486 contents = srealloc(contents,
487 (contentalloc += 200) * sizeof *contents);
488 contents[(*contentelem)++] = n;
490 closedir(dirfd);
491 if (*contentelem > 0) {
492 contents[*contentelem] = 0;
493 qsort(contents, *contentelem, sizeof *contents, longlt);
495 return contents;
498 static void
499 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
500 const char *name)
502 unsigned long *contents;
503 long i, j, contentelem;
505 if (chdir(name) < 0)
506 return;
507 contents = builds(&contentelem);
508 if (contents) {
509 i = j = 0;
510 while (j < contentelem) {
511 if (i < mc && m[i].m_uid == contents[j]) {
512 i++;
513 j++;
514 } else if (i < mc && m[i].m_uid < contents[j])
515 i++;
516 else
517 remve(contents[j++]);
520 if (cwret(cw) == STOP) {
521 fputs("Fatal: Cannot change back to current directory.\n",
522 stderr);
523 abort();
525 free(contents);
528 static int
529 longlt(const void *a, const void *b)
531 return *(long *)a - *(long *)b;
534 static void
535 remve(unsigned long n)
537 char buf[30];
539 snprintf(buf, sizeof buf, "%lu", n);
540 unlink(buf);
543 void
544 delcache(struct mailbox *mp, struct message *m)
546 char *fn;
548 fn = encuid(mp, m->m_uid);
549 if (fn && unlink(fn) == 0)
550 m->m_flag |= MUNLINKED;
553 enum okay
554 cache_setptr(int transparent)
556 int i;
557 struct cw cw;
558 char *name;
559 unsigned long *contents;
560 long contentelem;
561 enum okay ok = STOP;
562 struct message *omessage = NULL;
563 int omsgCount = 0;
565 if (transparent) {
566 omessage = message;
567 omsgCount = msgCount;
569 free(mb.mb_cache_directory);
570 mb.mb_cache_directory = NULL;
571 if ((name = encname(&mb, "", 1, NULL)) == NULL)
572 return STOP;
573 mb.mb_cache_directory = sstrdup(name);
574 if (cwget(&cw) == STOP)
575 return STOP;
576 if (chdir(name) < 0)
577 return STOP;
578 contents = builds(&contentelem);
579 msgCount = contentelem;
580 message = scalloc(msgCount + 1, sizeof *message);
581 if (cwret(&cw) == STOP) {
582 fputs("Fatal: Cannot change back to current directory.\n",
583 stderr);
584 abort();
586 cwrelse(&cw);
587 for (i = 0; i < msgCount; i++) {
588 message[i].m_uid = contents[i];
589 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
591 ok = OKAY;
592 if (ok == OKAY) {
593 mb.mb_type = MB_CACHE;
594 mb.mb_perm = Rflag ? 0 : MB_DELE;
595 if (transparent)
596 transflags(omessage, omsgCount, 1);
597 else
598 setdot(message);
600 return ok;
603 enum okay
604 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
606 char *name, *cachedir, *eaccount;
607 DIR *dirfd;
608 struct dirent *dp;
609 const char *cp, *bp, *sp;
610 int namesz;
612 if ((cachedir = value("imap-cache")) == NULL)
613 return STOP;
614 cachedir = expand(cachedir);
615 eaccount = strenc(mp->mb_imap_account);
616 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
617 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
618 if ((dirfd = opendir(name)) == NULL)
619 return STOP;
620 while ((dp = readdir(dirfd)) != NULL) {
621 if (dp->d_name[0] == '.')
622 continue;
623 cp = sp = strdec(dp->d_name);
624 for (bp = base; *bp && *bp == *sp; bp++)
625 sp++;
626 if (*bp)
627 continue;
628 cp = strip ? sp : cp;
629 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
631 closedir(dirfd);
632 return OKAY;
635 enum okay
636 cache_remove(const char *name)
638 struct stat st;
639 DIR *dirfd;
640 struct dirent *dp;
641 char *path;
642 int pathsize, pathend, n;
643 char *dir;
645 if ((dir = encname(&mb, "", 0, protfile(name))) == NULL)
646 return OKAY;
647 pathend = strlen(dir);
648 path = smalloc(pathsize = pathend + 30);
649 strcpy(path, dir);
650 path[pathend++] = '/';
651 path[pathend] = '\0';
652 if ((dirfd = opendir(path)) == NULL) {
653 free(path);
654 return OKAY;
656 while ((dp = readdir(dirfd)) != NULL) {
657 if (dp->d_name[0] == '.' &&
658 (dp->d_name[1] == '\0' ||
659 (dp->d_name[1] == '.' &&
660 dp->d_name[2] == '\0')))
661 continue;
662 n = strlen(dp->d_name);
663 if (pathend + n + 1 > pathsize)
664 path = srealloc(path, pathsize = pathend + n + 30);
665 strcpy(&path[pathend], dp->d_name);
666 if (stat(path, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)
667 continue;
668 if (unlink(path) < 0) {
669 perror(path);
670 closedir(dirfd);
671 free(path);
672 return STOP;
675 closedir(dirfd);
676 path[pathend] = '\0';
677 rmdir(path); /* no error on failure, might contain submailboxes */
678 free(path);
679 return OKAY;
682 enum okay
683 cache_rename(const char *old, const char *new)
685 char *olddir, *newdir;
687 if ((olddir = encname(&mb, "", 0, protfile(old))) == NULL ||
688 (newdir = encname(&mb, "", 0, protfile(new))) == NULL)
689 return OKAY;
690 if (rename(olddir, newdir) < 0) {
691 perror(olddir);
692 return STOP;
694 return OKAY;
697 unsigned long
698 cached_uidvalidity(struct mailbox *mp)
700 FILE *uvfp;
701 char *uvname;
702 unsigned long uv;
704 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
705 return 0;
706 if ((uvfp = Fopen(uvname, "r")) == NULL ||
707 (fcntl_lock(fileno(uvfp), F_RDLCK), 0) ||
708 fscanf(uvfp, "%lu", &uv) != 1)
709 uv = 0;
710 Fclose(uvfp);
711 return uv;
714 static FILE *
715 cache_queue1(struct mailbox *mp, char *mode, char **xname)
717 char *name;
718 FILE *fp;
720 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
721 return NULL;
722 if ((fp = Fopen(name, mode)) != NULL)
723 fcntl_lock(fileno(fp), F_WRLCK);
724 if (xname)
725 *xname = name;
726 return fp;
729 FILE *
730 cache_queue(struct mailbox *mp)
732 FILE *fp;
734 fp = cache_queue1(mp, "a", NULL);
735 if (fp == NULL)
736 fputs("Cannot queue IMAP command. Retry when online.\n",
737 stderr);
738 return fp;
741 enum okay
742 cache_dequeue(struct mailbox *mp)
744 int bufsz;
745 char *cachedir, *eaccount, *buf, *oldbox;
746 DIR *dirfd;
747 struct dirent *dp;
749 if ((cachedir = value("imap-cache")) == NULL)
750 return OKAY;
751 cachedir = expand(cachedir);
752 eaccount = strenc(mp->mb_imap_account);
753 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
754 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
755 if ((dirfd = opendir(buf)) == NULL)
756 return OKAY;
757 oldbox = mp->mb_imap_mailbox;
758 while ((dp = readdir(dirfd)) != NULL) {
759 if (dp->d_name[0] == '.')
760 continue;
761 mp->mb_imap_mailbox = strdec(dp->d_name);
762 dequeue1(mp);
764 closedir(dirfd);
765 mp->mb_imap_mailbox = oldbox;
766 return OKAY;
769 static enum okay
770 dequeue1(struct mailbox *mp)
772 FILE *fp = NULL, *uvfp = NULL;
773 char *qname, *uvname;
774 unsigned long uv;
775 off_t is_size;
776 int is_count;
778 fp = cache_queue1(mp, "r+", &qname);
779 if (fp != NULL && fsize(fp) > 0) {
780 if (imap_select(mp, &is_size, &is_count,
781 mp->mb_imap_mailbox) != OKAY) {
782 fprintf(stderr, "Cannot select \"%s\" for dequeuing.\n",
783 mp->mb_imap_mailbox);
784 goto save;
786 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
787 (uvfp = Fopen(uvname, "r")) == NULL ||
788 (fcntl_lock(fileno(uvfp), F_RDLCK), 0) ||
789 fscanf(uvfp, "%lu", &uv) != 1 ||
790 uv != mp->mb_uidvalidity) {
791 fprintf(stderr,
792 "Unique identifiers for \"%s\" are out of date. "
793 "Cannot commit IMAP commands.\n",
794 mp->mb_imap_mailbox);
795 save: fputs("Saving IMAP commands to dead.letter\n", stderr);
796 savedeadletter(fp);
797 ftruncate(fileno(fp), 0);
798 Fclose(fp);
799 if (uvfp)
800 Fclose(uvfp);
801 return STOP;
803 Fclose(uvfp);
804 printf("Committing IMAP commands for \"%s\"\n",
805 mp->mb_imap_mailbox);
806 imap_dequeue(mp, fp);
808 if (fp) {
809 Fclose(fp);
810 unlink(qname);
812 return OKAY;
814 #endif /* HAVE_SOCKETS */