make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / obs-imap-cache.c
bloba8f2aa8d2ea6718bfa5b5ef79b68b61b6151e690
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 * SPDX-License-Identifier: BSD-4-Clause
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE obs_imap_cache
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 EMPTY_FILE()
48 #ifdef HAVE_IMAP
49 # include <dirent.h>
51 static char * encname(struct mailbox *mp, const char *name, int same,
52 const char *box);
53 static char * encuid(struct mailbox *mp, ui64_t uid);
54 static FILE * clean(struct mailbox *mp, struct cw *cw);
55 static ui64_t * builds(long *contentelem);
56 static void purge(struct mailbox *mp, struct message *m, long mc,
57 struct cw *cw, const char *name);
58 static int longlt(const void *a, const void *b);
59 static void remve(unsigned long n);
60 static FILE * cache_queue1(struct mailbox *mp, char const *mode,
61 char **xname);
62 static enum okay dequeue1(struct mailbox *mp);
64 static const char infofmt[] = "%c %lu %d %lu %ld";
65 #define INITSKIP 128L
66 #define USEBITS(f) \
67 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
69 static const char README1[] = "\
70 This is a cache directory maintained by " VAL_UAGENT "(1).\n\
71 You should not change any files within.\n\
72 Nevertheless, the structure is as follows: Each subdirectory of the\n\
73 current directory represents an IMAP account, and each subdirectory\n\
74 below that represents a mailbox. Each mailbox directory contains a file\n\
75 named UIDVALIDITY which describes the validity in relation to the version\n\
76 on the server. Other files have names corresponding to their IMAP UID.\n";
77 static const char README2[] = "\n\
78 The first 128 bytes of these files are used to store message attributes; the\n\
79 following data is equivalent to compress(1) output. So if you have to save a\n\
80 message by hand because of an emergency, throw away the first 128 bytes and\n\
81 decompress the rest, as e.g. \"dd if=FILE skip=1 bs=128 | zcat\" does.\n";
82 static const char README3[] = "\n\
83 Files named QUEUE contain data that will be sent do the IMAP server next\n\
84 time a connection is made in online mode.\n";
85 static const char README4[] = "\n\
86 You can safely delete any file or directory here, unless it contains a QUEUE\n\
87 file that is not empty; " VAL_UAGENT
88 " will download the data again and will also\n\
89 write new cache entries if configured in this way. If you do not wish to use\n\
90 the cache anymore, delete the entire directory and unset the *imap-cache*\n\
91 variable in " VAL_UAGENT "(1).\n";
93 static char *
94 encname(struct mailbox *mp, const char *name, int same, const char *box)
96 char *cachedir, *eaccount, *ename, *res;
97 int resz;
98 NYD2_ENTER;
100 ename = urlxenc(name, TRU1);
101 if (mp->mb_cache_directory && same && box == NULL) {
102 res = n_autorec_alloc(resz = strlen(mp->mb_cache_directory) +
103 strlen(ename) + 2);
104 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
105 (*ename ? "/" : ""), ename);
106 } else {
107 res = NULL;
109 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
110 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
111 goto jleave;
112 eaccount = urlxenc(mp->mb_imap_account, TRU1);
114 if (box != NULL || asccasecmp(box = mp->mb_imap_mailbox, "INBOX")) {
115 bool_t err;
117 box = imap_path_encode(box, &err);
118 if(err)
119 goto jleave;
120 box = urlxenc(box, TRU1);
121 } else
122 box = "INBOX";
124 res = n_autorec_alloc(resz = strlen(cachedir) + strlen(eaccount) +
125 strlen(box) + strlen(ename) + 4);
126 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, box,
127 (*ename ? "/" : ""), ename);
129 jleave:
130 NYD2_LEAVE;
131 return res;
134 static char *
135 encuid(struct mailbox *mp, ui64_t uid)
137 char buf[64], *cp;
138 NYD2_ENTER;
140 snprintf(buf, sizeof buf, "%" PRIu64, uid);
141 cp = encname(mp, buf, 1, NULL);
142 NYD2_LEAVE;
143 return cp;
146 FL enum okay
147 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
148 int setflags)
150 FILE *fp;
151 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
152 int lastc = EOF, i, xflag, inheader = 1;
153 char b, iob[32768];
154 off_t offset;
155 void *zp;
156 enum okay rv = STOP;
157 NYD2_ENTER;
159 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
160 m->m_uid == 0))
161 goto jleave;
162 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
163 goto jleave;
165 n_file_lock(fileno(fp), FLT_READ, 0,0, 0);
166 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
167 (unsigned long*)&xtime, &xlines) < 4)
168 goto jfail;
169 if (need != NEED_UNSPEC) {
170 switch (b) {
171 case 'H':
172 if (need == NEED_HEADER)
173 goto jsuccess;
174 goto jfail;
175 case 'B':
176 if (need == NEED_HEADER || need == NEED_BODY)
177 goto jsuccess;
178 goto jfail;
179 default:
180 goto jfail;
183 jsuccess:
184 if (b == 'N')
185 goto jflags;
186 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
187 goto jfail;
188 zp = zalloc(fp);
189 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
190 zfree(zp);
191 goto jfail;
193 offset = ftell(mp->mb_otf);
194 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
195 size += n;
196 for (i = 0; i < n; i++) {
197 if (iob[i] == '\n') {
198 lines++;
199 if (lastc == '\n')
200 inheader = 0;
202 lastc = iob[i]&0377;
204 fwrite(iob, 1, n, mp->mb_otf);
206 if (n > 0 && need == NEED_BODY) {
207 while ((n = zread(zp, iob, sizeof iob)) > 0) {
208 size += n;
209 for (i = 0; i < n; i++)
210 if (iob[i] == '\n')
211 lines++;
212 fwrite(iob, 1, n, mp->mb_otf);
215 fflush(mp->mb_otf);
216 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
217 goto jfail;
219 m->m_size = size;
220 m->m_lines = lines;
221 m->m_block = mailx_blockof(offset);
222 m->m_offset = mailx_offsetof(offset);
223 jflags:
224 if (setflags) {
225 m->m_xsize = xsize;
226 m->m_time = xtime;
227 if (setflags & 2) {
228 m->m_flag = xflag | MNOFROM;
229 if (b != 'B')
230 m->m_flag |= MHIDDEN;
233 if (xlines > 0 && m->m_xlines <= 0)
234 m->m_xlines = xlines;
235 switch (b) {
236 case 'B':
237 m->m_xsize = xsize;
238 if (xflag == MREAD && xlines > 0)
239 m->m_flag |= MFULLYCACHED;
240 if (need == NEED_BODY) {
241 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
242 if (m->m_lines > 0)
243 m->m_xlines = m->m_lines;
244 break;
246 /*FALLTHRU*/
247 case 'H':
248 m->m_content_info |= CI_HAVE_HEADER;
249 break;
250 case 'N':
251 break;
253 rv = OKAY;
254 jfail:
255 Fclose(fp);
256 jleave:
257 NYD2_LEAVE;
258 return rv;
261 FL enum okay
262 getcache(struct mailbox *mp, struct message *m, enum needspec need)
264 enum okay rv;
265 NYD_ENTER;
267 rv = getcache1(mp, m, need, 0);
268 NYD_LEAVE;
269 return rv;
272 FL void
273 putcache(struct mailbox *mp, struct message *m)
275 char iob[32768], *name, ob;
276 FILE *ibuf, *obuf;
277 int c, oflag;
278 long n, cnt, oldoffset, osize, otime, olines = -1;
279 void *zp;
280 NYD_ENTER;
282 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
283 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
284 goto jleave;
285 if (m->m_content_info & CI_HAVE_BODY)
286 c = 'B';
287 else if (m->m_content_info & CI_HAVE_HEADER)
288 c = 'H';
289 else if (!(m->m_content_info & CI_HAVE_MASK))
290 c = 'N';
291 else
292 goto jleave;
293 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
294 oldoffset = 0;
295 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
296 if ((obuf = Fopen(name, "w")) == NULL)
297 goto jleave;
298 n_file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
299 } else {
300 n_file_lock(fileno(obuf), FLT_READ, 0,0, 0); /* XXX err hdl */
301 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
302 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
303 (ob == 'B' || (ob == 'H' && c != 'B'))) {
304 if (m->m_xlines <= 0 && olines > 0)
305 m->m_xlines = olines;
306 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
307 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
308 (m->m_xlines > 0 && olines != m->m_xlines)) {
309 fflush(obuf);
310 rewind(obuf);
311 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
312 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
313 putc('\n', obuf);
315 Fclose(obuf);
316 goto jleave;
318 fflush(obuf);
319 rewind(obuf);
320 ftruncate(fileno(obuf), 0);
322 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
323 Fclose(obuf);
324 goto jleave;
326 if (c == 'N')
327 goto jdone;
328 fseek(obuf, INITSKIP, SEEK_SET);
329 zp = zalloc(obuf);
330 cnt = m->m_size;
331 while (cnt > 0) {
332 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
333 cnt -= n;
334 if ((size_t)n != fread(iob, 1, n, ibuf) ||
335 n != (long)zwrite(zp, iob, n)) {
336 unlink(name);
337 zfree(zp);
338 goto jout;
341 if (zfree(zp) < 0) {
342 unlink(name);
343 goto jout;
345 jdone:
346 rewind(obuf);
347 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
348 (unsigned long)m->m_time, m->m_xlines);
349 putc('\n', obuf);
350 if (ferror(obuf)) {
351 unlink(name);
352 goto jout;
354 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
355 m->m_flag |= MFULLYCACHED;
356 jout:
357 if (Fclose(obuf) != 0) {
358 m->m_flag &= ~MFULLYCACHED;
359 unlink(name);
361 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
362 jleave:
363 NYD_LEAVE;
366 FL void
367 initcache(struct mailbox *mp)
369 char *name, *uvname;
370 FILE *uvfp;
371 ui64_t uv;
372 struct cw cw;
373 NYD_ENTER;
375 if (mp->mb_cache_directory != NULL)
376 n_free(mp->mb_cache_directory);
377 mp->mb_cache_directory = NULL;
378 if ((name = encname(mp, "", 1, NULL)) == NULL)
379 goto jleave;
380 mp->mb_cache_directory = sstrdup(name);
381 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
382 goto jleave;
383 if (cwget(&cw) == STOP)
384 goto jleave;
385 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
386 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
387 fscanf(uvfp, "%" PRIu64 , &uv) != 1 || uv != mp->mb_uidvalidity) {
388 if ((uvfp = clean(mp, &cw)) == NULL)
389 goto jout;
390 } else {
391 fflush(uvfp);
392 rewind(uvfp);
394 n_file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
395 fprintf(uvfp, "%" PRIu64 "\n", mp->mb_uidvalidity);
396 if (ferror(uvfp) || Fclose(uvfp) != 0) {
397 unlink(uvname);
398 mp->mb_uidvalidity = 0;
400 jout:
401 cwrelse(&cw);
402 jleave:
403 NYD_LEAVE;
406 FL void
407 purgecache(struct mailbox *mp, struct message *m, long mc)
409 char *name;
410 struct cw cw;
411 NYD_ENTER;
413 if ((name = encname(mp, "", 1, NULL)) == NULL)
414 goto jleave;
415 if (cwget(&cw) == STOP)
416 goto jleave;
417 purge(mp, m, mc, &cw, name);
418 cwrelse(&cw);
419 jleave:
420 NYD_LEAVE;
423 static FILE *
424 clean(struct mailbox *mp, struct cw *cw)
426 char *cachedir, *eaccount, *buf;
427 char const *emailbox;
428 int bufsz;
429 DIR *dirp;
430 struct dirent *dp;
431 FILE *fp = NULL;
432 NYD_ENTER;
434 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
435 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
436 goto jleave;
437 eaccount = urlxenc(mp->mb_imap_account, TRU1);
438 if (asccasecmp(emailbox = mp->mb_imap_mailbox, "INBOX")) {
439 bool_t err;
441 emailbox = imap_path_encode(emailbox, &err);
442 if(err)
443 goto jleave;
444 emailbox = urlxenc(emailbox, TRU1);
446 buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) +
447 strlen(emailbox) + 40);
448 if (!n_path_mkdir(cachedir))
449 goto jleave;
450 snprintf(buf, bufsz, "%s/README", cachedir);
451 if ((fp = Fopen(buf, "wx")) != NULL) {
452 fputs(README1, fp);
453 fputs(README2, fp);
454 fputs(README3, fp);
455 fputs(README4, fp);
456 Fclose(fp);
458 fp = NULL;
459 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
460 if (!n_path_mkdir(buf))
461 goto jleave;
462 if (chdir(buf) < 0)
463 goto jleave;
464 if ((dirp = opendir(".")) == NULL)
465 goto jout;
466 while ((dp = readdir(dirp)) != NULL) {
467 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
468 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
469 continue;
470 unlink(dp->d_name);
472 closedir(dirp);
473 fp = Fopen("UIDVALIDITY", "w");
474 jout:
475 if (cwret(cw) == STOP) {
476 n_err(_("Fatal: Cannot change back to current directory.\n"));
477 abort();
479 jleave:
480 NYD_LEAVE;
481 return fp;
484 static ui64_t *
485 builds(long *contentelem)
487 ui64_t n, *contents = NULL;
488 long contentalloc = 0;
489 char const *x;
490 DIR *dirp;
491 struct dirent *dp;
492 NYD_ENTER;
494 *contentelem = 0;
495 if ((dirp = opendir(".")) == NULL)
496 goto jleave;
497 while ((dp = readdir(dirp)) != NULL) {
498 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
499 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
500 continue;
502 n_idec_ui64_cp(&n, dp->d_name, 10, &x);/* TODO errors? */
503 if (*x != '\0')
504 continue;
505 if (*contentelem >= contentalloc - 1)
506 contents = n_realloc(contents,
507 (contentalloc += 200) * sizeof *contents);
508 contents[(*contentelem)++] = n;
510 closedir(dirp);
511 if (*contentelem > 0) {
512 contents[*contentelem] = 0;
513 qsort(contents, *contentelem, sizeof *contents, longlt);
515 jleave:
516 NYD_LEAVE;
517 return contents;
520 static void
521 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
522 const char *name)
524 ui64_t *contents;
525 long i, j, contentelem;
526 NYD_ENTER;
527 n_UNUSED(mp);
529 if (chdir(name) < 0)
530 goto jleave;
531 contents = builds(&contentelem);
532 if (contents != NULL) {
533 i = j = 0;
534 while (j < contentelem) {
535 if (i < mc && m[i].m_uid == contents[j]) {
536 i++;
537 j++;
538 } else if (i < mc && m[i].m_uid < contents[j])
539 i++;
540 else
541 remve(contents[j++]);
543 n_free(contents);
545 if (cwret(cw) == STOP) {
546 n_err(_("Fatal: Cannot change back to current directory.\n"));
547 abort();
549 jleave:
550 NYD_LEAVE;
553 static int
554 longlt(const void *a, const void *b)
556 union {long l; int i;} u;
557 NYD_ENTER;
559 u.l = *(long const*)a - *(long const*)b;
560 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
561 NYD_LEAVE;
562 return u.i;
565 static void
566 remve(unsigned long n)
568 char buf[30];
569 NYD_ENTER;
571 snprintf(buf, sizeof buf, "%lu", n);
572 unlink(buf);
573 NYD_LEAVE;
576 FL void
577 delcache(struct mailbox *mp, struct message *m)
579 char *fn;
580 NYD_ENTER;
582 fn = encuid(mp, m->m_uid);
583 if (fn && unlink(fn) == 0)
584 m->m_flag |= MUNLINKED;
585 NYD_LEAVE;
588 FL enum okay
589 cache_setptr(enum fedit_mode fm, int transparent)
591 struct cw cw;
592 int i, omsgCount = 0;
593 char *name;
594 ui64_t *contents;
595 long contentelem;
596 struct message *omessage;
597 enum okay rv = STOP;
598 NYD_ENTER;
600 omessage = message;
601 omsgCount = msgCount;
603 if (mb.mb_cache_directory != NULL) {
604 n_free(mb.mb_cache_directory);
605 mb.mb_cache_directory = NULL;
607 if ((name = encname(&mb, "", 1, NULL)) == NULL)
608 goto jleave;
609 mb.mb_cache_directory = sstrdup(name);
610 if (cwget(&cw) == STOP)
611 goto jleave;
612 if (chdir(name) < 0)
613 goto jleave;
614 contents = builds(&contentelem);
615 msgCount = contentelem;
616 message = n_calloc(msgCount + 1, sizeof *message);
617 if (cwret(&cw) == STOP) {
618 n_err(_("Fatal: Cannot change back to current directory.\n"));
619 abort();
621 cwrelse(&cw);
623 srelax_hold();
624 for (i = 0; i < msgCount; i++) {
625 message[i].m_uid = contents[i];
626 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
627 srelax();
629 srelax_rele();
631 if (contents != NULL)
632 n_free(contents);
633 mb.mb_type = MB_CACHE;
634 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
635 ) ? 0 : MB_DELE;
636 if(omessage != NULL){
637 if(transparent)
638 /* This frees the message */
639 transflags(omessage, omsgCount, 1);
640 else
641 n_free(omessage);
643 setdot(message);
644 rv = OKAY;
645 jleave:
646 NYD_LEAVE;
647 return rv;
650 FL enum okay
651 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
653 char *name, *cachedir, *eaccount;
654 DIR *dirp;
655 struct dirent *dp;
656 const char *cp, *bp, *sp;
657 int namesz;
658 enum okay rv = STOP;
659 NYD_ENTER;
661 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
662 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
663 goto jleave;
664 eaccount = urlxenc(mp->mb_imap_account, TRU1);
665 name = n_autorec_alloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
666 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
667 if ((dirp = opendir(name)) == NULL)
668 goto jleave;
669 while ((dp = readdir(dirp)) != NULL) {
670 if (dp->d_name[0] == '.')
671 continue;
672 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
673 for (bp = base; *bp && *bp == *sp; bp++)
674 sp++;
675 if (*bp)
676 continue;
677 cp = strip ? sp : cp;
678 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
680 closedir(dirp);
681 rv = OKAY;
682 jleave:
683 NYD_LEAVE;
684 return rv;
687 FL enum okay
688 cache_remove(const char *name)
690 struct stat st;
691 DIR *dirp;
692 struct dirent *dp;
693 char *path, *dir;
694 int pathsize, pathend, n;
695 enum okay rv = OKAY;
696 NYD_ENTER;
698 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
699 goto jleave;
700 pathend = strlen(dir);
701 path = n_alloc(pathsize = pathend + 30);
702 memcpy(path, dir, pathend);
703 path[pathend++] = '/';
704 path[pathend] = '\0';
705 if ((dirp = opendir(path)) == NULL) {
706 n_free(path);
707 goto jleave;
709 while ((dp = readdir(dirp)) != NULL) {
710 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
711 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
712 continue;
713 n = strlen(dp->d_name) + 1;
714 if (pathend + n > pathsize)
715 path = n_realloc(path, pathsize = pathend + n + 30);
716 memcpy(path + pathend, dp->d_name, n);
717 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
718 continue;
719 if (unlink(path) < 0) {
720 n_perr(path, 0);
721 closedir(dirp);
722 n_free(path);
723 rv = STOP;
724 goto jleave;
727 closedir(dirp);
728 path[pathend] = '\0';
729 rmdir(path); /* no error on failure, might contain submailboxes */
730 n_free(path);
731 jleave:
732 NYD_LEAVE;
733 return rv;
736 FL enum okay
737 cache_rename(const char *old, const char *new)
739 char *olddir, *newdir;
740 enum okay rv = OKAY;
741 NYD_ENTER;
743 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
744 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
745 goto jleave;
746 if (rename(olddir, newdir) < 0) {
747 n_perr(olddir, 0);
748 rv = STOP;
750 jleave:
751 NYD_LEAVE;
752 return rv;
755 FL ui64_t
756 cached_uidvalidity(struct mailbox *mp)
758 FILE *uvfp;
759 char *uvname;
760 ui64_t uv;
761 NYD_ENTER;
763 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
764 uv = 0;
765 goto jleave;
767 if ((uvfp = Fopen(uvname, "r")) == NULL ||
768 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
769 fscanf(uvfp, "%" PRIu64, &uv) != 1)
770 uv = 0;
771 if (uvfp != NULL)
772 Fclose(uvfp);
773 jleave:
774 NYD_LEAVE;
775 return uv;
778 static FILE *
779 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
781 char *name;
782 FILE *fp = NULL;
783 NYD_ENTER;
785 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
786 goto jleave;
787 if ((fp = Fopen(name, mode)) != NULL)
788 n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
789 if (xname)
790 *xname = name;
791 jleave:
792 NYD_LEAVE;
793 return fp;
796 FL FILE *
797 cache_queue(struct mailbox *mp)
799 FILE *fp;
800 NYD_ENTER;
802 fp = cache_queue1(mp, "a", NULL);
803 if (fp == NULL)
804 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
805 NYD_LEAVE;
806 return fp;
809 FL enum okay
810 cache_dequeue(struct mailbox *mp)
812 int bufsz;
813 char *cachedir, *eaccount, *buf, *oldbox;
814 DIR *dirp;
815 struct dirent *dp;
816 enum okay rv = OKAY;
817 NYD_ENTER;
819 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
820 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
821 goto jleave;
822 eaccount = urlxenc(mp->mb_imap_account, TRU1);
823 buf = n_autorec_alloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
824 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
825 if ((dirp = opendir(buf)) == NULL)
826 goto jleave;
827 oldbox = mp->mb_imap_mailbox;
828 while ((dp = readdir(dirp)) != NULL) {
829 if (dp->d_name[0] == '.')
830 continue;
831 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
832 * (but wuuuuh, what a shit!) */
833 mp->mb_imap_mailbox = sstrdup(
834 imap_path_decode(urlxdec(dp->d_name), NULL));
835 dequeue1(mp);
836 { char *x = mp->mb_imap_mailbox;
837 mp->mb_imap_mailbox = oldbox;
838 n_free(x);
841 closedir(dirp);
842 jleave:
843 NYD_LEAVE;
844 return rv;
847 static enum okay
848 dequeue1(struct mailbox *mp)
850 FILE *fp = NULL, *uvfp = NULL;
851 char *qname, *uvname;
852 ui64_t uv;
853 off_t is_size;
854 int is_count;
855 enum okay rv = OKAY;
856 NYD_ENTER;
858 fp = cache_queue1(mp, "r+", &qname);
859 if (fp != NULL && fsize(fp) > 0) {
860 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
861 != OKAY) {
862 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
863 goto jsave;
865 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
866 (uvfp = Fopen(uvname, "r")) == NULL ||
867 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
868 fscanf(uvfp, "%" PRIu64, &uv) != 1 || uv != mp->mb_uidvalidity) {
869 n_err(_("Unique identifiers for \"%s\" are out of date. "
870 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
871 jsave:
872 n_err(_("Saving IMAP commands to *DEAD*\n"));
873 savedeadletter(fp, 0);
874 ftruncate(fileno(fp), 0);
875 Fclose(fp);
876 if (uvfp)
877 Fclose(uvfp);
878 rv = STOP;
879 goto jleave;
881 Fclose(uvfp);
882 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
883 imap_dequeue(mp, fp);
885 if (fp) {
886 Fclose(fp);
887 unlink(qname);
889 jleave:
890 NYD_LEAVE;
891 return rv;
893 #endif /* HAVE_IMAP */
895 /* s-it-mode */