n_idna_to_ascii(): add support for idnkit 2.3
[s-mailx.git] / obs-imap-cache.c
blob1ee45a87b6ec0983ac179d0a6b5bd83d5f792dab
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(omessage != NULL){
634 if(transparent)
635 /* This frees the message */
636 transflags(omessage, omsgCount, 1);
637 else
638 n_free(omessage);
640 setdot(message);
641 rv = OKAY;
642 jleave:
643 NYD_LEAVE;
644 return rv;
647 FL enum okay
648 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
650 char *name, *cachedir, *eaccount;
651 DIR *dirp;
652 struct dirent *dp;
653 const char *cp, *bp, *sp;
654 int namesz;
655 enum okay rv = STOP;
656 NYD_ENTER;
658 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
659 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
660 goto jleave;
661 eaccount = urlxenc(mp->mb_imap_account, TRU1);
662 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
663 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
664 if ((dirp = opendir(name)) == NULL)
665 goto jleave;
666 while ((dp = readdir(dirp)) != NULL) {
667 if (dp->d_name[0] == '.')
668 continue;
669 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
670 for (bp = base; *bp && *bp == *sp; bp++)
671 sp++;
672 if (*bp)
673 continue;
674 cp = strip ? sp : cp;
675 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
677 closedir(dirp);
678 rv = OKAY;
679 jleave:
680 NYD_LEAVE;
681 return rv;
684 FL enum okay
685 cache_remove(const char *name)
687 struct stat st;
688 DIR *dirp;
689 struct dirent *dp;
690 char *path, *dir;
691 int pathsize, pathend, n;
692 enum okay rv = OKAY;
693 NYD_ENTER;
695 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
696 goto jleave;
697 pathend = strlen(dir);
698 path = smalloc(pathsize = pathend + 30);
699 memcpy(path, dir, pathend);
700 path[pathend++] = '/';
701 path[pathend] = '\0';
702 if ((dirp = opendir(path)) == NULL) {
703 free(path);
704 goto jleave;
706 while ((dp = readdir(dirp)) != NULL) {
707 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
708 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
709 continue;
710 n = strlen(dp->d_name) + 1;
711 if (pathend + n > pathsize)
712 path = srealloc(path, pathsize = pathend + n + 30);
713 memcpy(path + pathend, dp->d_name, n);
714 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
715 continue;
716 if (unlink(path) < 0) {
717 n_perr(path, 0);
718 closedir(dirp);
719 free(path);
720 rv = STOP;
721 goto jleave;
724 closedir(dirp);
725 path[pathend] = '\0';
726 rmdir(path); /* no error on failure, might contain submailboxes */
727 free(path);
728 jleave:
729 NYD_LEAVE;
730 return rv;
733 FL enum okay
734 cache_rename(const char *old, const char *new)
736 char *olddir, *newdir;
737 enum okay rv = OKAY;
738 NYD_ENTER;
740 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
741 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
742 goto jleave;
743 if (rename(olddir, newdir) < 0) {
744 n_perr(olddir, 0);
745 rv = STOP;
747 jleave:
748 NYD_LEAVE;
749 return rv;
752 FL unsigned long
753 cached_uidvalidity(struct mailbox *mp)
755 FILE *uvfp;
756 char *uvname;
757 unsigned long uv;
758 NYD_ENTER;
760 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
761 uv = 0;
762 goto jleave;
764 if ((uvfp = Fopen(uvname, "r")) == NULL ||
765 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
766 fscanf(uvfp, "%lu", &uv) != 1)
767 uv = 0;
768 if (uvfp != NULL)
769 Fclose(uvfp);
770 jleave:
771 NYD_LEAVE;
772 return uv;
775 static FILE *
776 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
778 char *name;
779 FILE *fp = NULL;
780 NYD_ENTER;
782 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
783 goto jleave;
784 if ((fp = Fopen(name, mode)) != NULL)
785 n_file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
786 if (xname)
787 *xname = name;
788 jleave:
789 NYD_LEAVE;
790 return fp;
793 FL FILE *
794 cache_queue(struct mailbox *mp)
796 FILE *fp;
797 NYD_ENTER;
799 fp = cache_queue1(mp, "a", NULL);
800 if (fp == NULL)
801 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
802 NYD_LEAVE;
803 return fp;
806 FL enum okay
807 cache_dequeue(struct mailbox *mp)
809 int bufsz;
810 char *cachedir, *eaccount, *buf, *oldbox;
811 DIR *dirp;
812 struct dirent *dp;
813 enum okay rv = OKAY;
814 NYD_ENTER;
816 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
817 (cachedir = fexpand(cachedir, FEXP_LOCAL | FEXP_NOPROTO)) == NULL)
818 goto jleave;
819 eaccount = urlxenc(mp->mb_imap_account, TRU1);
820 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
821 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
822 if ((dirp = opendir(buf)) == NULL)
823 goto jleave;
824 oldbox = mp->mb_imap_mailbox;
825 while ((dp = readdir(dirp)) != NULL) {
826 if (dp->d_name[0] == '.')
827 continue;
828 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
829 * (but wuuuuh, what a shit!) */
830 mp->mb_imap_mailbox = sstrdup(
831 imap_path_decode(urlxdec(dp->d_name), NULL));
832 dequeue1(mp);
833 { char *x = mp->mb_imap_mailbox;
834 mp->mb_imap_mailbox = oldbox;
835 free(x);
838 closedir(dirp);
839 jleave:
840 NYD_LEAVE;
841 return rv;
844 static enum okay
845 dequeue1(struct mailbox *mp)
847 FILE *fp = NULL, *uvfp = NULL;
848 char *qname, *uvname;
849 unsigned long uv;
850 off_t is_size;
851 int is_count;
852 enum okay rv = OKAY;
853 NYD_ENTER;
855 fp = cache_queue1(mp, "r+", &qname);
856 if (fp != NULL && fsize(fp) > 0) {
857 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
858 != OKAY) {
859 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
860 goto jsave;
862 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
863 (uvfp = Fopen(uvname, "r")) == NULL ||
864 (n_file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
865 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
866 n_err(_("Unique identifiers for \"%s\" are out of date. "
867 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
868 jsave:
869 n_err(_("Saving IMAP commands to *DEAD*\n"));
870 savedeadletter(fp, 0);
871 ftruncate(fileno(fp), 0);
872 Fclose(fp);
873 if (uvfp)
874 Fclose(uvfp);
875 rv = STOP;
876 goto jleave;
878 Fclose(uvfp);
879 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
880 imap_dequeue(mp, fp);
882 if (fp) {
883 Fclose(fp);
884 unlink(qname);
886 jleave:
887 NYD_LEAVE;
888 return rv;
890 #endif /* HAVE_IMAP */
892 /* s-it-mode */