Followup for [309dea0] in imap_cache.c..
[s-mailx.git] / imap_cache.c
blob8d0cc36016eb5a195ee409181d2d804c111e7be6
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ A cache for IMAP.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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 HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(imap_cache)
45 #ifdef HAVE_IMAP
46 # include <dirent.h>
48 static char * encname(struct mailbox *mp, const char *name, int same,
49 const char *box);
50 static char * encuid(struct mailbox *mp, unsigned long uid);
51 static FILE * clean(struct mailbox *mp, struct cw *cw);
52 static unsigned long * builds(long *contentelem);
53 static void purge(struct mailbox *mp, struct message *m, long mc,
54 struct cw *cw, const char *name);
55 static int longlt(const void *a, const void *b);
56 static void remve(unsigned long n);
57 static FILE * cache_queue1(struct mailbox *mp, char const *mode,
58 char **xname);
59 static enum okay dequeue1(struct mailbox *mp);
61 static const char infofmt[] = "%c %lu %d %lu %ld";
62 #define INITSKIP 128L
63 #define USEBITS(f) \
64 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
66 static const char README1[] = "\
67 This is a cache directory maintained by " UAGENT "(1).\n\
68 You should not change any files within.\n\
69 Nevertheless, the structure is as follows: Each subdirectory of the\n\
70 current directory represents an IMAP account, and each subdirectory\n\
71 below that represents a mailbox. Each mailbox directory contains a file\n\
72 named UIDVALIDITY which describes the validity in relation to the version\n\
73 on the server. Other files have names corresponding to their IMAP UID.\n";
74 static const char README2[] = "\n\
75 The first 128 bytes of these files are used to store message attributes; the\n\
76 following data is equivalent to compress(1) output. So if you have to save a\n\
77 message by hand because of an emergency, throw away the first 128 bytes and\n\
78 decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n";
79 static const char README3[] = "\n\
80 Files named QUEUE contain data that will be sent do the IMAP server next\n\
81 time a connection is made in online mode.\n";
82 static const char README4[] = "\n\
83 You can safely delete any file or directory here, unless it contains a QUEUE\n\
84 file that is not empty; " UAGENT " will download the data again and will also\n\
85 write new cache entries if configured in this way. If you do not wish to use\n\
86 the cache anymore, delete the entire directory and unset the 'imap-cache'\n\
87 variable in " UAGENT "(1).\n";
88 static const char README5[] = "\n\
89 For more information about " UAGENT "(1), visit\n\
90 <http://sdaoden.users.sourceforge.net/code.html>.\n"; /* TODO MAGIC CONSTANT */
92 static char *
93 encname(struct mailbox *mp, const char *name, int same, const char *box)
95 char *cachedir, *eaccount, *ename, *res;
96 char const *emailbox;
97 int resz;
98 NYD2_ENTER;
100 ename = urlxenc(name, TRU1);
101 if (mp->mb_cache_directory && same && box == NULL) {
102 res = salloc(resz = strlen(mp->mb_cache_directory) + strlen(ename) + 2);
103 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
104 (*ename ? "/" : ""), ename);
105 } else {
106 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
107 (cachedir = file_expand(cachedir)) == NULL) {
108 res = NULL;
109 goto jleave;
111 eaccount = urlxenc(mp->mb_imap_account, TRU1);
112 if (box)
113 emailbox = urlxenc(box, TRU1);
114 else if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
115 emailbox = urlxenc(mp->mb_imap_mailbox, TRU1);
116 else
117 emailbox = "INBOX";
118 res = salloc(resz = strlen(cachedir) + strlen(eaccount) +
119 strlen(emailbox) + strlen(ename) + 4);
120 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, emailbox,
121 (*ename ? "/" : ""), ename);
123 jleave:
124 NYD2_LEAVE;
125 return res;
128 static char *
129 encuid(struct mailbox *mp, unsigned long uid)
131 char buf[30], *cp;
132 NYD2_ENTER;
134 snprintf(buf, sizeof buf, "%lu", uid);
135 cp = encname(mp, buf, 1, NULL);
136 NYD2_LEAVE;
137 return cp;
140 FL enum okay
141 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
142 int setflags)
144 FILE *fp;
145 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
146 int lastc = EOF, i, xflag, inheader = 1;
147 char b, iob[32768];
148 off_t offset;
149 void *zp;
150 enum okay rv = STOP;
151 NYD2_ENTER;
153 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
154 m->m_uid == 0))
155 goto jleave;
156 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
157 goto jleave;
159 fcntl_lock(fileno(fp), FLOCK_READ);
160 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
161 (unsigned long*)&xtime, &xlines) < 4)
162 goto jfail;
163 if (need != NEED_UNSPEC) {
164 switch (b) {
165 case 'H':
166 if (need == NEED_HEADER)
167 goto jsuccess;
168 goto jfail;
169 case 'B':
170 if (need == NEED_HEADER || need == NEED_BODY)
171 goto jsuccess;
172 goto jfail;
173 default:
174 goto jfail;
177 jsuccess:
178 if (b == 'N')
179 goto jflags;
180 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
181 goto jfail;
182 zp = zalloc(fp);
183 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
184 zfree(zp);
185 goto jfail;
187 offset = ftell(mp->mb_otf);
188 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
189 size += n;
190 for (i = 0; i < n; i++) {
191 if (iob[i] == '\n') {
192 lines++;
193 if (lastc == '\n')
194 inheader = 0;
196 lastc = iob[i]&0377;
198 fwrite(iob, 1, n, mp->mb_otf);
200 if (n > 0 && need == NEED_BODY) {
201 while ((n = zread(zp, iob, sizeof iob)) > 0) {
202 size += n;
203 for (i = 0; i < n; i++)
204 if (iob[i] == '\n')
205 lines++;
206 fwrite(iob, 1, n, mp->mb_otf);
209 fflush(mp->mb_otf);
210 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
211 goto jfail;
213 m->m_size = size;
214 m->m_lines = lines;
215 m->m_block = mailx_blockof(offset);
216 m->m_offset = mailx_offsetof(offset);
217 jflags:
218 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 rv = OKAY;
248 jfail:
249 Fclose(fp);
250 jleave:
251 NYD2_LEAVE;
252 return rv;
255 FL enum okay
256 getcache(struct mailbox *mp, struct message *m, enum needspec need)
258 enum okay rv;
259 NYD_ENTER;
261 rv = getcache1(mp, m, need, 0);
262 NYD_LEAVE;
263 return rv;
266 FL void
267 putcache(struct mailbox *mp, struct message *m)
269 char iob[32768], *name, ob;
270 FILE *ibuf, *obuf;
271 int c, oflag;
272 long n, cnt, oldoffset, osize, otime, olines = -1;
273 void *zp;
274 NYD_ENTER;
276 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
277 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
278 goto jleave;
279 if (m->m_have & HAVE_BODY)
280 c = 'B';
281 else if (m->m_have & HAVE_HEADER)
282 c = 'H';
283 else if (m->m_have == HAVE_NOTHING)
284 c = 'N';
285 else
286 goto jleave;
287 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
288 oldoffset = 0;
289 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
290 if ((obuf = Fopen(name, "w")) == NULL)
291 goto jleave;
292 fcntl_lock(fileno(obuf), FLOCK_WRITE); /* XXX err hdl */
293 } else {
294 fcntl_lock(fileno(obuf), FLOCK_READ); /* XXX err hdl */
295 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
296 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
297 (ob == 'B' || (ob == 'H' && c != 'B'))) {
298 if (m->m_xlines <= 0 && olines > 0)
299 m->m_xlines = olines;
300 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
301 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
302 (m->m_xlines > 0 && olines != m->m_xlines)) {
303 fflush(obuf);
304 rewind(obuf);
305 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
306 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
307 putc('\n', obuf);
309 Fclose(obuf);
310 goto jleave;
312 fflush(obuf);
313 rewind(obuf);
314 ftruncate(fileno(obuf), 0);
316 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
317 Fclose(obuf);
318 goto jleave;
320 if (c == 'N')
321 goto jdone;
322 fseek(obuf, INITSKIP, SEEK_SET);
323 zp = zalloc(obuf);
324 cnt = m->m_size;
325 while (cnt > 0) {
326 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
327 cnt -= n;
328 if ((size_t)n != fread(iob, 1, n, ibuf) ||
329 n != (long)zwrite(zp, iob, n)) {
330 unlink(name);
331 zfree(zp);
332 goto jout;
335 if (zfree(zp) < 0) {
336 unlink(name);
337 goto jout;
339 jdone:
340 rewind(obuf);
341 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
342 (unsigned long)m->m_time, m->m_xlines);
343 putc('\n', obuf);
344 if (ferror(obuf)) {
345 unlink(name);
346 goto jout;
348 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
349 m->m_flag |= MFULLYCACHED;
350 jout:
351 if (Fclose(obuf) != 0) {
352 m->m_flag &= ~MFULLYCACHED;
353 unlink(name);
355 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
356 jleave:
357 NYD_LEAVE;
360 FL void
361 initcache(struct mailbox *mp)
363 char *name, *uvname;
364 FILE *uvfp;
365 unsigned long uv;
366 struct cw cw;
367 NYD_ENTER;
369 if (mp->mb_cache_directory != NULL)
370 free(mp->mb_cache_directory);
371 mp->mb_cache_directory = NULL;
372 if ((name = encname(mp, "", 1, NULL)) == NULL)
373 goto jleave;
374 mp->mb_cache_directory = sstrdup(name);
375 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
376 goto jleave;
377 if (cwget(&cw) == STOP)
378 goto jleave;
379 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
380 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
381 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
382 if ((uvfp = clean(mp, &cw)) == NULL)
383 goto jout;
384 } else {
385 fflush(uvfp);
386 rewind(uvfp);
388 fcntl_lock(fileno(uvfp), FLOCK_WRITE);
389 fprintf(uvfp, "%lu\n", mp->mb_uidvalidity);
390 if (ferror(uvfp) || Fclose(uvfp) != 0) {
391 unlink(uvname);
392 mp->mb_uidvalidity = 0;
394 jout:
395 cwrelse(&cw);
396 jleave:
397 NYD_LEAVE;
400 FL void
401 purgecache(struct mailbox *mp, struct message *m, long mc)
403 char *name;
404 struct cw cw;
405 NYD_ENTER;
407 if ((name = encname(mp, "", 1, NULL)) == NULL)
408 goto jleave;
409 if (cwget(&cw) == STOP)
410 goto jleave;
411 purge(mp, m, mc, &cw, name);
412 cwrelse(&cw);
413 jleave:
414 NYD_LEAVE;
417 static FILE *
418 clean(struct mailbox *mp, struct cw *cw)
420 char *cachedir, *eaccount, *buf;
421 char const *emailbox;
422 int bufsz;
423 DIR *dirp;
424 struct dirent *dp;
425 FILE *fp = NULL;
426 NYD_ENTER;
428 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
429 (cachedir = file_expand(cachedir)) == NULL)
430 goto jleave;
431 eaccount = urlxenc(mp->mb_imap_account, TRU1);
432 if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
433 emailbox = urlxenc(mp->mb_imap_mailbox, TRU1);
434 else
435 emailbox = "INBOX";
436 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
437 strlen(emailbox) + 40);
438 if (makedir(cachedir) != OKAY)
439 goto jleave;
440 snprintf(buf, bufsz, "%s/README", cachedir);
441 if ((fp = Fopen(buf, "wx")) != NULL) {
442 fputs(README1, fp);
443 fputs(README2, fp);
444 fputs(README3, fp);
445 fputs(README4, fp);
446 fputs(README5, fp);
447 Fclose(fp);
449 fp = NULL;
450 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
451 if (makedir(buf) != OKAY)
452 goto jleave;
453 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
454 if (makedir(buf) != OKAY)
455 goto jleave;
456 if (chdir(buf) < 0)
457 goto jleave;
458 if ((dirp = opendir(".")) == NULL)
459 goto jout;
460 while ((dp = readdir(dirp)) != NULL) {
461 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
462 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
463 continue;
464 unlink(dp->d_name);
466 closedir(dirp);
467 fp = Fopen("UIDVALIDITY", "w");
468 jout:
469 if (cwret(cw) == STOP) {
470 fputs("Fatal: Cannot change back to current directory.\n", stderr);
471 abort();
473 jleave:
474 NYD_LEAVE;
475 return fp;
478 static unsigned long *
479 builds(long *contentelem)
481 unsigned long n, *contents = NULL;
482 long contentalloc = 0;
483 char *x;
484 DIR *dirp;
485 struct dirent *dp;
486 NYD_ENTER;
488 *contentelem = 0;
489 if ((dirp = opendir(".")) == NULL)
490 goto jleave;
491 while ((dp = readdir(dirp)) != NULL) {
492 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
493 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
494 continue;
495 n = strtoul(dp->d_name, &x, 10);
496 if (*x != '\0')
497 continue;
498 if (*contentelem >= contentalloc - 1)
499 contents = srealloc(contents,
500 (contentalloc += 200) * sizeof *contents);
501 contents[(*contentelem)++] = n;
503 closedir(dirp);
504 if (*contentelem > 0) {
505 contents[*contentelem] = 0;
506 qsort(contents, *contentelem, sizeof *contents, longlt);
508 jleave:
509 NYD_LEAVE;
510 return contents;
513 static void
514 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
515 const char *name)
517 unsigned long *contents;
518 long i, j, contentelem;
519 NYD_ENTER;
520 UNUSED(mp);
522 if (chdir(name) < 0)
523 goto jleave;
524 contents = builds(&contentelem);
525 if (contents != NULL) {
526 i = j = 0;
527 while (j < contentelem) {
528 if (i < mc && m[i].m_uid == contents[j]) {
529 i++;
530 j++;
531 } else if (i < mc && m[i].m_uid < contents[j])
532 i++;
533 else
534 remve(contents[j++]);
536 free(contents);
538 if (cwret(cw) == STOP) {
539 fputs("Fatal: Cannot change back to current directory.\n", stderr);
540 abort();
542 jleave:
543 NYD_LEAVE;
546 static int
547 longlt(const void *a, const void *b)
549 union {long l; int i;} u;
550 NYD_ENTER;
552 u.l = *(long const*)a - *(long const*)b;
553 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
554 NYD_LEAVE;
555 return u.i;
558 static void
559 remve(unsigned long n)
561 char buf[30];
562 NYD_ENTER;
564 snprintf(buf, sizeof buf, "%lu", n);
565 unlink(buf);
566 NYD_LEAVE;
569 FL void
570 delcache(struct mailbox *mp, struct message *m)
572 char *fn;
573 NYD_ENTER;
575 fn = encuid(mp, m->m_uid);
576 if (fn && unlink(fn) == 0)
577 m->m_flag |= MUNLINKED;
578 NYD_LEAVE;
581 FL enum okay
582 cache_setptr(enum fedit_mode fm, int transparent)
584 struct cw cw;
585 int i, omsgCount = 0;
586 char *name;
587 unsigned long *contents;
588 long contentelem;
589 struct message *omessage = NULL;
590 enum okay rv = STOP;
591 NYD_ENTER;
593 if (transparent) {
594 omessage = message;
595 omsgCount = msgCount;
597 if (mb.mb_cache_directory != NULL) {
598 free(mb.mb_cache_directory);
599 mb.mb_cache_directory = NULL;
601 if ((name = encname(&mb, "", 1, NULL)) == NULL)
602 goto jleave;
603 mb.mb_cache_directory = sstrdup(name);
604 if (cwget(&cw) == STOP)
605 goto jleave;
606 if (chdir(name) < 0)
607 goto jleave;
608 contents = builds(&contentelem);
609 msgCount = contentelem;
610 message = scalloc(msgCount + 1, sizeof *message);
611 if (cwret(&cw) == STOP) {
612 fputs("Fatal: Cannot change back to current directory.\n", stderr);
613 abort();
615 cwrelse(&cw);
617 srelax_hold();
618 for (i = 0; i < msgCount; i++) {
619 message[i].m_uid = contents[i];
620 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
621 srelax();
623 srelax_rele();
625 if (contents != NULL)
626 free(contents);
627 mb.mb_type = MB_CACHE;
628 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
629 if (transparent)
630 transflags(omessage, omsgCount, 1);
631 else {
632 if (omessage != NULL)
633 free(omessage);
634 setdot(message);
636 rv = OKAY;
637 jleave:
638 NYD_LEAVE;
639 return rv;
642 FL enum okay
643 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
645 char *name, *cachedir, *eaccount;
646 DIR *dirp;
647 struct dirent *dp;
648 const char *cp, *bp, *sp;
649 int namesz;
650 enum okay rv = STOP;
651 NYD_ENTER;
653 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
654 (cachedir = file_expand(cachedir)) == NULL)
655 goto jleave;
656 eaccount = urlxenc(mp->mb_imap_account, TRU1);
657 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
658 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
659 if ((dirp = opendir(name)) == NULL)
660 goto jleave;
661 while ((dp = readdir(dirp)) != NULL) {
662 if (dp->d_name[0] == '.')
663 continue;
664 cp = sp = urlxdec(dp->d_name);
665 for (bp = base; *bp && *bp == *sp; bp++)
666 sp++;
667 if (*bp)
668 continue;
669 cp = strip ? sp : cp;
670 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
672 closedir(dirp);
673 rv = OKAY;
674 jleave:
675 NYD_LEAVE;
676 return rv;
679 FL enum okay
680 cache_remove(const char *name)
682 struct stat st;
683 DIR *dirp;
684 struct dirent *dp;
685 char *path, *dir;
686 int pathsize, pathend, n;
687 enum okay rv = OKAY;
688 NYD_ENTER;
690 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
691 goto jleave;
692 pathend = strlen(dir);
693 path = smalloc(pathsize = pathend + 30);
694 memcpy(path, dir, pathend);
695 path[pathend++] = '/';
696 path[pathend] = '\0';
697 if ((dirp = opendir(path)) == NULL) {
698 free(path);
699 goto jleave;
701 while ((dp = readdir(dirp)) != NULL) {
702 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
703 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
704 continue;
705 n = strlen(dp->d_name) + 1;
706 if (pathend + n > pathsize)
707 path = srealloc(path, pathsize = pathend + n + 30);
708 memcpy(path + pathend, dp->d_name, n);
709 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
710 continue;
711 if (unlink(path) < 0) {
712 perror(path);
713 closedir(dirp);
714 free(path);
715 rv = STOP;
716 goto jleave;
719 closedir(dirp);
720 path[pathend] = '\0';
721 rmdir(path); /* no error on failure, might contain submailboxes */
722 free(path);
723 jleave:
724 NYD_LEAVE;
725 return rv;
728 FL enum okay
729 cache_rename(const char *old, const char *new)
731 char *olddir, *newdir;
732 enum okay rv = OKAY;
733 NYD_ENTER;
735 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
736 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
737 goto jleave;
738 if (rename(olddir, newdir) < 0) {
739 perror(olddir);
740 rv = STOP;
742 jleave:
743 NYD_LEAVE;
744 return rv;
747 FL unsigned long
748 cached_uidvalidity(struct mailbox *mp)
750 FILE *uvfp;
751 char *uvname;
752 unsigned long uv;
753 NYD_ENTER;
755 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
756 uv = 0;
757 goto jleave;
759 if ((uvfp = Fopen(uvname, "r")) == NULL ||
760 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
761 fscanf(uvfp, "%lu", &uv) != 1)
762 uv = 0;
763 if (uvfp != NULL)
764 Fclose(uvfp);
765 jleave:
766 NYD_LEAVE;
767 return uv;
770 static FILE *
771 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
773 char *name;
774 FILE *fp = NULL;
775 NYD_ENTER;
777 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
778 goto jleave;
779 if ((fp = Fopen(name, mode)) != NULL)
780 fcntl_lock(fileno(fp), FLOCK_WRITE);
781 if (xname)
782 *xname = name;
783 jleave:
784 NYD_LEAVE;
785 return fp;
788 FL FILE *
789 cache_queue(struct mailbox *mp)
791 FILE *fp;
792 NYD_ENTER;
794 fp = cache_queue1(mp, "a", NULL);
795 if (fp == NULL)
796 fputs("Cannot queue IMAP command. Retry when online.\n", stderr);
797 NYD_LEAVE;
798 return fp;
801 FL enum okay
802 cache_dequeue(struct mailbox *mp)
804 int bufsz;
805 char *cachedir, *eaccount, *buf, *oldbox;
806 DIR *dirp;
807 struct dirent *dp;
808 enum okay rv = OKAY;
809 NYD_ENTER;
811 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
812 (cachedir = file_expand(cachedir)) == NULL)
813 goto jleave;
814 eaccount = urlxenc(mp->mb_imap_account, TRU1);
815 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
816 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
817 if ((dirp = opendir(buf)) == NULL)
818 goto jleave;
819 oldbox = mp->mb_imap_mailbox;
820 while ((dp = readdir(dirp)) != NULL) {
821 if (dp->d_name[0] == '.')
822 continue;
823 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
824 * (but wuuuuh, what a shit!) */
825 mp->mb_imap_mailbox = sstrdup(urlxdec(dp->d_name));
826 dequeue1(mp);
827 { char *x = mp->mb_imap_mailbox;
828 mp->mb_imap_mailbox = oldbox;
829 free(x);
832 closedir(dirp);
833 jleave:
834 NYD_LEAVE;
835 return rv;
838 static enum okay
839 dequeue1(struct mailbox *mp)
841 FILE *fp = NULL, *uvfp = NULL;
842 char *qname, *uvname;
843 unsigned long uv;
844 off_t is_size;
845 int is_count;
846 enum okay rv = OKAY;
847 NYD_ENTER;
849 fp = cache_queue1(mp, "r+", &qname);
850 if (fp != NULL && fsize(fp) > 0) {
851 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox) != OKAY) {
852 fprintf(stderr, "Cannot select \"%s\" for dequeuing.\n",
853 mp->mb_imap_mailbox);
854 goto jsave;
856 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
857 (uvfp = Fopen(uvname, "r")) == NULL ||
858 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
859 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
860 fprintf(stderr, "Unique identifiers for \"%s\" are out of date. "
861 "Cannot commit IMAP commands.\n", mp->mb_imap_mailbox);
862 jsave:
863 fputs("Saving IMAP commands to dead.letter\n", stderr);
864 savedeadletter(fp, 0);
865 ftruncate(fileno(fp), 0);
866 Fclose(fp);
867 if (uvfp)
868 Fclose(uvfp);
869 rv = STOP;
870 goto jleave;
872 Fclose(uvfp);
873 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
874 imap_dequeue(mp, fp);
876 if (fp) {
877 Fclose(fp);
878 unlink(qname);
880 jleave:
881 NYD_LEAVE;
882 return rv;
884 #endif /* HAVE_IMAP */
886 /* s-it-mode */