THANKS: Solar Designer
[s-mailx.git] / obs-imap.c
blob748b71b456267108168436e279c9e9a52d467d15
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 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
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_IMAP
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 EMPTY_FILE()
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() \
62 if (mp->mb_type != MB_CACHE) {\
63 enum okay ok = OKAY;\
64 while (mp->mb_active & MB_COMD)\
65 ok = imap_answer(mp, 1);\
66 if (ok == STOP)\
67 return STOP;\
71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
72 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
73 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
74 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
75 * TODO I.e., that should be a function, not a macro ... or so.
76 * TODO This entire module needs MASSIVE work! */
77 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
80 if (mp->mb_type != MB_CACHE) {\
81 if (imap_finish(mp) == STOP) {\
82 ACTIONBAIL;\
84 if (n_poption & n_PO_VERBVERB)\
85 n_err(">>> %s", X);\
86 mp->mb_active |= Y;\
87 if (swrite(&mp->mb_sock, X) == STOP) {\
88 ACTIONERR;\
90 } else {\
91 if (queuefp != NULL)\
92 fputs(X, queuefp);\
96 static struct record {
97 struct record *rec_next;
98 unsigned long rec_count;
99 enum rec_type {
100 REC_EXISTS,
101 REC_EXPUNGE
102 } rec_type;
103 } *record, *recend;
105 static enum {
106 RESPONSE_TAGGED,
107 RESPONSE_DATA,
108 RESPONSE_FATAL,
109 RESPONSE_CONT,
110 RESPONSE_ILLEGAL
111 } response_type;
113 static enum {
114 RESPONSE_OK,
115 RESPONSE_NO,
116 RESPONSE_BAD,
117 RESPONSE_PREAUTH,
118 RESPONSE_BYE,
119 RESPONSE_OTHER,
120 RESPONSE_UNKNOWN
121 } response_status;
123 static char *responded_tag;
124 static char *responded_text;
125 static char *responded_other_text;
126 static long responded_other_number;
128 static enum {
129 MAILBOX_DATA_FLAGS,
130 MAILBOX_DATA_LIST,
131 MAILBOX_DATA_LSUB,
132 MAILBOX_DATA_MAILBOX,
133 MAILBOX_DATA_SEARCH,
134 MAILBOX_DATA_STATUS,
135 MAILBOX_DATA_EXISTS,
136 MAILBOX_DATA_RECENT,
137 MESSAGE_DATA_EXPUNGE,
138 MESSAGE_DATA_FETCH,
139 CAPABILITY_DATA,
140 RESPONSE_OTHER_UNKNOWN
141 } response_other;
143 static enum list_attributes {
144 LIST_NONE = 000,
145 LIST_NOINFERIORS = 001,
146 LIST_NOSELECT = 002,
147 LIST_MARKED = 004,
148 LIST_UNMARKED = 010
149 } list_attributes;
151 static int list_hierarchy_delimiter;
152 static char *list_name;
154 struct list_item {
155 struct list_item *l_next;
156 char *l_name;
157 char *l_base;
158 enum list_attributes l_attr;
159 int l_delim;
160 int l_level;
161 int l_has_children;
164 static char *imapbuf; /* TODO not static, use pool */
165 static size_t imapbufsize;
166 static sigjmp_buf imapjmp;
167 static sighandler_type savealrm;
168 static int imapkeepalive;
169 static long had_exists = -1;
170 static long had_expunge = -1;
171 static long expunged_messages;
172 static int volatile imaplock;
173 static int same_imap_account;
174 static bool_t _imap_rdonly;
176 static char *imap_quotestr(char const *s);
177 static char *imap_unquotestr(char const *s);
178 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
179 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
180 /* Returns NULL on error */
181 static char *imap_path_quote(struct mailbox *mp, char const *cp);
182 static void imap_other_get(char *pp);
183 static void imap_response_get(const char **cp);
184 static void imap_response_parse(void);
185 static enum okay imap_answer(struct mailbox *mp, int errprnt);
186 static enum okay imap_parse_list(void);
187 static enum okay imap_finish(struct mailbox *mp);
188 static void imap_timer_off(void);
189 static void imapcatch(int s);
190 static void _imap_maincatch(int s);
191 static enum okay imap_noop1(struct mailbox *mp);
192 static void rec_queue(enum rec_type type, unsigned long cnt);
193 static enum okay rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s);
196 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
197 static enum okay imap_capability(struct mailbox *mp);
198 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
199 #ifdef HAVE_MD5
200 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
201 #endif
202 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
203 #ifdef HAVE_GSSAPI
204 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
205 #endif
206 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
207 static void imap_init(struct mailbox *mp, int n);
208 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
209 int *prevcount);
210 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
211 struct url *urlp);
212 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
213 int transparent);
214 static int imap_fetchdata(struct mailbox *mp, struct message *m,
215 size_t expected, int need, const char *head,
216 size_t headsize, long headlines);
217 static void imap_putstr(struct mailbox *mp, struct message *m,
218 const char *str, const char *head, size_t headsize,
219 long headlines);
220 static enum okay imap_get(struct mailbox *mp, struct message *m,
221 enum needspec need);
222 static void commitmsg(struct mailbox *mp, struct message *to,
223 struct message *from, enum content_info content_info);
224 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
225 int bot, int top);
226 static enum okay imap_exit(struct mailbox *mp);
227 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
228 int needstat);
229 static enum okay imap_close(struct mailbox *mp);
230 static enum okay imap_update(struct mailbox *mp);
231 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
232 int c, const char *sp, int needstat);
233 static enum okay imap_unstore(struct message *m, int n, const char *flag);
234 static const char *tag(int new);
235 static char * imap_putflags(int f);
236 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
237 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
238 off_t off1, long xsize, enum mflag flag, time_t t);
239 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
240 long offset);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend,
243 int level);
244 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
245 FILE *fp);
246 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
247 const char *name);
248 static enum okay imap_copyuid_parse(const char *cp,
249 unsigned long *uidvalidity, unsigned long *olduid,
250 unsigned long *newuid);
251 static enum okay imap_appenduid_parse(const char *cp,
252 unsigned long *uidvalidity, unsigned long *uid);
253 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
254 const char *name);
255 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
256 long off1, long xsize, long size, long lines, int flag,
257 const char *name);
258 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
259 #ifdef HAVE_IMAP_SEARCH
260 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
261 const char *spec, int f);
262 #endif
263 static enum okay imap_remove1(struct mailbox *mp, const char *name);
264 static enum okay imap_rename1(struct mailbox *mp, const char *old,
265 const char *new);
266 static char * imap_strex(char const *cp, char const **xp);
267 static enum okay check_expunged(void);
269 static char *
270 imap_quotestr(char const *s)
272 char *n, *np;
273 NYD2_ENTER;
275 np = n = salloc(2 * strlen(s) + 3);
276 *np++ = '"';
277 while (*s) {
278 if (*s == '"' || *s == '\\')
279 *np++ = '\\';
280 *np++ = *s++;
282 *np++ = '"';
283 *np = '\0';
284 NYD2_LEAVE;
285 return n;
288 static char *
289 imap_unquotestr(char const *s)
291 char *n, *np;
292 NYD2_ENTER;
294 if (*s != '"') {
295 n = savestr(s);
296 goto jleave;
299 np = n = salloc(strlen(s) + 1);
300 while (*++s) {
301 if (*s == '\\')
302 s++;
303 else if (*s == '"')
304 break;
305 *np++ = *s;
307 *np = '\0';
308 jleave:
309 NYD2_LEAVE;
310 return n;
313 static void
314 imap_delim_init(struct mailbox *mp, struct url const *urlp){
315 size_t i;
316 char const *cp;
317 NYD2_ENTER;
319 mp->mb_imap_delim[0] = '\0';
321 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
322 i = strlen(cp);
324 if(i == 0){
325 cp = n_IMAP_DELIM;
326 i = sizeof(n_IMAP_DELIM) -1;
327 goto jcopy;
330 if(i < n_NELEM(mp->mb_imap_delim))
331 jcopy:
332 memcpy(&mb.mb_imap_delim[0], cp, i +1);
333 else
334 n_err(_("*imap-delim* for %s is too long: %s\n"),
335 urlp->url_input, cp);
337 NYD2_LEAVE;
340 static char const *
341 imap_path_normalize(struct mailbox *mp, char const *cp){
342 char *rv_base, *rv, dc2, dc, c, lc;
343 char const *dcp;
344 NYD2_ENTER;
346 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
347 * exactly what i have specified" */
348 if(mp == NULL || mp->mb_imap_delim[0] == '\0')
349 dcp = &n_IMAP_DELIM[0];
350 else
351 dcp = &mp->mb_imap_delim[0];
352 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
354 /* Plain names don't need path quoting */
355 /* C99 */{
356 size_t i, j;
357 char const *cpx;
359 for(cpx = cp;; ++cpx)
360 if((c = *cpx) == '\0')
361 goto jleave;
362 else if(dc == '\0'){
363 if(strchr(n_IMAP_DELIM, c)){
364 dc = c;
365 break;
367 }else if(c == dc)
368 break;
369 else if(dc2 && strchr(dcp, c) != NULL)
370 break;
372 /* And we don't need to reevaluate what we have seen yet */
373 i = PTR2SIZE(cpx - cp);
374 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
375 if(i > 0)
376 memcpy(rv, cp, i);
377 memcpy(&rv[i], cpx, j);
378 rv += i;
379 cp = cpx;
382 /* Squeeze adjacent delimiters, convert remain to dc */
383 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
384 if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
385 c = dc;
386 if(c != dc || lc != dc)
387 *rv++ = c;
389 *rv = '\0';
391 cp = rv_base;
392 jleave:
393 NYD2_LEAVE;
394 return cp;
397 FL char const *
398 imap_path_encode(char const *cp, bool_t *err_or_null){
399 /* To a large extend inspired by dovecot(1) */
400 struct str in, out;
401 bool_t err_def;
402 ui8_t *be16p_base, *be16p;
403 char const *emsg;
404 char c;
405 size_t l, l_plain;
406 NYD2_ENTER;
408 if(err_or_null == NULL)
409 err_or_null = &err_def;
410 *err_or_null = FAL0;
412 /* Is this a string that works out as "plain US-ASCII"? */
413 for(l = 0;; ++l)
414 if((c = cp[l]) == '\0')
415 goto jleave;
416 else if(c <= 0x1F || c >= 0x7F || c == '&')
417 break;
419 *err_or_null = TRU1;
421 /* We need to encode in mUTF-7! For that, we first have to convert the
422 * local charset to UTF-8, then convert all characters which need to be
423 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
424 * We can skip the UTF-8 conversion occasionally, however */
425 if(!(n_psonce & n_PSO_UNICODE)){
426 int ir;
427 iconv_t icd;
429 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
431 if((icd = iconv_open("utf-8", ok_vlook(ttycharset))) == (iconv_t)-1)
432 goto jerr;
434 out.s = NULL, out.l = 0;
435 in.s = n_UNCONST(cp); /* logical */
436 l += strlen(&cp[l]);
437 in.l = l;
438 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
439 cp = savestrbuf(out.s, out.l);
441 if(out.s != NULL)
442 free(out.s);
443 iconv_close(icd);
445 if(ir != 0)
446 goto jerr;
449 * So: Why not start all over again?
452 /* Is this a string that works out as "plain US-ASCII"? */
453 for(l = 0;; ++l)
454 if((c = cp[l]) == '\0')
455 goto jleave;
456 else if(c <= 0x1F || c >= 0x7F || c == '&')
457 break;
460 /* We need to encode, save what we have, encode the rest */
461 l_plain = l;
463 for(cp += l, l = 0; cp[l] != '\0'; ++l)
465 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
467 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
468 if(l_plain > 0)
469 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
470 else
471 out.l = 0;
472 DBG( l_plain += (l << 2); )
474 while(l > 0){
475 c = *cp++;
476 --l;
478 if(c == '&'){
479 out.s[out.l + 0] = '&';
480 out.s[out.l + 1] = '-';
481 out.l += 2;
482 }else if(c > 0x1F && c < 0x7F)
483 out.s[out.l++] = c;
484 else{
485 static char const mb64ct[] =
486 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
487 ui32_t utf32;
489 /* Convert consecutive non-representables */
490 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
492 for(be16p = be16p_base, --cp, ++l;;){
493 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
494 goto jerr;
496 /* TODO S-CText: magic utf16 conversions */
497 if(utf32 < 0x10000){
498 be16p[1] = utf32 & 0xFF;
499 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
500 be16p += 2;
501 }else{
502 ui16_t s7e;
504 utf32 -= 0x10000;
505 s7e = 0xD800u | (utf32 >> 10);
506 be16p[1] = s7e & 0xFF;
507 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
508 s7e = 0xDC00u | (utf32 &= 0x03FF);
509 be16p[3] = s7e & 0xFF;
510 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
511 be16p += 4;
514 if(l == 0)
515 break;
516 if((c = *cp) > 0x1F && c < 0x7F)
517 break;
520 /* And then warp that UTF-16BE to mUTF-7 */
521 out.s[out.l++] = '&';
522 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
523 be16p = be16p_base;
525 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
526 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
527 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
528 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
529 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
530 out.l += 4;
532 if(utf32 > 0){
533 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
534 if(--utf32 == 0){
535 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
536 out.l += 2;
537 }else{
538 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
539 (be16p[1] >> 4)];
540 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
541 out.l += 3;
544 out.s[out.l++] = '-';
547 out.s[out.l] = '\0';
548 assert(out.l <= l_plain);
549 *err_or_null = FAL0;
550 cp = out.s;
551 jleave:
552 NYD2_LEAVE;
553 return cp;
554 jerr:
555 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
556 goto jleave;
559 FL char *
560 imap_path_decode(char const *path, bool_t *err_or_null){
561 /* To a large extend inspired by dovecot(1) TODO use string */
562 struct str in, out;
563 bool_t err_def;
564 ui8_t *mb64p_base, *mb64p, *mb64xp;
565 char const *emsg, *cp;
566 char *rv_base, *rv, c;
567 size_t l_orig, l, i;
568 NYD2_ENTER;
570 if(err_or_null == NULL)
571 err_or_null = &err_def;
572 *err_or_null = FAL0;
574 l = l_orig = strlen(path);
575 rv = rv_base = salloc(l << 1);
576 memcpy(rv, path, l +1);
578 /* xxx Don't check for invalid characters from malicious servers */
579 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
580 goto jleave;
582 *err_or_null = TRU1;
584 emsg = N_("Invalid mUTF-7 encoding");
585 i = PTR2SIZE(cp - path);
586 rv += i;
587 l -= i;
588 mb64p_base = NULL;
590 while(l > 0){
591 if((c = *cp) != '&'){
592 if(c <= 0x1F || c >= 0x7F){
593 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
594 goto jerr;
596 *rv++ = c;
597 ++cp;
598 --l;
599 }else if(--l == 0)
600 goto jeincpl;
601 else if(*++cp == '-'){
602 *rv++ = '&';
603 ++cp;
604 --l;
605 }else if(l < 3){
606 jeincpl:
607 emsg = N_("Invalid mUTF-7: incomplete input");
608 goto jerr;
609 }else{
610 /* mUTF-7 -> UTF-16BE -> UTF-8 */
611 static ui8_t const mb64dt[256] = {
612 #undef XX
613 #define XX 0xFFu
614 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
615 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
616 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
617 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
618 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
619 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
620 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
621 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
622 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
623 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
624 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
625 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
626 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
627 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
628 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
629 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
632 if(mb64p_base == NULL)
633 mb64p_base = salloc(l);
635 /* Decode the mUTF-7 to what is indeed UTF-16BE */
636 for(mb64p = mb64p_base;;){
637 assert(l >= 3);
638 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
639 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
640 goto jerr;
641 mb64p += 2;
643 c = cp[2];
644 cp += 3;
645 l -= 3;
646 if(c == '-')
647 break;
648 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
649 goto jerr;
651 if(l == 0)
652 goto jerr;
653 --l;
654 if((c = *cp++) == '-')
655 break;
656 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
657 goto jerr;
659 if(l < 3){
660 if(l > 0 && *cp == '-'){
661 --l;
662 ++cp;
663 break;
665 goto jerr;
668 #undef XX
670 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
671 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
672 goto jerr;
675 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
676 i = PTR2SIZE(mb64p - mb64p_base);
677 mb64p = mb64xp = mb64p_base;
679 while(i > 0){
680 ui8_t ul, u0, u1, u2, u3;
682 ul = (i >= 4) ? 4 : i & 0x3;
683 i -= ul;
684 u0 = mb64xp[0];
685 u1 = mb64xp[1];
686 u2 = (ul < 3) ? 0 : mb64xp[2];
687 u3 = (ul < 4) ? 0 : mb64xp[3];
688 mb64xp += ul;
689 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
690 if(ul < 3)
691 break;
692 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
693 if(ul < 4)
694 break;
695 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
698 /* UTF-16BE we convert to UTF-8 */
699 i = PTR2SIZE(mb64p - mb64p_base);
700 if(i & 1){
701 emsg = N_("Odd bytecount for UTF-16BE input");
702 goto jerr;
705 /* TODO S-CText: magic utf16 conversions */
706 emsg = N_("Invalid UTF-16BE encoding");
708 for(mb64p = mb64p_base; i > 0;){
709 ui32_t utf32;
710 ui16_t uhi, ulo;
712 uhi = mb64p[0];
713 uhi <<= 8;
714 uhi |= mb64p[1];
716 /* Not a surrogate? */
717 if(uhi < 0xD800 || uhi > 0xDFFF){
718 utf32 = uhi;
719 mb64p += 2;
720 i -= 2;
721 }else if(uhi > 0xDBFF)
722 goto jerr;
723 else if(i < 4){
724 emsg = N_("Incomplete UTF-16BE surrogate pair");
725 goto jerr;
726 }else{
727 ulo = mb64p[2];
728 ulo <<= 8;
729 ulo |= mb64p[3];
730 if(ulo < 0xDC00 || ulo > 0xDFFF)
731 goto jerr;
733 utf32 = (uhi &= 0x03FF);
734 utf32 <<= 10;
735 utf32 += 0x10000;
736 utf32 |= (ulo &= 0x03FF);
737 mb64p += 4;
738 i -= 4;
741 utf32 = n_utf32_to_utf8(utf32, rv);
742 rv += utf32;
746 *rv = '\0';
748 /* We can skip the UTF-8 conversion occasionally */
749 if(!(n_psonce & n_PSO_UNICODE)){
750 int ir;
751 iconv_t icd;
753 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
755 if((icd = iconv_open(ok_vlook(ttycharset), "utf-8")) == (iconv_t)-1)
756 goto jerr;
758 out.s = NULL, out.l = 0;
759 in.l = strlen(in.s = rv_base);
760 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
761 /* Because the length of this is unpredictable, copy */
762 rv_base = savestrbuf(out.s, out.l);
764 if(out.s != NULL)
765 free(out.s);
766 iconv_close(icd);
768 if(ir != 0)
769 goto jerr;
772 *err_or_null = FAL0;
773 rv = rv_base;
774 jleave:
775 NYD2_LEAVE;
776 return rv;
777 jerr:
778 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
779 memcpy(rv = rv_base, path, ++l_orig);
780 goto jleave;
783 static char *
784 imap_path_quote(struct mailbox *mp, char const *cp){
785 bool_t err;
786 char *rv;
787 NYD2_ENTER;
789 cp = imap_path_normalize(mp, cp);
790 cp = imap_path_encode(cp, &err);
791 rv = err ? NULL : imap_quotestr(cp);
792 NYD2_LEAVE;
793 return rv;
796 static void
797 imap_other_get(char *pp)
799 char *xp;
800 NYD2_ENTER;
802 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
803 pp += 6;
804 response_other = MAILBOX_DATA_FLAGS;
805 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
806 pp += 5;
807 response_other = MAILBOX_DATA_LIST;
808 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
809 pp += 5;
810 response_other = MAILBOX_DATA_LSUB;
811 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
812 pp += 8;
813 response_other = MAILBOX_DATA_MAILBOX;
814 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
815 pp += 7;
816 response_other = MAILBOX_DATA_SEARCH;
817 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
818 pp += 7;
819 response_other = MAILBOX_DATA_STATUS;
820 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
821 pp += 11;
822 response_other = CAPABILITY_DATA;
823 } else {
824 responded_other_number = strtol(pp, &xp, 10);
825 while (*xp == ' ')
826 ++xp;
827 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
828 response_other = MAILBOX_DATA_EXISTS;
829 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
830 response_other = MAILBOX_DATA_RECENT;
831 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
832 response_other = MESSAGE_DATA_EXPUNGE;
833 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
834 pp = &xp[6];
835 response_other = MESSAGE_DATA_FETCH;
836 } else
837 response_other = RESPONSE_OTHER_UNKNOWN;
839 responded_other_text = pp;
840 NYD2_LEAVE;
843 static void
844 imap_response_get(const char **cp)
846 NYD2_ENTER;
847 if (ascncasecmp(*cp, "OK ", 3) == 0) {
848 *cp += 3;
849 response_status = RESPONSE_OK;
850 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
851 *cp += 3;
852 response_status = RESPONSE_NO;
853 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
854 *cp += 4;
855 response_status = RESPONSE_BAD;
856 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
857 *cp += 8;
858 response_status = RESPONSE_PREAUTH;
859 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
860 *cp += 4;
861 response_status = RESPONSE_BYE;
862 } else
863 response_status = RESPONSE_OTHER;
864 NYD2_LEAVE;
867 static void
868 imap_response_parse(void)
870 static char *parsebuf; /* TODO Use pool */
871 static size_t parsebufsize;
873 const char *ip = imapbuf;
874 char *pp;
875 NYD2_ENTER;
877 if (parsebufsize < imapbufsize + 1)
878 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
879 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
880 pp = parsebuf;
881 switch (*ip) {
882 case '+':
883 response_type = RESPONSE_CONT;
884 ip++;
885 pp++;
886 while (*ip == ' ') {
887 ip++;
888 pp++;
890 break;
891 case '*':
892 ip++;
893 pp++;
894 while (*ip == ' ') {
895 ip++;
896 pp++;
898 imap_response_get(&ip);
899 pp = &parsebuf[ip - imapbuf];
900 switch (response_status) {
901 case RESPONSE_BYE:
902 response_type = RESPONSE_FATAL;
903 break;
904 default:
905 response_type = RESPONSE_DATA;
907 break;
908 default:
909 responded_tag = parsebuf;
910 while (*pp && *pp != ' ')
911 pp++;
912 if (*pp == '\0') {
913 response_type = RESPONSE_ILLEGAL;
914 break;
916 *pp++ = '\0';
917 while (*pp && *pp == ' ')
918 pp++;
919 if (*pp == '\0') {
920 response_type = RESPONSE_ILLEGAL;
921 break;
923 ip = &imapbuf[pp - parsebuf];
924 response_type = RESPONSE_TAGGED;
925 imap_response_get(&ip);
926 pp = &parsebuf[ip - imapbuf];
928 responded_text = pp;
929 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
930 response_status == RESPONSE_OTHER)
931 imap_other_get(pp);
932 NYD2_LEAVE;
935 static enum okay
936 imap_answer(struct mailbox *mp, int errprnt)
938 int i, complete;
939 enum okay rv;
940 NYD2_ENTER;
942 rv = OKAY;
943 if (mp->mb_type == MB_CACHE)
944 goto jleave;
945 rv = STOP;
946 jagain:
947 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
948 if (n_poption & n_PO_VERBVERB)
949 fputs(imapbuf, stderr);
950 imap_response_parse();
951 if (response_type == RESPONSE_ILLEGAL)
952 goto jagain;
953 if (response_type == RESPONSE_CONT) {
954 rv = OKAY;
955 goto jleave;
957 if (response_status == RESPONSE_OTHER) {
958 if (response_other == MAILBOX_DATA_EXISTS) {
959 had_exists = responded_other_number;
960 rec_queue(REC_EXISTS, responded_other_number);
961 if (had_expunge > 0)
962 had_expunge = 0;
963 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
964 rec_queue(REC_EXPUNGE, responded_other_number);
965 if (had_expunge < 0)
966 had_expunge = 0;
967 had_expunge++;
968 expunged_messages++;
971 complete = 0;
972 if (response_type == RESPONSE_TAGGED) {
973 if (asccasecmp(responded_tag, tag(0)) == 0)
974 complete |= 1;
975 else
976 goto jagain;
978 switch (response_status) {
979 case RESPONSE_PREAUTH:
980 mp->mb_active &= ~MB_PREAUTH;
981 /*FALLTHRU*/
982 case RESPONSE_OK:
983 jokay:
984 rv = OKAY;
985 complete |= 2;
986 break;
987 case RESPONSE_NO:
988 case RESPONSE_BAD:
989 jstop:
990 complete |= 2;
991 if (errprnt)
992 n_err(_("IMAP error: %s"), responded_text);
993 break;
994 case RESPONSE_UNKNOWN: /* does not happen */
995 case RESPONSE_BYE:
996 i = mp->mb_active;
997 mp->mb_active = MB_NONE;
998 if (i & MB_BYE)
999 goto jokay;
1000 goto jstop;
1001 case RESPONSE_OTHER:
1002 rv = OKAY;
1003 break;
1005 if (response_status != RESPONSE_OTHER &&
1006 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
1007 n_err(_("IMAP alert: %s"), &responded_text[8]);
1008 if (complete == 3)
1009 mp->mb_active &= ~MB_COMD;
1010 } else
1011 mp->mb_active = MB_NONE;
1012 jleave:
1013 NYD2_LEAVE;
1014 return rv;
1017 static enum okay
1018 imap_parse_list(void)
1020 char *cp;
1021 enum okay rv;
1022 NYD2_ENTER;
1024 rv = STOP;
1026 cp = responded_other_text;
1027 list_attributes = LIST_NONE;
1028 if (*cp == '(') {
1029 while (*cp && *cp != ')') {
1030 if (*cp == '\\') {
1031 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1032 list_attributes |= LIST_NOINFERIORS;
1033 cp += 12;
1034 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1035 list_attributes |= LIST_NOSELECT;
1036 cp += 9;
1037 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1038 list_attributes |= LIST_MARKED;
1039 cp += 7;
1040 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1041 list_attributes |= LIST_UNMARKED;
1042 cp += 9;
1045 cp++;
1047 if (*++cp != ' ')
1048 goto jleave;
1049 while (*cp == ' ')
1050 cp++;
1053 list_hierarchy_delimiter = EOF;
1054 if (*cp == '"') {
1055 if (*++cp == '\\')
1056 cp++;
1057 list_hierarchy_delimiter = *cp++ & 0377;
1058 if (cp[0] != '"' || cp[1] != ' ')
1059 goto jleave;
1060 cp++;
1061 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1062 list_hierarchy_delimiter = EOF;
1063 cp += 3;
1066 while (*cp == ' ')
1067 cp++;
1068 list_name = cp;
1069 while (*cp && *cp != '\r')
1070 cp++;
1071 *cp = '\0';
1072 rv = OKAY;
1073 jleave:
1074 NYD2_LEAVE;
1075 return rv;
1078 static enum okay
1079 imap_finish(struct mailbox *mp)
1081 NYD_ENTER;
1082 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1083 imap_answer(mp, 1);
1084 NYD_LEAVE;
1085 return OKAY;
1088 static void
1089 imap_timer_off(void)
1091 NYD_ENTER;
1092 if (imapkeepalive > 0) {
1093 alarm(0);
1094 safe_signal(SIGALRM, savealrm);
1096 NYD_LEAVE;
1099 static void
1100 imapcatch(int s)
1102 NYD_X; /* Signal handler */
1103 switch (s) {
1104 case SIGINT:
1105 n_err_sighdl(_("Interrupt\n"));
1106 siglongjmp(imapjmp, 1);
1107 /*NOTREACHED*/
1108 case SIGPIPE:
1109 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1110 break;
1114 static void
1115 _imap_maincatch(int s)
1117 NYD_X; /* Signal handler */
1118 n_UNUSED(s);
1119 if (interrupts++ == 0) {
1120 n_err_sighdl(_("Interrupt\n"));
1121 return;
1123 n_go_onintr_for_imap();
1126 static enum okay
1127 imap_noop1(struct mailbox *mp)
1129 char o[LINESIZE];
1130 FILE *queuefp = NULL;
1131 NYD_X;
1133 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1134 IMAP_OUT(o, MB_COMD, return STOP)
1135 IMAP_ANSWER()
1136 return OKAY;
1139 FL char const *
1140 imap_fileof(char const *xcp)
1142 char const *cp = xcp;
1143 int state = 0;
1144 NYD_ENTER;
1146 while (*cp) {
1147 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1148 cp += 3;
1149 state = 1;
1151 if (cp[0] == '/' && state == 1) {
1152 ++cp;
1153 goto jleave;
1155 if (cp[0] == '/') {
1156 cp = xcp;
1157 goto jleave;
1159 ++cp;
1161 jleave:
1162 NYD_LEAVE;
1163 return cp;
1166 FL enum okay
1167 imap_noop(void)
1169 sighandler_type volatile oldint, oldpipe;
1170 enum okay volatile rv = STOP;
1171 NYD_ENTER;
1173 if (mb.mb_type != MB_IMAP)
1174 goto jleave;
1176 imaplock = 1;
1177 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1178 safe_signal(SIGINT, &_imap_maincatch);
1179 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1180 if (sigsetjmp(imapjmp, 1) == 0) {
1181 if (oldpipe != SIG_IGN)
1182 safe_signal(SIGPIPE, imapcatch);
1184 rv = imap_noop1(&mb);
1186 safe_signal(SIGINT, oldint);
1187 safe_signal(SIGPIPE, oldpipe);
1188 imaplock = 0;
1189 jleave:
1190 NYD_LEAVE;
1191 if (interrupts)
1192 n_go_onintr_for_imap();
1193 return rv;
1196 static void
1197 rec_queue(enum rec_type rt, unsigned long cnt)
1199 struct record *rp;
1200 NYD_ENTER;
1202 rp = scalloc(1, sizeof *rp);
1203 rp->rec_type = rt;
1204 rp->rec_count = cnt;
1205 if (record && recend) {
1206 recend->rec_next = rp;
1207 recend = rp;
1208 } else
1209 record = recend = rp;
1210 NYD_LEAVE;
1213 static enum okay
1214 rec_dequeue(void)
1216 struct message *omessage;
1217 struct record *rp, *rq;
1218 uiz_t exists = 0, i;
1219 enum okay rv = STOP;
1220 NYD_ENTER;
1222 if (record == NULL)
1223 goto jleave;
1225 omessage = message;
1226 message = smalloc((msgCount+1) * sizeof *message);
1227 if (msgCount)
1228 memcpy(message, omessage, msgCount * sizeof *message);
1229 memset(&message[msgCount], 0, sizeof *message);
1231 rp = record, rq = NULL;
1232 rv = OKAY;
1233 while (rp != NULL) {
1234 switch (rp->rec_type) {
1235 case REC_EXISTS:
1236 exists = rp->rec_count;
1237 break;
1238 case REC_EXPUNGE:
1239 if (rp->rec_count == 0) {
1240 rv = STOP;
1241 break;
1243 if (rp->rec_count > (unsigned long)msgCount) {
1244 if (exists == 0 || rp->rec_count > exists--)
1245 rv = STOP;
1246 break;
1248 if (exists > 0)
1249 exists--;
1250 delcache(&mb, &message[rp->rec_count-1]);
1251 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1252 ((msgCount - rp->rec_count + 1) * sizeof *message));
1253 --msgCount;
1254 /* If the message was part of a collapsed thread,
1255 * the m_collapsed field of one of its ancestors
1256 * should be incremented. It seems hardly possible
1257 * to do this with the current message structure,
1258 * though. The result is that a '+' may be shown
1259 * in the header summary even if no collapsed
1260 * children exists */
1261 break;
1263 if (rq != NULL)
1264 free(rq);
1265 rq = rp;
1266 rp = rp->rec_next;
1268 if (rq != NULL)
1269 free(rq);
1271 record = recend = NULL;
1272 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1273 message = srealloc(message, (exists + 1) * sizeof *message);
1274 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1275 for (i = msgCount; i < exists; ++i)
1276 imap_init(&mb, i);
1277 imap_flags(&mb, msgCount+1, exists);
1278 msgCount = exists;
1281 if (rv == STOP) {
1282 free(message);
1283 message = omessage;
1285 jleave:
1286 NYD_LEAVE;
1287 return rv;
1290 static void
1291 rec_rmqueue(void)
1293 struct record *rp;
1294 NYD_ENTER;
1296 for (rp = record; rp != NULL;) {
1297 struct record *tmp = rp;
1298 rp = rp->rec_next;
1299 free(tmp);
1301 record = recend = NULL;
1302 NYD_LEAVE;
1305 /*ARGSUSED*/
1306 static void
1307 imapalarm(int s)
1309 sighandler_type volatile saveint, savepipe;
1310 NYD_X; /* Signal handler */
1311 n_UNUSED(s);
1313 if (imaplock++ == 0) {
1314 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1315 safe_signal(SIGINT, &_imap_maincatch);
1316 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1317 if (sigsetjmp(imapjmp, 1)) {
1318 safe_signal(SIGINT, saveint);
1319 safe_signal(SIGPIPE, savepipe);
1320 goto jbrk;
1322 if (savepipe != SIG_IGN)
1323 safe_signal(SIGPIPE, imapcatch);
1324 if (imap_noop1(&mb) != OKAY) {
1325 safe_signal(SIGINT, saveint);
1326 safe_signal(SIGPIPE, savepipe);
1327 goto jleave;
1329 safe_signal(SIGINT, saveint);
1330 safe_signal(SIGPIPE, savepipe);
1332 jbrk:
1333 alarm(imapkeepalive);
1334 jleave:
1335 --imaplock;
1338 static enum okay
1339 imap_preauth(struct mailbox *mp, struct url const *urlp)
1341 NYD_X;
1343 mp->mb_active |= MB_PREAUTH;
1344 imap_answer(mp, 1);
1346 #ifdef HAVE_SSL
1347 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1348 FILE *queuefp = NULL;
1349 char o[LINESIZE];
1351 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1352 IMAP_OUT(o, MB_COMD, return STOP)
1353 IMAP_ANSWER()
1354 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1355 return STOP;
1357 #else
1358 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1359 n_err(_("No SSL support compiled in\n"));
1360 return STOP;
1362 #endif
1364 imap_capability(mp);
1365 return OKAY;
1368 static enum okay
1369 imap_capability(struct mailbox *mp)
1371 char o[LINESIZE];
1372 FILE *queuefp = NULL;
1373 enum okay ok = STOP;
1374 const char *cp;
1375 NYD_X;
1377 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1378 IMAP_OUT(o, MB_COMD, return STOP)
1379 while (mp->mb_active & MB_COMD) {
1380 ok = imap_answer(mp, 0);
1381 if (response_status == RESPONSE_OTHER &&
1382 response_other == CAPABILITY_DATA) {
1383 cp = responded_other_text;
1384 while (*cp) {
1385 while (spacechar(*cp))
1386 ++cp;
1387 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1388 /* RFC 2359 */
1389 mp->mb_flags |= MB_UIDPLUS;
1390 while (*cp && !spacechar(*cp))
1391 ++cp;
1395 return ok;
1398 static enum okay
1399 imap_auth(struct mailbox *mp, struct ccred *ccred)
1401 enum okay rv;
1402 NYD_ENTER;
1404 if (!(mp->mb_active & MB_PREAUTH)) {
1405 rv = OKAY;
1406 goto jleave;
1409 switch (ccred->cc_authtype) {
1410 case AUTHTYPE_LOGIN:
1411 rv = imap_login(mp, ccred);
1412 break;
1413 #ifdef HAVE_MD5
1414 case AUTHTYPE_CRAM_MD5:
1415 rv = imap_cram_md5(mp, ccred);
1416 break;
1417 #endif
1418 #ifdef HAVE_GSSAPI
1419 case AUTHTYPE_GSSAPI:
1420 rv = _imap_gssapi(mp, ccred);
1421 break;
1422 #endif
1423 default:
1424 rv = STOP;
1425 break;
1427 jleave:
1428 NYD_LEAVE;
1429 return rv;
1432 #ifdef HAVE_MD5
1433 static enum okay
1434 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1436 char o[LINESIZE], *cp;
1437 FILE *queuefp = NULL;
1438 enum okay rv = STOP;
1439 NYD_ENTER;
1441 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1442 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1443 imap_answer(mp, 1);
1444 if (response_type != RESPONSE_CONT)
1445 goto jleave;
1447 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1448 if(cp == NULL)
1449 goto jleave;
1450 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1451 while (mp->mb_active & MB_COMD)
1452 rv = imap_answer(mp, 1);
1453 jleave:
1454 NYD_LEAVE;
1455 return rv;
1457 #endif /* HAVE_MD5 */
1459 static enum okay
1460 imap_login(struct mailbox *mp, struct ccred *ccred)
1462 char o[LINESIZE];
1463 FILE *queuefp = NULL;
1464 enum okay rv = STOP;
1465 NYD_ENTER;
1467 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1468 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1469 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1470 while (mp->mb_active & MB_COMD)
1471 rv = imap_answer(mp, 1);
1472 jleave:
1473 NYD_LEAVE;
1474 return rv;
1477 #ifdef HAVE_GSSAPI
1478 # include "obs-imap-gssapi.h"
1479 #endif
1481 FL enum okay
1482 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1483 enum fedit_mode fm)
1485 char o[LINESIZE];
1486 char const *qname, *cp;
1487 FILE *queuefp;
1488 enum okay ok;
1489 NYD_X;
1490 n_UNUSED(size);
1492 ok = STOP;
1493 queuefp = NULL;
1495 if((qname = imap_path_quote(mp, mbx)) == NULL)
1496 goto jleave;
1498 ok = OKAY;
1500 mp->mb_uidvalidity = 0;
1501 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1502 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1503 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1504 while (mp->mb_active & MB_COMD) {
1505 ok = imap_answer(mp, 1);
1506 if (response_status != RESPONSE_OTHER &&
1507 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1508 mp->mb_uidvalidity = atol(&cp[13]);
1510 *cnt = (had_exists > 0) ? had_exists : 0;
1511 if (response_status != RESPONSE_OTHER &&
1512 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1513 mp->mb_perm = 0;
1514 jleave:
1515 return ok;
1518 static enum okay
1519 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1521 char o[LINESIZE];
1522 FILE *queuefp = NULL;
1523 char const *cp;
1524 struct message *m;
1525 unsigned x = X, y = Y, n;
1526 NYD_X;
1528 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1529 IMAP_OUT(o, MB_COMD, return STOP)
1530 while (mp->mb_active & MB_COMD) {
1531 imap_answer(mp, 1);
1532 if (response_status == RESPONSE_OTHER &&
1533 response_other == MESSAGE_DATA_FETCH) {
1534 n = responded_other_number;
1535 if (n < x || n > y)
1536 continue;
1537 m = &message[n-1];
1538 m->m_xsize = 0;
1539 } else
1540 continue;
1542 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1543 cp += 5;
1544 while (*cp == ' ')
1545 cp++;
1546 if (*cp == '(')
1547 imap_getflags(cp, &cp, &m->m_flag);
1550 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1551 m->m_uid = strtoul(&cp[4], NULL, 10);
1552 getcache1(mp, m, NEED_UNSPEC, 1);
1553 m->m_flag &= ~MHIDDEN;
1556 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1557 x++;
1558 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1559 y--;
1560 if (x <= y) {
1561 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1562 tag(1), x, y);
1563 IMAP_OUT(o, MB_COMD, return STOP)
1564 while (mp->mb_active & MB_COMD) {
1565 imap_answer(mp, 1);
1566 if (response_status == RESPONSE_OTHER &&
1567 response_other == MESSAGE_DATA_FETCH) {
1568 n = responded_other_number;
1569 if (n < x || n > y)
1570 continue;
1571 m = &message[n-1];
1572 } else
1573 continue;
1574 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1575 m->m_xsize = strtol(&cp[12], NULL, 10);
1576 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1577 m->m_time = imap_read_date_time(&cp[13]);
1581 srelax_hold();
1582 for (n = X; n <= Y; ++n) {
1583 putcache(mp, &message[n-1]);
1584 srelax();
1586 srelax_rele();
1587 return OKAY;
1590 static void
1591 imap_init(struct mailbox *mp, int n)
1593 struct message *m;
1594 NYD_ENTER;
1595 n_UNUSED(mp);
1597 m = message + n;
1598 m->m_flag = MUSED | MNOFROM;
1599 m->m_block = 0;
1600 m->m_offset = 0;
1601 NYD_LEAVE;
1604 static void
1605 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1607 struct message *omessage = 0;
1608 int i, omsgCount = 0;
1609 enum okay dequeued = STOP;
1610 NYD_ENTER;
1612 if (nmail || transparent) {
1613 omessage = message;
1614 omsgCount = msgCount;
1616 if (nmail)
1617 dequeued = rec_dequeue();
1619 if (had_exists >= 0) {
1620 if (dequeued != OKAY)
1621 msgCount = had_exists;
1622 had_exists = -1;
1624 if (had_expunge >= 0) {
1625 if (dequeued != OKAY)
1626 msgCount -= had_expunge;
1627 had_expunge = -1;
1630 if (nmail && expunged_messages)
1631 printf("Expunged %ld message%s.\n", expunged_messages,
1632 (expunged_messages != 1 ? "s" : ""));
1633 *prevcount = omsgCount - expunged_messages;
1634 expunged_messages = 0;
1635 if (msgCount < 0) {
1636 fputs("IMAP error: Negative message count\n", stderr);
1637 msgCount = 0;
1640 if (dequeued != OKAY) {
1641 message = scalloc(msgCount + 1, sizeof *message);
1642 for (i = 0; i < msgCount; i++)
1643 imap_init(mp, i);
1644 if (!nmail && mp->mb_type == MB_IMAP)
1645 initcache(mp);
1646 if (msgCount > 0)
1647 imap_flags(mp, 1, msgCount);
1648 message[msgCount].m_size = 0;
1649 message[msgCount].m_lines = 0;
1650 rec_rmqueue();
1652 if (nmail || transparent)
1653 transflags(omessage, omsgCount, transparent);
1654 else
1655 setdot(message);
1656 NYD_LEAVE;
1659 FL int
1660 imap_setfile(const char *xserver, enum fedit_mode fm)
1662 struct url url;
1663 int rv;
1664 NYD_ENTER;
1666 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1667 rv = 1;
1668 goto jleave;
1670 if (!ok_blook(v15_compat) &&
1671 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1672 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1674 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1675 rv = _imap_setfile1(&url, fm, 0);
1676 jleave:
1677 NYD_LEAVE;
1678 return rv;
1681 static bool_t
1682 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1684 bool_t rv = FAL0;
1685 NYD_ENTER;
1687 if (ok_blook(v15_compat))
1688 rv = ccred_lookup(ccredp, urlp);
1689 else {
1690 char *var, *old,
1691 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1692 : urlp->url_u_h_p.s);
1694 if ((var = mbp->mb_imap_pass) != NULL) {
1695 var = savecat("password-", xuhp);
1696 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1697 old = sstrdup(old);
1698 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1700 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1701 if (var != NULL) {
1702 if (old != NULL) {
1703 n_var_vset(var, (uintptr_t)old);
1704 free(old);
1705 } else
1706 n_var_vclear(var);
1710 NYD_LEAVE;
1711 return rv;
1714 static int
1715 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1716 int volatile transparent)
1718 struct sock so;
1719 struct ccred ccred;
1720 sighandler_type volatile saveint, savepipe;
1721 char const *cp;
1722 int rv;
1723 int volatile prevcount = 0;
1724 enum mbflags same_flags;
1725 NYD_ENTER;
1727 if (fm & FEDIT_NEWMAIL) {
1728 saveint = safe_signal(SIGINT, SIG_IGN);
1729 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1730 if (saveint != SIG_IGN)
1731 safe_signal(SIGINT, imapcatch);
1732 if (savepipe != SIG_IGN)
1733 safe_signal(SIGPIPE, imapcatch);
1734 imaplock = 1;
1735 goto jnmail;
1738 same_flags = mb.mb_flags;
1739 same_imap_account = 0;
1740 if (mb.mb_imap_account != NULL &&
1741 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1742 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1743 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1744 disconnected(mb.mb_imap_account) == 0) {
1745 same_imap_account = 1;
1746 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1748 goto jduppass;
1749 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1750 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1751 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1752 jduppass:
1754 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1758 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1759 free(mb.mb_imap_pass);
1760 mb.mb_imap_pass = NULL;
1762 if (!_imap_getcred(&mb, &ccred, urlp)) {
1763 rv = -1;
1764 goto jleave;
1767 memset(&so, 0, sizeof so);
1768 so.s_fd = -1;
1769 if (!same_imap_account) {
1770 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1771 rv = -1;
1772 goto jleave;
1774 } else
1775 so = mb.mb_sock;
1776 if (!transparent) {
1777 if(!quit(FAL0)){
1778 rv = -1;
1779 goto jleave;
1783 if (fm & FEDIT_SYSBOX)
1784 n_pstate &= ~n_PS_EDIT;
1785 else
1786 n_pstate |= n_PS_EDIT;
1787 if (mb.mb_imap_account != NULL)
1788 free(mb.mb_imap_account);
1789 if (mb.mb_imap_pass != NULL)
1790 free(mb.mb_imap_pass);
1791 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1792 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1793 * TODO object, and mailbox will naturally have an URL and credentials */
1794 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1796 if (!same_imap_account) {
1797 if (mb.mb_sock.s_fd >= 0)
1798 sclose(&mb.mb_sock);
1800 same_imap_account = 0;
1802 if (!transparent) {
1803 if (mb.mb_itf) {
1804 fclose(mb.mb_itf);
1805 mb.mb_itf = NULL;
1807 if (mb.mb_otf) {
1808 fclose(mb.mb_otf);
1809 mb.mb_otf = NULL;
1811 if (mb.mb_imap_mailbox != NULL)
1812 free(mb.mb_imap_mailbox);
1813 assert(urlp->url_path.s != NULL);
1814 imap_delim_init(&mb, urlp);
1815 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1816 initbox(savecatsep(urlp->url_p_eu_h_p,
1817 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1818 mb.mb_imap_mailbox));
1820 mb.mb_type = MB_VOID;
1821 mb.mb_active = MB_NONE;
1823 imaplock = 1;
1824 saveint = safe_signal(SIGINT, SIG_IGN);
1825 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1826 if (sigsetjmp(imapjmp, 1)) {
1827 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1828 sclose(&mb.mb_sock);
1829 safe_signal(SIGINT, saveint);
1830 safe_signal(SIGPIPE, savepipe);
1831 imaplock = 0;
1833 mb.mb_type = MB_VOID;
1834 mb.mb_active = MB_NONE;
1835 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1836 goto jleave;
1838 if (saveint != SIG_IGN)
1839 safe_signal(SIGINT, imapcatch);
1840 if (savepipe != SIG_IGN)
1841 safe_signal(SIGPIPE, imapcatch);
1843 if (mb.mb_sock.s_fd < 0) {
1844 if (disconnected(mb.mb_imap_account)) {
1845 if (cache_setptr(fm, transparent) == STOP)
1846 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1847 goto jdone;
1849 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1850 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1851 savealrm = safe_signal(SIGALRM, imapalarm);
1852 alarm(imapkeepalive);
1856 mb.mb_sock = so;
1857 mb.mb_sock.s_desc = "IMAP";
1858 mb.mb_sock.s_onclose = imap_timer_off;
1859 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1860 sclose(&mb.mb_sock);
1861 imap_timer_off();
1862 safe_signal(SIGINT, saveint);
1863 safe_signal(SIGPIPE, savepipe);
1864 imaplock = 0;
1865 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1866 goto jleave;
1868 } else /* same account */
1869 mb.mb_flags |= same_flags;
1871 if (n_poption & n_PO_R_FLAG)
1872 fm |= FEDIT_RDONLY;
1873 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1874 mb.mb_type = MB_IMAP;
1875 cache_dequeue(&mb);
1876 assert(urlp->url_path.s != NULL);
1877 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1878 /*sclose(&mb.mb_sock);
1879 imap_timer_off();*/
1880 safe_signal(SIGINT, saveint);
1881 safe_signal(SIGPIPE, savepipe);
1882 imaplock = 0;
1883 mb.mb_type = MB_VOID;
1884 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1885 goto jleave;
1888 jnmail:
1889 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1890 n_UNVOLATILE(&prevcount));
1891 jdone:
1892 setmsize(msgCount);
1893 safe_signal(SIGINT, saveint);
1894 safe_signal(SIGPIPE, savepipe);
1895 imaplock = 0;
1897 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1898 purgecache(&mb, message, msgCount);
1899 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1900 mb.mb_threaded = 0;
1901 c_sort((void*)-1);
1904 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1905 n_pstate &= ~n_PS_SAW_COMMAND;
1906 n_pstate |= n_PS_SETFILE_OPENED;
1909 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1910 mb.mb_type == MB_CACHE)) {
1911 rv = (msgCount == 0);
1912 goto jleave;
1915 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1916 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1917 !ok_blook(emptystart))
1918 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1919 rv = 1;
1920 goto jleave;
1923 if (fm & FEDIT_NEWMAIL)
1924 newmailinfo(prevcount);
1925 rv = 0;
1926 jleave:
1927 NYD_LEAVE;
1928 return rv;
1931 static int
1932 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1933 int need, const char *head, size_t headsize, long headlines)
1935 char *line = NULL, *lp;
1936 size_t linesize = 0, linelen, size = 0;
1937 int emptyline = 0, lines = 0, excess = 0;
1938 off_t offset;
1939 NYD_ENTER;
1941 fseek(mp->mb_otf, 0L, SEEK_END);
1942 offset = ftell(mp->mb_otf);
1944 if (head)
1945 fwrite(head, 1, headsize, mp->mb_otf);
1947 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1948 lp = line;
1949 if (linelen > expected) {
1950 excess = linelen - expected;
1951 linelen = expected;
1953 /* TODO >>
1954 * Need to mask 'From ' lines. This cannot be done properly
1955 * since some servers pass them as 'From ' and others as
1956 * '>From '. Although one could identify the first kind of
1957 * server in principle, it is not possible to identify the
1958 * second as '>From ' may also come from a server of the
1959 * first type as actual data. So do what is absolutely
1960 * necessary only - mask 'From '.
1962 * If the line is the first line of the message header, it
1963 * is likely a real 'From ' line. In this case, it is just
1964 * ignored since it violates all standards.
1965 * TODO can the latter *really* happen??
1966 * TODO <<
1968 /* Since we simply copy over data without doing any transfer
1969 * encoding reclassification/adjustment we *have* to perform
1970 * RFC 4155 compliant From_ quoting here */
1971 if (emptyline && is_head(lp, linelen, FAL0)) {
1972 fputc('>', mp->mb_otf);
1973 ++size;
1975 emptyline = 0;
1976 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1977 if (linelen > 2) {
1978 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1979 size += linelen - 1;
1980 } else {
1981 emptyline = 1;
1982 ++size;
1984 fputc('\n', mp->mb_otf);
1985 } else {
1986 fwrite(lp, 1, linelen, mp->mb_otf);
1987 size += linelen;
1989 ++lines;
1990 if ((expected -= linelen) <= 0)
1991 break;
1993 if (!emptyline) {
1994 /* This is very ugly; but some IMAP daemons don't end a
1995 * message with \r\n\r\n, and we need \n\n for mbox format */
1996 fputc('\n', mp->mb_otf);
1997 ++lines;
1998 ++size;
2000 fflush(mp->mb_otf);
2002 if (m != NULL) {
2003 m->m_size = size + headsize;
2004 m->m_lines = lines + headlines;
2005 m->m_block = mailx_blockof(offset);
2006 m->m_offset = mailx_offsetof(offset);
2007 switch (need) {
2008 case NEED_HEADER:
2009 m->m_content_info = CI_HAVE_HEADER;
2010 break;
2011 case NEED_BODY:
2012 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
2013 m->m_xlines = m->m_lines;
2014 m->m_xsize = m->m_size;
2015 break;
2018 free(line);
2019 NYD_LEAVE;
2020 return excess;
2023 static void
2024 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2025 const char *head, size_t headsize, long headlines)
2027 off_t offset;
2028 size_t len;
2029 NYD_ENTER;
2031 len = strlen(str);
2032 fseek(mp->mb_otf, 0L, SEEK_END);
2033 offset = ftell(mp->mb_otf);
2034 if (head)
2035 fwrite(head, 1, headsize, mp->mb_otf);
2036 if (len > 0) {
2037 fwrite(str, 1, len, mp->mb_otf);
2038 fputc('\n', mp->mb_otf);
2039 ++len;
2041 fflush(mp->mb_otf);
2043 if (m != NULL) {
2044 m->m_size = headsize + len;
2045 m->m_lines = headlines + 1;
2046 m->m_block = mailx_blockof(offset);
2047 m->m_offset = mailx_offsetof(offset);
2048 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2049 m->m_xlines = m->m_lines;
2050 m->m_xsize = m->m_size;
2052 NYD_LEAVE;
2055 static enum okay
2056 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2058 char o[LINESIZE];
2059 struct message mt;
2060 sighandler_type volatile saveint, savepipe;
2061 char * volatile head;
2062 char const *cp, *loc, * volatile item, * volatile resp;
2063 size_t expected;
2064 size_t volatile headsize;
2065 int number;
2066 FILE *queuefp;
2067 long volatile headlines;
2068 long n;
2069 ul_i volatile u;
2070 enum okay ok;
2071 NYD_X;
2073 saveint = savepipe = SIG_IGN;
2074 head = NULL;
2075 cp = loc = item = resp = NULL;
2076 headsize = 0;
2077 number = (int)PTR2SIZE(m - message + 1);
2078 queuefp = NULL;
2079 headlines = 0;
2080 u = 0;
2081 ok = STOP;
2083 if (getcache(mp, m, need) == OKAY)
2084 return OKAY;
2085 if (mp->mb_type == MB_CACHE) {
2086 n_err(_("Message %lu not available\n"), (ul_i)number);
2087 return STOP;
2090 if (mp->mb_sock.s_fd < 0) {
2091 n_err(_("IMAP connection closed\n"));
2092 return STOP;
2095 switch (need) {
2096 case NEED_HEADER:
2097 resp = item = "RFC822.HEADER";
2098 break;
2099 case NEED_BODY:
2100 item = "BODY.PEEK[]";
2101 resp = "BODY[]";
2102 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2103 char *hdr = smalloc(m->m_size);
2104 fflush(mp->mb_otf);
2105 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2106 SEEK_SET) < 0 ||
2107 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2108 free(hdr);
2109 break;
2111 head = hdr;
2112 headsize = m->m_size;
2113 headlines = m->m_lines;
2114 item = "BODY.PEEK[TEXT]";
2115 resp = "BODY[TEXT]";
2117 break;
2118 case NEED_UNSPEC:
2119 return STOP;
2122 imaplock = 1;
2123 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2124 if (sigsetjmp(imapjmp, 1)) {
2125 safe_signal(SIGINT, saveint);
2126 safe_signal(SIGPIPE, savepipe);
2127 imaplock = 0;
2128 return STOP;
2130 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2131 safe_signal(SIGINT, &_imap_maincatch);
2132 if (savepipe != SIG_IGN)
2133 safe_signal(SIGPIPE, imapcatch);
2135 if (m->m_uid)
2136 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2137 tag(1), m->m_uid, item);
2138 else {
2139 if (check_expunged() == STOP)
2140 goto out;
2141 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2143 IMAP_OUT(o, MB_COMD, goto out)
2144 for (;;) {
2145 ok = imap_answer(mp, 1);
2146 if (ok == STOP)
2147 break;
2148 if (response_status != RESPONSE_OTHER ||
2149 response_other != MESSAGE_DATA_FETCH)
2150 continue;
2151 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2152 continue;
2153 if (m->m_uid) {
2154 if ((cp = asccasestr(responded_other_text, "UID "))) {
2155 u = atol(&cp[4]);
2156 n = 0;
2157 } else {
2158 u = 0;
2159 n = -1;
2161 } else
2162 n = responded_other_number;
2163 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2164 if (m->m_uid ? m->m_uid != u : n != number)
2165 continue;
2166 if ((cp = strchr(loc, '"')) != NULL) {
2167 cp = imap_unquotestr(cp);
2168 imap_putstr(mp, m, cp, head, headsize, headlines);
2169 } else {
2170 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2171 m->m_xlines = m->m_lines;
2172 m->m_xsize = m->m_size;
2174 goto out;
2176 expected = atol(&cp[1]);
2177 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2178 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2179 continue;
2181 mt = *m;
2182 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2183 if (n >= 0) {
2184 commitmsg(mp, m, &mt, mt.m_content_info);
2185 break;
2187 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2188 if (n_poption & n_PO_VERBVERB)
2189 fputs(imapbuf, stderr);
2190 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2191 u = atol(&cp[4]);
2192 if (u == m->m_uid) {
2193 commitmsg(mp, m, &mt, mt.m_content_info);
2194 break;
2199 out:
2200 while (mp->mb_active & MB_COMD)
2201 ok = imap_answer(mp, 1);
2203 if (saveint != SIG_IGN)
2204 safe_signal(SIGINT, saveint);
2205 if (savepipe != SIG_IGN)
2206 safe_signal(SIGPIPE, savepipe);
2207 imaplock--;
2209 if (ok == OKAY)
2210 putcache(mp, m);
2211 if (head != NULL)
2212 free(head);
2213 if (interrupts)
2214 n_go_onintr_for_imap();
2215 return ok;
2218 FL enum okay
2219 imap_header(struct message *m)
2221 enum okay rv;
2222 NYD_ENTER;
2224 rv = imap_get(&mb, m, NEED_HEADER);
2225 NYD_LEAVE;
2226 return rv;
2230 FL enum okay
2231 imap_body(struct message *m)
2233 enum okay rv;
2234 NYD_ENTER;
2236 rv = imap_get(&mb, m, NEED_BODY);
2237 NYD_LEAVE;
2238 return rv;
2241 static void
2242 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2243 enum content_info content_info)
2245 NYD_ENTER;
2246 tomp->m_size = frommp->m_size;
2247 tomp->m_lines = frommp->m_lines;
2248 tomp->m_block = frommp->m_block;
2249 tomp->m_offset = frommp->m_offset;
2250 tomp->m_content_info = content_info & CI_HAVE_MASK;
2251 if (content_info & CI_HAVE_BODY) {
2252 tomp->m_xlines = frommp->m_lines;
2253 tomp->m_xsize = frommp->m_size;
2255 putcache(mp, tomp);
2256 NYD_LEAVE;
2259 static enum okay
2260 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2262 /* bot > topp */
2263 char o[LINESIZE];
2264 char const *cp;
2265 struct message mt;
2266 size_t expected;
2267 int n = 0, u;
2268 FILE *queuefp = NULL;
2269 enum okay ok;
2270 NYD_X;
2272 if (m[bot].m_uid)
2273 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2274 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2275 else {
2276 if (check_expunged() == STOP)
2277 return STOP;
2278 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2279 tag(1), bot, topp);
2281 IMAP_OUT(o, MB_COMD, return STOP)
2283 srelax_hold();
2284 for (;;) {
2285 ok = imap_answer(mp, 1);
2286 if (response_status != RESPONSE_OTHER)
2287 break;
2288 if (response_other != MESSAGE_DATA_FETCH)
2289 continue;
2290 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2291 srelax_rele();
2292 return STOP;
2294 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2295 continue;
2296 expected = atol(&cp[1]);
2297 if (m[bot-1].m_uid) {
2298 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2299 u = atoi(&cp[4]);
2300 for (n = bot; n <= topp; n++)
2301 if ((unsigned long)u == m[n-1].m_uid)
2302 break;
2303 if (n > topp) {
2304 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2305 continue;
2307 } else
2308 n = -1;
2309 } else {
2310 n = responded_other_number;
2311 if (n <= 0 || n > msgCount) {
2312 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2313 continue;
2316 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2317 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2318 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2319 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2320 if (n_poption & n_PO_VERBVERB)
2321 fputs(imapbuf, stderr);
2322 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2323 u = atoi(&cp[4]);
2324 for (n = bot; n <= topp; n++)
2325 if ((unsigned long)u == m[n-1].m_uid)
2326 break;
2327 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2328 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2331 srelax();
2333 srelax_rele();
2335 while (mp->mb_active & MB_COMD)
2336 ok = imap_answer(mp, 1);
2337 return ok;
2340 FL void
2341 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2343 sighandler_type saveint, savepipe;
2344 /*enum okay ok = STOP;*/
2345 int i, chunk = 256;
2346 NYD_X;
2348 if (mb.mb_type == MB_CACHE)
2349 return;
2350 if (bot < 1)
2351 bot = 1;
2352 if (topp > msgCount)
2353 topp = msgCount;
2354 for (i = bot; i < topp; i++) {
2355 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2356 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2357 bot = i+1;
2358 else
2359 break;
2361 for (i = topp; i > bot; i--) {
2362 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2363 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2364 topp = i-1;
2365 else
2366 break;
2368 if (bot >= topp)
2369 return;
2371 imaplock = 1;
2372 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2373 safe_signal(SIGINT, &_imap_maincatch);
2374 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2375 if (sigsetjmp(imapjmp, 1) == 0) {
2376 if (savepipe != SIG_IGN)
2377 safe_signal(SIGPIPE, imapcatch);
2379 for (i = bot; i <= topp; i += chunk) {
2380 int j = i + chunk - 1;
2381 j = n_MIN(j, topp);
2382 if (visible(message + j))
2383 /*ok = */imap_fetchheaders(&mb, message, i, j);
2384 if (interrupts)
2385 n_go_onintr_for_imap(); /* XXX imaplock? */
2388 safe_signal(SIGINT, saveint);
2389 safe_signal(SIGPIPE, savepipe);
2390 imaplock = 0;
2393 static enum okay
2394 __imap_exit(struct mailbox *mp)
2396 char o[LINESIZE];
2397 FILE *queuefp = NULL;
2398 NYD_X;
2400 mp->mb_active |= MB_BYE;
2401 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2402 IMAP_OUT(o, MB_COMD, return STOP)
2403 IMAP_ANSWER()
2404 return OKAY;
2407 static enum okay
2408 imap_exit(struct mailbox *mp)
2410 enum okay rv;
2411 NYD_ENTER;
2413 rv = __imap_exit(mp);
2414 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2415 free(mp->mb_imap_pass);
2416 free(mp->mb_imap_account);
2417 free(mp->mb_imap_mailbox);
2418 if (mp->mb_cache_directory != NULL)
2419 free(mp->mb_cache_directory);
2420 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2421 mp->mb_imap_account =
2422 mp->mb_imap_mailbox =
2423 mp->mb_cache_directory = "";
2424 #else
2425 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2426 mp->mb_imap_mailbox = NULL;
2427 mp->mb_cache_directory = NULL;
2428 #endif
2429 #endif
2430 sclose(&mp->mb_sock);
2431 NYD_LEAVE;
2432 return rv;
2435 static enum okay
2436 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2438 NYD_ENTER;
2439 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2440 if (mp->mb_type == MB_IMAP)
2441 delcache(mp, m);
2442 NYD_LEAVE;
2443 return OKAY;
2446 static enum okay
2447 imap_close(struct mailbox *mp)
2449 char o[LINESIZE];
2450 FILE *queuefp = NULL;
2451 NYD_X;
2453 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2454 IMAP_OUT(o, MB_COMD, return STOP)
2455 IMAP_ANSWER()
2456 return OKAY;
2459 static enum okay
2460 imap_update(struct mailbox *mp)
2462 struct message *m;
2463 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2464 NYD_ENTER;
2466 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2467 holdbits();
2468 c = 0;
2469 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2470 if (m->m_flag & MBOX)
2471 ++c;
2472 if (c > 0)
2473 if (makembox() == STOP)
2474 goto jbypass;
2477 gotcha = held = 0;
2478 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2479 if (mp->mb_perm == 0)
2480 dodel = 0;
2481 else if (n_pstate & n_PS_EDIT)
2482 dodel = ((m->m_flag & MDELETED) != 0);
2483 else
2484 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2486 /* Fetch the result after around each 800 STORE commands
2487 * sent (approx. 32k data sent). Otherwise, servers will
2488 * try to flush the return queue at some point, leading
2489 * to a deadlock if we are still writing commands but not
2490 * reading their results */
2491 needstat = stored > 0 && stored % 800 == 0;
2492 /* Even if this message has been deleted, continue
2493 * to set further flags. This is necessary to support
2494 * Gmail semantics, where "delete" actually means
2495 * "archive", and the flags are applied to the copy
2496 * in "All Mail" */
2497 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2498 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2499 stored++;
2501 if (m->m_flag & MFLAG) {
2502 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2503 stored++;
2505 if (m->m_flag & MUNFLAG) {
2506 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2507 stored++;
2509 if (m->m_flag & MANSWER) {
2510 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2511 stored++;
2513 if (m->m_flag & MUNANSWER) {
2514 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2515 stored++;
2517 if (m->m_flag & MDRAFT) {
2518 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2519 stored++;
2521 if (m->m_flag & MUNDRAFT) {
2522 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2523 stored++;
2526 if (dodel) {
2527 imap_delete(mp, m-message+1, m, needstat);
2528 stored++;
2529 gotcha++;
2530 } else if (mp->mb_type != MB_CACHE ||
2531 (!(n_pstate & n_PS_EDIT) &&
2532 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2533 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2534 (MPRESERVE | MTOUCH) ||
2535 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2536 held++;
2537 if (m->m_flag & MNEW) {
2538 m->m_flag &= ~MNEW;
2539 m->m_flag |= MSTATUS;
2542 jbypass:
2543 if (gotcha)
2544 imap_close(mp);
2546 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2547 if (!(m->m_flag & MUNLINKED) &&
2548 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2549 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2550 putcache(mp, m);
2551 modflags++;
2554 /* XXX should be readonly (but our IMAP code is weird...) */
2555 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2556 mb.mb_perm != 0) {
2557 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2558 printf(_("\"%s\" "), displayname);
2559 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2560 ? _("complete\n") : _("updated.\n"));
2561 } else if (held && !(n_pstate & n_PS_EDIT)) {
2562 if (held == 1)
2563 printf(_("Held 1 message in %s\n"), displayname);
2564 else
2565 printf(_("Held %d messages in %s\n"), held, displayname);
2567 fflush(stdout);
2569 NYD_LEAVE;
2570 return OKAY;
2573 FL bool_t
2574 imap_quit(bool_t hold_sigs_on)
2576 sighandler_type volatile saveint, savepipe;
2577 bool_t rv;
2578 NYD_ENTER;
2580 if(hold_sigs_on)
2581 rele_sigs();
2583 if (mb.mb_type == MB_CACHE) {
2584 rv = (imap_update(&mb) == OKAY);
2585 goto jleave;
2588 rv = FAL0;
2590 if (mb.mb_sock.s_fd < 0) {
2591 n_err(_("IMAP connection closed\n"));
2592 goto jleave;
2595 imaplock = 1;
2596 saveint = safe_signal(SIGINT, SIG_IGN);
2597 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2598 if (sigsetjmp(imapjmp, 1)) {
2599 safe_signal(SIGINT, saveint);
2600 safe_signal(SIGPIPE, saveint);
2601 imaplock = 0;
2602 goto jleave;
2604 if (saveint != SIG_IGN)
2605 safe_signal(SIGINT, imapcatch);
2606 if (savepipe != SIG_IGN)
2607 safe_signal(SIGPIPE, imapcatch);
2609 rv = (imap_update(&mb) == OKAY);
2610 if(!same_imap_account && imap_exit(&mb) != OKAY)
2611 rv = FAL0;
2613 safe_signal(SIGINT, saveint);
2614 safe_signal(SIGPIPE, savepipe);
2615 imaplock = 0;
2616 jleave:
2617 if(hold_sigs_on)
2618 hold_sigs();
2619 NYD_LEAVE;
2620 return rv;
2623 static enum okay
2624 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2625 int needstat)
2627 char o[LINESIZE];
2628 FILE *queuefp = NULL;
2629 NYD_X;
2631 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2632 return STOP;
2633 if (m->m_uid)
2634 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2635 tag(1), m->m_uid, c, sp);
2636 else {
2637 if (check_expunged() == STOP)
2638 return STOP;
2639 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2641 IMAP_OUT(o, MB_COMD, return STOP)
2642 if (needstat)
2643 IMAP_ANSWER()
2644 else
2645 mb.mb_active &= ~MB_COMD;
2646 if (queuefp != NULL)
2647 Fclose(queuefp);
2648 return OKAY;
2651 FL enum okay
2652 imap_undelete(struct message *m, int n)
2654 enum okay rv;
2655 NYD_ENTER;
2657 rv = imap_unstore(m, n, "\\Deleted");
2658 NYD_LEAVE;
2659 return rv;
2662 FL enum okay
2663 imap_unread(struct message *m, int n)
2665 enum okay rv;
2666 NYD_ENTER;
2668 rv = imap_unstore(m, n, "\\Seen");
2669 NYD_LEAVE;
2670 return rv;
2673 static enum okay
2674 imap_unstore(struct message *m, int n, const char *flag)
2676 sighandler_type saveint, savepipe;
2677 enum okay volatile rv = STOP;
2678 NYD_ENTER;
2680 imaplock = 1;
2681 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2682 safe_signal(SIGINT, &_imap_maincatch);
2683 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2684 if (sigsetjmp(imapjmp, 1) == 0) {
2685 if (savepipe != SIG_IGN)
2686 safe_signal(SIGPIPE, imapcatch);
2688 rv = imap_store(&mb, m, n, '-', flag, 1);
2690 safe_signal(SIGINT, saveint);
2691 safe_signal(SIGPIPE, savepipe);
2692 imaplock = 0;
2694 NYD_LEAVE;
2695 if (interrupts)
2696 n_go_onintr_for_imap();
2697 return rv;
2700 static const char *
2701 tag(int new)
2703 static char ts[20];
2704 static long n;
2705 NYD2_ENTER;
2707 if (new)
2708 ++n;
2709 snprintf(ts, sizeof ts, "T%lu", n);
2710 NYD2_LEAVE;
2711 return ts;
2714 FL int
2715 c_imapcodec(void *vp){
2716 bool_t err;
2717 size_t alen;
2718 char const **argv, *varname, *varres, *act, *cp;
2719 NYD_ENTER;
2721 argv = vp;
2722 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2724 act = *argv;
2725 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2727 if(act == cp)
2728 goto jesynopsis;
2729 alen = PTR2SIZE(cp - act);
2730 if(*cp != '\0')
2731 ++cp;
2733 n_pstate_err_no = n_ERR_NONE;
2734 varres = imap_path_normalize(NULL, cp);
2736 if(is_ascncaseprefix(act, "encode", alen))
2737 varres = imap_path_encode(varres, &err);
2738 else if(is_ascncaseprefix(act, "decode", alen))
2739 varres = imap_path_decode(varres, &err);
2740 else
2741 goto jesynopsis;
2743 if(err){
2744 n_pstate_err_no = n_ERR_CANCELED;
2745 varres = cp;
2746 vp = NULL;
2749 if(varname != NULL){
2750 if(!n_var_vset(varname, (uintptr_t)varres)){
2751 n_pstate_err_no = n_ERR_NOTSUP;
2752 vp = NULL;
2754 }else{
2755 struct str in, out;
2757 in.l = strlen(in.s = n_UNCONST(varres));
2758 makeprint(&in, &out);
2759 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2760 n_pstate_err_no = n_err_no;
2761 vp = NULL;
2763 free(out.s);
2766 jleave:
2767 NYD_LEAVE;
2768 return (vp != NULL ? 0 : 1);
2769 jesynopsis:
2770 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2771 n_pstate_err_no = n_ERR_INVAL;
2772 vp = NULL;
2773 goto jleave;
2776 FL int
2777 c_imap_imap(void *vp)
2779 char o[LINESIZE];
2780 sighandler_type saveint, savepipe;
2781 struct mailbox *mp = &mb;
2782 FILE *queuefp = NULL;
2783 enum okay volatile ok = STOP;
2784 NYD_X;
2786 if (mp->mb_type != MB_IMAP) {
2787 printf("Not operating on an IMAP mailbox.\n");
2788 return 1;
2790 imaplock = 1;
2791 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2792 safe_signal(SIGINT, &_imap_maincatch);
2793 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2794 if (sigsetjmp(imapjmp, 1) == 0) {
2795 if (savepipe != SIG_IGN)
2796 safe_signal(SIGPIPE, imapcatch);
2798 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2799 IMAP_OUT(o, MB_COMD, goto out)
2800 while (mp->mb_active & MB_COMD) {
2801 ok = imap_answer(mp, 0);
2802 fputs(responded_text, stdout);
2805 out:
2806 safe_signal(SIGINT, saveint);
2807 safe_signal(SIGPIPE, savepipe);
2808 imaplock = 0;
2810 if (interrupts)
2811 n_go_onintr_for_imap();
2812 return ok != OKAY;
2815 FL int
2816 imap_newmail(int nmail)
2818 NYD_ENTER;
2820 if (nmail && had_exists < 0 && had_expunge < 0) {
2821 imaplock = 1;
2822 imap_noop();
2823 imaplock = 0;
2826 if (had_exists == msgCount && had_expunge < 0)
2827 /* Some servers always respond with EXISTS to NOOP. If
2828 * the mailbox has been changed but the number of messages
2829 * has not, an EXPUNGE must also had been sent; otherwise,
2830 * nothing has changed */
2831 had_exists = -1;
2832 NYD_LEAVE;
2833 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2836 static char *
2837 imap_putflags(int f)
2839 const char *cp;
2840 char *buf, *bp;
2841 NYD2_ENTER;
2843 bp = buf = salloc(100);
2844 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2845 *bp++ = '(';
2846 if (f & MREAD) {
2847 if (bp[-1] != '(')
2848 *bp++ = ' ';
2849 for (cp = "\\Seen"; *cp; cp++)
2850 *bp++ = *cp;
2852 if (f & MFLAGGED) {
2853 if (bp[-1] != '(')
2854 *bp++ = ' ';
2855 for (cp = "\\Flagged"; *cp; cp++)
2856 *bp++ = *cp;
2858 if (f & MANSWERED) {
2859 if (bp[-1] != '(')
2860 *bp++ = ' ';
2861 for (cp = "\\Answered"; *cp; cp++)
2862 *bp++ = *cp;
2864 if (f & MDRAFT) {
2865 if (bp[-1] != '(')
2866 *bp++ = ' ';
2867 for (cp = "\\Draft"; *cp; cp++)
2868 *bp++ = *cp;
2870 *bp++ = ')';
2871 *bp++ = ' ';
2873 *bp = '\0';
2874 NYD2_LEAVE;
2875 return buf;
2878 static void
2879 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2881 NYD2_ENTER;
2882 while (*cp != ')') {
2883 if (*cp == '\\') {
2884 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2885 *f |= MREAD;
2886 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2887 *f |= MNEW;
2888 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2889 *f |= MDELETED;
2890 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2891 *f |= MFLAGGED;
2892 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2893 *f |= MANSWERED;
2894 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2895 *f |= MDRAFTED;
2897 cp++;
2900 if (xp != NULL)
2901 *xp = cp;
2902 NYD2_LEAVE;
2905 static enum okay
2906 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2907 long xsize, enum mflag flag, time_t t)
2909 char o[LINESIZE], *buf;
2910 size_t bufsize, buflen, cnt;
2911 long size, lines, ysize;
2912 char const *qname;
2913 bool_t twice;
2914 FILE *queuefp;
2915 enum okay rv;
2916 NYD_ENTER;
2918 rv = STOP;
2919 queuefp = NULL;
2920 twice = FAL0;
2921 buf = NULL;
2923 if((qname = imap_path_quote(mp, name)) == NULL)
2924 goto jleave;
2926 if (mp->mb_type == MB_CACHE) {
2927 queuefp = cache_queue(mp);
2928 if (queuefp == NULL) {
2929 buf = NULL;
2930 goto jleave;
2932 rv = OKAY;
2935 buf = smalloc(bufsize = LINESIZE);
2936 buflen = 0;
2937 jagain:
2938 size = xsize;
2939 cnt = fsize(fp);
2940 if (fseek(fp, off1, SEEK_SET) < 0) {
2941 rv = STOP;
2942 goto jleave;
2945 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2946 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2947 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2948 while (mp->mb_active & MB_COMD) {
2949 rv = imap_answer(mp, twice);
2950 if (response_type == RESPONSE_CONT)
2951 break;
2954 if (mp->mb_type != MB_CACHE && rv == STOP) {
2955 if (!twice)
2956 goto jtrycreate;
2957 else
2958 goto jleave;
2961 lines = ysize = 0;
2962 while (size > 0) {
2963 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2964 lines++;
2965 ysize += buflen;
2966 buf[buflen - 1] = '\r';
2967 buf[buflen] = '\n';
2968 if (mp->mb_type != MB_CACHE)
2969 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2970 else if (queuefp)
2971 fwrite(buf, 1, buflen+1, queuefp);
2972 size -= buflen + 1;
2974 if (mp->mb_type != MB_CACHE)
2975 swrite(&mp->mb_sock, "\r\n");
2976 else if (queuefp)
2977 fputs("\r\n", queuefp);
2978 while (mp->mb_active & MB_COMD) {
2979 rv = imap_answer(mp, 0);
2980 if (response_status == RESPONSE_NO /*&&
2981 ascncasecmp(responded_text,
2982 "[TRYCREATE] ", 12) == 0*/) {
2983 jtrycreate:
2984 if (twice) {
2985 rv = STOP;
2986 goto jleave;
2988 twice = TRU1;
2989 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2990 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2991 while (mp->mb_active & MB_COMD)
2992 rv = imap_answer(mp, 1);
2993 if (rv == STOP)
2994 goto jleave;
2995 imap_created_mailbox++;
2996 goto jagain;
2997 } else if (rv != OKAY)
2998 n_err(_("IMAP error: %s"), responded_text);
2999 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
3000 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
3002 jleave:
3003 if (queuefp != NULL)
3004 Fclose(queuefp);
3005 if (buf != NULL)
3006 free(buf);
3007 NYD_LEAVE;
3008 return rv;
3011 static enum okay
3012 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3014 char *buf, *bp, *lp;
3015 size_t bufsize, buflen, cnt;
3016 off_t off1 = -1, offs;
3017 int flag;
3018 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3019 time_t tim;
3020 long size;
3021 enum okay rv;
3022 NYD_ENTER;
3024 buf = smalloc(bufsize = LINESIZE);
3025 buflen = 0;
3026 cnt = fsize(fp);
3027 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3028 time(&tim);
3029 size = 0;
3031 for (flag = MNEW, state = _NLSEP;;) {
3032 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3034 if (bp == NULL ||
3035 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3036 is_head(buf, buflen, FAL0))) {
3037 if (off1 != (off_t)-1) {
3038 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3039 if (rv == STOP)
3040 goto jleave;
3041 fseek(fp, offs+buflen, SEEK_SET);
3043 off1 = offs + buflen;
3044 size = 0;
3045 flag = MNEW;
3046 state = _INHEAD;
3047 if (bp == NULL)
3048 break;
3049 tim = unixtime(buf);
3050 } else
3051 size += buflen+1;
3052 offs += buflen;
3054 state &= ~_NLSEP;
3055 if (buf[0] == '\n') {
3056 state &= ~_INHEAD;
3057 state |= _NLSEP;
3058 } else if (state & _INHEAD) {
3059 if (ascncasecmp(buf, "status", 6) == 0) {
3060 lp = &buf[6];
3061 while (whitechar(*lp))
3062 lp++;
3063 if (*lp == ':')
3064 while (*++lp != '\0')
3065 switch (*lp) {
3066 case 'R':
3067 flag |= MREAD;
3068 break;
3069 case 'O':
3070 flag &= ~MNEW;
3071 break;
3073 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3074 lp = &buf[8];
3075 while (whitechar(*lp))
3076 lp++;
3077 if (*lp == ':')
3078 while (*++lp != '\0')
3079 switch (*lp) {
3080 case 'F':
3081 flag |= MFLAGGED;
3082 break;
3083 case 'A':
3084 flag |= MANSWERED;
3085 break;
3086 case 'T':
3087 flag |= MDRAFTED;
3088 break;
3093 rv = OKAY;
3094 jleave:
3095 free(buf);
3096 NYD_LEAVE;
3097 return rv;
3100 FL enum okay
3101 imap_append(const char *xserver, FILE *fp, long offset)
3103 sighandler_type volatile saveint, savepipe;
3104 struct url url;
3105 struct ccred ccred;
3106 enum okay rv = STOP;
3107 NYD_ENTER;
3109 if (!url_parse(&url, CPROTO_IMAP, xserver))
3110 goto j_leave;
3111 if (!ok_blook(v15_compat) &&
3112 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3113 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3114 assert(url.url_path.s != NULL);
3116 imaplock = 1;
3117 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3118 safe_signal(SIGINT, &_imap_maincatch);
3119 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3120 if (sigsetjmp(imapjmp, 1))
3121 goto jleave;
3122 if (savepipe != SIG_IGN)
3123 safe_signal(SIGPIPE, imapcatch);
3125 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3126 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3127 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3128 } else {
3129 struct mailbox mx;
3131 memset(&mx, 0, sizeof mx);
3133 if (!_imap_getcred(&mx, &ccred, &url))
3134 goto jleave;
3136 imap_delim_init(&mx, &url);
3137 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3139 if (disconnected(url.url_p_eu_h_p) == 0) {
3140 if (!sopen(&mx.mb_sock, &url))
3141 goto jfail;
3142 mx.mb_sock.s_desc = "IMAP";
3143 mx.mb_type = MB_IMAP;
3144 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3145 /* TODO the code now did
3146 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3147 * TODO though imap_mailbox is sfree()d and mbx
3148 * TODO is possibly even a constant
3149 * TODO i changed this to sstrdup() sofar, as is used
3150 * TODO somewhere else in this file for this! */
3151 if (imap_preauth(&mx, &url) != OKAY ||
3152 imap_auth(&mx, &ccred) != OKAY) {
3153 sclose(&mx.mb_sock);
3154 goto jfail;
3156 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3157 imap_exit(&mx);
3158 } else {
3159 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3160 mx.mb_type = MB_CACHE;
3161 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3163 jfail:
3167 jleave:
3168 safe_signal(SIGINT, saveint);
3169 safe_signal(SIGPIPE, savepipe);
3170 imaplock = 0;
3171 j_leave:
3172 NYD_LEAVE;
3173 if (interrupts)
3174 n_go_onintr_for_imap();
3175 return rv;
3178 static enum okay
3179 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3180 struct list_item **lend, int level)
3182 char o[LINESIZE], *cp;
3183 struct list_item *lp;
3184 const char *qname, *bp;
3185 FILE *queuefp;
3186 enum okay ok;
3187 NYD_X;
3189 ok = STOP;
3190 queuefp = NULL;
3192 if((qname = imap_path_quote(mp, base)) == NULL)
3193 goto jleave;
3195 *list = *lend = NULL;
3196 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3197 IMAP_OUT(o, MB_COMD, goto jleave)
3198 while (mp->mb_active & MB_COMD) {
3199 ok = imap_answer(mp, 1);
3200 if (response_status == RESPONSE_OTHER &&
3201 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3202 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3203 lp = csalloc(1, sizeof *lp);
3204 lp->l_name = cp;
3205 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3206 ++cp;
3207 lp->l_base = *cp ? cp : savestr(base);
3208 lp->l_attr = list_attributes;
3209 lp->l_level = level+1;
3210 lp->l_delim = list_hierarchy_delimiter;
3211 if (*list && *lend) {
3212 (*lend)->l_next = lp;
3213 *lend = lp;
3214 } else
3215 *list = *lend = lp;
3218 jleave:
3219 return ok;
3222 static enum okay
3223 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3225 struct list_item *list, *lend, *lp, *lx, *ly;
3226 int n, depth;
3227 const char *bp;
3228 char *cp;
3229 enum okay rv;
3230 NYD_ENTER;
3232 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3233 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3234 goto jleave;
3235 rv = OKAY;
3236 if (list == NULL || lend == NULL)
3237 goto jleave;
3239 for (lp = list; lp; lp = lp->l_next)
3240 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3241 !(lp->l_attr & LIST_NOINFERIORS)) {
3242 cp = salloc((n = strlen(lp->l_name)) + 2);
3243 memcpy(cp, lp->l_name, n);
3244 cp[n] = lp->l_delim;
3245 cp[n+1] = '\0';
3246 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3247 lp->l_has_children = 1;
3248 if (strcmp(cp, lx->l_name) == 0)
3249 lx = lx->l_next;
3250 if (lx) {
3251 lend->l_next = lx;
3252 lend = ly;
3257 for (lp = list; lp; lp = lp->l_next) {
3258 if (strip) {
3259 cp = lp->l_name;
3260 for (bp = base; *bp && *bp == *cp; bp++)
3261 cp++;
3262 } else
3263 cp = lp->l_name;
3264 if (!(lp->l_attr & LIST_NOSELECT))
3265 fprintf(fp, "%s\n", *cp ? cp : base);
3266 else if (lp->l_has_children == 0)
3267 fprintf(fp, "%s%c\n", *cp ? cp : base,
3268 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3270 jleave:
3271 NYD_LEAVE;
3272 return rv;
3275 FL int
3276 imap_folders(const char * volatile name, int strip)
3278 sighandler_type saveint, savepipe;
3279 const char * volatile fold, *cp, *sp;
3280 FILE * volatile fp;
3281 int rv = 1;
3282 NYD_ENTER;
3284 cp = protbase(name);
3285 sp = mb.mb_imap_account;
3286 if (sp == NULL || strcmp(cp, sp)) {
3287 n_err(
3288 _("Cannot perform `folders' but when on the very IMAP "
3289 "account; the current one is\n `%s' -- "
3290 "try `folders @'\n"),
3291 (sp != NULL ? sp : _("[NONE]")));
3292 goto jleave;
3295 fold = imap_fileof(name);
3296 if (n_psonce & n_PSO_TTYOUT) {
3297 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3298 == NULL) {
3299 n_perr(_("tmpfile"), 0);
3300 goto jleave;
3302 } else
3303 fp = stdout;
3305 imaplock = 1;
3306 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3307 safe_signal(SIGINT, &_imap_maincatch);
3308 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3309 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3310 goto junroll;
3311 if (savepipe != SIG_IGN)
3312 safe_signal(SIGPIPE, imapcatch);
3314 if (mb.mb_type == MB_CACHE)
3315 cache_list(&mb, fold, strip, fp);
3316 else
3317 imap_list(&mb, fold, strip, fp);
3319 imaplock = 0;
3320 if (interrupts) {
3321 if (n_psonce & n_PSO_TTYOUT)
3322 Fclose(fp);
3323 rv = 0;
3324 goto jleave;
3326 fflush(fp);
3328 if (n_psonce & n_PSO_TTYOUT) {
3329 rewind(fp);
3330 if (fsize(fp) > 0){
3331 page_or_print(fp, 0);
3332 rv = 0;
3333 }else
3334 n_err(_("Folder not found\n"));
3335 }else
3336 rv = 0;
3337 junroll:
3338 safe_signal(SIGINT, saveint);
3339 safe_signal(SIGPIPE, savepipe);
3340 if (n_psonce & n_PSO_TTYOUT)
3341 Fclose(fp);
3342 jleave:
3343 NYD_LEAVE;
3344 if (interrupts)
3345 n_go_onintr_for_imap();
3346 return rv;
3349 static enum okay
3350 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3352 char o[LINESIZE];
3353 const char *qname;
3354 bool_t twice, stored;
3355 FILE *queuefp;
3356 enum okay ok;
3357 NYD_X;
3359 ok = STOP;
3360 queuefp = NULL;
3361 twice = stored = FAL0;
3363 /* C99 */{
3364 size_t i;
3366 i = strlen(name = imap_fileof(name));
3367 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3368 name = savecat(name, "INBOX");
3369 if((qname = imap_path_quote(mp, name)) == NULL)
3370 goto jleave;
3373 if (mp->mb_type == MB_CACHE) {
3374 if ((queuefp = cache_queue(mp)) == NULL)
3375 goto jleave;
3376 ok = OKAY;
3379 /* Since it is not possible to set flags on the copy, recently
3380 * set flags must be set on the original to include it in the copy */
3381 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3382 imap_store(mp, m, n, '+', "\\Seen", 0);
3383 if (m->m_flag&MFLAG)
3384 imap_store(mp, m, n, '+', "\\Flagged", 0);
3385 if (m->m_flag&MUNFLAG)
3386 imap_store(mp, m, n, '-', "\\Flagged", 0);
3387 if (m->m_flag&MANSWER)
3388 imap_store(mp, m, n, '+', "\\Answered", 0);
3389 if (m->m_flag&MUNANSWER)
3390 imap_store(mp, m, n, '-', "\\Flagged", 0);
3391 if (m->m_flag&MDRAFT)
3392 imap_store(mp, m, n, '+', "\\Draft", 0);
3393 if (m->m_flag&MUNDRAFT)
3394 imap_store(mp, m, n, '-', "\\Draft", 0);
3395 again:
3396 if (m->m_uid)
3397 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3398 else {
3399 if (check_expunged() == STOP)
3400 goto out;
3401 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3403 IMAP_OUT(o, MB_COMD, goto out)
3404 while (mp->mb_active & MB_COMD)
3405 ok = imap_answer(mp, twice);
3407 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3408 response_status == RESPONSE_OK)
3409 imap_copyuid(mp, m, name);
3411 if (response_status == RESPONSE_NO && !twice) {
3412 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3413 IMAP_OUT(o, MB_COMD, goto out)
3414 while (mp->mb_active & MB_COMD)
3415 ok = imap_answer(mp, 1);
3416 if (ok == OKAY) {
3417 imap_created_mailbox++;
3418 goto again;
3422 if (queuefp != NULL)
3423 Fclose(queuefp);
3425 /* ... and reset the flag to its initial value so that the 'exit'
3426 * command still leaves the message unread */
3427 out:
3428 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3429 imap_store(mp, m, n, '-', "\\Seen", 0);
3430 stored = TRU1;
3432 if (m->m_flag & MFLAG) {
3433 imap_store(mp, m, n, '-', "\\Flagged", 0);
3434 stored = TRU1;
3436 if (m->m_flag & MUNFLAG) {
3437 imap_store(mp, m, n, '+', "\\Flagged", 0);
3438 stored = TRU1;
3440 if (m->m_flag & MANSWER) {
3441 imap_store(mp, m, n, '-', "\\Answered", 0);
3442 stored = TRU1;
3444 if (m->m_flag & MUNANSWER) {
3445 imap_store(mp, m, n, '+', "\\Answered", 0);
3446 stored = TRU1;
3448 if (m->m_flag & MDRAFT) {
3449 imap_store(mp, m, n, '-', "\\Draft", 0);
3450 stored = TRU1;
3452 if (m->m_flag & MUNDRAFT) {
3453 imap_store(mp, m, n, '+', "\\Draft", 0);
3454 stored = TRU1;
3456 if (stored) {
3457 mp->mb_active |= MB_COMD;
3458 (void)imap_finish(mp);
3460 jleave:
3461 return ok;
3464 FL enum okay
3465 imap_copy(struct message *m, int n, const char *name)
3467 sighandler_type saveint, savepipe;
3468 enum okay volatile rv = STOP;
3469 NYD_ENTER;
3471 imaplock = 1;
3472 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3473 safe_signal(SIGINT, &_imap_maincatch);
3474 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3475 if (sigsetjmp(imapjmp, 1) == 0) {
3476 if (savepipe != SIG_IGN)
3477 safe_signal(SIGPIPE, imapcatch);
3479 rv = imap_copy1(&mb, m, n, name);
3481 safe_signal(SIGINT, saveint);
3482 safe_signal(SIGPIPE, savepipe);
3483 imaplock = 0;
3485 NYD_LEAVE;
3486 if (interrupts)
3487 n_go_onintr_for_imap();
3488 return rv;
3491 static enum okay
3492 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3493 unsigned long *olduid, unsigned long *newuid)
3495 char *xp, *yp, *zp;
3496 enum okay rv;
3497 NYD_ENTER;
3499 *uidvalidity = strtoul(cp, &xp, 10);
3500 *olduid = strtoul(xp, &yp, 10);
3501 *newuid = strtoul(yp, &zp, 10);
3502 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3503 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3504 NYD_LEAVE;
3505 return rv;
3508 static enum okay
3509 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3510 unsigned long *uid)
3512 char *xp, *yp;
3513 enum okay rv;
3514 NYD_ENTER;
3516 *uidvalidity = strtoul(cp, &xp, 10);
3517 *uid = strtoul(xp, &yp, 10);
3518 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3519 *yp == ']');
3520 NYD_LEAVE;
3521 return rv;
3524 static enum okay
3525 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3527 struct mailbox xmb;
3528 struct message xm;
3529 const char *cp;
3530 unsigned long uidvalidity, olduid, newuid;
3531 enum okay rv;
3532 NYD_ENTER;
3534 rv = STOP;
3536 memset(&xmb, 0, sizeof xmb);
3538 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3539 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3540 goto jleave;
3542 rv = OKAY;
3544 xmb = *mp;
3545 xmb.mb_cache_directory = NULL;
3546 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3547 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3548 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3549 sizeof(xmb.mb_imap_delim));
3550 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3551 if (mp->mb_cache_directory != NULL)
3552 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3553 xmb.mb_uidvalidity = uidvalidity;
3554 initcache(&xmb);
3556 if (m == NULL) {
3557 memset(&xm, 0, sizeof xm);
3558 xm.m_uid = olduid;
3559 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3560 goto jleave;
3561 getcache(mp, &xm, NEED_HEADER);
3562 getcache(mp, &xm, NEED_BODY);
3563 } else {
3564 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3565 getcache(mp, m, NEED_HEADER);
3566 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3567 getcache(mp, m, NEED_BODY);
3568 xm = *m;
3570 xm.m_uid = newuid;
3571 xm.m_flag &= ~MFULLYCACHED;
3572 putcache(&xmb, &xm);
3573 jleave:
3574 if (xmb.mb_cache_directory != NULL)
3575 free(xmb.mb_cache_directory);
3576 if (xmb.mb_imap_mailbox != NULL)
3577 free(xmb.mb_imap_mailbox);
3578 if (xmb.mb_imap_pass != NULL)
3579 free(xmb.mb_imap_pass);
3580 if (xmb.mb_imap_account != NULL)
3581 free(xmb.mb_imap_account);
3582 NYD_LEAVE;
3583 return rv;
3586 static enum okay
3587 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3588 long size, long lines, int flag, const char *name)
3590 struct mailbox xmb;
3591 struct message xm;
3592 const char *cp;
3593 unsigned long uidvalidity, uid;
3594 enum okay rv;
3595 NYD_ENTER;
3597 rv = STOP;
3599 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3600 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3601 goto jleave;
3603 rv = OKAY;
3605 xmb = *mp;
3606 xmb.mb_cache_directory = NULL;
3607 /* XXX mb_imap_delim reused */
3608 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3609 xmb.mb_uidvalidity = uidvalidity;
3610 xmb.mb_otf = xmb.mb_itf = fp;
3611 initcache(&xmb);
3612 memset(&xm, 0, sizeof xm);
3613 xm.m_flag = (flag & MREAD) | MNEW;
3614 xm.m_time = t;
3615 xm.m_block = mailx_blockof(off1);
3616 xm.m_offset = mailx_offsetof(off1);
3617 xm.m_size = size;
3618 xm.m_xsize = xsize;
3619 xm.m_lines = xm.m_xlines = lines;
3620 xm.m_uid = uid;
3621 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3622 putcache(&xmb, &xm);
3624 free(xmb.mb_imap_mailbox);
3625 jleave:
3626 NYD_LEAVE;
3627 return rv;
3630 static enum okay
3631 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3633 FILE *tp = NULL;
3634 time_t t;
3635 long size, xsize, ysize, lines;
3636 enum mflag flag = MNEW;
3637 char *name, *buf, *bp;
3638 char const *cp;
3639 size_t bufsize, buflen, cnt;
3640 enum okay rv = STOP;
3641 NYD_ENTER;
3643 buf = smalloc(bufsize = LINESIZE);
3644 buflen = 0;
3645 cnt = fsize(fp);
3646 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3647 goto jstop;
3649 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3651 while (*bp == ' ')
3652 ++bp;
3654 if ((cp = strrchr(bp, '{')) == NULL)
3655 goto jstop;
3657 xsize = atol(&cp[1]) + 2;
3658 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3659 goto jstop;
3660 while (*cp == ' ')
3661 cp++;
3663 if (*cp == '(') {
3664 imap_getflags(cp, &cp, &flag);
3665 while (*++cp == ' ')
3668 t = imap_read_date_time(cp);
3670 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3671 == NULL)
3672 goto jstop;
3674 size = xsize;
3675 ysize = lines = 0;
3676 while (size > 0) {
3677 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3678 goto jstop;
3679 size -= buflen;
3680 buf[--buflen] = '\0';
3681 buf[buflen-1] = '\n';
3682 fwrite(buf, 1, buflen, tp);
3683 ysize += buflen;
3684 ++lines;
3686 fflush(tp);
3687 rewind(tp);
3689 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3690 imap_unquotestr(name));
3691 rv = OKAY;
3692 jstop:
3693 free(buf);
3694 if (tp)
3695 Fclose(tp);
3696 NYD_LEAVE;
3697 return rv;
3700 #ifdef HAVE_IMAP_SEARCH
3701 static enum okay
3702 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3703 int f)
3705 char *o, *xp, *cs, c;
3706 size_t osize;
3707 FILE *queuefp = NULL;
3708 int i;
3709 unsigned long n;
3710 const char *cp;
3711 enum okay ok = STOP;
3712 NYD_X;
3714 c = 0;
3715 for (cp = spec; *cp; cp++)
3716 c |= *cp;
3717 if (c & 0200) {
3718 cp = ok_vlook(ttycharset);
3719 # ifdef HAVE_ICONV
3720 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3721 iconv_t it;
3722 char *nsp, *nspec;
3723 size_t sz, nsz;
3725 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3726 sz = strlen(spec) + 1;
3727 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3728 if (n_iconv_buf(it, n_ICONV_DEFAULT,
3729 (char const**)&spec, &sz, &nsp, &nsz) == 0 &&
3730 sz == 0) {
3731 spec = nspec;
3732 cp = "utf-8";
3734 n_iconv_close(it);
3737 # endif
3738 cp = imap_quotestr(cp);
3739 cs = salloc(n = strlen(cp) + 10);
3740 snprintf(cs, n, "CHARSET %s ", cp);
3741 } else
3742 cs = n_UNCONST("");
3744 o = ac_alloc(osize = strlen(spec) + 60);
3745 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3746 IMAP_OUT(o, MB_COMD, goto out)
3747 while (mp->mb_active & MB_COMD) {
3748 ok = imap_answer(mp, 0);
3749 if (response_status == RESPONSE_OTHER &&
3750 response_other == MAILBOX_DATA_SEARCH) {
3751 xp = responded_other_text;
3752 while (*xp && *xp != '\r') {
3753 n = strtoul(xp, &xp, 10);
3754 for (i = 0; i < cnt; i++)
3755 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3756 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3757 mark(i+1, f);
3761 out:
3762 ac_free(o);
3763 return ok;
3766 FL enum okay
3767 imap_search1(const char * volatile spec, int f)
3769 sighandler_type saveint, savepipe;
3770 enum okay volatile rv = STOP;
3771 NYD_ENTER;
3773 if (mb.mb_type != MB_IMAP)
3774 goto jleave;
3776 imaplock = 1;
3777 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3778 safe_signal(SIGINT, &_imap_maincatch);
3779 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3780 if (sigsetjmp(imapjmp, 1) == 0) {
3781 if (savepipe != SIG_IGN)
3782 safe_signal(SIGPIPE, imapcatch);
3784 rv = imap_search2(&mb, message, msgCount, spec, f);
3786 safe_signal(SIGINT, saveint);
3787 safe_signal(SIGPIPE, savepipe);
3788 imaplock = 0;
3789 jleave:
3790 NYD_LEAVE;
3791 if (interrupts)
3792 n_go_onintr_for_imap();
3793 return rv;
3795 #endif /* HAVE_IMAP_SEARCH */
3797 FL int
3798 imap_thisaccount(const char *cp)
3800 int rv;
3801 NYD_ENTER;
3803 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3804 rv = 0;
3805 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3806 mb.mb_imap_account == NULL)
3807 rv = 0;
3808 else
3809 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3810 NYD_LEAVE;
3811 return rv;
3814 FL enum okay
3815 imap_remove(const char * volatile name)
3817 sighandler_type volatile saveint, savepipe;
3818 enum okay volatile rv = STOP;
3819 NYD_ENTER;
3821 if (mb.mb_type != MB_IMAP) {
3822 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3823 goto jleave;
3826 if (!imap_thisaccount(name)) {
3827 n_err(_("Can only remove mailboxes on current IMAP server: "
3828 "\"%s\" not removed\n"), name);
3829 goto jleave;
3832 imaplock = 1;
3833 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3834 safe_signal(SIGINT, &_imap_maincatch);
3835 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3836 if (sigsetjmp(imapjmp, 1) == 0) {
3837 if (savepipe != SIG_IGN)
3838 safe_signal(SIGPIPE, imapcatch);
3840 rv = imap_remove1(&mb, imap_fileof(name));
3842 safe_signal(SIGINT, saveint);
3843 safe_signal(SIGPIPE, savepipe);
3844 imaplock = 0;
3846 if (rv == OKAY)
3847 rv = cache_remove(name);
3848 jleave:
3849 NYD_LEAVE;
3850 if (interrupts)
3851 n_go_onintr_for_imap();
3852 return rv;
3855 static enum okay
3856 imap_remove1(struct mailbox *mp, const char *name)
3858 char *o;
3859 int os;
3860 char const *qname;
3861 FILE *queuefp;
3862 enum okay ok;
3863 NYD_X;
3865 ok = STOP;
3866 queuefp = NULL;
3868 if((qname = imap_path_quote(mp, name)) != NULL){
3869 o = ac_alloc(os = strlen(qname) + 100);
3870 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3871 IMAP_OUT(o, MB_COMD, goto out)
3872 while (mp->mb_active & MB_COMD)
3873 ok = imap_answer(mp, 1);
3874 out:
3875 ac_free(o);
3877 return ok;
3880 FL enum okay
3881 imap_rename(const char *old, const char *new)
3883 sighandler_type saveint, savepipe;
3884 enum okay volatile rv = STOP;
3885 NYD_ENTER;
3887 if (mb.mb_type != MB_IMAP) {
3888 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3889 goto jleave;
3892 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3893 n_err(_("Can only rename mailboxes on current IMAP "
3894 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3895 goto jleave;
3898 imaplock = 1;
3899 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3900 safe_signal(SIGINT, &_imap_maincatch);
3901 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3902 if (sigsetjmp(imapjmp, 1) == 0) {
3903 if (savepipe != SIG_IGN)
3904 safe_signal(SIGPIPE, imapcatch);
3906 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3908 safe_signal(SIGINT, saveint);
3909 safe_signal(SIGPIPE, savepipe);
3910 imaplock = 0;
3912 if (rv == OKAY)
3913 rv = cache_rename(old, new);
3914 jleave:
3915 NYD_LEAVE;
3916 if (interrupts)
3917 n_go_onintr_for_imap();
3918 return rv;
3921 static enum okay
3922 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3924 char *o;
3925 int os;
3926 char const *qoname, *qnname;
3927 FILE *queuefp;
3928 enum okay ok;
3929 NYD_X;
3931 ok = STOP;
3932 queuefp = NULL;
3934 if((qoname = imap_path_quote(mp, old)) != NULL &&
3935 (qnname = imap_path_quote(mp, new)) != NULL){
3936 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3937 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3938 IMAP_OUT(o, MB_COMD, goto out)
3939 while (mp->mb_active & MB_COMD)
3940 ok = imap_answer(mp, 1);
3941 out:
3942 ac_free(o);
3944 return ok;
3947 FL enum okay
3948 imap_dequeue(struct mailbox *mp, FILE *fp)
3950 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3951 size_t bufsize, buflen, cnt;
3952 long offs, offs1, offs2, octets;
3953 int twice, gotcha = 0;
3954 FILE *queuefp = NULL;
3955 enum okay ok = OKAY, rok = OKAY;
3956 NYD_X;
3958 buf = smalloc(bufsize = LINESIZE);
3959 buflen = 0;
3960 cnt = fsize(fp);
3961 while ((offs1 = ftell(fp)) >= 0 &&
3962 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3963 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3965 while (*bp == ' ')
3966 ++bp;
3967 twice = 0;
3968 if ((offs = ftell(fp)) < 0)
3969 goto fail;
3970 again:
3971 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3972 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3973 cp = &bp[9];
3974 while (digitchar(*cp))
3975 cp++;
3976 if (*cp != ' ')
3977 goto fail;
3978 while (*cp == ' ')
3979 cp++;
3980 if ((newname = imap_strex(cp, NULL)) == NULL)
3981 goto fail;
3982 IMAP_OUT(o, MB_COMD, continue)
3983 while (mp->mb_active & MB_COMD)
3984 ok = imap_answer(mp, twice);
3985 if (response_status == RESPONSE_NO && twice++ == 0)
3986 goto trycreate;
3987 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3988 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3990 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3991 IMAP_OUT(o, MB_COMD, continue)
3992 while (mp->mb_active & MB_COMD)
3993 ok = imap_answer(mp, 1);
3994 if (ok == OKAY)
3995 gotcha++;
3996 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3997 if ((cp = strrchr(bp, '{')) == NULL)
3998 goto fail;
3999 octets = atol(&cp[1]) + 2;
4000 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
4001 goto fail;
4002 IMAP_OUT(o, MB_COMD, continue)
4003 while (mp->mb_active & MB_COMD) {
4004 ok = imap_answer(mp, twice);
4005 if (response_type == RESPONSE_CONT)
4006 break;
4008 if (ok == STOP) {
4009 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
4010 goto trycreate;
4011 goto fail;
4013 while (octets > 0) {
4014 size_t n = (UICMP(z, octets, >, sizeof iob)
4015 ? sizeof iob : (size_t)octets);
4016 octets -= n;
4017 if (n != fread(iob, 1, n, fp))
4018 goto fail;
4019 swrite1(&mp->mb_sock, iob, n, 1);
4021 swrite(&mp->mb_sock, "");
4022 while (mp->mb_active & MB_COMD) {
4023 ok = imap_answer(mp, 0);
4024 if (response_status == RESPONSE_NO && twice++ == 0) {
4025 if (fseek(fp, offs, SEEK_SET) < 0)
4026 goto fail;
4027 goto trycreate;
4030 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4031 if ((offs2 = ftell(fp)) < 0)
4032 goto fail;
4033 fseek(fp, offs1, SEEK_SET);
4034 if (imap_appenduid_cached(mp, fp) == STOP) {
4035 (void)fseek(fp, offs2, SEEK_SET);
4036 goto fail;
4039 } else {
4040 fail:
4041 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4042 rok = STOP;
4044 continue;
4045 trycreate:
4046 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4047 IMAP_OUT(o, MB_COMD, continue)
4048 while (mp->mb_active & MB_COMD)
4049 ok = imap_answer(mp, 1);
4050 if (ok == OKAY)
4051 goto again;
4053 fflush(fp);
4054 rewind(fp);
4055 ftruncate(fileno(fp), 0);
4056 if (gotcha)
4057 imap_close(mp);
4058 free(buf);
4059 return rok;
4062 static char *
4063 imap_strex(char const *cp, char const **xp)
4065 char const *cq;
4066 char *n = NULL;
4067 NYD_ENTER;
4069 if (*cp != '"')
4070 goto jleave;
4072 for (cq = cp + 1; *cq != '\0'; ++cq) {
4073 if (*cq == '\\')
4074 cq++;
4075 else if (*cq == '"')
4076 break;
4078 if (*cq != '"')
4079 goto jleave;
4081 n = salloc(cq - cp + 2);
4082 memcpy(n, cp, cq - cp +1);
4083 n[cq - cp + 1] = '\0';
4084 if (xp != NULL)
4085 *xp = cq + 1;
4086 jleave:
4087 NYD_LEAVE;
4088 return n;
4091 static enum okay
4092 check_expunged(void)
4094 enum okay rv;
4095 NYD_ENTER;
4097 if (expunged_messages > 0) {
4098 n_err(_("Command not executed - messages have been expunged\n"));
4099 rv = STOP;
4100 } else
4101 rv = OKAY;
4102 NYD_LEAVE;
4103 return rv;
4106 FL int
4107 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4109 struct url url;
4110 int rv, omsgCount = msgCount;
4111 NYD_ENTER;
4112 n_UNUSED(vp);
4114 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4115 n_err(_("Already connected\n"));
4116 rv = 1;
4117 goto jleave;
4120 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4121 rv = 1;
4122 goto jleave;
4124 ok_bclear(disconnected);
4125 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4127 if (mb.mb_type == MB_CACHE) {
4128 enum fedit_mode fm = FEDIT_NONE;
4129 if (_imap_rdonly)
4130 fm |= FEDIT_RDONLY;
4131 if (!(n_pstate & n_PS_EDIT))
4132 fm |= FEDIT_SYSBOX;
4133 _imap_setfile1(&url, fm, 1);
4134 if (msgCount > omsgCount)
4135 newmailinfo(omsgCount);
4137 rv = 0;
4138 jleave:
4139 NYD_LEAVE;
4140 return rv;
4143 FL int
4144 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4146 struct url url;
4147 int rv = 1, *msgvec = vp;
4148 NYD_ENTER;
4150 if (mb.mb_type == MB_CACHE) {
4151 n_err(_("Not connected\n"));
4152 goto jleave;
4154 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4155 n_err(_("The current mailbox is not cached\n"));
4156 goto jleave;
4159 if (!url_parse(&url, CPROTO_IMAP, mailname))
4160 goto jleave;
4162 if (*msgvec)
4163 c_cache(vp);
4164 ok_bset(disconnected);
4165 if (mb.mb_type == MB_IMAP) {
4166 enum fedit_mode fm = FEDIT_NONE;
4167 if (_imap_rdonly)
4168 fm |= FEDIT_RDONLY;
4169 if (!(n_pstate & n_PS_EDIT))
4170 fm |= FEDIT_SYSBOX;
4171 sclose(&mb.mb_sock);
4172 _imap_setfile1(&url, fm, 1);
4174 rv = 0;
4175 jleave:
4176 NYD_LEAVE;
4177 return rv;
4180 FL int
4181 c_cache(void *vp)
4183 int rv = 1, *msgvec = vp, *ip;
4184 struct message *mp;
4185 NYD_ENTER;
4187 if (mb.mb_type != MB_IMAP) {
4188 n_err(_("Not connected to an IMAP server\n"));
4189 goto jleave;
4191 if (cached_uidvalidity(&mb) == 0) {
4192 n_err(_("The current mailbox is not cached\n"));
4193 goto jleave;
4196 srelax_hold();
4197 for (ip = msgvec; *ip; ++ip) {
4198 mp = &message[*ip - 1];
4199 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4200 get_body(mp);
4201 srelax();
4204 srelax_rele();
4205 rv = 0;
4206 jleave:
4207 NYD_LEAVE;
4208 return rv;
4211 FL int
4212 disconnected(const char *file)
4214 struct url url;
4215 int rv = 1;
4216 NYD_ENTER;
4218 if (ok_blook(disconnected)) {
4219 rv = 1;
4220 goto jleave;
4223 if (!url_parse(&url, CPROTO_IMAP, file)) {
4224 rv = 0;
4225 goto jleave;
4227 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4229 jleave:
4230 NYD_LEAVE;
4231 return rv;
4234 FL void
4235 transflags(struct message *omessage, long omsgCount, int transparent)
4237 struct message *omp, *nmp, *newdot, *newprevdot;
4238 int hf;
4239 NYD_ENTER;
4241 omp = omessage;
4242 nmp = message;
4243 newdot = message;
4244 newprevdot = NULL;
4245 while (PTRCMP(omp, <, omessage + omsgCount) &&
4246 PTRCMP(nmp, <, message + msgCount)) {
4247 if (dot && nmp->m_uid == dot->m_uid)
4248 newdot = nmp;
4249 if (prevdot && nmp->m_uid == prevdot->m_uid)
4250 newprevdot = nmp;
4251 if (omp->m_uid == nmp->m_uid) {
4252 hf = nmp->m_flag & MHIDDEN;
4253 if (transparent && mb.mb_type == MB_IMAP)
4254 omp->m_flag &= ~MHIDDEN;
4255 *nmp++ = *omp++;
4256 if (transparent && mb.mb_type == MB_CACHE)
4257 nmp[-1].m_flag |= hf;
4258 } else if (omp->m_uid < nmp->m_uid)
4259 ++omp;
4260 else
4261 ++nmp;
4263 dot = newdot;
4264 setdot(newdot);
4265 prevdot = newprevdot;
4266 free(omessage);
4267 NYD_LEAVE;
4270 FL time_t
4271 imap_read_date_time(const char *cp)
4273 char buf[3];
4274 time_t t;
4275 int i, year, month, day, hour, minute, second, sign = -1;
4276 NYD2_ENTER;
4278 /* "25-Jul-2004 15:33:44 +0200"
4279 * | | | | | |
4280 * 0 5 10 15 20 25 */
4281 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4282 goto jinvalid;
4283 day = strtol(&cp[1], NULL, 10);
4284 for (i = 0;;) {
4285 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4286 break;
4287 if (n_month_names[++i][0] == '\0')
4288 goto jinvalid;
4290 month = i + 1;
4291 year = strtol(&cp[8], NULL, 10);
4292 hour = strtol(&cp[13], NULL, 10);
4293 minute = strtol(&cp[16], NULL, 10);
4294 second = strtol(&cp[19], NULL, 10);
4295 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4296 goto jinvalid;
4297 switch (cp[22]) {
4298 case '-':
4299 sign = 1;
4300 break;
4301 case '+':
4302 break;
4303 default:
4304 goto jinvalid;
4306 buf[2] = '\0';
4307 buf[0] = cp[23];
4308 buf[1] = cp[24];
4309 t += strtol(buf, NULL, 10) * sign * 3600;
4310 buf[0] = cp[25];
4311 buf[1] = cp[26];
4312 t += strtol(buf, NULL, 10) * sign * 60;
4313 jleave:
4314 NYD2_LEAVE;
4315 return t;
4316 jinvalid:
4317 time(&t);
4318 goto jleave;
4321 FL const char *
4322 imap_make_date_time(time_t t)
4324 static char s[40];
4325 struct tm *tmptr;
4326 int tzdiff, tzdiff_hour, tzdiff_min;
4327 NYD2_ENTER;
4329 tzdiff = t - mktime(gmtime(&t));
4330 tzdiff_hour = (int)(tzdiff / 60);
4331 tzdiff_min = tzdiff_hour % 60;
4332 tzdiff_hour /= 60;
4333 tmptr = localtime(&t);
4334 if (tmptr->tm_isdst > 0)
4335 tzdiff_hour++;
4336 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4337 tmptr->tm_mday, n_month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4338 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4339 NYD2_LEAVE;
4340 return s;
4343 FL char *
4344 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4346 char *n, *np;
4347 NYD2_ENTER;
4349 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4350 n_MEMORY_DEBUG_ARGSCALL);
4352 /* Just ignore the `is-system-mailbox' prefix XXX */
4353 if (cp[0] == '%' && cp[1] == ':')
4354 cp += 2;
4356 while (*cp != '\0') {
4357 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4358 *np++ = *cp++;
4359 *np++ = *cp++;
4360 *np++ = *cp++;
4361 } else if (cp[0] == '/')
4362 break;
4363 else
4364 *np++ = *cp++;
4366 *np = '\0';
4367 NYD2_LEAVE;
4368 return n;
4370 #endif /* HAVE_IMAP */
4372 /* s-it-mode */