make-release.txt: new signing OpenPGP subkey
[s-mailx.git] / obs-imap-cache.c
blobb4391314486db9d1ebe165b9bd816d7ebf34d222
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 - 2018 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 obs_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, ui64_t uid);
53 static FILE * clean(struct mailbox *mp, struct cw *cw);
54 static ui64_t * 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 " VAL_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=FILE 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; " VAL_UAGENT
87 " will download the data again and will also\n\
88 write new cache entries if configured in this way. If you do not wish to use\n\
89 the cache anymore, delete the entire directory and unset the *imap-cache*\n\
90 variable in " VAL_UAGENT "(1).\n";
92 static char *
93 encname(struct mailbox *mp, const char *name, int same, const char *box)
95 char *cachedir, *eaccount, *ename, *res;
96 int resz;
97 NYD2_ENTER;
99 ename = urlxenc(name, TRU1);
100 if (mp->mb_cache_directory && same && box == NULL) {
101 res = n_autorec_alloc(resz = strlen(mp->mb_cache_directory) +
102 strlen(ename) + 2);
103 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
104 (*ename ? "/" : ""), ename);
105 } else {
106 res = NULL;
108 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
109 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
110 goto jleave;
111 eaccount = urlxenc(mp->mb_imap_account, TRU1);
113 if (box != NULL || asccasecmp(box = mp->mb_imap_mailbox, "INBOX")) {
114 bool_t err;
116 box = imap_path_encode(box, &err);
117 if(err)
118 goto jleave;
119 box = urlxenc(box, TRU1);
120 } else
121 box = "INBOX";
123 res = n_autorec_alloc(resz = strlen(cachedir) + strlen(eaccount) +
124 strlen(box) + strlen(ename) + 4);
125 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, box,
126 (*ename ? "/" : ""), ename);
128 jleave:
129 NYD2_LEAVE;
130 return res;
133 static char *
134 encuid(struct mailbox *mp, ui64_t uid)
136 char buf[64], *cp;
137 NYD2_ENTER;
139 snprintf(buf, sizeof buf, "%" PRIu64, uid);
140 cp = encname(mp, buf, 1, NULL);
141 NYD2_LEAVE;
142 return cp;
145 FL enum okay
146 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
147 int setflags)
149 FILE *fp;
150 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
151 int lastc = EOF, i, xflag, inheader = 1;
152 char b, iob[32768];
153 off_t offset;
154 void *zp;
155 enum okay rv = STOP;
156 NYD2_ENTER;
158 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
159 m->m_uid == 0))
160 goto jleave;
161 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
162 goto jleave;
164 n_file_lock(fileno(fp), FLT_READ, 0,0, 0);
165 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
166 (unsigned long*)&xtime, &xlines) < 4)
167 goto jfail;
168 if (need != NEED_UNSPEC) {
169 switch (b) {
170 case 'H':
171 if (need == NEED_HEADER)
172 goto jsuccess;
173 goto jfail;
174 case 'B':
175 if (need == NEED_HEADER || need == NEED_BODY)
176 goto jsuccess;
177 goto jfail;
178 default:
179 goto jfail;
182 jsuccess:
183 if (b == 'N')
184 goto jflags;
185 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
186 goto jfail;
187 zp = zalloc(fp);
188 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
189 zfree(zp);
190 goto jfail;
192 offset = ftell(mp->mb_otf);
193 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
194 size += n;
195 for (i = 0; i < n; i++) {
196 if (iob[i] == '\n') {
197 lines++;
198 if (lastc == '\n')
199 inheader = 0;
201 lastc = iob[i]&0377;
203 fwrite(iob, 1, n, mp->mb_otf);
205 if (n > 0 && need == NEED_BODY) {
206 while ((n = zread(zp, iob, sizeof iob)) > 0) {
207 size += n;
208 for (i = 0; i < n; i++)
209 if (iob[i] == '\n')
210 lines++;
211 fwrite(iob, 1, n, mp->mb_otf);
214 fflush(mp->mb_otf);
215 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
216 goto jfail;
218 m->m_size = size;
219 m->m_lines = lines;
220 m->m_block = mailx_blockof(offset);
221 m->m_offset = mailx_offsetof(offset);
222 jflags:
223 if (setflags) {
224 m->m_xsize = xsize;
225 m->m_time = xtime;
226 if (setflags & 2) {
227 m->m_flag = xflag | MNOFROM;
228 if (b != 'B')
229 m->m_flag |= MHIDDEN;
232 if (xlines > 0 && m->m_xlines <= 0)
233 m->m_xlines = xlines;
234 switch (b) {
235 case 'B':
236 m->m_xsize = xsize;
237 if (xflag == MREAD && xlines > 0)
238 m->m_flag |= MFULLYCACHED;
239 if (need == NEED_BODY) {
240 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
241 if (m->m_lines > 0)
242 m->m_xlines = m->m_lines;
243 break;
245 /*FALLTHRU*/
246 case 'H':
247 m->m_content_info |= CI_HAVE_HEADER;
248 break;
249 case 'N':
250 break;
252 rv = OKAY;
253 jfail:
254 Fclose(fp);
255 jleave:
256 NYD2_LEAVE;
257 return rv;
260 FL enum okay
261 getcache(struct mailbox *mp, struct message *m, enum needspec need)
263 enum okay rv;
264 NYD_ENTER;
266 rv = getcache1(mp, m, need, 0);
267 NYD_LEAVE;
268 return rv;
271 FL void
272 putcache(struct mailbox *mp, struct message *m)
274 char iob[32768], *name, ob;
275 FILE *ibuf, *obuf;
276 int c, oflag;
277 long n, cnt, oldoffset, osize, otime, olines = -1;
278 void *zp;
279 NYD_ENTER;
281 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
282 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
283 goto jleave;
284 if (m->m_content_info & CI_HAVE_BODY)
285 c = 'B';
286 else if (m->m_content_info & CI_HAVE_HEADER)
287 c = 'H';
288 else if (!(m->m_content_info & CI_HAVE_MASK))
289 c = 'N';
290 else
291 goto jleave;
292 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
293 oldoffset = 0;
294 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
295 if ((obuf = Fopen(name, "w")) == NULL)
296 goto jleave;
297 n_file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
298 } else {
299 n_file_lock(fileno(obuf), FLT_READ, 0,0, 0); /* XXX err hdl */
300 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
301 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
302 (ob == 'B' || (ob == 'H' && c != 'B'))) {
303 if (m->m_xlines <= 0 && olines > 0)
304 m->m_xlines = olines;
305 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
306 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
307 (m->m_xlines > 0 && olines != m->m_xlines)) {
308 fflush(obuf);
309 rewind(obuf);
310 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
311 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
312 putc('\n', obuf);
314 Fclose(obuf);
315 goto jleave;
317 fflush(obuf);
318 rewind(obuf);
319 ftruncate(fileno(obuf), 0);
321 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
322 Fclose(obuf);
323 goto jleave;
325 if (c == 'N')
326 goto jdone;
327 fseek(obuf, INITSKIP, SEEK_SET);
328 zp = zalloc(obuf);
329 cnt = m->m_size;
330 while (cnt > 0) {
331 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
332 cnt -= n;
333 if ((size_t)n != fread(iob, 1, n, ibuf) ||
334 n != (long)zwrite(zp, iob, n)) {
335 unlink(name);
336 zfree(zp);
337 goto jout;
340 if (zfree(zp) < 0) {
341 unlink(name);
342 goto jout;
344 jdone:
345 rewind(obuf);
346 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
347 (unsigned long)m->m_time, m->m_xlines);
348 putc('\n', obuf);
349 if (ferror(obuf)) {
350 unlink(name);
351 goto jout;
353 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
354 m->m_flag |= MFULLYCACHED;
355 jout:
356 if (Fclose(obuf) != 0) {
357 m->m_flag &= ~MFULLYCACHED;
358 unlink(name);
360 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
361 jleave:
362 NYD_LEAVE;
365 FL void
366 initcache(struct mailbox *mp)
368 char *name, *uvname;
369 FILE *uvfp;
370 ui64_t uv;
371 struct cw cw;
372 NYD_ENTER;
374 if (mp->mb_cache_directory != NULL)
375 n_free(mp->mb_cache_directory);
376 mp->mb_cache_directory = NULL;
377 if ((name = encname(mp, "", 1, NULL)) == NULL)
378 goto jleave;
379 mp->mb_cache_directory = sstrdup(name);
380 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
381 goto jleave;
382 if (cwget(&cw) == STOP)
383 goto jleave;
384 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
385 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
386 fscanf(uvfp, "%" PRIu64 , &uv) != 1 || uv != mp->mb_uidvalidity) {
387 if ((uvfp = clean(mp, &cw)) == NULL)
388 goto jout;
389 } else {
390 fflush(uvfp);
391 rewind(uvfp);
393 n_file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
394 fprintf(uvfp, "%" PRIu64 "\n", mp->mb_uidvalidity);
395 if (ferror(uvfp) || Fclose(uvfp) != 0) {
396 unlink(uvname);
397 mp->mb_uidvalidity = 0;
399 jout:
400 cwrelse(&cw);
401 jleave:
402 NYD_LEAVE;
405 FL void
406 purgecache(struct mailbox *mp, struct message *m, long mc)
408 char *name;
409 struct cw cw;
410 NYD_ENTER;
412 if ((name = encname(mp, "", 1, NULL)) == NULL)
413 goto jleave;
414 if (cwget(&cw) == STOP)
415 goto jleave;
416 purge(mp, m, mc, &cw, name);
417 cwrelse(&cw);
418 jleave:
419 NYD_LEAVE;
422 static FILE *
423 clean(struct mailbox *mp, struct cw *cw)
425 char *cachedir, *eaccount, *buf;
426 char const *emailbox;
427 int bufsz;
428 DIR *dirp;
429 struct dirent *dp;
430 FILE *fp = NULL;
431 NYD_ENTER;
433 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
434 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
435 goto jleave;
436 eaccount = urlxenc(mp->mb_imap_account, TRU1);
437 if (asccasecmp(emailbox = mp->mb_imap_mailbox, "INBOX")) {
438 bool_t err;
440 emailbox = imap_path_encode(emailbox, &err);
441 if(err)
442 goto jleave;
443 emailbox = urlxenc(emailbox, TRU1);
445 buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) +
446 strlen(emailbox) + 40);
447 if (!n_path_mkdir(cachedir))
448 goto jleave;
449 snprintf(buf, bufsz, "%s/README", cachedir);
450 if ((fp = Fopen(buf, "wx")) != NULL) {
451 fputs(README1, fp);
452 fputs(README2, fp);
453 fputs(README3, fp);
454 fputs(README4, fp);
455 Fclose(fp);
457 fp = NULL;
458 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
459 if (!n_path_mkdir(buf))
460 goto jleave;
461 if (chdir(buf) < 0)
462 goto jleave;
463 if ((dirp = opendir(".")) == NULL)
464 goto jout;
465 while ((dp = readdir(dirp)) != NULL) {
466 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
467 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
468 continue;
469 unlink(dp->d_name);
471 closedir(dirp);
472 fp = Fopen("UIDVALIDITY", "w");
473 jout:
474 if (cwret(cw) == STOP) {
475 n_err(_("Fatal: Cannot change back to current directory.\n"));
476 abort();
478 jleave:
479 NYD_LEAVE;
480 return fp;
483 static ui64_t *
484 builds(long *contentelem)
486 ui64_t n, *contents = NULL;
487 long contentalloc = 0;
488 char const *x;
489 DIR *dirp;
490 struct dirent *dp;
491 NYD_ENTER;
493 *contentelem = 0;
494 if ((dirp = opendir(".")) == NULL)
495 goto jleave;
496 while ((dp = readdir(dirp)) != NULL) {
497 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
498 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
499 continue;
501 n_idec_ui64_cp(&n, dp->d_name, 10, &x);/* TODO errors? */
502 if (*x != '\0')
503 continue;
504 if (*contentelem >= contentalloc - 1)
505 contents = n_realloc(contents,
506 (contentalloc += 200) * sizeof *contents);
507 contents[(*contentelem)++] = n;
509 closedir(dirp);
510 if (*contentelem > 0) {
511 contents[*contentelem] = 0;
512 qsort(contents, *contentelem, sizeof *contents, longlt);
514 jleave:
515 NYD_LEAVE;
516 return contents;
519 static void
520 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
521 const char *name)
523 ui64_t *contents;
524 long i, j, contentelem;
525 NYD_ENTER;
526 n_UNUSED(mp);
528 if (chdir(name) < 0)
529 goto jleave;
530 contents = builds(&contentelem);
531 if (contents != NULL) {
532 i = j = 0;
533 while (j < contentelem) {
534 if (i < mc && m[i].m_uid == contents[j]) {
535 i++;
536 j++;
537 } else if (i < mc && m[i].m_uid < contents[j])
538 i++;
539 else
540 remve(contents[j++]);
542 n_free(contents);
544 if (cwret(cw) == STOP) {
545 n_err(_("Fatal: Cannot change back to current directory.\n"));
546 abort();
548 jleave:
549 NYD_LEAVE;
552 static int
553 longlt(const void *a, const void *b)
555 union {long l; int i;} u;
556 NYD_ENTER;
558 u.l = *(long const*)a - *(long const*)b;
559 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
560 NYD_LEAVE;
561 return u.i;
564 static void
565 remve(unsigned long n)
567 char buf[30];
568 NYD_ENTER;
570 snprintf(buf, sizeof buf, "%lu", n);
571 unlink(buf);
572 NYD_LEAVE;
575 FL void
576 delcache(struct mailbox *mp, struct message *m)
578 char *fn;
579 NYD_ENTER;
581 fn = encuid(mp, m->m_uid);
582 if (fn && unlink(fn) == 0)
583 m->m_flag |= MUNLINKED;
584 NYD_LEAVE;
587 FL enum okay
588 cache_setptr(enum fedit_mode fm, int transparent)
590 struct cw cw;
591 int i, omsgCount = 0;
592 char *name;
593 ui64_t *contents;
594 long contentelem;
595 struct message *omessage;
596 enum okay rv = STOP;
597 NYD_ENTER;
599 omessage = message;
600 omsgCount = msgCount;
602 if (mb.mb_cache_directory != NULL) {
603 n_free(mb.mb_cache_directory);
604 mb.mb_cache_directory = NULL;
606 if ((name = encname(&mb, "", 1, NULL)) == NULL)
607 goto jleave;
608 mb.mb_cache_directory = sstrdup(name);
609 if (cwget(&cw) == STOP)
610 goto jleave;
611 if (chdir(name) < 0)
612 goto jleave;
613 contents = builds(&contentelem);
614 msgCount = contentelem;
615 message = n_calloc(msgCount + 1, sizeof *message);
616 if (cwret(&cw) == STOP) {
617 n_err(_("Fatal: Cannot change back to current directory.\n"));
618 abort();
620 cwrelse(&cw);
622 srelax_hold();
623 for (i = 0; i < msgCount; i++) {
624 message[i].m_uid = contents[i];
625 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
626 srelax();
628 srelax_rele();
630 if (contents != NULL)
631 n_free(contents);
632 mb.mb_type = MB_CACHE;
633 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
634 ) ? 0 : MB_DELE;
635 if(omessage != NULL){
636 if(transparent)
637 /* This frees the message */
638 transflags(omessage, omsgCount, 1);
639 else
640 n_free(omessage);
642 setdot(message);
643 rv = OKAY;
644 jleave:
645 NYD_LEAVE;
646 return rv;
649 FL enum okay
650 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
652 char *name, *cachedir, *eaccount;
653 DIR *dirp;
654 struct dirent *dp;
655 const char *cp, *bp, *sp;
656 int namesz;
657 enum okay rv = STOP;
658 NYD_ENTER;
660 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
661 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
662 goto jleave;
663 eaccount = urlxenc(mp->mb_imap_account, TRU1);
664 name = n_autorec_alloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
665 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
666 if ((dirp = opendir(name)) == NULL)
667 goto jleave;
668 while ((dp = readdir(dirp)) != NULL) {
669 if (dp->d_name[0] == '.')
670 continue;
671 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
672 for (bp = base; *bp && *bp == *sp; bp++)
673 sp++;
674 if (*bp)
675 continue;
676 cp = strip ? sp : cp;
677 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
679 closedir(dirp);
680 rv = OKAY;
681 jleave:
682 NYD_LEAVE;
683 return rv;
686 FL enum okay
687 cache_remove(const char *name)
689 struct stat st;
690 DIR *dirp;
691 struct dirent *dp;
692 char *path, *dir;
693 int pathsize, pathend, n;
694 enum okay rv = OKAY;
695 NYD_ENTER;
697 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
698 goto jleave;
699 pathend = strlen(dir);
700 path = n_alloc(pathsize = pathend + 30);
701 memcpy(path, dir, pathend);
702 path[pathend++] = '/';
703 path[pathend] = '\0';
704 if ((dirp = opendir(path)) == NULL) {
705 n_free(path);
706 goto jleave;
708 while ((dp = readdir(dirp)) != NULL) {
709 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
710 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
711 continue;
712 n = strlen(dp->d_name) + 1;
713 if (pathend + n > pathsize)
714 path = n_realloc(path, pathsize = pathend + n + 30);
715 memcpy(path + pathend, dp->d_name, n);
716 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
717 continue;
718 if (unlink(path) < 0) {
719 n_perr(path, 0);
720 closedir(dirp);
721 n_free(path);
722 rv = STOP;
723 goto jleave;
726 closedir(dirp);
727 path[pathend] = '\0';
728 rmdir(path); /* no error on failure, might contain submailboxes */
729 n_free(path);
730 jleave:
731 NYD_LEAVE;
732 return rv;
735 FL enum okay
736 cache_rename(const char *old, const char *new)
738 char *olddir, *newdir;
739 enum okay rv = OKAY;
740 NYD_ENTER;
742 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
743 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
744 goto jleave;
745 if (rename(olddir, newdir) < 0) {
746 n_perr(olddir, 0);
747 rv = STOP;
749 jleave:
750 NYD_LEAVE;
751 return rv;
754 FL ui64_t
755 cached_uidvalidity(struct mailbox *mp)
757 FILE *uvfp;
758 char *uvname;
759 ui64_t uv;
760 NYD_ENTER;
762 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
763 uv = 0;
764 goto jleave;
766 if ((uvfp = Fopen(uvname, "r")) == NULL ||
767 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
768 fscanf(uvfp, "%" PRIu64, &uv) != 1)
769 uv = 0;
770 if (uvfp != NULL)
771 Fclose(uvfp);
772 jleave:
773 NYD_LEAVE;
774 return uv;
777 static FILE *
778 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
780 char *name;
781 FILE *fp = NULL;
782 NYD_ENTER;
784 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
785 goto jleave;
786 if ((fp = Fopen(name, mode)) != NULL)
787 n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
788 if (xname)
789 *xname = name;
790 jleave:
791 NYD_LEAVE;
792 return fp;
795 FL FILE *
796 cache_queue(struct mailbox *mp)
798 FILE *fp;
799 NYD_ENTER;
801 fp = cache_queue1(mp, "a", NULL);
802 if (fp == NULL)
803 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
804 NYD_LEAVE;
805 return fp;
808 FL enum okay
809 cache_dequeue(struct mailbox *mp)
811 int bufsz;
812 char *cachedir, *eaccount, *buf, *oldbox;
813 DIR *dirp;
814 struct dirent *dp;
815 enum okay rv = OKAY;
816 NYD_ENTER;
818 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
819 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
820 goto jleave;
821 eaccount = urlxenc(mp->mb_imap_account, TRU1);
822 buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
823 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
824 if ((dirp = opendir(buf)) == NULL)
825 goto jleave;
826 oldbox = mp->mb_imap_mailbox;
827 while ((dp = readdir(dirp)) != NULL) {
828 if (dp->d_name[0] == '.')
829 continue;
830 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
831 * (but wuuuuh, what a shit!) */
832 mp->mb_imap_mailbox = sstrdup(
833 imap_path_decode(urlxdec(dp->d_name), NULL));
834 dequeue1(mp);
835 { char *x = mp->mb_imap_mailbox;
836 mp->mb_imap_mailbox = oldbox;
837 n_free(x);
840 closedir(dirp);
841 jleave:
842 NYD_LEAVE;
843 return rv;
846 static enum okay
847 dequeue1(struct mailbox *mp)
849 FILE *fp = NULL, *uvfp = NULL;
850 char *qname, *uvname;
851 ui64_t uv;
852 off_t is_size;
853 int is_count;
854 enum okay rv = OKAY;
855 NYD_ENTER;
857 fp = cache_queue1(mp, "r+", &qname);
858 if (fp != NULL && fsize(fp) > 0) {
859 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
860 != OKAY) {
861 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
862 goto jsave;
864 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
865 (uvfp = Fopen(uvname, "r")) == NULL ||
866 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
867 fscanf(uvfp, "%" PRIu64, &uv) != 1 || uv != mp->mb_uidvalidity) {
868 n_err(_("Unique identifiers for \"%s\" are out of date. "
869 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
870 jsave:
871 n_err(_("Saving IMAP commands to *DEAD*\n"));
872 savedeadletter(fp, 0);
873 ftruncate(fileno(fp), 0);
874 Fclose(fp);
875 if (uvfp)
876 Fclose(uvfp);
877 rv = STOP;
878 goto jleave;
880 Fclose(uvfp);
881 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
882 imap_dequeue(mp, fp);
884 if (fp) {
885 Fclose(fp);
886 unlink(qname);
888 jleave:
889 NYD_LEAVE;
890 return rv;
892 #endif /* HAVE_IMAP */
894 /* s-it-mode */