THANKS: Vincent Lefevre
[s-mailx.git] / imap_cache.c
bloba9c3f3e7efee31ef7e66f1141330873c6de4f6a0
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 - 2015 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.
39 #undef n_FILE
40 #define n_FILE imap_cache
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 EMPTY_FILE()
47 #ifdef HAVE_IMAP
48 # include <dirent.h>
50 static char * encname(struct mailbox *mp, const char *name, int same,
51 const char *box);
52 static char * encuid(struct mailbox *mp, unsigned long uid);
53 static FILE * clean(struct mailbox *mp, struct cw *cw);
54 static unsigned long * builds(long *contentelem);
55 static void purge(struct mailbox *mp, struct message *m, long mc,
56 struct cw *cw, const char *name);
57 static int longlt(const void *a, const void *b);
58 static void remve(unsigned long n);
59 static FILE * cache_queue1(struct mailbox *mp, char const *mode,
60 char **xname);
61 static enum okay dequeue1(struct mailbox *mp);
63 static const char infofmt[] = "%c %lu %d %lu %ld";
64 #define INITSKIP 128L
65 #define USEBITS(f) \
66 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
68 static const char README1[] = "\
69 This is a cache directory maintained by " UAGENT "(1).\n\
70 You should not change any files within.\n\
71 Nevertheless, the structure is as follows: Each subdirectory of the\n\
72 current directory represents an IMAP account, and each subdirectory\n\
73 below that represents a mailbox. Each mailbox directory contains a file\n\
74 named UIDVALIDITY which describes the validity in relation to the version\n\
75 on the server. Other files have names corresponding to their IMAP UID.\n";
76 static const char README2[] = "\n\
77 The first 128 bytes of these files are used to store message attributes; the\n\
78 following data is equivalent to compress(1) output. So if you have to save a\n\
79 message by hand because of an emergency, throw away the first 128 bytes and\n\
80 decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n";
81 static const char README3[] = "\n\
82 Files named QUEUE contain data that will be sent do the IMAP server next\n\
83 time a connection is made in online mode.\n";
84 static const char README4[] = "\n\
85 You can safely delete any file or directory here, unless it contains a QUEUE\n\
86 file that is not empty; " UAGENT " will download the data again and will also\n\
87 write new cache entries if configured in this way. If you do not wish to use\n\
88 the cache anymore, delete the entire directory and unset the 'imap-cache'\n\
89 variable in " UAGENT "(1).\n";
90 static const char README5[] = "\n\
91 For more information about " UAGENT "(1), visit\n\
92 <http://sdaoden.users.sourceforge.net/code.html>.\n"; /* TODO MAGIC CONSTANT */
94 static char *
95 encname(struct mailbox *mp, const char *name, int same, const char *box)
97 char *cachedir, *eaccount, *ename, *res;
98 char const *emailbox;
99 int resz;
100 NYD2_ENTER;
102 ename = urlxenc(name, TRU1);
103 if (mp->mb_cache_directory && same && box == NULL) {
104 res = salloc(resz = strlen(mp->mb_cache_directory) + strlen(ename) + 2);
105 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
106 (*ename ? "/" : ""), ename);
107 } else {
108 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
109 (cachedir = file_expand(cachedir)) == NULL) {
110 res = NULL;
111 goto jleave;
113 eaccount = urlxenc(mp->mb_imap_account, TRU1);
114 if (box)
115 emailbox = urlxenc(box, TRU1);
116 else if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
117 emailbox = urlxenc(mp->mb_imap_mailbox, TRU1);
118 else
119 emailbox = "INBOX";
120 res = salloc(resz = strlen(cachedir) + strlen(eaccount) +
121 strlen(emailbox) + strlen(ename) + 4);
122 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, emailbox,
123 (*ename ? "/" : ""), ename);
125 jleave:
126 NYD2_LEAVE;
127 return res;
130 static char *
131 encuid(struct mailbox *mp, unsigned long uid)
133 char buf[30], *cp;
134 NYD2_ENTER;
136 snprintf(buf, sizeof buf, "%lu", uid);
137 cp = encname(mp, buf, 1, NULL);
138 NYD2_LEAVE;
139 return cp;
142 FL enum okay
143 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
144 int setflags)
146 FILE *fp;
147 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
148 int lastc = EOF, i, xflag, inheader = 1;
149 char b, iob[32768];
150 off_t offset;
151 void *zp;
152 enum okay rv = STOP;
153 NYD2_ENTER;
155 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
156 m->m_uid == 0))
157 goto jleave;
158 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
159 goto jleave;
161 file_lock(fileno(fp), FLT_READ, 0,0, 0);
162 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
163 (unsigned long*)&xtime, &xlines) < 4)
164 goto jfail;
165 if (need != NEED_UNSPEC) {
166 switch (b) {
167 case 'H':
168 if (need == NEED_HEADER)
169 goto jsuccess;
170 goto jfail;
171 case 'B':
172 if (need == NEED_HEADER || need == NEED_BODY)
173 goto jsuccess;
174 goto jfail;
175 default:
176 goto jfail;
179 jsuccess:
180 if (b == 'N')
181 goto jflags;
182 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
183 goto jfail;
184 zp = zalloc(fp);
185 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
186 zfree(zp);
187 goto jfail;
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 jfail;
215 m->m_size = size;
216 m->m_lines = lines;
217 m->m_block = mailx_blockof(offset);
218 m->m_offset = mailx_offsetof(offset);
219 jflags:
220 if (setflags) {
221 m->m_xsize = xsize;
222 m->m_time = xtime;
223 if (setflags & 2) {
224 m->m_flag = xflag | MNOFROM;
225 if (b != 'B')
226 m->m_flag |= MHIDDEN;
229 if (xlines > 0 && m->m_xlines <= 0)
230 m->m_xlines = xlines;
231 switch (b) {
232 case 'B':
233 m->m_xsize = xsize;
234 if (xflag == MREAD && xlines > 0)
235 m->m_flag |= MFULLYCACHED;
236 if (need == NEED_BODY) {
237 m->m_have |= HAVE_HEADER | HAVE_BODY;
238 if (m->m_lines > 0)
239 m->m_xlines = m->m_lines;
240 break;
242 /*FALLTHRU*/
243 case 'H':
244 m->m_have |= HAVE_HEADER;
245 break;
246 case 'N':
247 break;
249 rv = OKAY;
250 jfail:
251 Fclose(fp);
252 jleave:
253 NYD2_LEAVE;
254 return rv;
257 FL enum okay
258 getcache(struct mailbox *mp, struct message *m, enum needspec need)
260 enum okay rv;
261 NYD_ENTER;
263 rv = getcache1(mp, m, need, 0);
264 NYD_LEAVE;
265 return rv;
268 FL void
269 putcache(struct mailbox *mp, struct message *m)
271 char iob[32768], *name, ob;
272 FILE *ibuf, *obuf;
273 int c, oflag;
274 long n, cnt, oldoffset, osize, otime, olines = -1;
275 void *zp;
276 NYD_ENTER;
278 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
279 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
280 goto jleave;
281 if (m->m_have & HAVE_BODY)
282 c = 'B';
283 else if (m->m_have & HAVE_HEADER)
284 c = 'H';
285 else if (m->m_have == HAVE_NOTHING)
286 c = 'N';
287 else
288 goto jleave;
289 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
290 oldoffset = 0;
291 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
292 if ((obuf = Fopen(name, "w")) == NULL)
293 goto jleave;
294 file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
295 } else {
296 file_lock(fileno(obuf), FLT_READ, 0,0, 0); /* XXX err hdl */
297 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
298 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
299 (ob == 'B' || (ob == 'H' && c != 'B'))) {
300 if (m->m_xlines <= 0 && olines > 0)
301 m->m_xlines = olines;
302 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
303 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
304 (m->m_xlines > 0 && olines != m->m_xlines)) {
305 fflush(obuf);
306 rewind(obuf);
307 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
308 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
309 putc('\n', obuf);
311 Fclose(obuf);
312 goto jleave;
314 fflush(obuf);
315 rewind(obuf);
316 ftruncate(fileno(obuf), 0);
318 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
319 Fclose(obuf);
320 goto jleave;
322 if (c == 'N')
323 goto jdone;
324 fseek(obuf, INITSKIP, SEEK_SET);
325 zp = zalloc(obuf);
326 cnt = m->m_size;
327 while (cnt > 0) {
328 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
329 cnt -= n;
330 if ((size_t)n != fread(iob, 1, n, ibuf) ||
331 n != (long)zwrite(zp, iob, n)) {
332 unlink(name);
333 zfree(zp);
334 goto jout;
337 if (zfree(zp) < 0) {
338 unlink(name);
339 goto jout;
341 jdone:
342 rewind(obuf);
343 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
344 (unsigned long)m->m_time, m->m_xlines);
345 putc('\n', obuf);
346 if (ferror(obuf)) {
347 unlink(name);
348 goto jout;
350 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
351 m->m_flag |= MFULLYCACHED;
352 jout:
353 if (Fclose(obuf) != 0) {
354 m->m_flag &= ~MFULLYCACHED;
355 unlink(name);
357 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
358 jleave:
359 NYD_LEAVE;
362 FL void
363 initcache(struct mailbox *mp)
365 char *name, *uvname;
366 FILE *uvfp;
367 unsigned long uv;
368 struct cw cw;
369 NYD_ENTER;
371 if (mp->mb_cache_directory != NULL)
372 free(mp->mb_cache_directory);
373 mp->mb_cache_directory = NULL;
374 if ((name = encname(mp, "", 1, NULL)) == NULL)
375 goto jleave;
376 mp->mb_cache_directory = sstrdup(name);
377 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
378 goto jleave;
379 if (cwget(&cw) == STOP)
380 goto jleave;
381 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
382 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
383 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
384 if ((uvfp = clean(mp, &cw)) == NULL)
385 goto jout;
386 } else {
387 fflush(uvfp);
388 rewind(uvfp);
390 file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
391 fprintf(uvfp, "%lu\n", mp->mb_uidvalidity);
392 if (ferror(uvfp) || Fclose(uvfp) != 0) {
393 unlink(uvname);
394 mp->mb_uidvalidity = 0;
396 jout:
397 cwrelse(&cw);
398 jleave:
399 NYD_LEAVE;
402 FL void
403 purgecache(struct mailbox *mp, struct message *m, long mc)
405 char *name;
406 struct cw cw;
407 NYD_ENTER;
409 if ((name = encname(mp, "", 1, NULL)) == NULL)
410 goto jleave;
411 if (cwget(&cw) == STOP)
412 goto jleave;
413 purge(mp, m, mc, &cw, name);
414 cwrelse(&cw);
415 jleave:
416 NYD_LEAVE;
419 static FILE *
420 clean(struct mailbox *mp, struct cw *cw)
422 char *cachedir, *eaccount, *buf;
423 char const *emailbox;
424 int bufsz;
425 DIR *dirp;
426 struct dirent *dp;
427 FILE *fp = NULL;
428 NYD_ENTER;
430 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
431 (cachedir = file_expand(cachedir)) == NULL)
432 goto jleave;
433 eaccount = urlxenc(mp->mb_imap_account, TRU1);
434 if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
435 emailbox = urlxenc(mp->mb_imap_mailbox, TRU1);
436 else
437 emailbox = "INBOX";
438 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
439 strlen(emailbox) + 40);
440 if (makedir(cachedir) != OKAY)
441 goto jleave;
442 snprintf(buf, bufsz, "%s/README", cachedir);
443 if ((fp = Fopen(buf, "wx")) != NULL) {
444 fputs(README1, fp);
445 fputs(README2, fp);
446 fputs(README3, fp);
447 fputs(README4, fp);
448 fputs(README5, fp);
449 Fclose(fp);
451 fp = NULL;
452 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
453 if (makedir(buf) != OKAY)
454 goto jleave;
455 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
456 if (makedir(buf) != OKAY)
457 goto jleave;
458 if (chdir(buf) < 0)
459 goto jleave;
460 if ((dirp = opendir(".")) == NULL)
461 goto jout;
462 while ((dp = readdir(dirp)) != NULL) {
463 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
464 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
465 continue;
466 unlink(dp->d_name);
468 closedir(dirp);
469 fp = Fopen("UIDVALIDITY", "w");
470 jout:
471 if (cwret(cw) == STOP) {
472 n_err(_("Fatal: Cannot change back to current directory.\n"));
473 abort();
475 jleave:
476 NYD_LEAVE;
477 return fp;
480 static unsigned long *
481 builds(long *contentelem)
483 unsigned long n, *contents = NULL;
484 long contentalloc = 0;
485 char *x;
486 DIR *dirp;
487 struct dirent *dp;
488 NYD_ENTER;
490 *contentelem = 0;
491 if ((dirp = opendir(".")) == NULL)
492 goto jleave;
493 while ((dp = readdir(dirp)) != NULL) {
494 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
495 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
496 continue;
497 n = strtoul(dp->d_name, &x, 10);
498 if (*x != '\0')
499 continue;
500 if (*contentelem >= contentalloc - 1)
501 contents = srealloc(contents,
502 (contentalloc += 200) * sizeof *contents);
503 contents[(*contentelem)++] = n;
505 closedir(dirp);
506 if (*contentelem > 0) {
507 contents[*contentelem] = 0;
508 qsort(contents, *contentelem, sizeof *contents, longlt);
510 jleave:
511 NYD_LEAVE;
512 return contents;
515 static void
516 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
517 const char *name)
519 unsigned long *contents;
520 long i, j, contentelem;
521 NYD_ENTER;
522 UNUSED(mp);
524 if (chdir(name) < 0)
525 goto jleave;
526 contents = builds(&contentelem);
527 if (contents != NULL) {
528 i = j = 0;
529 while (j < contentelem) {
530 if (i < mc && m[i].m_uid == contents[j]) {
531 i++;
532 j++;
533 } else if (i < mc && m[i].m_uid < contents[j])
534 i++;
535 else
536 remve(contents[j++]);
538 free(contents);
540 if (cwret(cw) == STOP) {
541 n_err(_("Fatal: Cannot change back to current directory.\n"));
542 abort();
544 jleave:
545 NYD_LEAVE;
548 static int
549 longlt(const void *a, const void *b)
551 union {long l; int i;} u;
552 NYD_ENTER;
554 u.l = *(long const*)a - *(long const*)b;
555 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
556 NYD_LEAVE;
557 return u.i;
560 static void
561 remve(unsigned long n)
563 char buf[30];
564 NYD_ENTER;
566 snprintf(buf, sizeof buf, "%lu", n);
567 unlink(buf);
568 NYD_LEAVE;
571 FL void
572 delcache(struct mailbox *mp, struct message *m)
574 char *fn;
575 NYD_ENTER;
577 fn = encuid(mp, m->m_uid);
578 if (fn && unlink(fn) == 0)
579 m->m_flag |= MUNLINKED;
580 NYD_LEAVE;
583 FL enum okay
584 cache_setptr(enum fedit_mode fm, int transparent)
586 struct cw cw;
587 int i, omsgCount = 0;
588 char *name;
589 unsigned long *contents;
590 long contentelem;
591 struct message *omessage = NULL;
592 enum okay rv = STOP;
593 NYD_ENTER;
595 if (transparent) {
596 omessage = message;
597 omsgCount = msgCount;
599 if (mb.mb_cache_directory != NULL) {
600 free(mb.mb_cache_directory);
601 mb.mb_cache_directory = NULL;
603 if ((name = encname(&mb, "", 1, NULL)) == NULL)
604 goto jleave;
605 mb.mb_cache_directory = sstrdup(name);
606 if (cwget(&cw) == STOP)
607 goto jleave;
608 if (chdir(name) < 0)
609 goto jleave;
610 contents = builds(&contentelem);
611 msgCount = contentelem;
612 message = scalloc(msgCount + 1, sizeof *message);
613 if (cwret(&cw) == STOP) {
614 n_err(_("Fatal: Cannot change back to current directory.\n"));
615 abort();
617 cwrelse(&cw);
619 srelax_hold();
620 for (i = 0; i < msgCount; i++) {
621 message[i].m_uid = contents[i];
622 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
623 srelax();
625 srelax_rele();
627 if (contents != NULL)
628 free(contents);
629 mb.mb_type = MB_CACHE;
630 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
631 if (transparent)
632 transflags(omessage, omsgCount, 1);
633 else {
634 if (omessage != NULL)
635 free(omessage);
636 setdot(message);
638 rv = OKAY;
639 jleave:
640 NYD_LEAVE;
641 return rv;
644 FL enum okay
645 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
647 char *name, *cachedir, *eaccount;
648 DIR *dirp;
649 struct dirent *dp;
650 const char *cp, *bp, *sp;
651 int namesz;
652 enum okay rv = STOP;
653 NYD_ENTER;
655 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
656 (cachedir = file_expand(cachedir)) == NULL)
657 goto jleave;
658 eaccount = urlxenc(mp->mb_imap_account, TRU1);
659 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
660 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
661 if ((dirp = opendir(name)) == NULL)
662 goto jleave;
663 while ((dp = readdir(dirp)) != NULL) {
664 if (dp->d_name[0] == '.')
665 continue;
666 cp = sp = urlxdec(dp->d_name);
667 for (bp = base; *bp && *bp == *sp; bp++)
668 sp++;
669 if (*bp)
670 continue;
671 cp = strip ? sp : cp;
672 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
674 closedir(dirp);
675 rv = OKAY;
676 jleave:
677 NYD_LEAVE;
678 return rv;
681 FL enum okay
682 cache_remove(const char *name)
684 struct stat st;
685 DIR *dirp;
686 struct dirent *dp;
687 char *path, *dir;
688 int pathsize, pathend, n;
689 enum okay rv = OKAY;
690 NYD_ENTER;
692 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
693 goto jleave;
694 pathend = strlen(dir);
695 path = smalloc(pathsize = pathend + 30);
696 memcpy(path, dir, pathend);
697 path[pathend++] = '/';
698 path[pathend] = '\0';
699 if ((dirp = opendir(path)) == NULL) {
700 free(path);
701 goto jleave;
703 while ((dp = readdir(dirp)) != NULL) {
704 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
705 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
706 continue;
707 n = strlen(dp->d_name) + 1;
708 if (pathend + n > pathsize)
709 path = srealloc(path, pathsize = pathend + n + 30);
710 memcpy(path + pathend, dp->d_name, n);
711 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
712 continue;
713 if (unlink(path) < 0) {
714 n_perr(path, 0);
715 closedir(dirp);
716 free(path);
717 rv = STOP;
718 goto jleave;
721 closedir(dirp);
722 path[pathend] = '\0';
723 rmdir(path); /* no error on failure, might contain submailboxes */
724 free(path);
725 jleave:
726 NYD_LEAVE;
727 return rv;
730 FL enum okay
731 cache_rename(const char *old, const char *new)
733 char *olddir, *newdir;
734 enum okay rv = OKAY;
735 NYD_ENTER;
737 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
738 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
739 goto jleave;
740 if (rename(olddir, newdir) < 0) {
741 n_perr(olddir, 0);
742 rv = STOP;
744 jleave:
745 NYD_LEAVE;
746 return rv;
749 FL unsigned long
750 cached_uidvalidity(struct mailbox *mp)
752 FILE *uvfp;
753 char *uvname;
754 unsigned long uv;
755 NYD_ENTER;
757 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
758 uv = 0;
759 goto jleave;
761 if ((uvfp = Fopen(uvname, "r")) == NULL ||
762 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
763 fscanf(uvfp, "%lu", &uv) != 1)
764 uv = 0;
765 if (uvfp != NULL)
766 Fclose(uvfp);
767 jleave:
768 NYD_LEAVE;
769 return uv;
772 static FILE *
773 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
775 char *name;
776 FILE *fp = NULL;
777 NYD_ENTER;
779 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
780 goto jleave;
781 if ((fp = Fopen(name, mode)) != NULL)
782 file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
783 if (xname)
784 *xname = name;
785 jleave:
786 NYD_LEAVE;
787 return fp;
790 FL FILE *
791 cache_queue(struct mailbox *mp)
793 FILE *fp;
794 NYD_ENTER;
796 fp = cache_queue1(mp, "a", NULL);
797 if (fp == NULL)
798 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
799 NYD_LEAVE;
800 return fp;
803 FL enum okay
804 cache_dequeue(struct mailbox *mp)
806 int bufsz;
807 char *cachedir, *eaccount, *buf, *oldbox;
808 DIR *dirp;
809 struct dirent *dp;
810 enum okay rv = OKAY;
811 NYD_ENTER;
813 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
814 (cachedir = file_expand(cachedir)) == NULL)
815 goto jleave;
816 eaccount = urlxenc(mp->mb_imap_account, TRU1);
817 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
818 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
819 if ((dirp = opendir(buf)) == NULL)
820 goto jleave;
821 oldbox = mp->mb_imap_mailbox;
822 while ((dp = readdir(dirp)) != NULL) {
823 if (dp->d_name[0] == '.')
824 continue;
825 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
826 * (but wuuuuh, what a shit!) */
827 mp->mb_imap_mailbox = sstrdup(urlxdec(dp->d_name));
828 dequeue1(mp);
829 { char *x = mp->mb_imap_mailbox;
830 mp->mb_imap_mailbox = oldbox;
831 free(x);
834 closedir(dirp);
835 jleave:
836 NYD_LEAVE;
837 return rv;
840 static enum okay
841 dequeue1(struct mailbox *mp)
843 FILE *fp = NULL, *uvfp = NULL;
844 char *qname, *uvname;
845 unsigned long uv;
846 off_t is_size;
847 int is_count;
848 enum okay rv = OKAY;
849 NYD_ENTER;
851 fp = cache_queue1(mp, "r+", &qname);
852 if (fp != NULL && fsize(fp) > 0) {
853 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
854 != OKAY) {
855 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
856 goto jsave;
858 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
859 (uvfp = Fopen(uvname, "r")) == NULL ||
860 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
861 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
862 n_err(_("Unique identifiers for \"%s\" are out of date. "
863 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
864 jsave:
865 n_err(_("Saving IMAP commands to *DEAD*\n"));
866 savedeadletter(fp, 0);
867 ftruncate(fileno(fp), 0);
868 Fclose(fp);
869 if (uvfp)
870 Fclose(uvfp);
871 rv = STOP;
872 goto jleave;
874 Fclose(uvfp);
875 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
876 imap_dequeue(mp, fp);
878 if (fp) {
879 Fclose(fp);
880 unlink(qname);
882 jleave:
883 NYD_LEAVE;
884 return rv;
886 #endif /* HAVE_IMAP */
888 /* s-it-mode */