fakedate(): implement ourselfs (Joseph Bisch)..
[s-mailx.git] / obs-imap-cache.c
blob6ac49904bff5f6e2ca86b05f20322def84f7ede6
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, unsigned long uid);
53 static FILE * clean(struct mailbox *mp, struct cw *cw);
54 static unsigned long * 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, unsigned long uid)
135 char buf[30], *cp;
136 NYD2_ENTER;
138 snprintf(buf, sizeof buf, "%lu", 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 unsigned long *
483 builds(long *contentelem)
485 unsigned long n, *contents = NULL;
486 long contentalloc = 0;
487 char *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;
499 n = strtoul(dp->d_name, &x, 10);
500 if (*x != '\0')
501 continue;
502 if (*contentelem >= contentalloc - 1)
503 contents = srealloc(contents,
504 (contentalloc += 200) * sizeof *contents);
505 contents[(*contentelem)++] = n;
507 closedir(dirp);
508 if (*contentelem > 0) {
509 contents[*contentelem] = 0;
510 qsort(contents, *contentelem, sizeof *contents, longlt);
512 jleave:
513 NYD_LEAVE;
514 return contents;
517 static void
518 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
519 const char *name)
521 unsigned long *contents;
522 long i, j, contentelem;
523 NYD_ENTER;
524 n_UNUSED(mp);
526 if (chdir(name) < 0)
527 goto jleave;
528 contents = builds(&contentelem);
529 if (contents != NULL) {
530 i = j = 0;
531 while (j < contentelem) {
532 if (i < mc && m[i].m_uid == contents[j]) {
533 i++;
534 j++;
535 } else if (i < mc && m[i].m_uid < contents[j])
536 i++;
537 else
538 remve(contents[j++]);
540 free(contents);
542 if (cwret(cw) == STOP) {
543 n_err(_("Fatal: Cannot change back to current directory.\n"));
544 abort();
546 jleave:
547 NYD_LEAVE;
550 static int
551 longlt(const void *a, const void *b)
553 union {long l; int i;} u;
554 NYD_ENTER;
556 u.l = *(long const*)a - *(long const*)b;
557 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
558 NYD_LEAVE;
559 return u.i;
562 static void
563 remve(unsigned long n)
565 char buf[30];
566 NYD_ENTER;
568 snprintf(buf, sizeof buf, "%lu", n);
569 unlink(buf);
570 NYD_LEAVE;
573 FL void
574 delcache(struct mailbox *mp, struct message *m)
576 char *fn;
577 NYD_ENTER;
579 fn = encuid(mp, m->m_uid);
580 if (fn && unlink(fn) == 0)
581 m->m_flag |= MUNLINKED;
582 NYD_LEAVE;
585 FL enum okay
586 cache_setptr(enum fedit_mode fm, int transparent)
588 struct cw cw;
589 int i, omsgCount = 0;
590 char *name;
591 unsigned long *contents;
592 long contentelem;
593 struct message *omessage;
594 enum okay rv = STOP;
595 NYD_ENTER;
597 omessage = message;
598 omsgCount = msgCount;
600 if (mb.mb_cache_directory != NULL) {
601 free(mb.mb_cache_directory);
602 mb.mb_cache_directory = NULL;
604 if ((name = encname(&mb, "", 1, NULL)) == NULL)
605 goto jleave;
606 mb.mb_cache_directory = sstrdup(name);
607 if (cwget(&cw) == STOP)
608 goto jleave;
609 if (chdir(name) < 0)
610 goto jleave;
611 contents = builds(&contentelem);
612 msgCount = contentelem;
613 message = scalloc(msgCount + 1, sizeof *message);
614 if (cwret(&cw) == STOP) {
615 n_err(_("Fatal: Cannot change back to current directory.\n"));
616 abort();
618 cwrelse(&cw);
620 srelax_hold();
621 for (i = 0; i < msgCount; i++) {
622 message[i].m_uid = contents[i];
623 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
624 srelax();
626 srelax_rele();
628 if (contents != NULL)
629 free(contents);
630 mb.mb_type = MB_CACHE;
631 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY)
632 ) ? 0 : MB_DELE;
633 if (transparent && omessage != NULL)
634 transflags(omessage, omsgCount, 1);
635 if (omessage != NULL)
636 free(omessage);
637 setdot(message);
638 rv = OKAY;
639 jleave:
640 NYD_LEAVE;
641 return rv;
644 FL enum okay
645 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
647 char *name, *cachedir, *eaccount;
648 DIR *dirp;
649 struct dirent *dp;
650 const char *cp, *bp, *sp;
651 int namesz;
652 enum okay rv = STOP;
653 NYD_ENTER;
655 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
656 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
657 goto jleave;
658 eaccount = urlxenc(mp->mb_imap_account, TRU1);
659 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
660 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
661 if ((dirp = opendir(name)) == NULL)
662 goto jleave;
663 while ((dp = readdir(dirp)) != NULL) {
664 if (dp->d_name[0] == '.')
665 continue;
666 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
667 for (bp = base; *bp && *bp == *sp; bp++)
668 sp++;
669 if (*bp)
670 continue;
671 cp = strip ? sp : cp;
672 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
674 closedir(dirp);
675 rv = OKAY;
676 jleave:
677 NYD_LEAVE;
678 return rv;
681 FL enum okay
682 cache_remove(const char *name)
684 struct stat st;
685 DIR *dirp;
686 struct dirent *dp;
687 char *path, *dir;
688 int pathsize, pathend, n;
689 enum okay rv = OKAY;
690 NYD_ENTER;
692 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
693 goto jleave;
694 pathend = strlen(dir);
695 path = smalloc(pathsize = pathend + 30);
696 memcpy(path, dir, pathend);
697 path[pathend++] = '/';
698 path[pathend] = '\0';
699 if ((dirp = opendir(path)) == NULL) {
700 free(path);
701 goto jleave;
703 while ((dp = readdir(dirp)) != NULL) {
704 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
705 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
706 continue;
707 n = strlen(dp->d_name) + 1;
708 if (pathend + n > pathsize)
709 path = srealloc(path, pathsize = pathend + n + 30);
710 memcpy(path + pathend, dp->d_name, n);
711 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
712 continue;
713 if (unlink(path) < 0) {
714 n_perr(path, 0);
715 closedir(dirp);
716 free(path);
717 rv = STOP;
718 goto jleave;
721 closedir(dirp);
722 path[pathend] = '\0';
723 rmdir(path); /* no error on failure, might contain submailboxes */
724 free(path);
725 jleave:
726 NYD_LEAVE;
727 return rv;
730 FL enum okay
731 cache_rename(const char *old, const char *new)
733 char *olddir, *newdir;
734 enum okay rv = OKAY;
735 NYD_ENTER;
737 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
738 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
739 goto jleave;
740 if (rename(olddir, newdir) < 0) {
741 n_perr(olddir, 0);
742 rv = STOP;
744 jleave:
745 NYD_LEAVE;
746 return rv;
749 FL unsigned long
750 cached_uidvalidity(struct mailbox *mp)
752 FILE *uvfp;
753 char *uvname;
754 unsigned long uv;
755 NYD_ENTER;
757 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
758 uv = 0;
759 goto jleave;
761 if ((uvfp = Fopen(uvname, "r")) == NULL ||
762 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
763 fscanf(uvfp, "%lu", &uv) != 1)
764 uv = 0;
765 if (uvfp != NULL)
766 Fclose(uvfp);
767 jleave:
768 NYD_LEAVE;
769 return uv;
772 static FILE *
773 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
775 char *name;
776 FILE *fp = NULL;
777 NYD_ENTER;
779 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
780 goto jleave;
781 if ((fp = Fopen(name, mode)) != NULL)
782 n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
783 if (xname)
784 *xname = name;
785 jleave:
786 NYD_LEAVE;
787 return fp;
790 FL FILE *
791 cache_queue(struct mailbox *mp)
793 FILE *fp;
794 NYD_ENTER;
796 fp = cache_queue1(mp, "a", NULL);
797 if (fp == NULL)
798 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
799 NYD_LEAVE;
800 return fp;
803 FL enum okay
804 cache_dequeue(struct mailbox *mp)
806 int bufsz;
807 char *cachedir, *eaccount, *buf, *oldbox;
808 DIR *dirp;
809 struct dirent *dp;
810 enum okay rv = OKAY;
811 NYD_ENTER;
813 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
814 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
815 goto jleave;
816 eaccount = urlxenc(mp->mb_imap_account, TRU1);
817 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
818 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
819 if ((dirp = opendir(buf)) == NULL)
820 goto jleave;
821 oldbox = mp->mb_imap_mailbox;
822 while ((dp = readdir(dirp)) != NULL) {
823 if (dp->d_name[0] == '.')
824 continue;
825 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
826 * (but wuuuuh, what a shit!) */
827 mp->mb_imap_mailbox = sstrdup(
828 imap_path_decode(urlxdec(dp->d_name), NULL));
829 dequeue1(mp);
830 { char *x = mp->mb_imap_mailbox;
831 mp->mb_imap_mailbox = oldbox;
832 free(x);
835 closedir(dirp);
836 jleave:
837 NYD_LEAVE;
838 return rv;
841 static enum okay
842 dequeue1(struct mailbox *mp)
844 FILE *fp = NULL, *uvfp = NULL;
845 char *qname, *uvname;
846 unsigned long uv;
847 off_t is_size;
848 int is_count;
849 enum okay rv = OKAY;
850 NYD_ENTER;
852 fp = cache_queue1(mp, "r+", &qname);
853 if (fp != NULL && fsize(fp) > 0) {
854 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
855 != OKAY) {
856 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
857 goto jsave;
859 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
860 (uvfp = Fopen(uvname, "r")) == NULL ||
861 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
862 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
863 n_err(_("Unique identifiers for \"%s\" are out of date. "
864 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
865 jsave:
866 n_err(_("Saving IMAP commands to *DEAD*\n"));
867 savedeadletter(fp, 0);
868 ftruncate(fileno(fp), 0);
869 Fclose(fp);
870 if (uvfp)
871 Fclose(uvfp);
872 rv = STOP;
873 goto jleave;
875 Fclose(uvfp);
876 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
877 imap_dequeue(mp, fp);
879 if (fp) {
880 Fclose(fp);
881 unlink(qname);
883 jleave:
884 NYD_LEAVE;
885 return rv;
887 #endif /* HAVE_IMAP */
889 /* s-it-mode */