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