imap_path_decode(): fix UTF-16 arithmetic
[s-mailx.git] / imap_cache.c
blob1d240cfbdc6f6079c706800917abd3475307a94f
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 - 2015 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 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 " 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=MESSAGEFILE 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; " UAGENT " will download the data again and will also\n\
87 write new cache entries if configured in this way. If you do not wish to use\n\
88 the cache anymore, delete the entire directory and unset the *imap-cache*\n\
89 variable in " UAGENT "(1).\n";
90 static const char README5[] = "\n\
91 For more information about " UAGENT "(1), visit\n\
92 <https://www.sdaoden.eu/code.html>.\n"; /* TODO MAGIC CONSTANT */
94 static char *
95 encname(struct mailbox *mp, const char *name, int same, const char *box)
97 char *cachedir, *eaccount, *ename, *res;
98 int resz;
99 NYD2_ENTER;
101 ename = urlxenc(name, TRU1);
102 if (mp->mb_cache_directory && same && box == NULL) {
103 res = salloc(resz = strlen(mp->mb_cache_directory) + 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 = file_expand(cachedir)) == 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 = salloc(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, unsigned long uid)
137 char buf[30], *cp;
138 NYD2_ENTER;
140 snprintf(buf, sizeof buf, "%lu", 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 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_have |= HAVE_HEADER | 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_have |= 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_have & HAVE_BODY)
286 c = 'B';
287 else if (m->m_have & HAVE_HEADER)
288 c = 'H';
289 else if (m->m_have == HAVE_NOTHING)
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 file_lock(fileno(obuf), FLT_WRITE, 0,0, 0); /* XXX err hdl */
299 } else {
300 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 unsigned long uv;
372 struct cw cw;
373 NYD_ENTER;
375 if (mp->mb_cache_directory != NULL)
376 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 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
387 fscanf(uvfp, "%lu", &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 file_lock(fileno(uvfp), FLT_WRITE, 0,0, 0);
395 fprintf(uvfp, "%lu\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 = file_expand(cachedir)) == 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 = salloc(bufsz = strlen(cachedir) + strlen(eaccount) +
447 strlen(emailbox) + 40);
448 if (makedir(cachedir) != OKAY)
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 fputs(README5, fp);
457 Fclose(fp);
459 fp = NULL;
460 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
461 if (makedir(buf) != OKAY)
462 goto jleave;
463 snprintf(buf, bufsz, "%s/%s/%s", cachedir, eaccount, emailbox);
464 if (makedir(buf) != OKAY)
465 goto jleave;
466 if (chdir(buf) < 0)
467 goto jleave;
468 if ((dirp = opendir(".")) == NULL)
469 goto jout;
470 while ((dp = readdir(dirp)) != NULL) {
471 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
472 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
473 continue;
474 unlink(dp->d_name);
476 closedir(dirp);
477 fp = Fopen("UIDVALIDITY", "w");
478 jout:
479 if (cwret(cw) == STOP) {
480 n_err(_("Fatal: Cannot change back to current directory.\n"));
481 abort();
483 jleave:
484 NYD_LEAVE;
485 return fp;
488 static unsigned long *
489 builds(long *contentelem)
491 unsigned long n, *contents = NULL;
492 long contentalloc = 0;
493 char *x;
494 DIR *dirp;
495 struct dirent *dp;
496 NYD_ENTER;
498 *contentelem = 0;
499 if ((dirp = opendir(".")) == NULL)
500 goto jleave;
501 while ((dp = readdir(dirp)) != NULL) {
502 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
503 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
504 continue;
505 n = strtoul(dp->d_name, &x, 10);
506 if (*x != '\0')
507 continue;
508 if (*contentelem >= contentalloc - 1)
509 contents = srealloc(contents,
510 (contentalloc += 200) * sizeof *contents);
511 contents[(*contentelem)++] = n;
513 closedir(dirp);
514 if (*contentelem > 0) {
515 contents[*contentelem] = 0;
516 qsort(contents, *contentelem, sizeof *contents, longlt);
518 jleave:
519 NYD_LEAVE;
520 return contents;
523 static void
524 purge(struct mailbox *mp, struct message *m, long mc, struct cw *cw,
525 const char *name)
527 unsigned long *contents;
528 long i, j, contentelem;
529 NYD_ENTER;
530 UNUSED(mp);
532 if (chdir(name) < 0)
533 goto jleave;
534 contents = builds(&contentelem);
535 if (contents != NULL) {
536 i = j = 0;
537 while (j < contentelem) {
538 if (i < mc && m[i].m_uid == contents[j]) {
539 i++;
540 j++;
541 } else if (i < mc && m[i].m_uid < contents[j])
542 i++;
543 else
544 remve(contents[j++]);
546 free(contents);
548 if (cwret(cw) == STOP) {
549 n_err(_("Fatal: Cannot change back to current directory.\n"));
550 abort();
552 jleave:
553 NYD_LEAVE;
556 static int
557 longlt(const void *a, const void *b)
559 union {long l; int i;} u;
560 NYD_ENTER;
562 u.l = *(long const*)a - *(long const*)b;
563 u.i = (u.l < 0) ? -1 : ((u.l > 0) ? 1 : 0);
564 NYD_LEAVE;
565 return u.i;
568 static void
569 remve(unsigned long n)
571 char buf[30];
572 NYD_ENTER;
574 snprintf(buf, sizeof buf, "%lu", n);
575 unlink(buf);
576 NYD_LEAVE;
579 FL void
580 delcache(struct mailbox *mp, struct message *m)
582 char *fn;
583 NYD_ENTER;
585 fn = encuid(mp, m->m_uid);
586 if (fn && unlink(fn) == 0)
587 m->m_flag |= MUNLINKED;
588 NYD_LEAVE;
591 FL enum okay
592 cache_setptr(enum fedit_mode fm, int transparent)
594 struct cw cw;
595 int i, omsgCount = 0;
596 char *name;
597 unsigned long *contents;
598 long contentelem;
599 struct message *omessage = NULL;
600 enum okay rv = STOP;
601 NYD_ENTER;
603 if (transparent) {
604 omessage = message;
605 omsgCount = msgCount;
607 if (mb.mb_cache_directory != NULL) {
608 free(mb.mb_cache_directory);
609 mb.mb_cache_directory = NULL;
611 if ((name = encname(&mb, "", 1, NULL)) == NULL)
612 goto jleave;
613 mb.mb_cache_directory = sstrdup(name);
614 if (cwget(&cw) == STOP)
615 goto jleave;
616 if (chdir(name) < 0)
617 goto jleave;
618 contents = builds(&contentelem);
619 msgCount = contentelem;
620 message = scalloc(msgCount + 1, sizeof *message);
621 if (cwret(&cw) == STOP) {
622 n_err(_("Fatal: Cannot change back to current directory.\n"));
623 abort();
625 cwrelse(&cw);
627 srelax_hold();
628 for (i = 0; i < msgCount; i++) {
629 message[i].m_uid = contents[i];
630 getcache1(&mb, &message[i], NEED_UNSPEC, 3);
631 srelax();
633 srelax_rele();
635 if (contents != NULL)
636 free(contents);
637 mb.mb_type = MB_CACHE;
638 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
639 if (transparent)
640 transflags(omessage, omsgCount, 1);
641 else {
642 if (omessage != NULL)
643 free(omessage);
644 setdot(message);
646 rv = OKAY;
647 jleave:
648 NYD_LEAVE;
649 return rv;
652 FL enum okay
653 cache_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
655 char *name, *cachedir, *eaccount;
656 DIR *dirp;
657 struct dirent *dp;
658 const char *cp, *bp, *sp;
659 int namesz;
660 enum okay rv = STOP;
661 NYD_ENTER;
663 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
664 (cachedir = file_expand(cachedir)) == NULL)
665 goto jleave;
666 eaccount = urlxenc(mp->mb_imap_account, TRU1);
667 name = salloc(namesz = strlen(cachedir) + strlen(eaccount) + 2);
668 snprintf(name, namesz, "%s/%s", cachedir, eaccount);
669 if ((dirp = opendir(name)) == NULL)
670 goto jleave;
671 while ((dp = readdir(dirp)) != NULL) {
672 if (dp->d_name[0] == '.')
673 continue;
674 cp = sp = imap_path_decode(urlxdec(dp->d_name), NULL);
675 for (bp = base; *bp && *bp == *sp; bp++)
676 sp++;
677 if (*bp)
678 continue;
679 cp = strip ? sp : cp;
680 fprintf(fp, "%s\n", *cp ? cp : "INBOX");
682 closedir(dirp);
683 rv = OKAY;
684 jleave:
685 NYD_LEAVE;
686 return rv;
689 FL enum okay
690 cache_remove(const char *name)
692 struct stat st;
693 DIR *dirp;
694 struct dirent *dp;
695 char *path, *dir;
696 int pathsize, pathend, n;
697 enum okay rv = OKAY;
698 NYD_ENTER;
700 if ((dir = encname(&mb, "", 0, imap_fileof(name))) == NULL)
701 goto jleave;
702 pathend = strlen(dir);
703 path = smalloc(pathsize = pathend + 30);
704 memcpy(path, dir, pathend);
705 path[pathend++] = '/';
706 path[pathend] = '\0';
707 if ((dirp = opendir(path)) == NULL) {
708 free(path);
709 goto jleave;
711 while ((dp = readdir(dirp)) != NULL) {
712 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
713 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
714 continue;
715 n = strlen(dp->d_name) + 1;
716 if (pathend + n > pathsize)
717 path = srealloc(path, pathsize = pathend + n + 30);
718 memcpy(path + pathend, dp->d_name, n);
719 if (stat(path, &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)
720 continue;
721 if (unlink(path) < 0) {
722 n_perr(path, 0);
723 closedir(dirp);
724 free(path);
725 rv = STOP;
726 goto jleave;
729 closedir(dirp);
730 path[pathend] = '\0';
731 rmdir(path); /* no error on failure, might contain submailboxes */
732 free(path);
733 jleave:
734 NYD_LEAVE;
735 return rv;
738 FL enum okay
739 cache_rename(const char *old, const char *new)
741 char *olddir, *newdir;
742 enum okay rv = OKAY;
743 NYD_ENTER;
745 if ((olddir = encname(&mb, "", 0, imap_fileof(old))) == NULL ||
746 (newdir = encname(&mb, "",0, imap_fileof(new))) == NULL)
747 goto jleave;
748 if (rename(olddir, newdir) < 0) {
749 n_perr(olddir, 0);
750 rv = STOP;
752 jleave:
753 NYD_LEAVE;
754 return rv;
757 FL unsigned long
758 cached_uidvalidity(struct mailbox *mp)
760 FILE *uvfp;
761 char *uvname;
762 unsigned long uv;
763 NYD_ENTER;
765 if ((uvname = encname(mp, "UIDVALIDITY", 1, NULL)) == NULL) {
766 uv = 0;
767 goto jleave;
769 if ((uvfp = Fopen(uvname, "r")) == NULL ||
770 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
771 fscanf(uvfp, "%lu", &uv) != 1)
772 uv = 0;
773 if (uvfp != NULL)
774 Fclose(uvfp);
775 jleave:
776 NYD_LEAVE;
777 return uv;
780 static FILE *
781 cache_queue1(struct mailbox *mp, char const *mode, char **xname)
783 char *name;
784 FILE *fp = NULL;
785 NYD_ENTER;
787 if ((name = encname(mp, "QUEUE", 0, NULL)) == NULL)
788 goto jleave;
789 if ((fp = Fopen(name, mode)) != NULL)
790 file_lock(fileno(fp), FLT_WRITE, 0,0, 0);
791 if (xname)
792 *xname = name;
793 jleave:
794 NYD_LEAVE;
795 return fp;
798 FL FILE *
799 cache_queue(struct mailbox *mp)
801 FILE *fp;
802 NYD_ENTER;
804 fp = cache_queue1(mp, "a", NULL);
805 if (fp == NULL)
806 n_err(_("Cannot queue IMAP command. Retry when online.\n"));
807 NYD_LEAVE;
808 return fp;
811 FL enum okay
812 cache_dequeue(struct mailbox *mp)
814 int bufsz;
815 char *cachedir, *eaccount, *buf, *oldbox;
816 DIR *dirp;
817 struct dirent *dp;
818 enum okay rv = OKAY;
819 NYD_ENTER;
821 if ((cachedir = ok_vlook(imap_cache)) == NULL ||
822 (cachedir = file_expand(cachedir)) == NULL)
823 goto jleave;
824 eaccount = urlxenc(mp->mb_imap_account, TRU1);
825 buf = salloc(bufsz = strlen(cachedir) + strlen(eaccount) + 2);
826 snprintf(buf, bufsz, "%s/%s", cachedir, eaccount);
827 if ((dirp = opendir(buf)) == NULL)
828 goto jleave;
829 oldbox = mp->mb_imap_mailbox;
830 while ((dp = readdir(dirp)) != NULL) {
831 if (dp->d_name[0] == '.')
832 continue;
833 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
834 * (but wuuuuh, what a shit!) */
835 mp->mb_imap_mailbox = sstrdup(
836 imap_path_decode(urlxdec(dp->d_name), NULL));
837 dequeue1(mp);
838 { char *x = mp->mb_imap_mailbox;
839 mp->mb_imap_mailbox = oldbox;
840 free(x);
843 closedir(dirp);
844 jleave:
845 NYD_LEAVE;
846 return rv;
849 static enum okay
850 dequeue1(struct mailbox *mp)
852 FILE *fp = NULL, *uvfp = NULL;
853 char *qname, *uvname;
854 unsigned long uv;
855 off_t is_size;
856 int is_count;
857 enum okay rv = OKAY;
858 NYD_ENTER;
860 fp = cache_queue1(mp, "r+", &qname);
861 if (fp != NULL && fsize(fp) > 0) {
862 if (imap_select(mp, &is_size, &is_count, mp->mb_imap_mailbox, FEDIT_NONE)
863 != OKAY) {
864 n_err(_("Cannot select \"%s\" for dequeuing.\n"), mp->mb_imap_mailbox);
865 goto jsave;
867 if ((uvname = encname(mp, "UIDVALIDITY", 0, NULL)) == NULL ||
868 (uvfp = Fopen(uvname, "r")) == NULL ||
869 (file_lock(fileno(uvfp), FLT_READ, 0,0, 0), 0) ||
870 fscanf(uvfp, "%lu", &uv) != 1 || uv != mp->mb_uidvalidity) {
871 n_err(_("Unique identifiers for \"%s\" are out of date. "
872 "Cannot commit IMAP commands.\n"), mp->mb_imap_mailbox);
873 jsave:
874 n_err(_("Saving IMAP commands to *DEAD*\n"));
875 savedeadletter(fp, 0);
876 ftruncate(fileno(fp), 0);
877 Fclose(fp);
878 if (uvfp)
879 Fclose(uvfp);
880 rv = STOP;
881 goto jleave;
883 Fclose(uvfp);
884 printf("Committing IMAP commands for \"%s\"\n", mp->mb_imap_mailbox);
885 imap_dequeue(mp, fp);
887 if (fp) {
888 Fclose(fp);
889 unlink(qname);
891 jleave:
892 NYD_LEAVE;
893 return rv;
895 #endif /* HAVE_IMAP */
897 /* s-it-mode */