n_idna_to_ascii(): add support for idnkit 2.3
[s-mailx.git] / obs-imap.c
blob159a65e43de18eff021ea96bd96ebd29463f8e96
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 - 2018 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE obs_imap
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 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 (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
426 !defined HAVE_ALWAYS_UNICODE_LOCALE
427 if(!(n_psonce & n_PSO_UNICODE)){
428 char const *x;
430 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
431 if((x = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
432 cp)) == NULL)
433 goto jerr;
434 cp = x;
436 /* So: Why not start all over again?
437 * Is this a string that works out as "plain US-ASCII"? */
438 for(l = 0;; ++l)
439 if((c = cp[l]) == '\0')
440 goto jleave;
441 else if(c <= 0x1F || c >= 0x7F || c == '&')
442 break;
444 #endif
446 /* We need to encode, save what we have, encode the rest */
447 l_plain = l;
449 for(cp += l, l = 0; cp[l] != '\0'; ++l)
451 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
453 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
454 if(l_plain > 0)
455 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
456 else
457 out.l = 0;
458 DBG( l_plain += (l << 2); )
460 while(l > 0){
461 c = *cp++;
462 --l;
464 if(c == '&'){
465 out.s[out.l + 0] = '&';
466 out.s[out.l + 1] = '-';
467 out.l += 2;
468 }else if(c > 0x1F && c < 0x7F)
469 out.s[out.l++] = c;
470 else{
471 static char const mb64ct[] =
472 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
473 ui32_t utf32;
475 /* Convert consecutive non-representables */
476 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
478 for(be16p = be16p_base, --cp, ++l;;){
479 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
480 goto jerr;
482 /* TODO S-CText: magic utf16 conversions */
483 if(utf32 < 0x10000){
484 be16p[1] = utf32 & 0xFF;
485 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
486 be16p += 2;
487 }else{
488 ui16_t s7e;
490 utf32 -= 0x10000;
491 s7e = 0xD800u | (utf32 >> 10);
492 be16p[1] = s7e & 0xFF;
493 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
494 s7e = 0xDC00u | (utf32 &= 0x03FF);
495 be16p[3] = s7e & 0xFF;
496 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
497 be16p += 4;
500 if(l == 0)
501 break;
502 if((c = *cp) > 0x1F && c < 0x7F)
503 break;
506 /* And then warp that UTF-16BE to mUTF-7 */
507 out.s[out.l++] = '&';
508 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
509 be16p = be16p_base;
511 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
512 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
513 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
514 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
515 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
516 out.l += 4;
518 if(utf32 > 0){
519 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
520 if(--utf32 == 0){
521 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
522 out.l += 2;
523 }else{
524 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
525 (be16p[1] >> 4)];
526 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
527 out.l += 3;
530 out.s[out.l++] = '-';
533 out.s[out.l] = '\0';
534 assert(out.l <= l_plain);
535 *err_or_null = FAL0;
536 cp = out.s;
537 jleave:
538 NYD2_LEAVE;
539 return cp;
540 jerr:
541 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
542 goto jleave;
545 FL char *
546 imap_path_decode(char const *path, bool_t *err_or_null){
547 /* To a large extend inspired by dovecot(1) TODO use string */
548 bool_t err_def;
549 ui8_t *mb64p_base, *mb64p, *mb64xp;
550 char const *emsg, *cp;
551 char *rv_base, *rv, c;
552 size_t l_orig, l, i;
553 NYD2_ENTER;
555 if(err_or_null == NULL)
556 err_or_null = &err_def;
557 *err_or_null = FAL0;
559 l = l_orig = strlen(path);
560 rv = rv_base = salloc(l << 1);
561 memcpy(rv, path, l +1);
563 /* xxx Don't check for invalid characters from malicious servers */
564 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
565 goto jleave;
567 *err_or_null = TRU1;
569 emsg = N_("Invalid mUTF-7 encoding");
570 i = PTR2SIZE(cp - path);
571 rv += i;
572 l -= i;
573 mb64p_base = NULL;
575 while(l > 0){
576 if((c = *cp) != '&'){
577 if(c <= 0x1F || c >= 0x7F){
578 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
579 goto jerr;
581 *rv++ = c;
582 ++cp;
583 --l;
584 }else if(--l == 0)
585 goto jeincpl;
586 else if(*++cp == '-'){
587 *rv++ = '&';
588 ++cp;
589 --l;
590 }else if(l < 3){
591 jeincpl:
592 emsg = N_("Invalid mUTF-7: incomplete input");
593 goto jerr;
594 }else{
595 /* mUTF-7 -> UTF-16BE -> UTF-8 */
596 static ui8_t const mb64dt[256] = {
597 #undef XX
598 #define XX 0xFFu
599 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
600 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
601 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
602 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
603 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
604 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
605 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
606 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
607 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
608 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
609 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
610 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
611 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
612 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
613 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
614 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
617 if(mb64p_base == NULL)
618 mb64p_base = salloc(l);
620 /* Decode the mUTF-7 to what is indeed UTF-16BE */
621 for(mb64p = mb64p_base;;){
622 assert(l >= 3);
623 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
624 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
625 goto jerr;
626 mb64p += 2;
628 c = cp[2];
629 cp += 3;
630 l -= 3;
631 if(c == '-')
632 break;
633 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
634 goto jerr;
636 if(l == 0)
637 goto jerr;
638 --l;
639 if((c = *cp++) == '-')
640 break;
641 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
642 goto jerr;
644 if(l < 3){
645 if(l > 0 && *cp == '-'){
646 --l;
647 ++cp;
648 break;
650 goto jerr;
653 #undef XX
655 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
656 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
657 goto jerr;
660 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
661 i = PTR2SIZE(mb64p - mb64p_base);
662 mb64p = mb64xp = mb64p_base;
664 while(i > 0){
665 ui8_t ul, u0, u1, u2, u3;
667 ul = (i >= 4) ? 4 : i & 0x3;
668 i -= ul;
669 u0 = mb64xp[0];
670 u1 = mb64xp[1];
671 u2 = (ul < 3) ? 0 : mb64xp[2];
672 u3 = (ul < 4) ? 0 : mb64xp[3];
673 mb64xp += ul;
674 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
675 if(ul < 3)
676 break;
677 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
678 if(ul < 4)
679 break;
680 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
683 /* UTF-16BE we convert to UTF-8 */
684 i = PTR2SIZE(mb64p - mb64p_base);
685 if(i & 1){
686 emsg = N_("Odd bytecount for UTF-16BE input");
687 goto jerr;
690 /* TODO S-CText: magic utf16 conversions */
691 emsg = N_("Invalid UTF-16BE encoding");
693 for(mb64p = mb64p_base; i > 0;){
694 ui32_t utf32;
695 ui16_t uhi, ulo;
697 uhi = mb64p[0];
698 uhi <<= 8;
699 uhi |= mb64p[1];
701 /* Not a surrogate? */
702 if(uhi < 0xD800 || uhi > 0xDFFF){
703 utf32 = uhi;
704 mb64p += 2;
705 i -= 2;
706 }else if(uhi > 0xDBFF)
707 goto jerr;
708 else if(i < 4){
709 emsg = N_("Incomplete UTF-16BE surrogate pair");
710 goto jerr;
711 }else{
712 ulo = mb64p[2];
713 ulo <<= 8;
714 ulo |= mb64p[3];
715 if(ulo < 0xDC00 || ulo > 0xDFFF)
716 goto jerr;
718 utf32 = (uhi &= 0x03FF);
719 utf32 <<= 10;
720 utf32 += 0x10000;
721 utf32 |= (ulo &= 0x03FF);
722 mb64p += 4;
723 i -= 4;
726 utf32 = n_utf32_to_utf8(utf32, rv);
727 rv += utf32;
731 *rv = '\0';
733 /* We can skip the UTF-8 conversion occasionally */
734 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
735 !defined HAVE_ALWAYS_UNICODE_LOCALE
736 if(!(n_psonce & n_PSO_UNICODE)){
737 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
738 if((rv = n_iconv_onetime_cp(n_ICONV_NONE, NULL, NULL, rv_base)) == NULL)
739 goto jerr;
741 #endif
743 *err_or_null = FAL0;
744 rv = rv_base;
745 jleave:
746 NYD2_LEAVE;
747 return rv;
748 jerr:
749 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
750 memcpy(rv = rv_base, path, ++l_orig);
751 goto jleave;
754 static char *
755 imap_path_quote(struct mailbox *mp, char const *cp){
756 bool_t err;
757 char *rv;
758 NYD2_ENTER;
760 cp = imap_path_normalize(mp, cp);
761 cp = imap_path_encode(cp, &err);
762 rv = err ? NULL : imap_quotestr(cp);
763 NYD2_LEAVE;
764 return rv;
767 static void
768 imap_other_get(char *pp)
770 char *xp;
771 NYD2_ENTER;
773 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
774 pp += 6;
775 response_other = MAILBOX_DATA_FLAGS;
776 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
777 pp += 5;
778 response_other = MAILBOX_DATA_LIST;
779 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
780 pp += 5;
781 response_other = MAILBOX_DATA_LSUB;
782 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
783 pp += 8;
784 response_other = MAILBOX_DATA_MAILBOX;
785 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
786 pp += 7;
787 response_other = MAILBOX_DATA_SEARCH;
788 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
789 pp += 7;
790 response_other = MAILBOX_DATA_STATUS;
791 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
792 pp += 11;
793 response_other = CAPABILITY_DATA;
794 } else {
795 responded_other_number = strtol(pp, &xp, 10);
796 while (*xp == ' ')
797 ++xp;
798 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
799 response_other = MAILBOX_DATA_EXISTS;
800 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
801 response_other = MAILBOX_DATA_RECENT;
802 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
803 response_other = MESSAGE_DATA_EXPUNGE;
804 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
805 pp = &xp[6];
806 response_other = MESSAGE_DATA_FETCH;
807 } else
808 response_other = RESPONSE_OTHER_UNKNOWN;
810 responded_other_text = pp;
811 NYD2_LEAVE;
814 static void
815 imap_response_get(const char **cp)
817 NYD2_ENTER;
818 if (ascncasecmp(*cp, "OK ", 3) == 0) {
819 *cp += 3;
820 response_status = RESPONSE_OK;
821 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
822 *cp += 3;
823 response_status = RESPONSE_NO;
824 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
825 *cp += 4;
826 response_status = RESPONSE_BAD;
827 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
828 *cp += 8;
829 response_status = RESPONSE_PREAUTH;
830 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
831 *cp += 4;
832 response_status = RESPONSE_BYE;
833 } else
834 response_status = RESPONSE_OTHER;
835 NYD2_LEAVE;
838 static void
839 imap_response_parse(void)
841 static char *parsebuf; /* TODO Use pool */
842 static size_t parsebufsize;
844 const char *ip = imapbuf;
845 char *pp;
846 NYD2_ENTER;
848 if (parsebufsize < imapbufsize + 1)
849 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
850 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
851 pp = parsebuf;
852 switch (*ip) {
853 case '+':
854 response_type = RESPONSE_CONT;
855 ip++;
856 pp++;
857 while (*ip == ' ') {
858 ip++;
859 pp++;
861 break;
862 case '*':
863 ip++;
864 pp++;
865 while (*ip == ' ') {
866 ip++;
867 pp++;
869 imap_response_get(&ip);
870 pp = &parsebuf[ip - imapbuf];
871 switch (response_status) {
872 case RESPONSE_BYE:
873 response_type = RESPONSE_FATAL;
874 break;
875 default:
876 response_type = RESPONSE_DATA;
878 break;
879 default:
880 responded_tag = parsebuf;
881 while (*pp && *pp != ' ')
882 pp++;
883 if (*pp == '\0') {
884 response_type = RESPONSE_ILLEGAL;
885 break;
887 *pp++ = '\0';
888 while (*pp && *pp == ' ')
889 pp++;
890 if (*pp == '\0') {
891 response_type = RESPONSE_ILLEGAL;
892 break;
894 ip = &imapbuf[pp - parsebuf];
895 response_type = RESPONSE_TAGGED;
896 imap_response_get(&ip);
897 pp = &parsebuf[ip - imapbuf];
899 responded_text = pp;
900 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
901 response_status == RESPONSE_OTHER)
902 imap_other_get(pp);
903 NYD2_LEAVE;
906 static enum okay
907 imap_answer(struct mailbox *mp, int errprnt)
909 int i, complete;
910 enum okay rv;
911 NYD2_ENTER;
913 rv = OKAY;
914 if (mp->mb_type == MB_CACHE)
915 goto jleave;
916 rv = STOP;
917 jagain:
918 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
919 if (n_poption & n_PO_VERBVERB)
920 fputs(imapbuf, stderr);
921 imap_response_parse();
922 if (response_type == RESPONSE_ILLEGAL)
923 goto jagain;
924 if (response_type == RESPONSE_CONT) {
925 rv = OKAY;
926 goto jleave;
928 if (response_status == RESPONSE_OTHER) {
929 if (response_other == MAILBOX_DATA_EXISTS) {
930 had_exists = responded_other_number;
931 rec_queue(REC_EXISTS, responded_other_number);
932 if (had_expunge > 0)
933 had_expunge = 0;
934 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
935 rec_queue(REC_EXPUNGE, responded_other_number);
936 if (had_expunge < 0)
937 had_expunge = 0;
938 had_expunge++;
939 expunged_messages++;
942 complete = 0;
943 if (response_type == RESPONSE_TAGGED) {
944 if (asccasecmp(responded_tag, tag(0)) == 0)
945 complete |= 1;
946 else
947 goto jagain;
949 switch (response_status) {
950 case RESPONSE_PREAUTH:
951 mp->mb_active &= ~MB_PREAUTH;
952 /*FALLTHRU*/
953 case RESPONSE_OK:
954 jokay:
955 rv = OKAY;
956 complete |= 2;
957 break;
958 case RESPONSE_NO:
959 case RESPONSE_BAD:
960 jstop:
961 complete |= 2;
962 if (errprnt)
963 n_err(_("IMAP error: %s"), responded_text);
964 break;
965 case RESPONSE_UNKNOWN: /* does not happen */
966 case RESPONSE_BYE:
967 i = mp->mb_active;
968 mp->mb_active = MB_NONE;
969 if (i & MB_BYE)
970 goto jokay;
971 goto jstop;
972 case RESPONSE_OTHER:
973 rv = OKAY;
974 break;
976 if (response_status != RESPONSE_OTHER &&
977 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
978 n_err(_("IMAP alert: %s"), &responded_text[8]);
979 if (complete == 3)
980 mp->mb_active &= ~MB_COMD;
981 } else
982 mp->mb_active = MB_NONE;
983 jleave:
984 NYD2_LEAVE;
985 return rv;
988 static enum okay
989 imap_parse_list(void)
991 char *cp;
992 enum okay rv;
993 NYD2_ENTER;
995 rv = STOP;
997 cp = responded_other_text;
998 list_attributes = LIST_NONE;
999 if (*cp == '(') {
1000 while (*cp && *cp != ')') {
1001 if (*cp == '\\') {
1002 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1003 list_attributes |= LIST_NOINFERIORS;
1004 cp += 12;
1005 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1006 list_attributes |= LIST_NOSELECT;
1007 cp += 9;
1008 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1009 list_attributes |= LIST_MARKED;
1010 cp += 7;
1011 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1012 list_attributes |= LIST_UNMARKED;
1013 cp += 9;
1016 cp++;
1018 if (*++cp != ' ')
1019 goto jleave;
1020 while (*cp == ' ')
1021 cp++;
1024 list_hierarchy_delimiter = EOF;
1025 if (*cp == '"') {
1026 if (*++cp == '\\')
1027 cp++;
1028 list_hierarchy_delimiter = *cp++ & 0377;
1029 if (cp[0] != '"' || cp[1] != ' ')
1030 goto jleave;
1031 cp++;
1032 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1033 list_hierarchy_delimiter = EOF;
1034 cp += 3;
1037 while (*cp == ' ')
1038 cp++;
1039 list_name = cp;
1040 while (*cp && *cp != '\r')
1041 cp++;
1042 *cp = '\0';
1043 rv = OKAY;
1044 jleave:
1045 NYD2_LEAVE;
1046 return rv;
1049 static enum okay
1050 imap_finish(struct mailbox *mp)
1052 NYD_ENTER;
1053 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1054 imap_answer(mp, 1);
1055 NYD_LEAVE;
1056 return OKAY;
1059 static void
1060 imap_timer_off(void)
1062 NYD_ENTER;
1063 if (imapkeepalive > 0) {
1064 alarm(0);
1065 safe_signal(SIGALRM, savealrm);
1067 NYD_LEAVE;
1070 static void
1071 imapcatch(int s)
1073 NYD_X; /* Signal handler */
1074 switch (s) {
1075 case SIGINT:
1076 n_err_sighdl(_("Interrupt\n"));
1077 siglongjmp(imapjmp, 1);
1078 /*NOTREACHED*/
1079 case SIGPIPE:
1080 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1081 break;
1085 static void
1086 _imap_maincatch(int s)
1088 NYD_X; /* Signal handler */
1089 n_UNUSED(s);
1090 if (interrupts++ == 0) {
1091 n_err_sighdl(_("Interrupt\n"));
1092 return;
1094 n_go_onintr_for_imap();
1097 static enum okay
1098 imap_noop1(struct mailbox *mp)
1100 char o[LINESIZE];
1101 FILE *queuefp = NULL;
1102 NYD_X;
1104 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1105 IMAP_OUT(o, MB_COMD, return STOP)
1106 IMAP_ANSWER()
1107 return OKAY;
1110 FL char const *
1111 imap_fileof(char const *xcp)
1113 char const *cp = xcp;
1114 int state = 0;
1115 NYD_ENTER;
1117 while (*cp) {
1118 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1119 cp += 3;
1120 state = 1;
1122 if (cp[0] == '/' && state == 1) {
1123 ++cp;
1124 goto jleave;
1126 if (cp[0] == '/') {
1127 cp = xcp;
1128 goto jleave;
1130 ++cp;
1132 jleave:
1133 NYD_LEAVE;
1134 return cp;
1137 FL enum okay
1138 imap_noop(void)
1140 sighandler_type volatile oldint, oldpipe;
1141 enum okay volatile rv = STOP;
1142 NYD_ENTER;
1144 if (mb.mb_type != MB_IMAP)
1145 goto jleave;
1147 imaplock = 1;
1148 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1149 safe_signal(SIGINT, &_imap_maincatch);
1150 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1151 if (sigsetjmp(imapjmp, 1) == 0) {
1152 if (oldpipe != SIG_IGN)
1153 safe_signal(SIGPIPE, imapcatch);
1155 rv = imap_noop1(&mb);
1157 safe_signal(SIGINT, oldint);
1158 safe_signal(SIGPIPE, oldpipe);
1159 imaplock = 0;
1160 jleave:
1161 NYD_LEAVE;
1162 if (interrupts)
1163 n_go_onintr_for_imap();
1164 return rv;
1167 static void
1168 rec_queue(enum rec_type rt, unsigned long cnt)
1170 struct record *rp;
1171 NYD_ENTER;
1173 rp = scalloc(1, sizeof *rp);
1174 rp->rec_type = rt;
1175 rp->rec_count = cnt;
1176 if (record && recend) {
1177 recend->rec_next = rp;
1178 recend = rp;
1179 } else
1180 record = recend = rp;
1181 NYD_LEAVE;
1184 static enum okay
1185 rec_dequeue(void)
1187 struct message *omessage;
1188 struct record *rp, *rq;
1189 uiz_t exists = 0, i;
1190 enum okay rv = STOP;
1191 NYD_ENTER;
1193 if (record == NULL)
1194 goto jleave;
1196 omessage = message;
1197 message = smalloc((msgCount+1) * sizeof *message);
1198 if (msgCount)
1199 memcpy(message, omessage, msgCount * sizeof *message);
1200 memset(&message[msgCount], 0, sizeof *message);
1202 rp = record, rq = NULL;
1203 rv = OKAY;
1204 while (rp != NULL) {
1205 switch (rp->rec_type) {
1206 case REC_EXISTS:
1207 exists = rp->rec_count;
1208 break;
1209 case REC_EXPUNGE:
1210 if (rp->rec_count == 0) {
1211 rv = STOP;
1212 break;
1214 if (rp->rec_count > (unsigned long)msgCount) {
1215 if (exists == 0 || rp->rec_count > exists--)
1216 rv = STOP;
1217 break;
1219 if (exists > 0)
1220 exists--;
1221 delcache(&mb, &message[rp->rec_count-1]);
1222 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1223 ((msgCount - rp->rec_count + 1) * sizeof *message));
1224 --msgCount;
1225 /* If the message was part of a collapsed thread,
1226 * the m_collapsed field of one of its ancestors
1227 * should be incremented. It seems hardly possible
1228 * to do this with the current message structure,
1229 * though. The result is that a '+' may be shown
1230 * in the header summary even if no collapsed
1231 * children exists */
1232 break;
1234 if (rq != NULL)
1235 free(rq);
1236 rq = rp;
1237 rp = rp->rec_next;
1239 if (rq != NULL)
1240 free(rq);
1242 record = recend = NULL;
1243 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1244 message = srealloc(message, (exists + 1) * sizeof *message);
1245 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1246 for (i = msgCount; i < exists; ++i)
1247 imap_init(&mb, i);
1248 imap_flags(&mb, msgCount+1, exists);
1249 msgCount = exists;
1252 if (rv == STOP) {
1253 free(message);
1254 message = omessage;
1256 jleave:
1257 NYD_LEAVE;
1258 return rv;
1261 static void
1262 rec_rmqueue(void)
1264 struct record *rp;
1265 NYD_ENTER;
1267 for (rp = record; rp != NULL;) {
1268 struct record *tmp = rp;
1269 rp = rp->rec_next;
1270 free(tmp);
1272 record = recend = NULL;
1273 NYD_LEAVE;
1276 /*ARGSUSED*/
1277 static void
1278 imapalarm(int s)
1280 sighandler_type volatile saveint, savepipe;
1281 NYD_X; /* Signal handler */
1282 n_UNUSED(s);
1284 if (imaplock++ == 0) {
1285 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1286 safe_signal(SIGINT, &_imap_maincatch);
1287 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1288 if (sigsetjmp(imapjmp, 1)) {
1289 safe_signal(SIGINT, saveint);
1290 safe_signal(SIGPIPE, savepipe);
1291 goto jbrk;
1293 if (savepipe != SIG_IGN)
1294 safe_signal(SIGPIPE, imapcatch);
1295 if (imap_noop1(&mb) != OKAY) {
1296 safe_signal(SIGINT, saveint);
1297 safe_signal(SIGPIPE, savepipe);
1298 goto jleave;
1300 safe_signal(SIGINT, saveint);
1301 safe_signal(SIGPIPE, savepipe);
1303 jbrk:
1304 alarm(imapkeepalive);
1305 jleave:
1306 --imaplock;
1309 static enum okay
1310 imap_preauth(struct mailbox *mp, struct url const *urlp)
1312 NYD_X;
1314 mp->mb_active |= MB_PREAUTH;
1315 imap_answer(mp, 1);
1317 #ifdef HAVE_SSL
1318 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1319 FILE *queuefp = NULL;
1320 char o[LINESIZE];
1322 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1323 IMAP_OUT(o, MB_COMD, return STOP)
1324 IMAP_ANSWER()
1325 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1326 return STOP;
1328 #else
1329 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1330 n_err(_("No SSL support compiled in\n"));
1331 return STOP;
1333 #endif
1335 imap_capability(mp);
1336 return OKAY;
1339 static enum okay
1340 imap_capability(struct mailbox *mp)
1342 char o[LINESIZE];
1343 FILE *queuefp = NULL;
1344 enum okay ok = STOP;
1345 const char *cp;
1346 NYD_X;
1348 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1349 IMAP_OUT(o, MB_COMD, return STOP)
1350 while (mp->mb_active & MB_COMD) {
1351 ok = imap_answer(mp, 0);
1352 if (response_status == RESPONSE_OTHER &&
1353 response_other == CAPABILITY_DATA) {
1354 cp = responded_other_text;
1355 while (*cp) {
1356 while (spacechar(*cp))
1357 ++cp;
1358 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1359 /* RFC 2359 */
1360 mp->mb_flags |= MB_UIDPLUS;
1361 while (*cp && !spacechar(*cp))
1362 ++cp;
1366 return ok;
1369 static enum okay
1370 imap_auth(struct mailbox *mp, struct ccred *ccred)
1372 enum okay rv;
1373 NYD_ENTER;
1375 if (!(mp->mb_active & MB_PREAUTH)) {
1376 rv = OKAY;
1377 goto jleave;
1380 switch (ccred->cc_authtype) {
1381 case AUTHTYPE_LOGIN:
1382 rv = imap_login(mp, ccred);
1383 break;
1384 #ifdef HAVE_MD5
1385 case AUTHTYPE_CRAM_MD5:
1386 rv = imap_cram_md5(mp, ccred);
1387 break;
1388 #endif
1389 #ifdef HAVE_GSSAPI
1390 case AUTHTYPE_GSSAPI:
1391 rv = _imap_gssapi(mp, ccred);
1392 break;
1393 #endif
1394 default:
1395 rv = STOP;
1396 break;
1398 jleave:
1399 NYD_LEAVE;
1400 return rv;
1403 #ifdef HAVE_MD5
1404 static enum okay
1405 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1407 char o[LINESIZE], *cp;
1408 FILE *queuefp = NULL;
1409 enum okay rv = STOP;
1410 NYD_ENTER;
1412 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1413 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1414 imap_answer(mp, 1);
1415 if (response_type != RESPONSE_CONT)
1416 goto jleave;
1418 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1419 if(cp == NULL)
1420 goto jleave;
1421 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1422 while (mp->mb_active & MB_COMD)
1423 rv = imap_answer(mp, 1);
1424 jleave:
1425 NYD_LEAVE;
1426 return rv;
1428 #endif /* HAVE_MD5 */
1430 static enum okay
1431 imap_login(struct mailbox *mp, struct ccred *ccred)
1433 char o[LINESIZE];
1434 FILE *queuefp = NULL;
1435 enum okay rv = STOP;
1436 NYD_ENTER;
1438 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1439 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1440 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1441 while (mp->mb_active & MB_COMD)
1442 rv = imap_answer(mp, 1);
1443 jleave:
1444 NYD_LEAVE;
1445 return rv;
1448 #ifdef HAVE_GSSAPI
1449 # include "obs-imap-gssapi.h"
1450 #endif
1452 FL enum okay
1453 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1454 enum fedit_mode fm)
1456 char o[LINESIZE];
1457 char const *qname, *cp;
1458 FILE *queuefp;
1459 enum okay ok;
1460 NYD_X;
1461 n_UNUSED(size);
1463 ok = STOP;
1464 queuefp = NULL;
1466 if((qname = imap_path_quote(mp, mbx)) == NULL)
1467 goto jleave;
1469 ok = OKAY;
1471 mp->mb_uidvalidity = 0;
1472 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1473 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1474 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1475 while (mp->mb_active & MB_COMD) {
1476 ok = imap_answer(mp, 1);
1477 if (response_status != RESPONSE_OTHER &&
1478 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1479 mp->mb_uidvalidity = atol(&cp[13]);
1481 *cnt = (had_exists > 0) ? had_exists : 0;
1482 if (response_status != RESPONSE_OTHER &&
1483 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1484 mp->mb_perm = 0;
1485 jleave:
1486 return ok;
1489 static enum okay
1490 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1492 char o[LINESIZE];
1493 FILE *queuefp = NULL;
1494 char const *cp;
1495 struct message *m;
1496 unsigned x = X, y = Y, n;
1497 NYD_X;
1499 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1500 IMAP_OUT(o, MB_COMD, return STOP)
1501 while (mp->mb_active & MB_COMD) {
1502 imap_answer(mp, 1);
1503 if (response_status == RESPONSE_OTHER &&
1504 response_other == MESSAGE_DATA_FETCH) {
1505 n = responded_other_number;
1506 if (n < x || n > y)
1507 continue;
1508 m = &message[n-1];
1509 m->m_xsize = 0;
1510 } else
1511 continue;
1513 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1514 cp += 5;
1515 while (*cp == ' ')
1516 cp++;
1517 if (*cp == '(')
1518 imap_getflags(cp, &cp, &m->m_flag);
1521 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1522 m->m_uid = strtoul(&cp[4], NULL, 10);
1523 getcache1(mp, m, NEED_UNSPEC, 1);
1524 m->m_flag &= ~MHIDDEN;
1527 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1528 x++;
1529 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1530 y--;
1531 if (x <= y) {
1532 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1533 tag(1), x, y);
1534 IMAP_OUT(o, MB_COMD, return STOP)
1535 while (mp->mb_active & MB_COMD) {
1536 imap_answer(mp, 1);
1537 if (response_status == RESPONSE_OTHER &&
1538 response_other == MESSAGE_DATA_FETCH) {
1539 n = responded_other_number;
1540 if (n < x || n > y)
1541 continue;
1542 m = &message[n-1];
1543 } else
1544 continue;
1545 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1546 m->m_xsize = strtol(&cp[12], NULL, 10);
1547 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1548 m->m_time = imap_read_date_time(&cp[13]);
1552 srelax_hold();
1553 for (n = X; n <= Y; ++n) {
1554 putcache(mp, &message[n-1]);
1555 srelax();
1557 srelax_rele();
1558 return OKAY;
1561 static void
1562 imap_init(struct mailbox *mp, int n)
1564 struct message *m;
1565 NYD_ENTER;
1566 n_UNUSED(mp);
1568 m = message + n;
1569 m->m_flag = MUSED | MNOFROM;
1570 m->m_block = 0;
1571 m->m_offset = 0;
1572 NYD_LEAVE;
1575 static void
1576 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1578 struct message *omessage = 0;
1579 int i, omsgCount = 0;
1580 enum okay dequeued = STOP;
1581 NYD_ENTER;
1583 if (nmail || transparent) {
1584 omessage = message;
1585 omsgCount = msgCount;
1587 if (nmail)
1588 dequeued = rec_dequeue();
1590 if (had_exists >= 0) {
1591 if (dequeued != OKAY)
1592 msgCount = had_exists;
1593 had_exists = -1;
1595 if (had_expunge >= 0) {
1596 if (dequeued != OKAY)
1597 msgCount -= had_expunge;
1598 had_expunge = -1;
1601 if (nmail && expunged_messages)
1602 printf("Expunged %ld message%s.\n", expunged_messages,
1603 (expunged_messages != 1 ? "s" : ""));
1604 *prevcount = omsgCount - expunged_messages;
1605 expunged_messages = 0;
1606 if (msgCount < 0) {
1607 fputs("IMAP error: Negative message count\n", stderr);
1608 msgCount = 0;
1611 if (dequeued != OKAY) {
1612 message = scalloc(msgCount + 1, sizeof *message);
1613 for (i = 0; i < msgCount; i++)
1614 imap_init(mp, i);
1615 if (!nmail && mp->mb_type == MB_IMAP)
1616 initcache(mp);
1617 if (msgCount > 0)
1618 imap_flags(mp, 1, msgCount);
1619 message[msgCount].m_size = 0;
1620 message[msgCount].m_lines = 0;
1621 rec_rmqueue();
1623 if (nmail || transparent)
1624 transflags(omessage, omsgCount, transparent);
1625 else
1626 setdot(message);
1627 NYD_LEAVE;
1630 FL int
1631 imap_setfile(const char *xserver, enum fedit_mode fm)
1633 struct url url;
1634 int rv;
1635 NYD_ENTER;
1637 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1638 rv = 1;
1639 goto jleave;
1641 if (!ok_blook(v15_compat) &&
1642 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1643 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1645 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1646 rv = _imap_setfile1(&url, fm, 0);
1647 jleave:
1648 NYD_LEAVE;
1649 return rv;
1652 static bool_t
1653 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1655 bool_t rv = FAL0;
1656 NYD_ENTER;
1658 if (ok_blook(v15_compat))
1659 rv = ccred_lookup(ccredp, urlp);
1660 else {
1661 char *var, *old,
1662 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1663 : urlp->url_u_h_p.s);
1665 if ((var = mbp->mb_imap_pass) != NULL) {
1666 var = savecat("password-", xuhp);
1667 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1668 old = sstrdup(old);
1669 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1671 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1672 if (var != NULL) {
1673 if (old != NULL) {
1674 n_var_vset(var, (uintptr_t)old);
1675 free(old);
1676 } else
1677 n_var_vclear(var);
1681 NYD_LEAVE;
1682 return rv;
1685 static int
1686 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1687 int volatile transparent)
1689 struct sock so;
1690 struct ccred ccred;
1691 sighandler_type volatile saveint, savepipe;
1692 char const *cp;
1693 int rv;
1694 int volatile prevcount = 0;
1695 enum mbflags same_flags;
1696 NYD_ENTER;
1698 if (fm & FEDIT_NEWMAIL) {
1699 saveint = safe_signal(SIGINT, SIG_IGN);
1700 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1701 if (saveint != SIG_IGN)
1702 safe_signal(SIGINT, imapcatch);
1703 if (savepipe != SIG_IGN)
1704 safe_signal(SIGPIPE, imapcatch);
1705 imaplock = 1;
1706 goto jnmail;
1709 same_flags = mb.mb_flags;
1710 same_imap_account = 0;
1711 if (mb.mb_imap_account != NULL &&
1712 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1713 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1714 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1715 disconnected(mb.mb_imap_account) == 0) {
1716 same_imap_account = 1;
1717 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1719 goto jduppass;
1720 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1721 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1722 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1723 jduppass:
1725 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1729 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1730 free(mb.mb_imap_pass);
1731 mb.mb_imap_pass = NULL;
1733 if (!_imap_getcred(&mb, &ccred, urlp)) {
1734 rv = -1;
1735 goto jleave;
1738 memset(&so, 0, sizeof so);
1739 so.s_fd = -1;
1740 if (!same_imap_account) {
1741 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1742 rv = -1;
1743 goto jleave;
1745 } else
1746 so = mb.mb_sock;
1747 if (!transparent) {
1748 if(!quit(FAL0)){
1749 rv = -1;
1750 goto jleave;
1754 if (fm & FEDIT_SYSBOX)
1755 n_pstate &= ~n_PS_EDIT;
1756 else
1757 n_pstate |= n_PS_EDIT;
1758 if (mb.mb_imap_account != NULL)
1759 free(mb.mb_imap_account);
1760 if (mb.mb_imap_pass != NULL)
1761 free(mb.mb_imap_pass);
1762 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1763 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1764 * TODO object, and mailbox will naturally have an URL and credentials */
1765 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1767 if (!same_imap_account) {
1768 if (mb.mb_sock.s_fd >= 0)
1769 sclose(&mb.mb_sock);
1771 same_imap_account = 0;
1773 if (!transparent) {
1774 if (mb.mb_itf) {
1775 fclose(mb.mb_itf);
1776 mb.mb_itf = NULL;
1778 if (mb.mb_otf) {
1779 fclose(mb.mb_otf);
1780 mb.mb_otf = NULL;
1782 if (mb.mb_imap_mailbox != NULL)
1783 free(mb.mb_imap_mailbox);
1784 assert(urlp->url_path.s != NULL);
1785 imap_delim_init(&mb, urlp);
1786 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1787 initbox(savecatsep(urlp->url_p_eu_h_p,
1788 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1789 mb.mb_imap_mailbox));
1791 mb.mb_type = MB_VOID;
1792 mb.mb_active = MB_NONE;
1794 imaplock = 1;
1795 saveint = safe_signal(SIGINT, SIG_IGN);
1796 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1797 if (sigsetjmp(imapjmp, 1)) {
1798 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1799 sclose(&mb.mb_sock);
1800 safe_signal(SIGINT, saveint);
1801 safe_signal(SIGPIPE, savepipe);
1802 imaplock = 0;
1804 mb.mb_type = MB_VOID;
1805 mb.mb_active = MB_NONE;
1806 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1807 goto jleave;
1809 if (saveint != SIG_IGN)
1810 safe_signal(SIGINT, imapcatch);
1811 if (savepipe != SIG_IGN)
1812 safe_signal(SIGPIPE, imapcatch);
1814 if (mb.mb_sock.s_fd < 0) {
1815 if (disconnected(mb.mb_imap_account)) {
1816 if (cache_setptr(fm, transparent) == STOP)
1817 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1818 goto jdone;
1820 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1821 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1822 savealrm = safe_signal(SIGALRM, imapalarm);
1823 alarm(imapkeepalive);
1827 mb.mb_sock = so;
1828 mb.mb_sock.s_desc = "IMAP";
1829 mb.mb_sock.s_onclose = imap_timer_off;
1830 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1831 sclose(&mb.mb_sock);
1832 imap_timer_off();
1833 safe_signal(SIGINT, saveint);
1834 safe_signal(SIGPIPE, savepipe);
1835 imaplock = 0;
1836 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1837 goto jleave;
1839 } else /* same account */
1840 mb.mb_flags |= same_flags;
1842 if (n_poption & n_PO_R_FLAG)
1843 fm |= FEDIT_RDONLY;
1844 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1845 mb.mb_type = MB_IMAP;
1846 cache_dequeue(&mb);
1847 assert(urlp->url_path.s != NULL);
1848 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1849 /*sclose(&mb.mb_sock);
1850 imap_timer_off();*/
1851 safe_signal(SIGINT, saveint);
1852 safe_signal(SIGPIPE, savepipe);
1853 imaplock = 0;
1854 mb.mb_type = MB_VOID;
1855 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1856 goto jleave;
1859 jnmail:
1860 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1861 n_UNVOLATILE(&prevcount));
1862 jdone:
1863 setmsize(msgCount);
1864 safe_signal(SIGINT, saveint);
1865 safe_signal(SIGPIPE, savepipe);
1866 imaplock = 0;
1868 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1869 purgecache(&mb, message, msgCount);
1870 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1871 mb.mb_threaded = 0;
1872 c_sort((void*)-1);
1875 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1876 n_pstate &= ~n_PS_SAW_COMMAND;
1877 n_pstate |= n_PS_SETFILE_OPENED;
1880 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1881 mb.mb_type == MB_CACHE)) {
1882 rv = (msgCount == 0);
1883 goto jleave;
1886 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1887 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1888 !ok_blook(emptystart))
1889 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1890 rv = 1;
1891 goto jleave;
1894 if (fm & FEDIT_NEWMAIL)
1895 newmailinfo(prevcount);
1896 rv = 0;
1897 jleave:
1898 NYD_LEAVE;
1899 return rv;
1902 static int
1903 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1904 int need, const char *head, size_t headsize, long headlines)
1906 char *line = NULL, *lp;
1907 size_t linesize = 0, linelen, size = 0;
1908 int emptyline = 0, lines = 0, excess = 0;
1909 off_t offset;
1910 NYD_ENTER;
1912 fseek(mp->mb_otf, 0L, SEEK_END);
1913 offset = ftell(mp->mb_otf);
1915 if (head)
1916 fwrite(head, 1, headsize, mp->mb_otf);
1918 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1919 lp = line;
1920 if (linelen > expected) {
1921 excess = linelen - expected;
1922 linelen = expected;
1924 /* TODO >>
1925 * Need to mask 'From ' lines. This cannot be done properly
1926 * since some servers pass them as 'From ' and others as
1927 * '>From '. Although one could identify the first kind of
1928 * server in principle, it is not possible to identify the
1929 * second as '>From ' may also come from a server of the
1930 * first type as actual data. So do what is absolutely
1931 * necessary only - mask 'From '.
1933 * If the line is the first line of the message header, it
1934 * is likely a real 'From ' line. In this case, it is just
1935 * ignored since it violates all standards.
1936 * TODO can the latter *really* happen??
1937 * TODO <<
1939 /* Since we simply copy over data without doing any transfer
1940 * encoding reclassification/adjustment we *have* to perform
1941 * RFC 4155 compliant From_ quoting here */
1942 if (emptyline && is_head(lp, linelen, FAL0)) {
1943 fputc('>', mp->mb_otf);
1944 ++size;
1946 emptyline = 0;
1947 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1948 if (linelen > 2) {
1949 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1950 size += linelen - 1;
1951 } else {
1952 emptyline = 1;
1953 ++size;
1955 fputc('\n', mp->mb_otf);
1956 } else {
1957 fwrite(lp, 1, linelen, mp->mb_otf);
1958 size += linelen;
1960 ++lines;
1961 if ((expected -= linelen) <= 0)
1962 break;
1964 if (!emptyline) {
1965 /* This is very ugly; but some IMAP daemons don't end a
1966 * message with \r\n\r\n, and we need \n\n for mbox format */
1967 fputc('\n', mp->mb_otf);
1968 ++lines;
1969 ++size;
1971 fflush(mp->mb_otf);
1973 if (m != NULL) {
1974 m->m_size = size + headsize;
1975 m->m_lines = lines + headlines;
1976 m->m_block = mailx_blockof(offset);
1977 m->m_offset = mailx_offsetof(offset);
1978 switch (need) {
1979 case NEED_HEADER:
1980 m->m_content_info = CI_HAVE_HEADER;
1981 break;
1982 case NEED_BODY:
1983 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
1984 m->m_xlines = m->m_lines;
1985 m->m_xsize = m->m_size;
1986 break;
1989 free(line);
1990 NYD_LEAVE;
1991 return excess;
1994 static void
1995 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1996 const char *head, size_t headsize, long headlines)
1998 off_t offset;
1999 size_t len;
2000 NYD_ENTER;
2002 len = strlen(str);
2003 fseek(mp->mb_otf, 0L, SEEK_END);
2004 offset = ftell(mp->mb_otf);
2005 if (head)
2006 fwrite(head, 1, headsize, mp->mb_otf);
2007 if (len > 0) {
2008 fwrite(str, 1, len, mp->mb_otf);
2009 fputc('\n', mp->mb_otf);
2010 ++len;
2012 fflush(mp->mb_otf);
2014 if (m != NULL) {
2015 m->m_size = headsize + len;
2016 m->m_lines = headlines + 1;
2017 m->m_block = mailx_blockof(offset);
2018 m->m_offset = mailx_offsetof(offset);
2019 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2020 m->m_xlines = m->m_lines;
2021 m->m_xsize = m->m_size;
2023 NYD_LEAVE;
2026 static enum okay
2027 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2029 char o[LINESIZE];
2030 struct message mt;
2031 sighandler_type volatile saveint, savepipe;
2032 char * volatile head;
2033 char const *cp, *loc, * volatile item, * volatile resp;
2034 size_t expected;
2035 size_t volatile headsize;
2036 int number;
2037 FILE *queuefp;
2038 long volatile headlines;
2039 long n;
2040 ul_i volatile u;
2041 enum okay ok;
2042 NYD_X;
2044 saveint = savepipe = SIG_IGN;
2045 head = NULL;
2046 cp = loc = item = resp = NULL;
2047 headsize = 0;
2048 number = (int)PTR2SIZE(m - message + 1);
2049 queuefp = NULL;
2050 headlines = 0;
2051 u = 0;
2052 ok = STOP;
2054 if (getcache(mp, m, need) == OKAY)
2055 return OKAY;
2056 if (mp->mb_type == MB_CACHE) {
2057 n_err(_("Message %lu not available\n"), (ul_i)number);
2058 return STOP;
2061 if (mp->mb_sock.s_fd < 0) {
2062 n_err(_("IMAP connection closed\n"));
2063 return STOP;
2066 switch (need) {
2067 case NEED_HEADER:
2068 resp = item = "RFC822.HEADER";
2069 break;
2070 case NEED_BODY:
2071 item = "BODY.PEEK[]";
2072 resp = "BODY[]";
2073 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2074 char *hdr = smalloc(m->m_size);
2075 fflush(mp->mb_otf);
2076 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2077 SEEK_SET) < 0 ||
2078 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2079 free(hdr);
2080 break;
2082 head = hdr;
2083 headsize = m->m_size;
2084 headlines = m->m_lines;
2085 item = "BODY.PEEK[TEXT]";
2086 resp = "BODY[TEXT]";
2088 break;
2089 case NEED_UNSPEC:
2090 return STOP;
2093 imaplock = 1;
2094 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2095 if (sigsetjmp(imapjmp, 1)) {
2096 safe_signal(SIGINT, saveint);
2097 safe_signal(SIGPIPE, savepipe);
2098 imaplock = 0;
2099 return STOP;
2101 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2102 safe_signal(SIGINT, &_imap_maincatch);
2103 if (savepipe != SIG_IGN)
2104 safe_signal(SIGPIPE, imapcatch);
2106 if (m->m_uid)
2107 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2108 tag(1), m->m_uid, item);
2109 else {
2110 if (check_expunged() == STOP)
2111 goto out;
2112 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2114 IMAP_OUT(o, MB_COMD, goto out)
2115 for (;;) {
2116 ok = imap_answer(mp, 1);
2117 if (ok == STOP)
2118 break;
2119 if (response_status != RESPONSE_OTHER ||
2120 response_other != MESSAGE_DATA_FETCH)
2121 continue;
2122 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2123 continue;
2124 if (m->m_uid) {
2125 if ((cp = asccasestr(responded_other_text, "UID "))) {
2126 u = atol(&cp[4]);
2127 n = 0;
2128 } else {
2129 u = 0;
2130 n = -1;
2132 } else
2133 n = responded_other_number;
2134 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2135 if (m->m_uid ? m->m_uid != u : n != number)
2136 continue;
2137 if ((cp = strchr(loc, '"')) != NULL) {
2138 cp = imap_unquotestr(cp);
2139 imap_putstr(mp, m, cp, head, headsize, headlines);
2140 } else {
2141 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2142 m->m_xlines = m->m_lines;
2143 m->m_xsize = m->m_size;
2145 goto out;
2147 expected = atol(&cp[1]);
2148 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2149 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2150 continue;
2152 mt = *m;
2153 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2154 if (n >= 0) {
2155 commitmsg(mp, m, &mt, mt.m_content_info);
2156 break;
2158 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2159 if (n_poption & n_PO_VERBVERB)
2160 fputs(imapbuf, stderr);
2161 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2162 u = atol(&cp[4]);
2163 if (u == m->m_uid) {
2164 commitmsg(mp, m, &mt, mt.m_content_info);
2165 break;
2170 out:
2171 while (mp->mb_active & MB_COMD)
2172 ok = imap_answer(mp, 1);
2174 if (saveint != SIG_IGN)
2175 safe_signal(SIGINT, saveint);
2176 if (savepipe != SIG_IGN)
2177 safe_signal(SIGPIPE, savepipe);
2178 imaplock--;
2180 if (ok == OKAY)
2181 putcache(mp, m);
2182 if (head != NULL)
2183 free(head);
2184 if (interrupts)
2185 n_go_onintr_for_imap();
2186 return ok;
2189 FL enum okay
2190 imap_header(struct message *m)
2192 enum okay rv;
2193 NYD_ENTER;
2195 rv = imap_get(&mb, m, NEED_HEADER);
2196 NYD_LEAVE;
2197 return rv;
2201 FL enum okay
2202 imap_body(struct message *m)
2204 enum okay rv;
2205 NYD_ENTER;
2207 rv = imap_get(&mb, m, NEED_BODY);
2208 NYD_LEAVE;
2209 return rv;
2212 static void
2213 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2214 enum content_info content_info)
2216 NYD_ENTER;
2217 tomp->m_size = frommp->m_size;
2218 tomp->m_lines = frommp->m_lines;
2219 tomp->m_block = frommp->m_block;
2220 tomp->m_offset = frommp->m_offset;
2221 tomp->m_content_info = content_info & CI_HAVE_MASK;
2222 if (content_info & CI_HAVE_BODY) {
2223 tomp->m_xlines = frommp->m_lines;
2224 tomp->m_xsize = frommp->m_size;
2226 putcache(mp, tomp);
2227 NYD_LEAVE;
2230 static enum okay
2231 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2233 /* bot > topp */
2234 char o[LINESIZE];
2235 char const *cp;
2236 struct message mt;
2237 size_t expected;
2238 int n = 0, u;
2239 FILE *queuefp = NULL;
2240 enum okay ok;
2241 NYD_X;
2243 if (m[bot].m_uid)
2244 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2245 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2246 else {
2247 if (check_expunged() == STOP)
2248 return STOP;
2249 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2250 tag(1), bot, topp);
2252 IMAP_OUT(o, MB_COMD, return STOP)
2254 srelax_hold();
2255 for (;;) {
2256 ok = imap_answer(mp, 1);
2257 if (response_status != RESPONSE_OTHER)
2258 break;
2259 if (response_other != MESSAGE_DATA_FETCH)
2260 continue;
2261 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2262 srelax_rele();
2263 return STOP;
2265 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2266 continue;
2267 expected = atol(&cp[1]);
2268 if (m[bot-1].m_uid) {
2269 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2270 u = atoi(&cp[4]);
2271 for (n = bot; n <= topp; n++)
2272 if ((unsigned long)u == m[n-1].m_uid)
2273 break;
2274 if (n > topp) {
2275 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2276 continue;
2278 } else
2279 n = -1;
2280 } else {
2281 n = responded_other_number;
2282 if (n <= 0 || n > msgCount) {
2283 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2284 continue;
2287 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2288 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2289 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2290 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2291 if (n_poption & n_PO_VERBVERB)
2292 fputs(imapbuf, stderr);
2293 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2294 u = atoi(&cp[4]);
2295 for (n = bot; n <= topp; n++)
2296 if ((unsigned long)u == m[n-1].m_uid)
2297 break;
2298 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2299 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2302 srelax();
2304 srelax_rele();
2306 while (mp->mb_active & MB_COMD)
2307 ok = imap_answer(mp, 1);
2308 return ok;
2311 FL void
2312 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2314 sighandler_type saveint, savepipe;
2315 /*enum okay ok = STOP;*/
2316 int i, chunk = 256;
2317 NYD_X;
2319 if (mb.mb_type == MB_CACHE)
2320 return;
2321 if (bot < 1)
2322 bot = 1;
2323 if (topp > msgCount)
2324 topp = msgCount;
2325 for (i = bot; i < topp; i++) {
2326 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2327 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2328 bot = i+1;
2329 else
2330 break;
2332 for (i = topp; i > bot; i--) {
2333 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2334 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2335 topp = i-1;
2336 else
2337 break;
2339 if (bot >= topp)
2340 return;
2342 imaplock = 1;
2343 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2344 safe_signal(SIGINT, &_imap_maincatch);
2345 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2346 if (sigsetjmp(imapjmp, 1) == 0) {
2347 if (savepipe != SIG_IGN)
2348 safe_signal(SIGPIPE, imapcatch);
2350 for (i = bot; i <= topp; i += chunk) {
2351 int j = i + chunk - 1;
2352 j = n_MIN(j, topp);
2353 if (visible(message + j))
2354 /*ok = */imap_fetchheaders(&mb, message, i, j);
2355 if (interrupts)
2356 n_go_onintr_for_imap(); /* XXX imaplock? */
2359 safe_signal(SIGINT, saveint);
2360 safe_signal(SIGPIPE, savepipe);
2361 imaplock = 0;
2364 static enum okay
2365 __imap_exit(struct mailbox *mp)
2367 char o[LINESIZE];
2368 FILE *queuefp = NULL;
2369 NYD_X;
2371 mp->mb_active |= MB_BYE;
2372 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2373 IMAP_OUT(o, MB_COMD, return STOP)
2374 IMAP_ANSWER()
2375 return OKAY;
2378 static enum okay
2379 imap_exit(struct mailbox *mp)
2381 enum okay rv;
2382 NYD_ENTER;
2384 rv = __imap_exit(mp);
2385 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2386 free(mp->mb_imap_pass);
2387 free(mp->mb_imap_account);
2388 free(mp->mb_imap_mailbox);
2389 if (mp->mb_cache_directory != NULL)
2390 free(mp->mb_cache_directory);
2391 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2392 mp->mb_imap_account =
2393 mp->mb_imap_mailbox =
2394 mp->mb_cache_directory = "";
2395 #else
2396 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2397 mp->mb_imap_mailbox = NULL;
2398 mp->mb_cache_directory = NULL;
2399 #endif
2400 #endif
2401 sclose(&mp->mb_sock);
2402 NYD_LEAVE;
2403 return rv;
2406 static enum okay
2407 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2409 NYD_ENTER;
2410 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2411 if (mp->mb_type == MB_IMAP)
2412 delcache(mp, m);
2413 NYD_LEAVE;
2414 return OKAY;
2417 static enum okay
2418 imap_close(struct mailbox *mp)
2420 char o[LINESIZE];
2421 FILE *queuefp = NULL;
2422 NYD_X;
2424 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2425 IMAP_OUT(o, MB_COMD, return STOP)
2426 IMAP_ANSWER()
2427 return OKAY;
2430 static enum okay
2431 imap_update(struct mailbox *mp)
2433 struct message *m;
2434 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2435 NYD_ENTER;
2437 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2438 holdbits();
2439 c = 0;
2440 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2441 if (m->m_flag & MBOX)
2442 ++c;
2443 if (c > 0)
2444 if (makembox() == STOP)
2445 goto jbypass;
2448 gotcha = held = 0;
2449 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2450 if (mp->mb_perm == 0)
2451 dodel = 0;
2452 else if (n_pstate & n_PS_EDIT)
2453 dodel = ((m->m_flag & MDELETED) != 0);
2454 else
2455 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2457 /* Fetch the result after around each 800 STORE commands
2458 * sent (approx. 32k data sent). Otherwise, servers will
2459 * try to flush the return queue at some point, leading
2460 * to a deadlock if we are still writing commands but not
2461 * reading their results */
2462 needstat = stored > 0 && stored % 800 == 0;
2463 /* Even if this message has been deleted, continue
2464 * to set further flags. This is necessary to support
2465 * Gmail semantics, where "delete" actually means
2466 * "archive", and the flags are applied to the copy
2467 * in "All Mail" */
2468 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2469 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2470 stored++;
2472 if (m->m_flag & MFLAG) {
2473 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2474 stored++;
2476 if (m->m_flag & MUNFLAG) {
2477 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2478 stored++;
2480 if (m->m_flag & MANSWER) {
2481 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2482 stored++;
2484 if (m->m_flag & MUNANSWER) {
2485 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2486 stored++;
2488 if (m->m_flag & MDRAFT) {
2489 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2490 stored++;
2492 if (m->m_flag & MUNDRAFT) {
2493 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2494 stored++;
2497 if (dodel) {
2498 imap_delete(mp, m-message+1, m, needstat);
2499 stored++;
2500 gotcha++;
2501 } else if (mp->mb_type != MB_CACHE ||
2502 (!(n_pstate & n_PS_EDIT) &&
2503 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2504 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2505 (MPRESERVE | MTOUCH) ||
2506 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2507 held++;
2508 if (m->m_flag & MNEW) {
2509 m->m_flag &= ~MNEW;
2510 m->m_flag |= MSTATUS;
2513 jbypass:
2514 if (gotcha)
2515 imap_close(mp);
2517 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2518 if (!(m->m_flag & MUNLINKED) &&
2519 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2520 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2521 putcache(mp, m);
2522 modflags++;
2525 /* XXX should be readonly (but our IMAP code is weird...) */
2526 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2527 mb.mb_perm != 0) {
2528 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2529 printf(_("\"%s\" "), displayname);
2530 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2531 ? _("complete\n") : _("updated.\n"));
2532 } else if (held && !(n_pstate & n_PS_EDIT)) {
2533 if (held == 1)
2534 printf(_("Held 1 message in %s\n"), displayname);
2535 else
2536 printf(_("Held %d messages in %s\n"), held, displayname);
2538 fflush(stdout);
2540 NYD_LEAVE;
2541 return OKAY;
2544 FL bool_t
2545 imap_quit(bool_t hold_sigs_on)
2547 sighandler_type volatile saveint, savepipe;
2548 bool_t rv;
2549 NYD_ENTER;
2551 if(hold_sigs_on)
2552 rele_sigs();
2554 if (mb.mb_type == MB_CACHE) {
2555 rv = (imap_update(&mb) == OKAY);
2556 goto jleave;
2559 rv = FAL0;
2561 if (mb.mb_sock.s_fd < 0) {
2562 n_err(_("IMAP connection closed\n"));
2563 goto jleave;
2566 imaplock = 1;
2567 saveint = safe_signal(SIGINT, SIG_IGN);
2568 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2569 if (sigsetjmp(imapjmp, 1)) {
2570 safe_signal(SIGINT, saveint);
2571 safe_signal(SIGPIPE, saveint);
2572 imaplock = 0;
2573 goto jleave;
2575 if (saveint != SIG_IGN)
2576 safe_signal(SIGINT, imapcatch);
2577 if (savepipe != SIG_IGN)
2578 safe_signal(SIGPIPE, imapcatch);
2580 rv = (imap_update(&mb) == OKAY);
2581 if(!same_imap_account && imap_exit(&mb) != OKAY)
2582 rv = FAL0;
2584 safe_signal(SIGINT, saveint);
2585 safe_signal(SIGPIPE, savepipe);
2586 imaplock = 0;
2587 jleave:
2588 if(hold_sigs_on)
2589 hold_sigs();
2590 NYD_LEAVE;
2591 return rv;
2594 static enum okay
2595 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2596 int needstat)
2598 char o[LINESIZE];
2599 FILE *queuefp = NULL;
2600 NYD_X;
2602 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2603 return STOP;
2604 if (m->m_uid)
2605 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2606 tag(1), m->m_uid, c, sp);
2607 else {
2608 if (check_expunged() == STOP)
2609 return STOP;
2610 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2612 IMAP_OUT(o, MB_COMD, return STOP)
2613 if (needstat)
2614 IMAP_ANSWER()
2615 else
2616 mb.mb_active &= ~MB_COMD;
2617 if (queuefp != NULL)
2618 Fclose(queuefp);
2619 return OKAY;
2622 FL enum okay
2623 imap_undelete(struct message *m, int n)
2625 enum okay rv;
2626 NYD_ENTER;
2628 rv = imap_unstore(m, n, "\\Deleted");
2629 NYD_LEAVE;
2630 return rv;
2633 FL enum okay
2634 imap_unread(struct message *m, int n)
2636 enum okay rv;
2637 NYD_ENTER;
2639 rv = imap_unstore(m, n, "\\Seen");
2640 NYD_LEAVE;
2641 return rv;
2644 static enum okay
2645 imap_unstore(struct message *m, int n, const char *flag)
2647 sighandler_type saveint, savepipe;
2648 enum okay volatile rv = STOP;
2649 NYD_ENTER;
2651 imaplock = 1;
2652 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2653 safe_signal(SIGINT, &_imap_maincatch);
2654 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2655 if (sigsetjmp(imapjmp, 1) == 0) {
2656 if (savepipe != SIG_IGN)
2657 safe_signal(SIGPIPE, imapcatch);
2659 rv = imap_store(&mb, m, n, '-', flag, 1);
2661 safe_signal(SIGINT, saveint);
2662 safe_signal(SIGPIPE, savepipe);
2663 imaplock = 0;
2665 NYD_LEAVE;
2666 if (interrupts)
2667 n_go_onintr_for_imap();
2668 return rv;
2671 static const char *
2672 tag(int new)
2674 static char ts[20];
2675 static long n;
2676 NYD2_ENTER;
2678 if (new)
2679 ++n;
2680 snprintf(ts, sizeof ts, "T%lu", n);
2681 NYD2_LEAVE;
2682 return ts;
2685 FL int
2686 c_imapcodec(void *vp){
2687 bool_t err;
2688 size_t alen;
2689 char const **argv, *varname, *varres, *act, *cp;
2690 NYD_ENTER;
2692 argv = vp;
2693 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2695 act = *argv;
2696 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2698 if(act == cp)
2699 goto jesynopsis;
2700 alen = PTR2SIZE(cp - act);
2701 if(*cp != '\0')
2702 ++cp;
2704 n_pstate_err_no = n_ERR_NONE;
2705 varres = imap_path_normalize(NULL, cp);
2707 if(is_ascncaseprefix(act, "encode", alen))
2708 varres = imap_path_encode(varres, &err);
2709 else if(is_ascncaseprefix(act, "decode", alen))
2710 varres = imap_path_decode(varres, &err);
2711 else
2712 goto jesynopsis;
2714 if(err){
2715 n_pstate_err_no = n_ERR_CANCELED;
2716 varres = cp;
2717 vp = NULL;
2720 if(varname != NULL){
2721 if(!n_var_vset(varname, (uintptr_t)varres)){
2722 n_pstate_err_no = n_ERR_NOTSUP;
2723 vp = NULL;
2725 }else{
2726 struct str in, out;
2728 in.l = strlen(in.s = n_UNCONST(varres));
2729 makeprint(&in, &out);
2730 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2731 n_pstate_err_no = n_err_no;
2732 vp = NULL;
2734 free(out.s);
2737 jleave:
2738 NYD_LEAVE;
2739 return (vp != NULL ? 0 : 1);
2740 jesynopsis:
2741 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2742 n_pstate_err_no = n_ERR_INVAL;
2743 vp = NULL;
2744 goto jleave;
2747 FL int
2748 c_imap_imap(void *vp)
2750 char o[LINESIZE];
2751 sighandler_type saveint, savepipe;
2752 struct mailbox *mp = &mb;
2753 FILE *queuefp = NULL;
2754 enum okay volatile ok = STOP;
2755 NYD_X;
2757 if (mp->mb_type != MB_IMAP) {
2758 printf("Not operating on an IMAP mailbox.\n");
2759 return 1;
2761 imaplock = 1;
2762 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2763 safe_signal(SIGINT, &_imap_maincatch);
2764 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2765 if (sigsetjmp(imapjmp, 1) == 0) {
2766 if (savepipe != SIG_IGN)
2767 safe_signal(SIGPIPE, imapcatch);
2769 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2770 IMAP_OUT(o, MB_COMD, goto out)
2771 while (mp->mb_active & MB_COMD) {
2772 ok = imap_answer(mp, 0);
2773 fputs(responded_text, stdout);
2776 out:
2777 safe_signal(SIGINT, saveint);
2778 safe_signal(SIGPIPE, savepipe);
2779 imaplock = 0;
2781 if (interrupts)
2782 n_go_onintr_for_imap();
2783 return ok != OKAY;
2786 FL int
2787 imap_newmail(int nmail)
2789 NYD_ENTER;
2791 if (nmail && had_exists < 0 && had_expunge < 0) {
2792 imaplock = 1;
2793 imap_noop();
2794 imaplock = 0;
2797 if (had_exists == msgCount && had_expunge < 0)
2798 /* Some servers always respond with EXISTS to NOOP. If
2799 * the mailbox has been changed but the number of messages
2800 * has not, an EXPUNGE must also had been sent; otherwise,
2801 * nothing has changed */
2802 had_exists = -1;
2803 NYD_LEAVE;
2804 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2807 static char *
2808 imap_putflags(int f)
2810 const char *cp;
2811 char *buf, *bp;
2812 NYD2_ENTER;
2814 bp = buf = salloc(100);
2815 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2816 *bp++ = '(';
2817 if (f & MREAD) {
2818 if (bp[-1] != '(')
2819 *bp++ = ' ';
2820 for (cp = "\\Seen"; *cp; cp++)
2821 *bp++ = *cp;
2823 if (f & MFLAGGED) {
2824 if (bp[-1] != '(')
2825 *bp++ = ' ';
2826 for (cp = "\\Flagged"; *cp; cp++)
2827 *bp++ = *cp;
2829 if (f & MANSWERED) {
2830 if (bp[-1] != '(')
2831 *bp++ = ' ';
2832 for (cp = "\\Answered"; *cp; cp++)
2833 *bp++ = *cp;
2835 if (f & MDRAFT) {
2836 if (bp[-1] != '(')
2837 *bp++ = ' ';
2838 for (cp = "\\Draft"; *cp; cp++)
2839 *bp++ = *cp;
2841 *bp++ = ')';
2842 *bp++ = ' ';
2844 *bp = '\0';
2845 NYD2_LEAVE;
2846 return buf;
2849 static void
2850 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2852 NYD2_ENTER;
2853 while (*cp != ')') {
2854 if (*cp == '\\') {
2855 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2856 *f |= MREAD;
2857 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2858 *f |= MNEW;
2859 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2860 *f |= MDELETED;
2861 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2862 *f |= MFLAGGED;
2863 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2864 *f |= MANSWERED;
2865 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2866 *f |= MDRAFTED;
2868 cp++;
2871 if (xp != NULL)
2872 *xp = cp;
2873 NYD2_LEAVE;
2876 static enum okay
2877 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2878 long xsize, enum mflag flag, time_t t)
2880 char o[LINESIZE], *buf;
2881 size_t bufsize, buflen, cnt;
2882 long size, lines, ysize;
2883 char const *qname;
2884 bool_t twice;
2885 FILE *queuefp;
2886 enum okay rv;
2887 NYD_ENTER;
2889 rv = STOP;
2890 queuefp = NULL;
2891 twice = FAL0;
2892 buf = NULL;
2894 if((qname = imap_path_quote(mp, name)) == NULL)
2895 goto jleave;
2897 if (mp->mb_type == MB_CACHE) {
2898 queuefp = cache_queue(mp);
2899 if (queuefp == NULL) {
2900 buf = NULL;
2901 goto jleave;
2903 rv = OKAY;
2906 buf = smalloc(bufsize = LINESIZE);
2907 buflen = 0;
2908 jagain:
2909 size = xsize;
2910 cnt = fsize(fp);
2911 if (fseek(fp, off1, SEEK_SET) < 0) {
2912 rv = STOP;
2913 goto jleave;
2916 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2917 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2918 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2919 while (mp->mb_active & MB_COMD) {
2920 rv = imap_answer(mp, twice);
2921 if (response_type == RESPONSE_CONT)
2922 break;
2925 if (mp->mb_type != MB_CACHE && rv == STOP) {
2926 if (!twice)
2927 goto jtrycreate;
2928 else
2929 goto jleave;
2932 lines = ysize = 0;
2933 while (size > 0) {
2934 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2935 lines++;
2936 ysize += buflen;
2937 buf[buflen - 1] = '\r';
2938 buf[buflen] = '\n';
2939 if (mp->mb_type != MB_CACHE)
2940 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2941 else if (queuefp)
2942 fwrite(buf, 1, buflen+1, queuefp);
2943 size -= buflen + 1;
2945 if (mp->mb_type != MB_CACHE)
2946 swrite(&mp->mb_sock, "\r\n");
2947 else if (queuefp)
2948 fputs("\r\n", queuefp);
2949 while (mp->mb_active & MB_COMD) {
2950 rv = imap_answer(mp, 0);
2951 if (response_status == RESPONSE_NO /*&&
2952 ascncasecmp(responded_text,
2953 "[TRYCREATE] ", 12) == 0*/) {
2954 jtrycreate:
2955 if (twice) {
2956 rv = STOP;
2957 goto jleave;
2959 twice = TRU1;
2960 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2961 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2962 while (mp->mb_active & MB_COMD)
2963 rv = imap_answer(mp, 1);
2964 if (rv == STOP)
2965 goto jleave;
2966 imap_created_mailbox++;
2967 goto jagain;
2968 } else if (rv != OKAY)
2969 n_err(_("IMAP error: %s"), responded_text);
2970 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2971 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2973 jleave:
2974 if (queuefp != NULL)
2975 Fclose(queuefp);
2976 if (buf != NULL)
2977 free(buf);
2978 NYD_LEAVE;
2979 return rv;
2982 static enum okay
2983 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
2985 char *buf, *bp, *lp;
2986 size_t bufsize, buflen, cnt;
2987 off_t off1 = -1, offs;
2988 int flag;
2989 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2990 time_t tim;
2991 long size;
2992 enum okay rv;
2993 NYD_ENTER;
2995 buf = smalloc(bufsize = LINESIZE);
2996 buflen = 0;
2997 cnt = fsize(fp);
2998 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
2999 time(&tim);
3000 size = 0;
3002 for (flag = MNEW, state = _NLSEP;;) {
3003 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3005 if (bp == NULL ||
3006 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3007 is_head(buf, buflen, FAL0))) {
3008 if (off1 != (off_t)-1) {
3009 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3010 if (rv == STOP)
3011 goto jleave;
3012 fseek(fp, offs+buflen, SEEK_SET);
3014 off1 = offs + buflen;
3015 size = 0;
3016 flag = MNEW;
3017 state = _INHEAD;
3018 if (bp == NULL)
3019 break;
3020 tim = unixtime(buf);
3021 } else
3022 size += buflen+1;
3023 offs += buflen;
3025 state &= ~_NLSEP;
3026 if (buf[0] == '\n') {
3027 state &= ~_INHEAD;
3028 state |= _NLSEP;
3029 } else if (state & _INHEAD) {
3030 if (ascncasecmp(buf, "status", 6) == 0) {
3031 lp = &buf[6];
3032 while (whitechar(*lp))
3033 lp++;
3034 if (*lp == ':')
3035 while (*++lp != '\0')
3036 switch (*lp) {
3037 case 'R':
3038 flag |= MREAD;
3039 break;
3040 case 'O':
3041 flag &= ~MNEW;
3042 break;
3044 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3045 lp = &buf[8];
3046 while (whitechar(*lp))
3047 lp++;
3048 if (*lp == ':')
3049 while (*++lp != '\0')
3050 switch (*lp) {
3051 case 'F':
3052 flag |= MFLAGGED;
3053 break;
3054 case 'A':
3055 flag |= MANSWERED;
3056 break;
3057 case 'T':
3058 flag |= MDRAFTED;
3059 break;
3064 rv = OKAY;
3065 jleave:
3066 free(buf);
3067 NYD_LEAVE;
3068 return rv;
3071 FL enum okay
3072 imap_append(const char *xserver, FILE *fp, long offset)
3074 sighandler_type volatile saveint, savepipe;
3075 struct url url;
3076 struct ccred ccred;
3077 enum okay rv = STOP;
3078 NYD_ENTER;
3080 if (!url_parse(&url, CPROTO_IMAP, xserver))
3081 goto j_leave;
3082 if (!ok_blook(v15_compat) &&
3083 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3084 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3085 assert(url.url_path.s != NULL);
3087 imaplock = 1;
3088 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3089 safe_signal(SIGINT, &_imap_maincatch);
3090 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3091 if (sigsetjmp(imapjmp, 1))
3092 goto jleave;
3093 if (savepipe != SIG_IGN)
3094 safe_signal(SIGPIPE, imapcatch);
3096 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3097 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3098 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3099 } else {
3100 struct mailbox mx;
3102 memset(&mx, 0, sizeof mx);
3104 if (!_imap_getcred(&mx, &ccred, &url))
3105 goto jleave;
3107 imap_delim_init(&mx, &url);
3108 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3110 if (disconnected(url.url_p_eu_h_p) == 0) {
3111 if (!sopen(&mx.mb_sock, &url))
3112 goto jfail;
3113 mx.mb_sock.s_desc = "IMAP";
3114 mx.mb_type = MB_IMAP;
3115 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3116 /* TODO the code now did
3117 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3118 * TODO though imap_mailbox is sfree()d and mbx
3119 * TODO is possibly even a constant
3120 * TODO i changed this to sstrdup() sofar, as is used
3121 * TODO somewhere else in this file for this! */
3122 if (imap_preauth(&mx, &url) != OKAY ||
3123 imap_auth(&mx, &ccred) != OKAY) {
3124 sclose(&mx.mb_sock);
3125 goto jfail;
3127 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3128 imap_exit(&mx);
3129 } else {
3130 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3131 mx.mb_type = MB_CACHE;
3132 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3134 jfail:
3138 jleave:
3139 safe_signal(SIGINT, saveint);
3140 safe_signal(SIGPIPE, savepipe);
3141 imaplock = 0;
3142 j_leave:
3143 NYD_LEAVE;
3144 if (interrupts)
3145 n_go_onintr_for_imap();
3146 return rv;
3149 static enum okay
3150 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3151 struct list_item **lend, int level)
3153 char o[LINESIZE], *cp;
3154 struct list_item *lp;
3155 const char *qname, *bp;
3156 FILE *queuefp;
3157 enum okay ok;
3158 NYD_X;
3160 ok = STOP;
3161 queuefp = NULL;
3163 if((qname = imap_path_quote(mp, base)) == NULL)
3164 goto jleave;
3166 *list = *lend = NULL;
3167 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3168 IMAP_OUT(o, MB_COMD, goto jleave)
3169 while (mp->mb_active & MB_COMD) {
3170 ok = imap_answer(mp, 1);
3171 if (response_status == RESPONSE_OTHER &&
3172 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3173 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3174 lp = csalloc(1, sizeof *lp);
3175 lp->l_name = cp;
3176 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3177 ++cp;
3178 lp->l_base = *cp ? cp : savestr(base);
3179 lp->l_attr = list_attributes;
3180 lp->l_level = level+1;
3181 lp->l_delim = list_hierarchy_delimiter;
3182 if (*list && *lend) {
3183 (*lend)->l_next = lp;
3184 *lend = lp;
3185 } else
3186 *list = *lend = lp;
3189 jleave:
3190 return ok;
3193 static enum okay
3194 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3196 struct list_item *list, *lend, *lp, *lx, *ly;
3197 int n, depth;
3198 const char *bp;
3199 char *cp;
3200 enum okay rv;
3201 NYD_ENTER;
3203 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3204 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3205 goto jleave;
3206 rv = OKAY;
3207 if (list == NULL || lend == NULL)
3208 goto jleave;
3210 for (lp = list; lp; lp = lp->l_next)
3211 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3212 !(lp->l_attr & LIST_NOINFERIORS)) {
3213 cp = salloc((n = strlen(lp->l_name)) + 2);
3214 memcpy(cp, lp->l_name, n);
3215 cp[n] = lp->l_delim;
3216 cp[n+1] = '\0';
3217 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3218 lp->l_has_children = 1;
3219 if (strcmp(cp, lx->l_name) == 0)
3220 lx = lx->l_next;
3221 if (lx) {
3222 lend->l_next = lx;
3223 lend = ly;
3228 for (lp = list; lp; lp = lp->l_next) {
3229 if (strip) {
3230 cp = lp->l_name;
3231 for (bp = base; *bp && *bp == *cp; bp++)
3232 cp++;
3233 } else
3234 cp = lp->l_name;
3235 if (!(lp->l_attr & LIST_NOSELECT))
3236 fprintf(fp, "%s\n", *cp ? cp : base);
3237 else if (lp->l_has_children == 0)
3238 fprintf(fp, "%s%c\n", *cp ? cp : base,
3239 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3241 jleave:
3242 NYD_LEAVE;
3243 return rv;
3246 FL int
3247 imap_folders(const char * volatile name, int strip)
3249 sighandler_type saveint, savepipe;
3250 const char * volatile fold, *cp, *sp;
3251 FILE * volatile fp;
3252 int rv = 1;
3253 NYD_ENTER;
3255 cp = protbase(name);
3256 sp = mb.mb_imap_account;
3257 if (sp == NULL || strcmp(cp, sp)) {
3258 n_err(
3259 _("Cannot perform `folders' but when on the very IMAP "
3260 "account; the current one is\n `%s' -- "
3261 "try `folders @'\n"),
3262 (sp != NULL ? sp : _("[NONE]")));
3263 goto jleave;
3266 fold = imap_fileof(name);
3267 if (n_psonce & n_PSO_TTYOUT) {
3268 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3269 == NULL) {
3270 n_perr(_("tmpfile"), 0);
3271 goto jleave;
3273 } else
3274 fp = stdout;
3276 imaplock = 1;
3277 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3278 safe_signal(SIGINT, &_imap_maincatch);
3279 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3280 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3281 goto junroll;
3282 if (savepipe != SIG_IGN)
3283 safe_signal(SIGPIPE, imapcatch);
3285 if (mb.mb_type == MB_CACHE)
3286 cache_list(&mb, fold, strip, fp);
3287 else
3288 imap_list(&mb, fold, strip, fp);
3290 imaplock = 0;
3291 if (interrupts) {
3292 if (n_psonce & n_PSO_TTYOUT)
3293 Fclose(fp);
3294 rv = 0;
3295 goto jleave;
3297 fflush(fp);
3299 if (n_psonce & n_PSO_TTYOUT) {
3300 rewind(fp);
3301 if (fsize(fp) > 0){
3302 page_or_print(fp, 0);
3303 rv = 0;
3304 }else
3305 n_err(_("Folder not found\n"));
3306 }else
3307 rv = 0;
3308 junroll:
3309 safe_signal(SIGINT, saveint);
3310 safe_signal(SIGPIPE, savepipe);
3311 if (n_psonce & n_PSO_TTYOUT)
3312 Fclose(fp);
3313 jleave:
3314 NYD_LEAVE;
3315 if (interrupts)
3316 n_go_onintr_for_imap();
3317 return rv;
3320 static enum okay
3321 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3323 char o[LINESIZE];
3324 const char *qname;
3325 bool_t twice, stored;
3326 FILE *queuefp;
3327 enum okay ok;
3328 NYD_X;
3330 ok = STOP;
3331 queuefp = NULL;
3332 twice = stored = FAL0;
3334 /* C99 */{
3335 size_t i;
3337 i = strlen(name = imap_fileof(name));
3338 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3339 name = savecat(name, "INBOX");
3340 if((qname = imap_path_quote(mp, name)) == NULL)
3341 goto jleave;
3344 if (mp->mb_type == MB_CACHE) {
3345 if ((queuefp = cache_queue(mp)) == NULL)
3346 goto jleave;
3347 ok = OKAY;
3350 /* Since it is not possible to set flags on the copy, recently
3351 * set flags must be set on the original to include it in the copy */
3352 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3353 imap_store(mp, m, n, '+', "\\Seen", 0);
3354 if (m->m_flag&MFLAG)
3355 imap_store(mp, m, n, '+', "\\Flagged", 0);
3356 if (m->m_flag&MUNFLAG)
3357 imap_store(mp, m, n, '-', "\\Flagged", 0);
3358 if (m->m_flag&MANSWER)
3359 imap_store(mp, m, n, '+', "\\Answered", 0);
3360 if (m->m_flag&MUNANSWER)
3361 imap_store(mp, m, n, '-', "\\Flagged", 0);
3362 if (m->m_flag&MDRAFT)
3363 imap_store(mp, m, n, '+', "\\Draft", 0);
3364 if (m->m_flag&MUNDRAFT)
3365 imap_store(mp, m, n, '-', "\\Draft", 0);
3366 again:
3367 if (m->m_uid)
3368 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3369 else {
3370 if (check_expunged() == STOP)
3371 goto out;
3372 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3374 IMAP_OUT(o, MB_COMD, goto out)
3375 while (mp->mb_active & MB_COMD)
3376 ok = imap_answer(mp, twice);
3378 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3379 response_status == RESPONSE_OK)
3380 imap_copyuid(mp, m, name);
3382 if (response_status == RESPONSE_NO && !twice) {
3383 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3384 IMAP_OUT(o, MB_COMD, goto out)
3385 while (mp->mb_active & MB_COMD)
3386 ok = imap_answer(mp, 1);
3387 if (ok == OKAY) {
3388 imap_created_mailbox++;
3389 goto again;
3393 if (queuefp != NULL)
3394 Fclose(queuefp);
3396 /* ... and reset the flag to its initial value so that the 'exit'
3397 * command still leaves the message unread */
3398 out:
3399 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3400 imap_store(mp, m, n, '-', "\\Seen", 0);
3401 stored = TRU1;
3403 if (m->m_flag & MFLAG) {
3404 imap_store(mp, m, n, '-', "\\Flagged", 0);
3405 stored = TRU1;
3407 if (m->m_flag & MUNFLAG) {
3408 imap_store(mp, m, n, '+', "\\Flagged", 0);
3409 stored = TRU1;
3411 if (m->m_flag & MANSWER) {
3412 imap_store(mp, m, n, '-', "\\Answered", 0);
3413 stored = TRU1;
3415 if (m->m_flag & MUNANSWER) {
3416 imap_store(mp, m, n, '+', "\\Answered", 0);
3417 stored = TRU1;
3419 if (m->m_flag & MDRAFT) {
3420 imap_store(mp, m, n, '-', "\\Draft", 0);
3421 stored = TRU1;
3423 if (m->m_flag & MUNDRAFT) {
3424 imap_store(mp, m, n, '+', "\\Draft", 0);
3425 stored = TRU1;
3427 if (stored) {
3428 mp->mb_active |= MB_COMD;
3429 (void)imap_finish(mp);
3431 jleave:
3432 return ok;
3435 FL enum okay
3436 imap_copy(struct message *m, int n, const char *name)
3438 sighandler_type saveint, savepipe;
3439 enum okay volatile rv = STOP;
3440 NYD_ENTER;
3442 imaplock = 1;
3443 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3444 safe_signal(SIGINT, &_imap_maincatch);
3445 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3446 if (sigsetjmp(imapjmp, 1) == 0) {
3447 if (savepipe != SIG_IGN)
3448 safe_signal(SIGPIPE, imapcatch);
3450 rv = imap_copy1(&mb, m, n, name);
3452 safe_signal(SIGINT, saveint);
3453 safe_signal(SIGPIPE, savepipe);
3454 imaplock = 0;
3456 NYD_LEAVE;
3457 if (interrupts)
3458 n_go_onintr_for_imap();
3459 return rv;
3462 static enum okay
3463 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3464 unsigned long *olduid, unsigned long *newuid)
3466 char *xp, *yp, *zp;
3467 enum okay rv;
3468 NYD_ENTER;
3470 *uidvalidity = strtoul(cp, &xp, 10);
3471 *olduid = strtoul(xp, &yp, 10);
3472 *newuid = strtoul(yp, &zp, 10);
3473 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3474 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3475 NYD_LEAVE;
3476 return rv;
3479 static enum okay
3480 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3481 unsigned long *uid)
3483 char *xp, *yp;
3484 enum okay rv;
3485 NYD_ENTER;
3487 *uidvalidity = strtoul(cp, &xp, 10);
3488 *uid = strtoul(xp, &yp, 10);
3489 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3490 *yp == ']');
3491 NYD_LEAVE;
3492 return rv;
3495 static enum okay
3496 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3498 struct mailbox xmb;
3499 struct message xm;
3500 const char *cp;
3501 unsigned long uidvalidity, olduid, newuid;
3502 enum okay rv;
3503 NYD_ENTER;
3505 rv = STOP;
3507 memset(&xmb, 0, sizeof xmb);
3509 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3510 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3511 goto jleave;
3513 rv = OKAY;
3515 xmb = *mp;
3516 xmb.mb_cache_directory = NULL;
3517 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3518 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3519 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3520 sizeof(xmb.mb_imap_delim));
3521 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3522 if (mp->mb_cache_directory != NULL)
3523 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3524 xmb.mb_uidvalidity = uidvalidity;
3525 initcache(&xmb);
3527 if (m == NULL) {
3528 memset(&xm, 0, sizeof xm);
3529 xm.m_uid = olduid;
3530 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3531 goto jleave;
3532 getcache(mp, &xm, NEED_HEADER);
3533 getcache(mp, &xm, NEED_BODY);
3534 } else {
3535 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3536 getcache(mp, m, NEED_HEADER);
3537 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3538 getcache(mp, m, NEED_BODY);
3539 xm = *m;
3541 xm.m_uid = newuid;
3542 xm.m_flag &= ~MFULLYCACHED;
3543 putcache(&xmb, &xm);
3544 jleave:
3545 if (xmb.mb_cache_directory != NULL)
3546 free(xmb.mb_cache_directory);
3547 if (xmb.mb_imap_mailbox != NULL)
3548 free(xmb.mb_imap_mailbox);
3549 if (xmb.mb_imap_pass != NULL)
3550 free(xmb.mb_imap_pass);
3551 if (xmb.mb_imap_account != NULL)
3552 free(xmb.mb_imap_account);
3553 NYD_LEAVE;
3554 return rv;
3557 static enum okay
3558 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3559 long size, long lines, int flag, const char *name)
3561 struct mailbox xmb;
3562 struct message xm;
3563 const char *cp;
3564 unsigned long uidvalidity, uid;
3565 enum okay rv;
3566 NYD_ENTER;
3568 rv = STOP;
3570 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3571 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3572 goto jleave;
3574 rv = OKAY;
3576 xmb = *mp;
3577 xmb.mb_cache_directory = NULL;
3578 /* XXX mb_imap_delim reused */
3579 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3580 xmb.mb_uidvalidity = uidvalidity;
3581 xmb.mb_otf = xmb.mb_itf = fp;
3582 initcache(&xmb);
3583 memset(&xm, 0, sizeof xm);
3584 xm.m_flag = (flag & MREAD) | MNEW;
3585 xm.m_time = t;
3586 xm.m_block = mailx_blockof(off1);
3587 xm.m_offset = mailx_offsetof(off1);
3588 xm.m_size = size;
3589 xm.m_xsize = xsize;
3590 xm.m_lines = xm.m_xlines = lines;
3591 xm.m_uid = uid;
3592 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3593 putcache(&xmb, &xm);
3595 free(xmb.mb_imap_mailbox);
3596 jleave:
3597 NYD_LEAVE;
3598 return rv;
3601 static enum okay
3602 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3604 FILE *tp = NULL;
3605 time_t t;
3606 long size, xsize, ysize, lines;
3607 enum mflag flag = MNEW;
3608 char *name, *buf, *bp;
3609 char const *cp;
3610 size_t bufsize, buflen, cnt;
3611 enum okay rv = STOP;
3612 NYD_ENTER;
3614 buf = smalloc(bufsize = LINESIZE);
3615 buflen = 0;
3616 cnt = fsize(fp);
3617 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3618 goto jstop;
3620 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3622 while (*bp == ' ')
3623 ++bp;
3625 if ((cp = strrchr(bp, '{')) == NULL)
3626 goto jstop;
3628 xsize = atol(&cp[1]) + 2;
3629 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3630 goto jstop;
3631 while (*cp == ' ')
3632 cp++;
3634 if (*cp == '(') {
3635 imap_getflags(cp, &cp, &flag);
3636 while (*++cp == ' ')
3639 t = imap_read_date_time(cp);
3641 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3642 == NULL)
3643 goto jstop;
3645 size = xsize;
3646 ysize = lines = 0;
3647 while (size > 0) {
3648 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3649 goto jstop;
3650 size -= buflen;
3651 buf[--buflen] = '\0';
3652 buf[buflen-1] = '\n';
3653 fwrite(buf, 1, buflen, tp);
3654 ysize += buflen;
3655 ++lines;
3657 fflush(tp);
3658 rewind(tp);
3660 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3661 imap_unquotestr(name));
3662 rv = OKAY;
3663 jstop:
3664 free(buf);
3665 if (tp)
3666 Fclose(tp);
3667 NYD_LEAVE;
3668 return rv;
3671 #ifdef HAVE_IMAP_SEARCH
3672 static enum okay
3673 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3674 int f)
3676 char *o, *xp, *cs, c;
3677 size_t osize;
3678 FILE *queuefp = NULL;
3679 int i;
3680 unsigned long n;
3681 const char *cp;
3682 enum okay ok = STOP;
3683 NYD_X;
3685 c = 0;
3686 for (cp = spec; *cp; cp++)
3687 c |= *cp;
3688 if (c & 0200) {
3689 cp = ok_vlook(ttycharset);
3690 # ifdef HAVE_ICONV
3691 if(asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")){ /* XXX */
3692 char const *nspec;
3694 if((nspec = n_iconv_onetime_cp(n_ICONV_DEFAULT, "utf-8", cp, spec)
3695 ) != NULL){
3696 spec = nspec;
3697 cp = "utf-8";
3700 # endif
3701 cp = imap_quotestr(cp);
3702 cs = salloc(n = strlen(cp) + 10);
3703 snprintf(cs, n, "CHARSET %s ", cp);
3704 } else
3705 cs = n_UNCONST("");
3707 o = ac_alloc(osize = strlen(spec) + 60);
3708 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3709 IMAP_OUT(o, MB_COMD, goto out)
3710 while (mp->mb_active & MB_COMD) {
3711 ok = imap_answer(mp, 0);
3712 if (response_status == RESPONSE_OTHER &&
3713 response_other == MAILBOX_DATA_SEARCH) {
3714 xp = responded_other_text;
3715 while (*xp && *xp != '\r') {
3716 n = strtoul(xp, &xp, 10);
3717 for (i = 0; i < cnt; i++)
3718 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3719 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3720 mark(i+1, f);
3724 out:
3725 ac_free(o);
3726 return ok;
3729 FL enum okay
3730 imap_search1(const char * volatile spec, int f)
3732 sighandler_type saveint, savepipe;
3733 enum okay volatile rv = STOP;
3734 NYD_ENTER;
3736 if (mb.mb_type != MB_IMAP)
3737 goto jleave;
3739 imaplock = 1;
3740 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3741 safe_signal(SIGINT, &_imap_maincatch);
3742 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3743 if (sigsetjmp(imapjmp, 1) == 0) {
3744 if (savepipe != SIG_IGN)
3745 safe_signal(SIGPIPE, imapcatch);
3747 rv = imap_search2(&mb, message, msgCount, spec, f);
3749 safe_signal(SIGINT, saveint);
3750 safe_signal(SIGPIPE, savepipe);
3751 imaplock = 0;
3752 jleave:
3753 NYD_LEAVE;
3754 if (interrupts)
3755 n_go_onintr_for_imap();
3756 return rv;
3758 #endif /* HAVE_IMAP_SEARCH */
3760 FL int
3761 imap_thisaccount(const char *cp)
3763 int rv;
3764 NYD_ENTER;
3766 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3767 rv = 0;
3768 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3769 mb.mb_imap_account == NULL)
3770 rv = 0;
3771 else
3772 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3773 NYD_LEAVE;
3774 return rv;
3777 FL enum okay
3778 imap_remove(const char * volatile name)
3780 sighandler_type volatile saveint, savepipe;
3781 enum okay volatile rv = STOP;
3782 NYD_ENTER;
3784 if (mb.mb_type != MB_IMAP) {
3785 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3786 goto jleave;
3789 if (!imap_thisaccount(name)) {
3790 n_err(_("Can only remove mailboxes on current IMAP server: "
3791 "\"%s\" not removed\n"), name);
3792 goto jleave;
3795 imaplock = 1;
3796 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3797 safe_signal(SIGINT, &_imap_maincatch);
3798 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3799 if (sigsetjmp(imapjmp, 1) == 0) {
3800 if (savepipe != SIG_IGN)
3801 safe_signal(SIGPIPE, imapcatch);
3803 rv = imap_remove1(&mb, imap_fileof(name));
3805 safe_signal(SIGINT, saveint);
3806 safe_signal(SIGPIPE, savepipe);
3807 imaplock = 0;
3809 if (rv == OKAY)
3810 rv = cache_remove(name);
3811 jleave:
3812 NYD_LEAVE;
3813 if (interrupts)
3814 n_go_onintr_for_imap();
3815 return rv;
3818 static enum okay
3819 imap_remove1(struct mailbox *mp, const char *name)
3821 char *o;
3822 int os;
3823 char const *qname;
3824 FILE *queuefp;
3825 enum okay ok;
3826 NYD_X;
3828 ok = STOP;
3829 queuefp = NULL;
3831 if((qname = imap_path_quote(mp, name)) != NULL){
3832 o = ac_alloc(os = strlen(qname) + 100);
3833 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3834 IMAP_OUT(o, MB_COMD, goto out)
3835 while (mp->mb_active & MB_COMD)
3836 ok = imap_answer(mp, 1);
3837 out:
3838 ac_free(o);
3840 return ok;
3843 FL enum okay
3844 imap_rename(const char *old, const char *new)
3846 sighandler_type saveint, savepipe;
3847 enum okay volatile rv = STOP;
3848 NYD_ENTER;
3850 if (mb.mb_type != MB_IMAP) {
3851 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3852 goto jleave;
3855 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3856 n_err(_("Can only rename mailboxes on current IMAP "
3857 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3858 goto jleave;
3861 imaplock = 1;
3862 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3863 safe_signal(SIGINT, &_imap_maincatch);
3864 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3865 if (sigsetjmp(imapjmp, 1) == 0) {
3866 if (savepipe != SIG_IGN)
3867 safe_signal(SIGPIPE, imapcatch);
3869 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3871 safe_signal(SIGINT, saveint);
3872 safe_signal(SIGPIPE, savepipe);
3873 imaplock = 0;
3875 if (rv == OKAY)
3876 rv = cache_rename(old, new);
3877 jleave:
3878 NYD_LEAVE;
3879 if (interrupts)
3880 n_go_onintr_for_imap();
3881 return rv;
3884 static enum okay
3885 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3887 char *o;
3888 int os;
3889 char const *qoname, *qnname;
3890 FILE *queuefp;
3891 enum okay ok;
3892 NYD_X;
3894 ok = STOP;
3895 queuefp = NULL;
3897 if((qoname = imap_path_quote(mp, old)) != NULL &&
3898 (qnname = imap_path_quote(mp, new)) != NULL){
3899 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3900 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3901 IMAP_OUT(o, MB_COMD, goto out)
3902 while (mp->mb_active & MB_COMD)
3903 ok = imap_answer(mp, 1);
3904 out:
3905 ac_free(o);
3907 return ok;
3910 FL enum okay
3911 imap_dequeue(struct mailbox *mp, FILE *fp)
3913 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3914 size_t bufsize, buflen, cnt;
3915 long offs, offs1, offs2, octets;
3916 int twice, gotcha = 0;
3917 FILE *queuefp = NULL;
3918 enum okay ok = OKAY, rok = OKAY;
3919 NYD_X;
3921 buf = smalloc(bufsize = LINESIZE);
3922 buflen = 0;
3923 cnt = fsize(fp);
3924 while ((offs1 = ftell(fp)) >= 0 &&
3925 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3926 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3928 while (*bp == ' ')
3929 ++bp;
3930 twice = 0;
3931 if ((offs = ftell(fp)) < 0)
3932 goto fail;
3933 again:
3934 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3935 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3936 cp = &bp[9];
3937 while (digitchar(*cp))
3938 cp++;
3939 if (*cp != ' ')
3940 goto fail;
3941 while (*cp == ' ')
3942 cp++;
3943 if ((newname = imap_strex(cp, NULL)) == NULL)
3944 goto fail;
3945 IMAP_OUT(o, MB_COMD, continue)
3946 while (mp->mb_active & MB_COMD)
3947 ok = imap_answer(mp, twice);
3948 if (response_status == RESPONSE_NO && twice++ == 0)
3949 goto trycreate;
3950 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3951 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3953 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3954 IMAP_OUT(o, MB_COMD, continue)
3955 while (mp->mb_active & MB_COMD)
3956 ok = imap_answer(mp, 1);
3957 if (ok == OKAY)
3958 gotcha++;
3959 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3960 if ((cp = strrchr(bp, '{')) == NULL)
3961 goto fail;
3962 octets = atol(&cp[1]) + 2;
3963 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3964 goto fail;
3965 IMAP_OUT(o, MB_COMD, continue)
3966 while (mp->mb_active & MB_COMD) {
3967 ok = imap_answer(mp, twice);
3968 if (response_type == RESPONSE_CONT)
3969 break;
3971 if (ok == STOP) {
3972 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3973 goto trycreate;
3974 goto fail;
3976 while (octets > 0) {
3977 size_t n = (UICMP(z, octets, >, sizeof iob)
3978 ? sizeof iob : (size_t)octets);
3979 octets -= n;
3980 if (n != fread(iob, 1, n, fp))
3981 goto fail;
3982 swrite1(&mp->mb_sock, iob, n, 1);
3984 swrite(&mp->mb_sock, "");
3985 while (mp->mb_active & MB_COMD) {
3986 ok = imap_answer(mp, 0);
3987 if (response_status == RESPONSE_NO && twice++ == 0) {
3988 if (fseek(fp, offs, SEEK_SET) < 0)
3989 goto fail;
3990 goto trycreate;
3993 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3994 if ((offs2 = ftell(fp)) < 0)
3995 goto fail;
3996 fseek(fp, offs1, SEEK_SET);
3997 if (imap_appenduid_cached(mp, fp) == STOP) {
3998 (void)fseek(fp, offs2, SEEK_SET);
3999 goto fail;
4002 } else {
4003 fail:
4004 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4005 rok = STOP;
4007 continue;
4008 trycreate:
4009 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4010 IMAP_OUT(o, MB_COMD, continue)
4011 while (mp->mb_active & MB_COMD)
4012 ok = imap_answer(mp, 1);
4013 if (ok == OKAY)
4014 goto again;
4016 fflush(fp);
4017 rewind(fp);
4018 ftruncate(fileno(fp), 0);
4019 if (gotcha)
4020 imap_close(mp);
4021 free(buf);
4022 return rok;
4025 static char *
4026 imap_strex(char const *cp, char const **xp)
4028 char const *cq;
4029 char *n = NULL;
4030 NYD_ENTER;
4032 if (*cp != '"')
4033 goto jleave;
4035 for (cq = cp + 1; *cq != '\0'; ++cq) {
4036 if (*cq == '\\')
4037 cq++;
4038 else if (*cq == '"')
4039 break;
4041 if (*cq != '"')
4042 goto jleave;
4044 n = salloc(cq - cp + 2);
4045 memcpy(n, cp, cq - cp +1);
4046 n[cq - cp + 1] = '\0';
4047 if (xp != NULL)
4048 *xp = cq + 1;
4049 jleave:
4050 NYD_LEAVE;
4051 return n;
4054 static enum okay
4055 check_expunged(void)
4057 enum okay rv;
4058 NYD_ENTER;
4060 if (expunged_messages > 0) {
4061 n_err(_("Command not executed - messages have been expunged\n"));
4062 rv = STOP;
4063 } else
4064 rv = OKAY;
4065 NYD_LEAVE;
4066 return rv;
4069 FL int
4070 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4072 struct url url;
4073 int rv, omsgCount = msgCount;
4074 NYD_ENTER;
4075 n_UNUSED(vp);
4077 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4078 n_err(_("Already connected\n"));
4079 rv = 1;
4080 goto jleave;
4083 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4084 rv = 1;
4085 goto jleave;
4087 ok_bclear(disconnected);
4088 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4090 if (mb.mb_type == MB_CACHE) {
4091 enum fedit_mode fm = FEDIT_NONE;
4092 if (_imap_rdonly)
4093 fm |= FEDIT_RDONLY;
4094 if (!(n_pstate & n_PS_EDIT))
4095 fm |= FEDIT_SYSBOX;
4096 _imap_setfile1(&url, fm, 1);
4097 if (msgCount > omsgCount)
4098 newmailinfo(omsgCount);
4100 rv = 0;
4101 jleave:
4102 NYD_LEAVE;
4103 return rv;
4106 FL int
4107 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4109 struct url url;
4110 int rv = 1, *msgvec = vp;
4111 NYD_ENTER;
4113 if (mb.mb_type == MB_CACHE) {
4114 n_err(_("Not connected\n"));
4115 goto jleave;
4117 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4118 n_err(_("The current mailbox is not cached\n"));
4119 goto jleave;
4122 if (!url_parse(&url, CPROTO_IMAP, mailname))
4123 goto jleave;
4125 if (*msgvec)
4126 c_cache(vp);
4127 ok_bset(disconnected);
4128 if (mb.mb_type == MB_IMAP) {
4129 enum fedit_mode fm = FEDIT_NONE;
4130 if (_imap_rdonly)
4131 fm |= FEDIT_RDONLY;
4132 if (!(n_pstate & n_PS_EDIT))
4133 fm |= FEDIT_SYSBOX;
4134 sclose(&mb.mb_sock);
4135 _imap_setfile1(&url, fm, 1);
4137 rv = 0;
4138 jleave:
4139 NYD_LEAVE;
4140 return rv;
4143 FL int
4144 c_cache(void *vp)
4146 int rv = 1, *msgvec = vp, *ip;
4147 struct message *mp;
4148 NYD_ENTER;
4150 if (mb.mb_type != MB_IMAP) {
4151 n_err(_("Not connected to an IMAP server\n"));
4152 goto jleave;
4154 if (cached_uidvalidity(&mb) == 0) {
4155 n_err(_("The current mailbox is not cached\n"));
4156 goto jleave;
4159 srelax_hold();
4160 for (ip = msgvec; *ip; ++ip) {
4161 mp = &message[*ip - 1];
4162 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4163 get_body(mp);
4164 srelax();
4167 srelax_rele();
4168 rv = 0;
4169 jleave:
4170 NYD_LEAVE;
4171 return rv;
4174 FL int
4175 disconnected(const char *file)
4177 struct url url;
4178 int rv = 1;
4179 NYD_ENTER;
4181 if (ok_blook(disconnected)) {
4182 rv = 1;
4183 goto jleave;
4186 if (!url_parse(&url, CPROTO_IMAP, file)) {
4187 rv = 0;
4188 goto jleave;
4190 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4192 jleave:
4193 NYD_LEAVE;
4194 return rv;
4197 FL void
4198 transflags(struct message *omessage, long omsgCount, int transparent)
4200 struct message *omp, *nmp, *newdot, *newprevdot;
4201 int hf;
4202 NYD_ENTER;
4204 omp = omessage;
4205 nmp = message;
4206 newdot = message;
4207 newprevdot = NULL;
4208 while (PTRCMP(omp, <, omessage + omsgCount) &&
4209 PTRCMP(nmp, <, message + msgCount)) {
4210 if (dot && nmp->m_uid == dot->m_uid)
4211 newdot = nmp;
4212 if (prevdot && nmp->m_uid == prevdot->m_uid)
4213 newprevdot = nmp;
4214 if (omp->m_uid == nmp->m_uid) {
4215 hf = nmp->m_flag & MHIDDEN;
4216 if (transparent && mb.mb_type == MB_IMAP)
4217 omp->m_flag &= ~MHIDDEN;
4218 *nmp++ = *omp++;
4219 if (transparent && mb.mb_type == MB_CACHE)
4220 nmp[-1].m_flag |= hf;
4221 } else if (omp->m_uid < nmp->m_uid)
4222 ++omp;
4223 else
4224 ++nmp;
4226 dot = newdot;
4227 setdot(newdot);
4228 prevdot = newprevdot;
4229 free(omessage);
4230 NYD_LEAVE;
4233 FL time_t
4234 imap_read_date_time(const char *cp)
4236 char buf[3];
4237 time_t t;
4238 int i, year, month, day, hour, minute, second, sign = -1;
4239 NYD2_ENTER;
4241 /* "25-Jul-2004 15:33:44 +0200"
4242 * | | | | | |
4243 * 0 5 10 15 20 25 */
4244 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4245 goto jinvalid;
4246 day = strtol(&cp[1], NULL, 10);
4247 for (i = 0;;) {
4248 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4249 break;
4250 if (n_month_names[++i][0] == '\0')
4251 goto jinvalid;
4253 month = i + 1;
4254 year = strtol(&cp[8], NULL, 10);
4255 hour = strtol(&cp[13], NULL, 10);
4256 minute = strtol(&cp[16], NULL, 10);
4257 second = strtol(&cp[19], NULL, 10);
4258 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4259 goto jinvalid;
4260 switch (cp[22]) {
4261 case '-':
4262 sign = 1;
4263 break;
4264 case '+':
4265 break;
4266 default:
4267 goto jinvalid;
4269 buf[2] = '\0';
4270 buf[0] = cp[23];
4271 buf[1] = cp[24];
4272 t += strtol(buf, NULL, 10) * sign * 3600;
4273 buf[0] = cp[25];
4274 buf[1] = cp[26];
4275 t += strtol(buf, NULL, 10) * sign * 60;
4276 jleave:
4277 NYD2_LEAVE;
4278 return t;
4279 jinvalid:
4280 time(&t);
4281 goto jleave;
4284 FL const char *
4285 imap_make_date_time(time_t t)
4287 static char s[40];
4288 char const *mn;
4289 si32_t y, md, th, tm, ts;
4290 struct tm *tmp;
4291 int tzdiff, tzdiff_hour, tzdiff_min;
4292 time_t t2;
4293 NYD2_ENTER;
4295 jredo:
4296 if((t2 = mktime(gmtime(&t))) == (time_t)-1){
4297 t = 0;
4298 goto jredo;
4300 tzdiff = t - t2;
4301 if((tmp = localtime(&t)) == NULL){
4302 t = 0;
4303 goto jredo;
4306 tzdiff_hour = (int)(tzdiff / 60);
4307 tzdiff_min = tzdiff_hour % 60;
4308 tzdiff_hour /= 60;
4309 if (tmp->tm_isdst > 0)
4310 tzdiff_hour++;
4312 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
4313 y = 1970;
4314 mn = n_month_names[0];
4315 md = 1;
4316 th = tm = ts = 0;
4317 }else{
4318 y += 1900;
4319 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
4320 ? n_month_names[tmp->tm_mon] : n_qm;
4322 if((md = tmp->tm_mday) < 1 || md > 31)
4323 md = 1;
4325 if((th = tmp->tm_hour) < 0 || th > 23)
4326 th = 0;
4327 if((tm = tmp->tm_min) < 0 || tm > 59)
4328 tm = 0;
4329 if((ts = tmp->tm_sec) < 0 || ts > 60)
4330 ts = 0;
4333 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4334 md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
4335 NYD2_LEAVE;
4336 return s;
4339 FL char *
4340 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4342 char *n, *np;
4343 NYD2_ENTER;
4345 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4346 n_MEMORY_DEBUG_ARGSCALL);
4348 /* Just ignore the `is-system-mailbox' prefix XXX */
4349 if (cp[0] == '%' && cp[1] == ':')
4350 cp += 2;
4352 while (*cp != '\0') {
4353 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4354 *np++ = *cp++;
4355 *np++ = *cp++;
4356 *np++ = *cp++;
4357 } else if (cp[0] == '/')
4358 break;
4359 else
4360 *np++ = *cp++;
4362 *np = '\0';
4363 NYD2_LEAVE;
4364 return n;
4366 #endif /* HAVE_IMAP */
4368 /* s-it-mode */