Drop strcomma() legacy (it is n_strsep())
[s-mailx.git] / imap_cache.c
blobdefa23329d1725d7db473d68788252d9bb54ef8f
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>
47 # include <fcntl.h>
49 static char * encname(struct mailbox *mp, const char *name, int same,
50 const char *box);
51 static char * encuid(struct mailbox *mp, unsigned long uid);
52 static FILE * clean(struct mailbox *mp, struct cw *cw);
53 static unsigned long * builds(long *contentelem);
54 static void purge(struct mailbox *mp, struct message *m, long mc,
55 struct cw *cw, const char *name);
56 static int longlt(const void *a, const void *b);
57 static void remve(unsigned long n);
58 static FILE * cache_queue1(struct mailbox *mp, char const *mode,
59 char **xname);
60 static enum okay dequeue1(struct mailbox *mp);
62 static const char infofmt[] = "%c %lu %d %lu %ld";
63 #define INITSKIP 128L
64 #define USEBITS(f) \
65 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
67 static const char README1[] = "\
68 This is a cache directory maintained by " UAGENT "(1).\n\
69 You should not change any files within.\n\
70 Nevertheless, the structure is as follows: Each subdirectory of the\n\
71 current directory represents an IMAP account, and each subdirectory\n\
72 below that represents a mailbox. Each mailbox directory contains a file\n\
73 named UIDVALIDITY which describes the validity in relation to the version\n\
74 on the server. Other files have names corresponding to their IMAP UID.\n";
75 static const char README2[] = "\n\
76 The first 128 bytes of these files are used to store message attributes; the\n\
77 following data is equivalent to compress(1) output. So if you have to save a\n\
78 message by hand because of an emergency, throw away the first 128 bytes and\n\
79 decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n";
80 static const char README3[] = "\n\
81 Files named QUEUE contain data that will be sent do the IMAP server next\n\
82 time a connection is made in online mode.\n";
83 static const char README4[] = "\n\
84 You can safely delete any file or directory here, unless it contains a QUEUE\n\
85 file that is not empty; " UAGENT
86 "mailx(1) 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 NYD_ENTER;
102 ename = urlxenc(name);
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);
114 if (box)
115 emailbox = urlxenc(box);
116 else if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
117 emailbox = urlxenc(mp->mb_imap_mailbox);
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 NYD_LEAVE;
127 return res;
130 static char *
131 encuid(struct mailbox *mp, unsigned long uid)
133 char buf[30], *cp;
134 NYD_ENTER;
136 snprintf(buf, sizeof buf, "%lu", uid);
137 cp = encname(mp, buf, 1, NULL);
138 NYD_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 NYD_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 (void)fcntl_lock(fileno(fp), F_RDLCK);
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 NYD_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 (void)fcntl_lock(fileno(obuf), F_WRLCK); /* XXX err hdl */
295 } else {
296 (void)fcntl_lock(fileno(obuf), F_WRLCK); /* 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 (fcntl_lock(fileno(uvfp), F_RDLCK), 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 fcntl_lock(fileno(uvfp), F_WRLCK);
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);
434 if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
435 emailbox = urlxenc(mp->mb_imap_mailbox);
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 fputs("Fatal: Cannot change back to current directory.\n", stderr);
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) {
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++]);
539 if (cwret(cw) == STOP) {
540 fputs("Fatal: Cannot change back to current directory.\n", stderr);
541 abort();
543 free(contents);
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(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 free(mb.mb_cache_directory);
600 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);
616 for (i = 0; i < msgCount; i++) {
617 message[i].m_uid = contents[i];
618 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
620 mb.mb_type = MB_CACHE;
621 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
622 if (transparent)
623 transflags(omessage, omsgCount, 1);
624 else
625 setdot(message);
626 rv = OKAY;
627 jleave:
628 NYD_LEAVE;
629 return rv;
632 FL enum okay
633 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
635 char *name, *cachedir, *eaccount;
636 DIR *dirp;
637 struct dirent *dp;
638 const char *cp, *bp, *sp;
639 int namesz;
640 enum okay rv = STOP;
641 NYD_ENTER;
643 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
644 (cachedir = file_expand(cachedir)) == NULL)
645 goto jleave;
646 eaccount = urlxenc(mp->mb_imap_account);
647 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
648 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
649 if ((dirp = opendir(name)) == NULL)
650 goto jleave;
651 while ((dp = readdir(dirp)) != NULL) {
652 if (dp->d_name[0] == '.')
653 continue;
654 cp = sp = urlxdec(dp->d_name);
655 for (bp = base; *bp && *bp == *sp; bp++)
656 sp++;
657 if (*bp)
658 continue;
659 cp = strip ? sp : cp;
660 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
662 closedir(dirp);
663 rv = OKAY;
664 jleave:
665 NYD_LEAVE;
666 return rv;
669 FL enum okay
670 cache_remove(const char *name)
672 struct stat st;
673 DIR *dirp;
674 struct dirent *dp;
675 char *path, *dir;
676 int pathsize, pathend, n;
677 enum okay rv = OKAY;
678 NYD_ENTER;
680 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
681 goto jleave;
682 pathend = strlen(dir);
683 path = smalloc(pathsize = pathend + 30);
684 memcpy(path, dir, pathend);
685 path[pathend++] = '/';
686 path[pathend] = '\0';
687 if ((dirp = opendir(path)) == NULL) {
688 free(path);
689 goto jleave;
691 while ((dp = readdir(dirp)) != NULL) {
692 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
693 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
694 continue;
695 n = strlen(dp->d_name) + 1;
696 if (pathend + n > pathsize)
697 path = srealloc(path, pathsize = pathend + n + 30);
698 memcpy(path + pathend, dp->d_name, n);
699 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
700 continue;
701 if (unlink(path) < 0) {
702 perror(path);
703 closedir(dirp);
704 free(path);
705 rv = STOP;
706 goto jleave;
709 closedir(dirp);
710 path[pathend] = '\0';
711 rmdir(path); /* no error on failure, might contain submailboxes */
712 free(path);
713 jleave:
714 NYD_LEAVE;
715 return rv;
718 FL enum okay
719 cache_rename(const char *old, const char *new)
721 char *olddir, *newdir;
722 enum okay rv = OKAY;
723 NYD_ENTER;
725 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
726 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
727 goto jleave;
728 if (rename(olddir, newdir) < 0) {
729 perror(olddir);
730 rv = STOP;
732 jleave:
733 NYD_LEAVE;
734 return rv;
737 FL unsigned long
738 cached_uidvalidity(struct mailbox *mp)
740 FILE *uvfp;
741 char *uvname;
742 unsigned long uv;
743 NYD_ENTER;
745 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
746 uv = 0;
747 goto jleave;
749 if ((uvfp = Fopen(uvname, "r")) == NULL ||
750 (fcntl_lock(fileno(uvfp), F_RDLCK), 0) ||
751 fscanf(uvfp, "%lu", &uv) != 1)
752 uv = 0;
753 if (uvfp != NULL)
754 Fclose(uvfp);
755 jleave:
756 NYD_LEAVE;
757 return uv;
760 static FILE *
761 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
763 char *name;
764 FILE *fp = NULL;
765 NYD_ENTER;
767 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
768 goto jleave;
769 if ((fp = Fopen(name, mode)) != NULL)
770 fcntl_lock(fileno(fp), F_WRLCK);
771 if (xname)
772 *xname = name;
773 jleave:
774 NYD_LEAVE;
775 return fp;
778 FL FILE *
779 cache_queue(struct mailbox *mp)
781 FILE *fp;
782 NYD_ENTER;
784 fp = cache_queue1(mp, "a", NULL);
785 if (fp == NULL)
786 fputs("Cannot queue IMAP command. Retry when online.\n", stderr);
787 NYD_LEAVE;
788 return fp;
791 FL enum okay
792 cache_dequeue(struct mailbox *mp)
794 int bufsz;
795 char *cachedir, *eaccount, *buf, *oldbox;
796 DIR *dirp;
797 struct dirent *dp;
798 enum okay rv = OKAY;
799 NYD_ENTER;
801 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
802 (cachedir = file_expand(cachedir)) == NULL)
803 goto jleave;
804 eaccount = urlxenc(mp->mb_imap_account);
805 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
806 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
807 if ((dirp = opendir(buf)) == NULL)
808 goto jleave;
809 oldbox = mp->mb_imap_mailbox;
810 while ((dp = readdir(dirp)) != NULL) {
811 if (dp->d_name[0] == '.')
812 continue;
813 mp->mb_imap_mailbox = urlxdec(dp->d_name);
814 dequeue1(mp);
816 closedir(dirp);
817 mp->mb_imap_mailbox = oldbox;
818 jleave:
819 NYD_LEAVE;
820 return rv;
823 static enum okay
824 dequeue1(struct mailbox *mp)
826 FILE *fp = NULL, *uvfp = NULL;
827 char *qname, *uvname;
828 unsigned long uv;
829 off_t is_size;
830 int is_count;
831 enum okay rv = OKAY;
832 NYD_ENTER;
834 fp = cache_queue1(mp, "r+", &qname);
835 if (fp != NULL && fsize(fp) > 0) {
836 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox) != OKAY) {
837 fprintf(stderr, "Cannot select \"%s\" for dequeuing.\n",
838 mp->mb_imap_mailbox);
839 goto jsave;
841 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
842 (uvfp = Fopen(uvname, "r")) == NULL ||
843 (fcntl_lock(fileno(uvfp), F_RDLCK), 0) ||
844 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
845 fprintf(stderr, "Unique identifiers for \"%s\" are out of date. "
846 "Cannot commit IMAP commands.\n", mp->mb_imap_mailbox);
847 jsave:
848 fputs("Saving IMAP commands to dead.letter\n", stderr);
849 savedeadletter(fp, 0);
850 ftruncate(fileno(fp), 0);
851 Fclose(fp);
852 if (uvfp)
853 Fclose(uvfp);
854 rv = STOP;
855 goto jleave;
857 Fclose(uvfp);
858 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
859 imap_dequeue(mp, fp);
861 if (fp) {
862 Fclose(fp);
863 unlink(qname);
865 jleave:
866 NYD_LEAVE;
867 return rv;
869 #endif /* HAVE_IMAP */
871 /* vim:set fenc=utf-8:s-it-mode */