setfile(): -# uses /dev/null: make this sole !ISREG exception..
[s-mailx.git] / imap_cache.c
blob06679582134fd44b5660216ba83a0e1d48d38a98
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
85 "mailx(1) will download the data again and will also\n\
86 write new cache entries if configured in this way. If you do not wish to use\n\
87 the cache anymore, delete the entire directory and unset the 'imap-cache'\n\
88 variable in " UAGENT "(1).\n";
89 static const char README5[] = "\n\
90 For more information about " UAGENT "(1), visit\n\
91 <http://sdaoden.users.sourceforge.net/code.html>.\n"; /* TODO MAGIC CONSTANT */
93 static char *
94 encname(struct mailbox *mp, const char *name, int same, const char *box)
96 char *cachedir, *eaccount, *ename, *res;
97 char const *emailbox;
98 int resz;
99 NYD_ENTER;
101 ename = urlxenc(name);
102 if (mp->mb_cache_directory && same && box == NULL) {
103 res = salloc(resz = strlen(mp->mb_cache_directory) + strlen(ename) + 2);
104 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
105 (*ename ? "/" : ""), ename);
106 } else {
107 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
108 (cachedir = file_expand(cachedir)) == NULL) {
109 res = NULL;
110 goto jleave;
112 eaccount = urlxenc(mp->mb_imap_account);
113 if (box)
114 emailbox = urlxenc(box);
115 else if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
116 emailbox = urlxenc(mp->mb_imap_mailbox);
117 else
118 emailbox = "INBOX";
119 res = salloc(resz = strlen(cachedir) + strlen(eaccount) +
120 strlen(emailbox) + strlen(ename) + 4);
121 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, emailbox,
122 (*ename ? "/" : ""), ename);
124 jleave:
125 NYD_LEAVE;
126 return res;
129 static char *
130 encuid(struct mailbox *mp, unsigned long uid)
132 char buf[30], *cp;
133 NYD_ENTER;
135 snprintf(buf, sizeof buf, "%lu", uid);
136 cp = encname(mp, buf, 1, NULL);
137 NYD_LEAVE;
138 return cp;
141 FL enum okay
142 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
143 int setflags)
145 FILE *fp;
146 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
147 int lastc = EOF, i, xflag, inheader = 1;
148 char b, iob[32768];
149 off_t offset;
150 void *zp;
151 enum okay rv = STOP;
152 NYD_ENTER;
154 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
155 m->m_uid == 0))
156 goto jleave;
157 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
158 goto jleave;
160 fcntl_lock(fileno(fp), FLOCK_READ);
161 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
162 (unsigned long*)&xtime, &xlines) < 4)
163 goto jfail;
164 if (need != NEED_UNSPEC) {
165 switch (b) {
166 case 'H':
167 if (need == NEED_HEADER)
168 goto jsuccess;
169 goto jfail;
170 case 'B':
171 if (need == NEED_HEADER || need == NEED_BODY)
172 goto jsuccess;
173 goto jfail;
174 default:
175 goto jfail;
178 jsuccess:
179 if (b == 'N')
180 goto jflags;
181 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
182 goto jfail;
183 zp = zalloc(fp);
184 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
185 zfree(zp);
186 goto jfail;
188 offset = ftell(mp->mb_otf);
189 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
190 size += n;
191 for (i = 0; i < n; i++) {
192 if (iob[i] == '\n') {
193 lines++;
194 if (lastc == '\n')
195 inheader = 0;
197 lastc = iob[i]&0377;
199 fwrite(iob, 1, n, mp->mb_otf);
201 if (n > 0 && need == NEED_BODY) {
202 while ((n = zread(zp, iob, sizeof iob)) > 0) {
203 size += n;
204 for (i = 0; i < n; i++)
205 if (iob[i] == '\n')
206 lines++;
207 fwrite(iob, 1, n, mp->mb_otf);
210 fflush(mp->mb_otf);
211 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
212 goto jfail;
214 m->m_size = size;
215 m->m_lines = lines;
216 m->m_block = mailx_blockof(offset);
217 m->m_offset = mailx_offsetof(offset);
218 jflags:
219 if (setflags) {
220 m->m_xsize = xsize;
221 m->m_time = xtime;
222 if (setflags & 2) {
223 m->m_flag = xflag | MNOFROM;
224 if (b != 'B')
225 m->m_flag |= MHIDDEN;
228 if (xlines > 0 && m->m_xlines <= 0)
229 m->m_xlines = xlines;
230 switch (b) {
231 case 'B':
232 m->m_xsize = xsize;
233 if (xflag == MREAD && xlines > 0)
234 m->m_flag |= MFULLYCACHED;
235 if (need == NEED_BODY) {
236 m->m_have |= HAVE_HEADER | HAVE_BODY;
237 if (m->m_lines > 0)
238 m->m_xlines = m->m_lines;
239 break;
241 /*FALLTHRU*/
242 case 'H':
243 m->m_have |= HAVE_HEADER;
244 break;
245 case 'N':
246 break;
248 rv = OKAY;
249 jfail:
250 Fclose(fp);
251 jleave:
252 NYD_LEAVE;
253 return rv;
256 FL enum okay
257 getcache(struct mailbox *mp, struct message *m, enum needspec need)
259 enum okay rv;
260 NYD_ENTER;
262 rv = getcache1(mp, m, need, 0);
263 NYD_LEAVE;
264 return rv;
267 FL void
268 putcache(struct mailbox *mp, struct message *m)
270 char iob[32768], *name, ob;
271 FILE *ibuf, *obuf;
272 int c, oflag;
273 long n, cnt, oldoffset, osize, otime, olines = -1;
274 void *zp;
275 NYD_ENTER;
277 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
278 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
279 goto jleave;
280 if (m->m_have & HAVE_BODY)
281 c = 'B';
282 else if (m->m_have & HAVE_HEADER)
283 c = 'H';
284 else if (m->m_have == HAVE_NOTHING)
285 c = 'N';
286 else
287 goto jleave;
288 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
289 oldoffset = 0;
290 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
291 if ((obuf = Fopen(name, "w")) == NULL)
292 goto jleave;
293 fcntl_lock(fileno(obuf), FLOCK_WRITE); /* XXX err hdl */
294 } else {
295 fcntl_lock(fileno(obuf), FLOCK_READ); /* XXX err hdl */
296 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
297 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
298 (ob == 'B' || (ob == 'H' && c != 'B'))) {
299 if (m->m_xlines <= 0 && olines > 0)
300 m->m_xlines = olines;
301 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
302 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
303 (m->m_xlines > 0 && olines != m->m_xlines)) {
304 fflush(obuf);
305 rewind(obuf);
306 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
307 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
308 putc('\n', obuf);
310 Fclose(obuf);
311 goto jleave;
313 fflush(obuf);
314 rewind(obuf);
315 ftruncate(fileno(obuf), 0);
317 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
318 Fclose(obuf);
319 goto jleave;
321 if (c == 'N')
322 goto jdone;
323 fseek(obuf, INITSKIP, SEEK_SET);
324 zp = zalloc(obuf);
325 cnt = m->m_size;
326 while (cnt > 0) {
327 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
328 cnt -= n;
329 if ((size_t)n != fread(iob, 1, n, ibuf) ||
330 n != (long)zwrite(zp, iob, n)) {
331 unlink(name);
332 zfree(zp);
333 goto jout;
336 if (zfree(zp) < 0) {
337 unlink(name);
338 goto jout;
340 jdone:
341 rewind(obuf);
342 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
343 (unsigned long)m->m_time, m->m_xlines);
344 putc('\n', obuf);
345 if (ferror(obuf)) {
346 unlink(name);
347 goto jout;
349 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
350 m->m_flag |= MFULLYCACHED;
351 jout:
352 if (Fclose(obuf) != 0) {
353 m->m_flag &= ~MFULLYCACHED;
354 unlink(name);
356 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
357 jleave:
358 NYD_LEAVE;
361 FL void
362 initcache(struct mailbox *mp)
364 char *name, *uvname;
365 FILE *uvfp;
366 unsigned long uv;
367 struct cw cw;
368 NYD_ENTER;
370 if (mp->mb_cache_directory != NULL)
371 free(mp->mb_cache_directory);
372 mp->mb_cache_directory = NULL;
373 if ((name = encname(mp, "", 1, NULL)) == NULL)
374 goto jleave;
375 mp->mb_cache_directory = sstrdup(name);
376 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
377 goto jleave;
378 if (cwget(&cw) == STOP)
379 goto jleave;
380 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
381 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
382 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
383 if ((uvfp = clean(mp, &cw)) == NULL)
384 goto jout;
385 } else {
386 fflush(uvfp);
387 rewind(uvfp);
389 fcntl_lock(fileno(uvfp), FLOCK_WRITE);
390 fprintf(uvfp, "%lu\n", mp->mb_uidvalidity);
391 if (ferror(uvfp) || Fclose(uvfp) != 0) {
392 unlink(uvname);
393 mp->mb_uidvalidity = 0;
395 jout:
396 cwrelse(&cw);
397 jleave:
398 NYD_LEAVE;
401 FL void
402 purgecache(struct mailbox *mp, struct message *m, long mc)
404 char *name;
405 struct cw cw;
406 NYD_ENTER;
408 if ((name = encname(mp, "", 1, NULL)) == NULL)
409 goto jleave;
410 if (cwget(&cw) == STOP)
411 goto jleave;
412 purge(mp, m, mc, &cw, name);
413 cwrelse(&cw);
414 jleave:
415 NYD_LEAVE;
418 static FILE *
419 clean(struct mailbox *mp, struct cw *cw)
421 char *cachedir, *eaccount, *buf;
422 char const *emailbox;
423 int bufsz;
424 DIR *dirp;
425 struct dirent *dp;
426 FILE *fp = NULL;
427 NYD_ENTER;
429 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
430 (cachedir = file_expand(cachedir)) == NULL)
431 goto jleave;
432 eaccount = urlxenc(mp->mb_imap_account);
433 if (asccasecmp(mp->mb_imap_mailbox, "INBOX"))
434 emailbox = urlxenc(mp->mb_imap_mailbox);
435 else
436 emailbox = "INBOX";
437 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
438 strlen(emailbox) + 40);
439 if (makedir(cachedir) != OKAY)
440 goto jleave;
441 snprintf(buf, bufsz, "%s/README", cachedir);
442 if ((fp = Fopen(buf, "wx")) != NULL) {
443 fputs(README1, fp);
444 fputs(README2, fp);
445 fputs(README3, fp);
446 fputs(README4, fp);
447 fputs(README5, fp);
448 Fclose(fp);
450 fp = NULL;
451 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
452 if (makedir(buf) != OKAY)
453 goto jleave;
454 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
455 if (makedir(buf) != OKAY)
456 goto jleave;
457 if (chdir(buf) < 0)
458 goto jleave;
459 if ((dirp = opendir(".")) == NULL)
460 goto jout;
461 while ((dp = readdir(dirp)) != NULL) {
462 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
463 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
464 continue;
465 unlink(dp->d_name);
467 closedir(dirp);
468 fp = Fopen("UIDVALIDITY", "w");
469 jout:
470 if (cwret(cw) == STOP) {
471 fputs("Fatal: Cannot change back to current directory.\n", stderr);
472 abort();
474 jleave:
475 NYD_LEAVE;
476 return fp;
479 static unsigned long *
480 builds(long *contentelem)
482 unsigned long n, *contents = NULL;
483 long contentalloc = 0;
484 char *x;
485 DIR *dirp;
486 struct dirent *dp;
487 NYD_ENTER;
489 *contentelem = 0;
490 if ((dirp = opendir(".")) == NULL)
491 goto jleave;
492 while ((dp = readdir(dirp)) != NULL) {
493 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
494 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
495 continue;
496 n = strtoul(dp->d_name, &x, 10);
497 if (*x != '\0')
498 continue;
499 if (*contentelem >= contentalloc - 1)
500 contents = srealloc(contents,
501 (contentalloc += 200) * sizeof *contents);
502 contents[(*contentelem)++] = n;
504 closedir(dirp);
505 if (*contentelem > 0) {
506 contents[*contentelem] = 0;
507 qsort(contents, *contentelem, sizeof *contents, longlt);
509 jleave:
510 NYD_LEAVE;
511 return contents;
514 static void
515 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
516 const char *name)
518 unsigned long *contents;
519 long i, j, contentelem;
520 NYD_ENTER;
521 UNUSED(mp);
523 if (chdir(name) < 0)
524 goto jleave;
525 contents = builds(&contentelem);
526 if (contents) {
527 i = j = 0;
528 while (j < contentelem) {
529 if (i < mc && m[i].m_uid == contents[j]) {
530 i++;
531 j++;
532 } else if (i < mc && m[i].m_uid < contents[j])
533 i++;
534 else
535 remve(contents[j++]);
538 if (cwret(cw) == STOP) {
539 fputs("Fatal: Cannot change back to current directory.\n", stderr);
540 abort();
542 free(contents);
543 jleave:
544 NYD_LEAVE;
547 static int
548 longlt(const void *a, const void *b)
550 union {long l; int i;} u;
551 NYD_ENTER;
553 u.l = *(long const*)a - *(long const*)b;
554 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
555 NYD_LEAVE;
556 return u.i;
559 static void
560 remve(unsigned long n)
562 char buf[30];
563 NYD_ENTER;
565 snprintf(buf, sizeof buf, "%lu", n);
566 unlink(buf);
567 NYD_LEAVE;
570 FL void
571 delcache(struct mailbox *mp, struct message *m)
573 char *fn;
574 NYD_ENTER;
576 fn = encuid(mp, m->m_uid);
577 if (fn && unlink(fn) == 0)
578 m->m_flag |= MUNLINKED;
579 NYD_LEAVE;
582 FL enum okay
583 cache_setptr(int transparent)
585 struct cw cw;
586 int i, omsgCount = 0;
587 char *name;
588 unsigned long *contents;
589 long contentelem;
590 struct message *omessage = NULL;
591 enum okay rv = STOP;
592 NYD_ENTER;
594 if (transparent) {
595 omessage = message;
596 omsgCount = msgCount;
598 free(mb.mb_cache_directory);
599 mb.mb_cache_directory = NULL;
600 if ((name = encname(&mb, "", 1, NULL)) == NULL)
601 goto jleave;
602 mb.mb_cache_directory = sstrdup(name);
603 if (cwget(&cw) == STOP)
604 goto jleave;
605 if (chdir(name) < 0)
606 goto jleave;
607 contents = builds(&contentelem);
608 msgCount = contentelem;
609 message = scalloc(msgCount + 1, sizeof *message);
610 if (cwret(&cw) == STOP) {
611 fputs("Fatal: Cannot change back to current directory.\n", stderr);
612 abort();
614 cwrelse(&cw);
615 for (i = 0; i < msgCount; i++) {
616 message[i].m_uid = contents[i];
617 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
619 mb.mb_type = MB_CACHE;
620 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
621 if (transparent)
622 transflags(omessage, omsgCount, 1);
623 else
624 setdot(message);
625 rv = OKAY;
626 jleave:
627 NYD_LEAVE;
628 return rv;
631 FL enum okay
632 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
634 char *name, *cachedir, *eaccount;
635 DIR *dirp;
636 struct dirent *dp;
637 const char *cp, *bp, *sp;
638 int namesz;
639 enum okay rv = STOP;
640 NYD_ENTER;
642 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
643 (cachedir = file_expand(cachedir)) == NULL)
644 goto jleave;
645 eaccount = urlxenc(mp->mb_imap_account);
646 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
647 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
648 if ((dirp = opendir(name)) == NULL)
649 goto jleave;
650 while ((dp = readdir(dirp)) != NULL) {
651 if (dp->d_name[0] == '.')
652 continue;
653 cp = sp = urlxdec(dp->d_name);
654 for (bp = base; *bp && *bp == *sp; bp++)
655 sp++;
656 if (*bp)
657 continue;
658 cp = strip ? sp : cp;
659 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
661 closedir(dirp);
662 rv = OKAY;
663 jleave:
664 NYD_LEAVE;
665 return rv;
668 FL enum okay
669 cache_remove(const char *name)
671 struct stat st;
672 DIR *dirp;
673 struct dirent *dp;
674 char *path, *dir;
675 int pathsize, pathend, n;
676 enum okay rv = OKAY;
677 NYD_ENTER;
679 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
680 goto jleave;
681 pathend = strlen(dir);
682 path = smalloc(pathsize = pathend + 30);
683 memcpy(path, dir, pathend);
684 path[pathend++] = '/';
685 path[pathend] = '\0';
686 if ((dirp = opendir(path)) == NULL) {
687 free(path);
688 goto jleave;
690 while ((dp = readdir(dirp)) != NULL) {
691 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
692 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
693 continue;
694 n = strlen(dp->d_name) + 1;
695 if (pathend + n > pathsize)
696 path = srealloc(path, pathsize = pathend + n + 30);
697 memcpy(path + pathend, dp->d_name, n);
698 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
699 continue;
700 if (unlink(path) < 0) {
701 perror(path);
702 closedir(dirp);
703 free(path);
704 rv = STOP;
705 goto jleave;
708 closedir(dirp);
709 path[pathend] = '\0';
710 rmdir(path); /* no error on failure, might contain submailboxes */
711 free(path);
712 jleave:
713 NYD_LEAVE;
714 return rv;
717 FL enum okay
718 cache_rename(const char *old, const char *new)
720 char *olddir, *newdir;
721 enum okay rv = OKAY;
722 NYD_ENTER;
724 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
725 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
726 goto jleave;
727 if (rename(olddir, newdir) < 0) {
728 perror(olddir);
729 rv = STOP;
731 jleave:
732 NYD_LEAVE;
733 return rv;
736 FL unsigned long
737 cached_uidvalidity(struct mailbox *mp)
739 FILE *uvfp;
740 char *uvname;
741 unsigned long uv;
742 NYD_ENTER;
744 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
745 uv = 0;
746 goto jleave;
748 if ((uvfp = Fopen(uvname, "r")) == NULL ||
749 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
750 fscanf(uvfp, "%lu", &uv) != 1)
751 uv = 0;
752 if (uvfp != NULL)
753 Fclose(uvfp);
754 jleave:
755 NYD_LEAVE;
756 return uv;
759 static FILE *
760 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
762 char *name;
763 FILE *fp = NULL;
764 NYD_ENTER;
766 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
767 goto jleave;
768 if ((fp = Fopen(name, mode)) != NULL)
769 fcntl_lock(fileno(fp), FLOCK_WRITE);
770 if (xname)
771 *xname = name;
772 jleave:
773 NYD_LEAVE;
774 return fp;
777 FL FILE *
778 cache_queue(struct mailbox *mp)
780 FILE *fp;
781 NYD_ENTER;
783 fp = cache_queue1(mp, "a", NULL);
784 if (fp == NULL)
785 fputs("Cannot queue IMAP command. Retry when online.\n", stderr);
786 NYD_LEAVE;
787 return fp;
790 FL enum okay
791 cache_dequeue(struct mailbox *mp)
793 int bufsz;
794 char *cachedir, *eaccount, *buf, *oldbox;
795 DIR *dirp;
796 struct dirent *dp;
797 enum okay rv = OKAY;
798 NYD_ENTER;
800 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
801 (cachedir = file_expand(cachedir)) == NULL)
802 goto jleave;
803 eaccount = urlxenc(mp->mb_imap_account);
804 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
805 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
806 if ((dirp = opendir(buf)) == NULL)
807 goto jleave;
808 oldbox = mp->mb_imap_mailbox;
809 while ((dp = readdir(dirp)) != NULL) {
810 if (dp->d_name[0] == '.')
811 continue;
812 mp->mb_imap_mailbox = urlxdec(dp->d_name);
813 dequeue1(mp);
815 closedir(dirp);
816 mp->mb_imap_mailbox = oldbox;
817 jleave:
818 NYD_LEAVE;
819 return rv;
822 static enum okay
823 dequeue1(struct mailbox *mp)
825 FILE *fp = NULL, *uvfp = NULL;
826 char *qname, *uvname;
827 unsigned long uv;
828 off_t is_size;
829 int is_count;
830 enum okay rv = OKAY;
831 NYD_ENTER;
833 fp = cache_queue1(mp, "r+", &qname);
834 if (fp != NULL && fsize(fp) > 0) {
835 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox) != OKAY) {
836 fprintf(stderr, "Cannot select \"%s\" for dequeuing.\n",
837 mp->mb_imap_mailbox);
838 goto jsave;
840 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
841 (uvfp = Fopen(uvname, "r")) == NULL ||
842 (fcntl_lock(fileno(uvfp), FLOCK_READ), 0) ||
843 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
844 fprintf(stderr, "Unique identifiers for \"%s\" are out of date. "
845 "Cannot commit IMAP commands.\n", mp->mb_imap_mailbox);
846 jsave:
847 fputs("Saving IMAP commands to dead.letter\n", stderr);
848 savedeadletter(fp, 0);
849 ftruncate(fileno(fp), 0);
850 Fclose(fp);
851 if (uvfp)
852 Fclose(uvfp);
853 rv = STOP;
854 goto jleave;
856 Fclose(uvfp);
857 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
858 imap_dequeue(mp, fp);
860 if (fp) {
861 Fclose(fp);
862 unlink(qname);
864 jleave:
865 NYD_LEAVE;
866 return rv;
868 #endif /* HAVE_IMAP */
870 /* vim:set fenc=utf-8:s-it-mode */