collect(): ~[FfMmUu]: should default to the "dot" (Andrew Gee)
[s-mailx.git] / obs-imap-cache.c
blob380bdc27cb5b390725784b43a701e3282fe8aa3c
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 = salloc(resz = strlen(mp->mb_cache_directory) + strlen(ename) + 2);
102 snprintf(res, resz, "%s%s%s", mp->mb_cache_directory,
103 (*ename ? "/" : ""), ename);
104 } else {
105 res = NULL;
107 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
108 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
109 goto jleave;
110 eaccount = urlxenc(mp->mb_imap_account, TRU1);
112 if (box != NULL || asccasecmp(box = mp->mb_imap_mailbox, "INBOX")) {
113 bool_t err;
115 box = imap_path_encode(box, &err);
116 if(err)
117 goto jleave;
118 box = urlxenc(box, TRU1);
119 } else
120 box = "INBOX";
122 res = salloc(resz = strlen(cachedir) + strlen(eaccount) +
123 strlen(box) + strlen(ename) + 4);
124 snprintf(res, resz, "%s/%s/%s%s%s", cachedir, eaccount, box,
125 (*ename ? "/" : ""), ename);
127 jleave:
128 NYD2_LEAVE;
129 return res;
132 static char *
133 encuid(struct mailbox *mp, ui64_t uid)
135 char buf[64], *cp;
136 NYD2_ENTER;
138 snprintf(buf, sizeof buf, "%" PRIu64, uid);
139 cp = encname(mp, buf, 1, NULL);
140 NYD2_LEAVE;
141 return cp;
144 FL enum okay
145 getcache1(struct mailbox *mp, struct message *m, enum needspec need,
146 int setflags)
148 FILE *fp;
149 long n = 0, size = 0, xsize, xtime, xlines = -1, lines = 0;
150 int lastc = EOF, i, xflag, inheader = 1;
151 char b, iob[32768];
152 off_t offset;
153 void *zp;
154 enum okay rv = STOP;
155 NYD2_ENTER;
157 if (setflags == 0 && ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) ||
158 m->m_uid == 0))
159 goto jleave;
160 if ((fp = Fopen(encuid(mp, m->m_uid), "r")) == NULL)
161 goto jleave;
163 n_file_lock(fileno(fp), FLT_READ, 0,0, 0);
164 if (fscanf(fp, infofmt, &b, (unsigned long*)&xsize, &xflag,
165 (unsigned long*)&xtime, &xlines) < 4)
166 goto jfail;
167 if (need != NEED_UNSPEC) {
168 switch (b) {
169 case 'H':
170 if (need == NEED_HEADER)
171 goto jsuccess;
172 goto jfail;
173 case 'B':
174 if (need == NEED_HEADER || need == NEED_BODY)
175 goto jsuccess;
176 goto jfail;
177 default:
178 goto jfail;
181 jsuccess:
182 if (b == 'N')
183 goto jflags;
184 if (fseek(fp, INITSKIP, SEEK_SET) < 0)
185 goto jfail;
186 zp = zalloc(fp);
187 if (fseek(mp->mb_otf, 0L, SEEK_END) < 0) {
188 zfree(zp);
189 goto jfail;
191 offset = ftell(mp->mb_otf);
192 while (inheader && (n = zread(zp, iob, sizeof iob)) > 0) {
193 size += n;
194 for (i = 0; i < n; i++) {
195 if (iob[i] == '\n') {
196 lines++;
197 if (lastc == '\n')
198 inheader = 0;
200 lastc = iob[i]&0377;
202 fwrite(iob, 1, n, mp->mb_otf);
204 if (n > 0 && need == NEED_BODY) {
205 while ((n = zread(zp, iob, sizeof iob)) > 0) {
206 size += n;
207 for (i = 0; i < n; i++)
208 if (iob[i] == '\n')
209 lines++;
210 fwrite(iob, 1, n, mp->mb_otf);
213 fflush(mp->mb_otf);
214 if (zfree(zp) < 0 || n < 0 || ferror(fp) || ferror(mp->mb_otf))
215 goto jfail;
217 m->m_size = size;
218 m->m_lines = lines;
219 m->m_block = mailx_blockof(offset);
220 m->m_offset = mailx_offsetof(offset);
221 jflags:
222 if (setflags) {
223 m->m_xsize = xsize;
224 m->m_time = xtime;
225 if (setflags & 2) {
226 m->m_flag = xflag | MNOFROM;
227 if (b != 'B')
228 m->m_flag |= MHIDDEN;
231 if (xlines > 0 && m->m_xlines <= 0)
232 m->m_xlines = xlines;
233 switch (b) {
234 case 'B':
235 m->m_xsize = xsize;
236 if (xflag == MREAD && xlines > 0)
237 m->m_flag |= MFULLYCACHED;
238 if (need == NEED_BODY) {
239 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
240 if (m->m_lines > 0)
241 m->m_xlines = m->m_lines;
242 break;
244 /*FALLTHRU*/
245 case 'H':
246 m->m_content_info |= CI_HAVE_HEADER;
247 break;
248 case 'N':
249 break;
251 rv = OKAY;
252 jfail:
253 Fclose(fp);
254 jleave:
255 NYD2_LEAVE;
256 return rv;
259 FL enum okay
260 getcache(struct mailbox *mp, struct message *m, enum needspec need)
262 enum okay rv;
263 NYD_ENTER;
265 rv = getcache1(mp, m, need, 0);
266 NYD_LEAVE;
267 return rv;
270 FL void
271 putcache(struct mailbox *mp, struct message *m)
273 char iob[32768], *name, ob;
274 FILE *ibuf, *obuf;
275 int c, oflag;
276 long n, cnt, oldoffset, osize, otime, olines = -1;
277 void *zp;
278 NYD_ENTER;
280 if ((mp->mb_type != MB_IMAP && mp->mb_type != MB_CACHE) || m->m_uid == 0 ||
281 m->m_time == 0 || (m->m_flag & (MTOUCH|MFULLYCACHED)) == MFULLYCACHED)
282 goto jleave;
283 if (m->m_content_info & CI_HAVE_BODY)
284 c = 'B';
285 else if (m->m_content_info & CI_HAVE_HEADER)
286 c = 'H';
287 else if (!(m->m_content_info & CI_HAVE_MASK))
288 c = 'N';
289 else
290 goto jleave;
291 if ((oldoffset = ftell(mp->mb_itf)) < 0) /* XXX weird err hdling */
292 oldoffset = 0;
293 if ((obuf = Fopen(name = encuid(mp, m->m_uid), "r+")) == NULL) {
294 if ((obuf = Fopen(name, "w")) == NULL)
295 goto jleave;
296 n_file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
297 } else {
298 n_file_lock(fileno(obuf), FLT_READ, 0,0, 0); /* XXX err hdl */
299 if (fscanf(obuf, infofmt, &ob, (unsigned long*)&osize, &oflag,
300 (unsigned long*)&otime, &olines) >= 4 && ob != '\0' &&
301 (ob == 'B' || (ob == 'H' && c != 'B'))) {
302 if (m->m_xlines <= 0 && olines > 0)
303 m->m_xlines = olines;
304 if ((c != 'N' && (size_t)osize != m->m_xsize) ||
305 oflag != (int)USEBITS(m->m_flag) || otime != m->m_time ||
306 (m->m_xlines > 0 && olines != m->m_xlines)) {
307 fflush(obuf);
308 rewind(obuf);
309 fprintf(obuf, infofmt, ob, (unsigned long)m->m_xsize,
310 USEBITS(m->m_flag), (unsigned long)m->m_time, m->m_xlines);
311 putc('\n', obuf);
313 Fclose(obuf);
314 goto jleave;
316 fflush(obuf);
317 rewind(obuf);
318 ftruncate(fileno(obuf), 0);
320 if ((ibuf = setinput(mp, m, NEED_UNSPEC)) == NULL) {
321 Fclose(obuf);
322 goto jleave;
324 if (c == 'N')
325 goto jdone;
326 fseek(obuf, INITSKIP, SEEK_SET);
327 zp = zalloc(obuf);
328 cnt = m->m_size;
329 while (cnt > 0) {
330 n = (cnt > (long)sizeof iob) ? (long)sizeof iob : cnt;
331 cnt -= n;
332 if ((size_t)n != fread(iob, 1, n, ibuf) ||
333 n != (long)zwrite(zp, iob, n)) {
334 unlink(name);
335 zfree(zp);
336 goto jout;
339 if (zfree(zp) < 0) {
340 unlink(name);
341 goto jout;
343 jdone:
344 rewind(obuf);
345 fprintf(obuf, infofmt, c, (unsigned long)m->m_xsize, USEBITS(m->m_flag),
346 (unsigned long)m->m_time, m->m_xlines);
347 putc('\n', obuf);
348 if (ferror(obuf)) {
349 unlink(name);
350 goto jout;
352 if (c == 'B' && USEBITS(m->m_flag) == MREAD)
353 m->m_flag |= MFULLYCACHED;
354 jout:
355 if (Fclose(obuf) != 0) {
356 m->m_flag &= ~MFULLYCACHED;
357 unlink(name);
359 (void)fseek(mp->mb_itf, oldoffset, SEEK_SET);
360 jleave:
361 NYD_LEAVE;
364 FL void
365 initcache(struct mailbox *mp)
367 char *name, *uvname;
368 FILE *uvfp;
369 unsigned long uv;
370 struct cw cw;
371 NYD_ENTER;
373 if (mp->mb_cache_directory != NULL)
374 free(mp->mb_cache_directory);
375 mp->mb_cache_directory = NULL;
376 if ((name = encname(mp, "", 1, NULL)) == NULL)
377 goto jleave;
378 mp->mb_cache_directory = sstrdup(name);
379 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL)
380 goto jleave;
381 if (cwget(&cw) == STOP)
382 goto jleave;
383 if ((uvfp = Fopen(uvname, "r+")) == NULL ||
384 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
385 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
386 if ((uvfp = clean(mp, &cw)) == NULL)
387 goto jout;
388 } else {
389 fflush(uvfp);
390 rewind(uvfp);
392 n_file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
393 fprintf(uvfp, "%lu\n", mp->mb_uidvalidity);
394 if (ferror(uvfp) || Fclose(uvfp) != 0) {
395 unlink(uvname);
396 mp->mb_uidvalidity = 0;
398 jout:
399 cwrelse(&cw);
400 jleave:
401 NYD_LEAVE;
404 FL void
405 purgecache(struct mailbox *mp, struct message *m, long mc)
407 char *name;
408 struct cw cw;
409 NYD_ENTER;
411 if ((name = encname(mp, "", 1, NULL)) == NULL)
412 goto jleave;
413 if (cwget(&cw) == STOP)
414 goto jleave;
415 purge(mp, m, mc, &cw, name);
416 cwrelse(&cw);
417 jleave:
418 NYD_LEAVE;
421 static FILE *
422 clean(struct mailbox *mp, struct cw *cw)
424 char *cachedir, *eaccount, *buf;
425 char const *emailbox;
426 int bufsz;
427 DIR *dirp;
428 struct dirent *dp;
429 FILE *fp = NULL;
430 NYD_ENTER;
432 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
433 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
434 goto jleave;
435 eaccount = urlxenc(mp->mb_imap_account, TRU1);
436 if (asccasecmp(emailbox = mp->mb_imap_mailbox, "INBOX")) {
437 bool_t err;
439 emailbox = imap_path_encode(emailbox, &err);
440 if(err)
441 goto jleave;
442 emailbox = urlxenc(emailbox, TRU1);
444 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
445 strlen(emailbox) + 40);
446 if (!n_path_mkdir(cachedir))
447 goto jleave;
448 snprintf(buf, bufsz, "%s/README", cachedir);
449 if ((fp = Fopen(buf, "wx")) != NULL) {
450 fputs(README1, fp);
451 fputs(README2, fp);
452 fputs(README3, fp);
453 fputs(README4, fp);
454 Fclose(fp);
456 fp = NULL;
457 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
458 if (!n_path_mkdir(buf))
459 goto jleave;
460 if (chdir(buf) < 0)
461 goto jleave;
462 if ((dirp = opendir(".")) == NULL)
463 goto jout;
464 while ((dp = readdir(dirp)) != NULL) {
465 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
466 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
467 continue;
468 unlink(dp->d_name);
470 closedir(dirp);
471 fp = Fopen("UIDVALIDITY", "w");
472 jout:
473 if (cwret(cw) == STOP) {
474 n_err(_("Fatal: Cannot change back to current directory.\n"));
475 abort();
477 jleave:
478 NYD_LEAVE;
479 return fp;
482 static ui64_t *
483 builds(long *contentelem)
485 ui64_t n, *contents = NULL;
486 long contentalloc = 0;
487 char const *x;
488 DIR *dirp;
489 struct dirent *dp;
490 NYD_ENTER;
492 *contentelem = 0;
493 if ((dirp = opendir(".")) == NULL)
494 goto jleave;
495 while ((dp = readdir(dirp)) != NULL) {
496 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
497 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
498 continue;
500 n_idec_ui64_cp(&n, dp->d_name, 10, &x);/* TODO errors? */
501 if (*x != '\0')
502 continue;
503 if (*contentelem >= contentalloc - 1)
504 contents = n_realloc(contents,
505 (contentalloc += 200) * sizeof *contents);
506 contents[(*contentelem)++] = n;
508 closedir(dirp);
509 if (*contentelem > 0) {
510 contents[*contentelem] = 0;
511 qsort(contents, *contentelem, sizeof *contents, longlt);
513 jleave:
514 NYD_LEAVE;
515 return contents;
518 static void
519 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
520 const char *name)
522 ui64_t *contents;
523 long i, j, contentelem;
524 NYD_ENTER;
525 n_UNUSED(mp);
527 if (chdir(name) < 0)
528 goto jleave;
529 contents = builds(&contentelem);
530 if (contents != NULL) {
531 i = j = 0;
532 while (j < contentelem) {
533 if (i < mc && m[i].m_uid == contents[j]) {
534 i++;
535 j++;
536 } else if (i < mc && m[i].m_uid < contents[j])
537 i++;
538 else
539 remve(contents[j++]);
541 free(contents);
543 if (cwret(cw) == STOP) {
544 n_err(_("Fatal: Cannot change back to current directory.\n"));
545 abort();
547 jleave:
548 NYD_LEAVE;
551 static int
552 longlt(const void *a, const void *b)
554 union {long l; int i;} u;
555 NYD_ENTER;
557 u.l = *(long const*)a - *(long const*)b;
558 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
559 NYD_LEAVE;
560 return u.i;
563 static void
564 remve(unsigned long n)
566 char buf[30];
567 NYD_ENTER;
569 snprintf(buf, sizeof buf, "%lu", n);
570 unlink(buf);
571 NYD_LEAVE;
574 FL void
575 delcache(struct mailbox *mp, struct message *m)
577 char *fn;
578 NYD_ENTER;
580 fn = encuid(mp, m->m_uid);
581 if (fn && unlink(fn) == 0)
582 m->m_flag |= MUNLINKED;
583 NYD_LEAVE;
586 FL enum okay
587 cache_setptr(enum fedit_mode fm, int transparent)
589 struct cw cw;
590 int i, omsgCount = 0;
591 char *name;
592 ui64_t *contents;
593 long contentelem;
594 struct message *omessage;
595 enum okay rv = STOP;
596 NYD_ENTER;
598 omessage = message;
599 omsgCount = msgCount;
601 if (mb.mb_cache_directory != NULL) {
602 free(mb.mb_cache_directory);
603 mb.mb_cache_directory = NULL;
605 if ((name = encname(&mb, "", 1, NULL)) == NULL)
606 goto jleave;
607 mb.mb_cache_directory = sstrdup(name);
608 if (cwget(&cw) == STOP)
609 goto jleave;
610 if (chdir(name) < 0)
611 goto jleave;
612 contents = builds(&contentelem);
613 msgCount = contentelem;
614 message = scalloc(msgCount + 1, sizeof *message);
615 if (cwret(&cw) == STOP) {
616 n_err(_("Fatal: Cannot change back to current directory.\n"));
617 abort();
619 cwrelse(&cw);
621 srelax_hold();
622 for (i = 0; i < msgCount; i++) {
623 message[i].m_uid = contents[i];
624 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
625 srelax();
627 srelax_rele();
629 if (contents != NULL)
630 free(contents);
631 mb.mb_type = MB_CACHE;
632 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
633 ) ? 0 : MB_DELE;
634 if(omessage != NULL){
635 if(transparent)
636 /* This frees the message */
637 transflags(omessage, omsgCount, 1);
638 else
639 n_free(omessage);
641 setdot(message);
642 rv = OKAY;
643 jleave:
644 NYD_LEAVE;
645 return rv;
648 FL enum okay
649 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
651 char *name, *cachedir, *eaccount;
652 DIR *dirp;
653 struct dirent *dp;
654 const char *cp, *bp, *sp;
655 int namesz;
656 enum okay rv = STOP;
657 NYD_ENTER;
659 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
660 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
661 goto jleave;
662 eaccount = urlxenc(mp->mb_imap_account, TRU1);
663 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
664 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
665 if ((dirp = opendir(name)) == NULL)
666 goto jleave;
667 while ((dp = readdir(dirp)) != NULL) {
668 if (dp->d_name[0] == '.')
669 continue;
670 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
671 for (bp = base; *bp && *bp == *sp; bp++)
672 sp++;
673 if (*bp)
674 continue;
675 cp = strip ? sp : cp;
676 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
678 closedir(dirp);
679 rv = OKAY;
680 jleave:
681 NYD_LEAVE;
682 return rv;
685 FL enum okay
686 cache_remove(const char *name)
688 struct stat st;
689 DIR *dirp;
690 struct dirent *dp;
691 char *path, *dir;
692 int pathsize, pathend, n;
693 enum okay rv = OKAY;
694 NYD_ENTER;
696 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
697 goto jleave;
698 pathend = strlen(dir);
699 path = smalloc(pathsize = pathend + 30);
700 memcpy(path, dir, pathend);
701 path[pathend++] = '/';
702 path[pathend] = '\0';
703 if ((dirp = opendir(path)) == NULL) {
704 free(path);
705 goto jleave;
707 while ((dp = readdir(dirp)) != NULL) {
708 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
709 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
710 continue;
711 n = strlen(dp->d_name) + 1;
712 if (pathend + n > pathsize)
713 path = srealloc(path, pathsize = pathend + n + 30);
714 memcpy(path + pathend, dp->d_name, n);
715 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
716 continue;
717 if (unlink(path) < 0) {
718 n_perr(path, 0);
719 closedir(dirp);
720 free(path);
721 rv = STOP;
722 goto jleave;
725 closedir(dirp);
726 path[pathend] = '\0';
727 rmdir(path); /* no error on failure, might contain submailboxes */
728 free(path);
729 jleave:
730 NYD_LEAVE;
731 return rv;
734 FL enum okay
735 cache_rename(const char *old, const char *new)
737 char *olddir, *newdir;
738 enum okay rv = OKAY;
739 NYD_ENTER;
741 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
742 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
743 goto jleave;
744 if (rename(olddir, newdir) < 0) {
745 n_perr(olddir, 0);
746 rv = STOP;
748 jleave:
749 NYD_LEAVE;
750 return rv;
753 FL unsigned long
754 cached_uidvalidity(struct mailbox *mp)
756 FILE *uvfp;
757 char *uvname;
758 unsigned long uv;
759 NYD_ENTER;
761 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
762 uv = 0;
763 goto jleave;
765 if ((uvfp = Fopen(uvname, "r")) == NULL ||
766 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
767 fscanf(uvfp, "%lu", &uv) != 1)
768 uv = 0;
769 if (uvfp != NULL)
770 Fclose(uvfp);
771 jleave:
772 NYD_LEAVE;
773 return uv;
776 static FILE *
777 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
779 char *name;
780 FILE *fp = NULL;
781 NYD_ENTER;
783 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
784 goto jleave;
785 if ((fp = Fopen(name, mode)) != NULL)
786 n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
787 if (xname)
788 *xname = name;
789 jleave:
790 NYD_LEAVE;
791 return fp;
794 FL FILE *
795 cache_queue(struct mailbox *mp)
797 FILE *fp;
798 NYD_ENTER;
800 fp = cache_queue1(mp, "a", NULL);
801 if (fp == NULL)
802 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
803 NYD_LEAVE;
804 return fp;
807 FL enum okay
808 cache_dequeue(struct mailbox *mp)
810 int bufsz;
811 char *cachedir, *eaccount, *buf, *oldbox;
812 DIR *dirp;
813 struct dirent *dp;
814 enum okay rv = OKAY;
815 NYD_ENTER;
817 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
818 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
819 goto jleave;
820 eaccount = urlxenc(mp->mb_imap_account, TRU1);
821 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
822 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
823 if ((dirp = opendir(buf)) == NULL)
824 goto jleave;
825 oldbox = mp->mb_imap_mailbox;
826 while ((dp = readdir(dirp)) != NULL) {
827 if (dp->d_name[0] == '.')
828 continue;
829 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
830 * (but wuuuuh, what a shit!) */
831 mp->mb_imap_mailbox = sstrdup(
832 imap_path_decode(urlxdec(dp->d_name), NULL));
833 dequeue1(mp);
834 { char *x = mp->mb_imap_mailbox;
835 mp->mb_imap_mailbox = oldbox;
836 free(x);
839 closedir(dirp);
840 jleave:
841 NYD_LEAVE;
842 return rv;
845 static enum okay
846 dequeue1(struct mailbox *mp)
848 FILE *fp = NULL, *uvfp = NULL;
849 char *qname, *uvname;
850 unsigned long uv;
851 off_t is_size;
852 int is_count;
853 enum okay rv = OKAY;
854 NYD_ENTER;
856 fp = cache_queue1(mp, "r+", &qname);
857 if (fp != NULL && fsize(fp) > 0) {
858 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
859 != OKAY) {
860 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
861 goto jsave;
863 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
864 (uvfp = Fopen(uvname, "r")) == NULL ||
865 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
866 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
867 n_err(_("Unique identifiers for \"%s\" are out of date. "
868 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
869 jsave:
870 n_err(_("Saving IMAP commands to *DEAD*\n"));
871 savedeadletter(fp, 0);
872 ftruncate(fileno(fp), 0);
873 Fclose(fp);
874 if (uvfp)
875 Fclose(uvfp);
876 rv = STOP;
877 goto jleave;
879 Fclose(uvfp);
880 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
881 imap_dequeue(mp, fp);
883 if (fp) {
884 Fclose(fp);
885 unlink(qname);
887 jleave:
888 NYD_LEAVE;
889 return rv;
891 #endif /* HAVE_IMAP */
893 /* s-it-mode */