Fix typo introduced in IMAP reinstantiation
[s-mailx.git] / obs-imap.c
blob020cd29df1e52bcb2f0b5f8a770e54b92092bc68
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE obs_imap
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_IMAP
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 EMPTY_FILE()
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() \
62 if (mp->mb_type != MB_CACHE) {\
63 enum okay ok = OKAY;\
64 while (mp->mb_active & MB_COMD)\
65 ok = imap_answer(mp, 1);\
66 if (ok == STOP)\
67 return STOP;\
71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
72 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
73 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
74 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
75 * TODO I.e., that should be a function, not a macro ... or so.
76 * TODO This entire module needs MASSIVE work! */
77 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
79 do {\
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);\
94 } while (0);
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 void dopr(FILE *fp);
247 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
248 const char *name);
249 static enum okay imap_copyuid_parse(const char *cp,
250 unsigned long *uidvalidity, unsigned long *olduid,
251 unsigned long *newuid);
252 static enum okay imap_appenduid_parse(const char *cp,
253 unsigned long *uidvalidity, unsigned long *uid);
254 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
255 const char *name);
256 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
257 long off1, long xsize, long size, long lines, int flag,
258 const char *name);
259 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
260 #ifdef HAVE_IMAP_SEARCH
261 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
262 const char *spec, int f);
263 #endif
264 static enum okay imap_remove1(struct mailbox *mp, const char *name);
265 static enum okay imap_rename1(struct mailbox *mp, const char *old,
266 const char *new);
267 static char * imap_strex(char const *cp, char const **xp);
268 static enum okay check_expunged(void);
270 static char *
271 imap_quotestr(char const *s)
273 char *n, *np;
274 NYD2_ENTER;
276 np = n = salloc(2 * strlen(s) + 3);
277 *np++ = '"';
278 while (*s) {
279 if (*s == '"' || *s == '\\')
280 *np++ = '\\';
281 *np++ = *s++;
283 *np++ = '"';
284 *np = '\0';
285 NYD2_LEAVE;
286 return n;
289 static char *
290 imap_unquotestr(char const *s)
292 char *n, *np;
293 NYD2_ENTER;
295 if (*s != '"') {
296 n = savestr(s);
297 goto jleave;
300 np = n = salloc(strlen(s) + 1);
301 while (*++s) {
302 if (*s == '\\')
303 s++;
304 else if (*s == '"')
305 break;
306 *np++ = *s;
308 *np = '\0';
309 jleave:
310 NYD2_LEAVE;
311 return n;
314 static void
315 imap_delim_init(struct mailbox *mp, struct url const *urlp){
316 size_t i;
317 char const *cp;
318 NYD2_ENTER;
320 mp->mb_imap_delim[0] = '\0';
322 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
323 i = strlen(cp);
325 if(i == 0){
326 cp = n_IMAP_DELIM;
327 i = sizeof(n_IMAP_DELIM) -1;
328 goto jcopy;
331 if(i < n_NELEM(mp->mb_imap_delim))
332 jcopy:
333 memcpy(&mb.mb_imap_delim[0], cp, i +1);
334 else
335 n_err(_("*imap-delim* for %s is too long: %s\n"),
336 urlp->url_input, cp);
338 NYD2_LEAVE;
341 static char const *
342 imap_path_normalize(struct mailbox *mp, char const *cp){
343 char *rv_base, *rv, dc2, dc, c, lc;
344 char const *dcp;
345 NYD2_ENTER;
347 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
348 * exactly what i have specified" */
349 if(mp == NULL || mp->mb_imap_delim[0] == '\0')
350 dcp = &n_IMAP_DELIM[0];
351 else
352 dcp = &mp->mb_imap_delim[0];
353 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
355 /* Plain names don't need path quoting */
356 /* C99 */{
357 size_t i, j;
358 char const *cpx;
360 for(cpx = cp;; ++cpx)
361 if((c = *cpx) == '\0')
362 goto jleave;
363 else if(dc == '\0'){
364 if(strchr(n_IMAP_DELIM, c)){
365 dc = c;
366 break;
368 }else if(c == dc)
369 break;
370 else if(dc2 && strchr(dcp, c) != NULL)
371 break;
373 /* And we don't need to reevaluate what we have seen yet */
374 i = PTR2SIZE(cpx - cp);
375 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
376 if(i > 0)
377 memcpy(rv, cp, i);
378 memcpy(&rv[i], cpx, j);
379 rv += i;
380 cp = cpx;
383 /* Squeeze adjacent delimiters, convert remain to dc */
384 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
385 if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
386 c = dc;
387 if(c != dc || lc != dc)
388 *rv++ = c;
390 *rv = '\0';
392 cp = rv_base;
393 jleave:
394 NYD2_LEAVE;
395 return cp;
398 FL char const *
399 imap_path_encode(char const *cp, bool_t *err_or_null){
400 /* To a large extend inspired by dovecot(1) */
401 struct str in, out;
402 bool_t err_def;
403 ui8_t *be16p_base, *be16p;
404 char const *emsg;
405 char c;
406 size_t l, l_plain;
407 NYD2_ENTER;
409 if(err_or_null == NULL)
410 err_or_null = &err_def;
411 *err_or_null = FAL0;
413 /* Is this a string that works out as "plain US-ASCII"? */
414 for(l = 0;; ++l)
415 if((c = cp[l]) == '\0')
416 goto jleave;
417 else if(c <= 0x1F || c >= 0x7F || c == '&')
418 break;
420 *err_or_null = TRU1;
422 /* We need to encode in mUTF-7! For that, we first have to convert the
423 * local charset to UTF-8, then convert all characters which need to be
424 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
425 * We can skip the UTF-8 conversion occasionally, however */
426 if(!(n_psonce & n_PSO_UNICODE)){
427 int ir;
428 iconv_t icd;
430 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
432 if((icd = iconv_open("utf-8", ok_vlook(ttycharset))) == (iconv_t)-1)
433 goto jerr;
435 out.s = NULL, out.l = 0;
436 in.s = n_UNCONST(cp); /* logical */
437 l += strlen(&cp[l]);
438 in.l = l;
439 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
440 cp = savestrbuf(out.s, out.l);
442 if(out.s != NULL)
443 free(out.s);
444 iconv_close(icd);
446 if(ir != 0)
447 goto jerr;
450 * So: Why not start all over again?
453 /* Is this a string that works out as "plain US-ASCII"? */
454 for(l = 0;; ++l)
455 if((c = cp[l]) == '\0')
456 goto jleave;
457 else if(c <= 0x1F || c >= 0x7F || c == '&')
458 break;
461 /* We need to encode, save what we have, encode the rest */
462 l_plain = l;
464 for(cp += l, l = 0; cp[l] != '\0'; ++l)
466 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
468 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
469 if(l_plain > 0)
470 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
471 else
472 out.l = 0;
473 DBG( l_plain += (l << 2); )
475 while(l > 0){
476 c = *cp++;
477 --l;
479 if(c == '&'){
480 out.s[out.l + 0] = '&';
481 out.s[out.l + 1] = '-';
482 out.l += 2;
483 }else if(c > 0x1F && c < 0x7F)
484 out.s[out.l++] = c;
485 else{
486 static char const mb64ct[] =
487 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
488 ui32_t utf32;
490 /* Convert consecutive non-representables */
491 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
493 for(be16p = be16p_base, --cp, ++l;;){
494 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
495 goto jerr;
497 /* TODO S-CText: magic utf16 conversions */
498 if(utf32 < 0x10000){
499 be16p[1] = utf32 & 0xFF;
500 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
501 be16p += 2;
502 }else{
503 ui16_t s7e;
505 utf32 -= 0x10000;
506 s7e = 0xD800u | (utf32 >> 10);
507 be16p[1] = s7e & 0xFF;
508 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
509 s7e = 0xDC00u | (utf32 &= 0x03FF);
510 be16p[3] = s7e & 0xFF;
511 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
512 be16p += 4;
515 if(l == 0)
516 break;
517 if((c = *cp) > 0x1F && c < 0x7F)
518 break;
521 /* And then warp that UTF-16BE to mUTF-7 */
522 out.s[out.l++] = '&';
523 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
524 be16p = be16p_base;
526 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
527 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
528 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
529 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
530 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
531 out.l += 4;
533 if(utf32 > 0){
534 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
535 if(--utf32 == 0){
536 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
537 out.l += 2;
538 }else{
539 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
540 (be16p[1] >> 4)];
541 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
542 out.l += 3;
545 out.s[out.l++] = '-';
548 out.s[out.l] = '\0';
549 assert(out.l <= l_plain);
550 *err_or_null = FAL0;
551 cp = out.s;
552 jleave:
553 NYD2_LEAVE;
554 return cp;
555 jerr:
556 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
557 goto jleave;
560 FL char *
561 imap_path_decode(char const *path, bool_t *err_or_null){
562 /* To a large extend inspired by dovecot(1) TODO use string */
563 struct str in, out;
564 bool_t err_def;
565 ui8_t *mb64p_base, *mb64p, *mb64xp;
566 char const *emsg, *cp;
567 char *rv_base, *rv, c;
568 size_t l_orig, l, i;
569 NYD2_ENTER;
571 if(err_or_null == NULL)
572 err_or_null = &err_def;
573 *err_or_null = FAL0;
575 l = l_orig = strlen(path);
576 rv = rv_base = salloc(l << 1);
577 memcpy(rv, path, l +1);
579 /* xxx Don't check for invalid characters from malicious servers */
580 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
581 goto jleave;
583 *err_or_null = TRU1;
585 emsg = N_("Invalid mUTF-7 encoding");
586 i = PTR2SIZE(cp - path);
587 rv += i;
588 l -= i;
589 mb64p_base = NULL;
591 while(l > 0){
592 if((c = *cp) != '&'){
593 if(c <= 0x1F || c >= 0x7F){
594 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
595 goto jerr;
597 *rv++ = c;
598 ++cp;
599 --l;
600 }else if(--l == 0)
601 goto jeincpl;
602 else if(*++cp == '-'){
603 *rv++ = '&';
604 ++cp;
605 --l;
606 }else if(l < 3){
607 jeincpl:
608 emsg = N_("Invalid mUTF-7: incomplete input");
609 goto jerr;
610 }else{
611 /* mUTF-7 -> UTF-16BE -> UTF-8 */
612 static ui8_t const mb64dt[256] = {
613 #undef XX
614 #define XX 0xFFu
615 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
616 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
617 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
618 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
619 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
620 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
621 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
622 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
623 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
624 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
625 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
626 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
627 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
628 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
629 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
630 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
633 if(mb64p_base == NULL)
634 mb64p_base = salloc(l);
636 /* Decode the mUTF-7 to what is indeed UTF-16BE */
637 for(mb64p = mb64p_base;;){
638 assert(l >= 3);
639 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
640 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
641 goto jerr;
642 mb64p += 2;
644 c = cp[2];
645 cp += 3;
646 l -= 3;
647 if(c == '-')
648 break;
649 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
650 goto jerr;
652 if(l == 0)
653 goto jerr;
654 --l;
655 if((c = *cp++) == '-')
656 break;
657 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
658 goto jerr;
660 if(l < 3){
661 if(l > 0 && *cp == '-'){
662 --l;
663 ++cp;
664 break;
666 goto jerr;
669 #undef XX
671 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
672 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
673 goto jerr;
676 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
677 i = PTR2SIZE(mb64p - mb64p_base);
678 mb64p = mb64xp = mb64p_base;
680 while(i > 0){
681 ui8_t ul, u0, u1, u2, u3;
683 ul = (i >= 4) ? 4 : i & 0x3;
684 i -= ul;
685 u0 = mb64xp[0];
686 u1 = mb64xp[1];
687 u2 = (ul < 3) ? 0 : mb64xp[2];
688 u3 = (ul < 4) ? 0 : mb64xp[3];
689 mb64xp += ul;
690 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
691 if(ul < 3)
692 break;
693 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
694 if(ul < 4)
695 break;
696 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
699 /* UTF-16BE we convert to UTF-8 */
700 i = PTR2SIZE(mb64p - mb64p_base);
701 if(i & 1){
702 emsg = N_("Odd bytecount for UTF-16BE input");
703 goto jerr;
706 /* TODO S-CText: magic utf16 conversions */
707 emsg = N_("Invalid UTF-16BE encoding");
709 for(mb64p = mb64p_base; i > 0;){
710 ui32_t utf32;
711 ui16_t uhi, ulo;
713 uhi = mb64p[0];
714 uhi <<= 8;
715 uhi |= mb64p[1];
717 /* Not a surrogate? */
718 if(uhi < 0xD800 || uhi > 0xDFFF){
719 utf32 = uhi;
720 mb64p += 2;
721 i -= 2;
722 }else if(uhi > 0xDBFF)
723 goto jerr;
724 else if(i < 4){
725 emsg = N_("Incomplete UTF-16BE surrogate pair");
726 goto jerr;
727 }else{
728 ulo = mb64p[2];
729 ulo <<= 8;
730 ulo |= mb64p[3];
731 if(ulo < 0xDC00 || ulo > 0xDFFF)
732 goto jerr;
734 utf32 = (uhi &= 0x03FF);
735 utf32 <<= 10;
736 utf32 += 0x10000;
737 utf32 |= (ulo &= 0x03FF);
738 mb64p += 4;
739 i -= 4;
742 utf32 = n_utf32_to_utf8(utf32, rv);
743 rv += utf32;
747 *rv = '\0';
749 /* We can skip the UTF-8 conversion occasionally */
750 if(!(n_psonce & n_PSO_UNICODE)){
751 int ir;
752 iconv_t icd;
754 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
756 if((icd = iconv_open(ok_vlook(ttycharset), "utf-8")) == (iconv_t)-1)
757 goto jerr;
759 out.s = NULL, out.l = 0;
760 in.l = strlen(in.s = rv_base);
761 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
762 /* Because the length of this is unpredictable, copy */
763 rv_base = savestrbuf(out.s, out.l);
765 if(out.s != NULL)
766 free(out.s);
767 iconv_close(icd);
769 if(ir != 0)
770 goto jerr;
773 *err_or_null = FAL0;
774 rv = rv_base;
775 jleave:
776 NYD2_LEAVE;
777 return rv;
778 jerr:
779 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
780 memcpy(rv = rv_base, path, ++l_orig);
781 goto jleave;
784 static char *
785 imap_path_quote(struct mailbox *mp, char const *cp){
786 bool_t err;
787 char *rv;
788 NYD2_ENTER;
790 cp = imap_path_normalize(mp, cp);
791 cp = imap_path_encode(cp, &err);
792 rv = err ? NULL : imap_quotestr(cp);
793 NYD2_LEAVE;
794 return rv;
797 static void
798 imap_other_get(char *pp)
800 char *xp;
801 NYD2_ENTER;
803 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
804 pp += 6;
805 response_other = MAILBOX_DATA_FLAGS;
806 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
807 pp += 5;
808 response_other = MAILBOX_DATA_LIST;
809 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
810 pp += 5;
811 response_other = MAILBOX_DATA_LSUB;
812 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
813 pp += 8;
814 response_other = MAILBOX_DATA_MAILBOX;
815 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
816 pp += 7;
817 response_other = MAILBOX_DATA_SEARCH;
818 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
819 pp += 7;
820 response_other = MAILBOX_DATA_STATUS;
821 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
822 pp += 11;
823 response_other = CAPABILITY_DATA;
824 } else {
825 responded_other_number = strtol(pp, &xp, 10);
826 while (*xp == ' ')
827 ++xp;
828 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
829 response_other = MAILBOX_DATA_EXISTS;
830 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
831 response_other = MAILBOX_DATA_RECENT;
832 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
833 response_other = MESSAGE_DATA_EXPUNGE;
834 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
835 pp = &xp[6];
836 response_other = MESSAGE_DATA_FETCH;
837 } else
838 response_other = RESPONSE_OTHER_UNKNOWN;
840 responded_other_text = pp;
841 NYD2_LEAVE;
844 static void
845 imap_response_get(const char **cp)
847 NYD2_ENTER;
848 if (ascncasecmp(*cp, "OK ", 3) == 0) {
849 *cp += 3;
850 response_status = RESPONSE_OK;
851 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
852 *cp += 3;
853 response_status = RESPONSE_NO;
854 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
855 *cp += 4;
856 response_status = RESPONSE_BAD;
857 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
858 *cp += 8;
859 response_status = RESPONSE_PREAUTH;
860 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
861 *cp += 4;
862 response_status = RESPONSE_BYE;
863 } else
864 response_status = RESPONSE_OTHER;
865 NYD2_LEAVE;
868 static void
869 imap_response_parse(void)
871 static char *parsebuf; /* TODO Use pool */
872 static size_t parsebufsize;
874 const char *ip = imapbuf;
875 char *pp;
876 NYD2_ENTER;
878 if (parsebufsize < imapbufsize)
879 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
880 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
881 pp = parsebuf;
882 switch (*ip) {
883 case '+':
884 response_type = RESPONSE_CONT;
885 ip++;
886 pp++;
887 while (*ip == ' ') {
888 ip++;
889 pp++;
891 break;
892 case '*':
893 ip++;
894 pp++;
895 while (*ip == ' ') {
896 ip++;
897 pp++;
899 imap_response_get(&ip);
900 pp = &parsebuf[ip - imapbuf];
901 switch (response_status) {
902 case RESPONSE_BYE:
903 response_type = RESPONSE_FATAL;
904 break;
905 default:
906 response_type = RESPONSE_DATA;
908 break;
909 default:
910 responded_tag = parsebuf;
911 while (*pp && *pp != ' ')
912 pp++;
913 if (*pp == '\0') {
914 response_type = RESPONSE_ILLEGAL;
915 break;
917 *pp++ = '\0';
918 while (*pp && *pp == ' ')
919 pp++;
920 if (*pp == '\0') {
921 response_type = RESPONSE_ILLEGAL;
922 break;
924 ip = &imapbuf[pp - parsebuf];
925 response_type = RESPONSE_TAGGED;
926 imap_response_get(&ip);
927 pp = &parsebuf[ip - imapbuf];
929 responded_text = pp;
930 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
931 response_status == RESPONSE_OTHER)
932 imap_other_get(pp);
933 NYD2_LEAVE;
936 static enum okay
937 imap_answer(struct mailbox *mp, int errprnt)
939 int i, complete;
940 enum okay rv;
941 NYD2_ENTER;
943 rv = OKAY;
944 if (mp->mb_type == MB_CACHE)
945 goto jleave;
946 rv = STOP;
947 jagain:
948 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
949 if (n_poption & n_PO_VERBVERB)
950 fputs(imapbuf, stderr);
951 imap_response_parse();
952 if (response_type == RESPONSE_ILLEGAL)
953 goto jagain;
954 if (response_type == RESPONSE_CONT) {
955 rv = OKAY;
956 goto jleave;
958 if (response_status == RESPONSE_OTHER) {
959 if (response_other == MAILBOX_DATA_EXISTS) {
960 had_exists = responded_other_number;
961 rec_queue(REC_EXISTS, responded_other_number);
962 if (had_expunge > 0)
963 had_expunge = 0;
964 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
965 rec_queue(REC_EXPUNGE, responded_other_number);
966 if (had_expunge < 0)
967 had_expunge = 0;
968 had_expunge++;
969 expunged_messages++;
972 complete = 0;
973 if (response_type == RESPONSE_TAGGED) {
974 if (asccasecmp(responded_tag, tag(0)) == 0)
975 complete |= 1;
976 else
977 goto jagain;
979 switch (response_status) {
980 case RESPONSE_PREAUTH:
981 mp->mb_active &= ~MB_PREAUTH;
982 /*FALLTHRU*/
983 case RESPONSE_OK:
984 jokay:
985 rv = OKAY;
986 complete |= 2;
987 break;
988 case RESPONSE_NO:
989 case RESPONSE_BAD:
990 jstop:
991 rv = STOP;
992 complete |= 2;
993 if (errprnt)
994 n_err(_("IMAP error: %s"), responded_text);
995 break;
996 case RESPONSE_UNKNOWN: /* does not happen */
997 case RESPONSE_BYE:
998 i = mp->mb_active;
999 mp->mb_active = MB_NONE;
1000 if (i & MB_BYE)
1001 goto jokay;
1002 goto jstop;
1003 case RESPONSE_OTHER:
1004 rv = OKAY;
1005 break;
1007 if (response_status != RESPONSE_OTHER &&
1008 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
1009 n_err(_("IMAP alert: %s"), &responded_text[8]);
1010 if (complete == 3)
1011 mp->mb_active &= ~MB_COMD;
1012 } else {
1013 rv = STOP;
1014 mp->mb_active = MB_NONE;
1016 jleave:
1017 NYD2_LEAVE;
1018 return rv;
1021 static enum okay
1022 imap_parse_list(void)
1024 char *cp;
1025 enum okay rv;
1026 NYD2_ENTER;
1028 rv = STOP;
1030 cp = responded_other_text;
1031 list_attributes = LIST_NONE;
1032 if (*cp == '(') {
1033 while (*cp && *cp != ')') {
1034 if (*cp == '\\') {
1035 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1036 list_attributes |= LIST_NOINFERIORS;
1037 cp += 12;
1038 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1039 list_attributes |= LIST_NOSELECT;
1040 cp += 9;
1041 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1042 list_attributes |= LIST_MARKED;
1043 cp += 7;
1044 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1045 list_attributes |= LIST_UNMARKED;
1046 cp += 9;
1049 cp++;
1051 if (*++cp != ' ')
1052 goto jleave;
1053 while (*cp == ' ')
1054 cp++;
1057 list_hierarchy_delimiter = EOF;
1058 if (*cp == '"') {
1059 if (*++cp == '\\')
1060 cp++;
1061 list_hierarchy_delimiter = *cp++ & 0377;
1062 if (cp[0] != '"' || cp[1] != ' ')
1063 goto jleave;
1064 cp++;
1065 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1066 list_hierarchy_delimiter = EOF;
1067 cp += 3;
1070 while (*cp == ' ')
1071 cp++;
1072 list_name = cp;
1073 while (*cp && *cp != '\r')
1074 cp++;
1075 *cp = '\0';
1076 rv = OKAY;
1077 jleave:
1078 NYD2_LEAVE;
1079 return rv;
1082 static enum okay
1083 imap_finish(struct mailbox *mp)
1085 NYD_ENTER;
1086 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1087 imap_answer(mp, 1);
1088 NYD_LEAVE;
1089 return OKAY;
1092 static void
1093 imap_timer_off(void)
1095 NYD_ENTER;
1096 if (imapkeepalive > 0) {
1097 alarm(0);
1098 safe_signal(SIGALRM, savealrm);
1100 NYD_LEAVE;
1103 static void
1104 imapcatch(int s)
1106 NYD_X; /* Signal handler */
1107 switch (s) {
1108 case SIGINT:
1109 n_err_sighdl(_("Interrupt\n"));
1110 siglongjmp(imapjmp, 1);
1111 /*NOTREACHED*/
1112 case SIGPIPE:
1113 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1114 break;
1118 static void
1119 _imap_maincatch(int s)
1121 NYD_X; /* Signal handler */
1122 n_UNUSED(s);
1123 if (interrupts++ == 0) {
1124 n_err_sighdl(_("Interrupt\n"));
1125 return;
1127 n_go_onintr_for_imap();
1130 static enum okay
1131 imap_noop1(struct mailbox *mp)
1133 char o[LINESIZE];
1134 FILE *queuefp = NULL;
1135 NYD_X;
1137 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1138 IMAP_OUT(o, MB_COMD, return STOP)
1139 IMAP_ANSWER()
1140 return OKAY;
1143 FL char const *
1144 imap_fileof(char const *xcp)
1146 char const *cp = xcp;
1147 int state = 0;
1148 NYD_ENTER;
1150 while (*cp) {
1151 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1152 cp += 3;
1153 state = 1;
1155 if (cp[0] == '/' && state == 1) {
1156 ++cp;
1157 goto jleave;
1159 if (cp[0] == '/') {
1160 cp = xcp;
1161 goto jleave;
1163 ++cp;
1165 jleave:
1166 NYD_LEAVE;
1167 return cp;
1170 FL enum okay
1171 imap_noop(void)
1173 sighandler_type volatile oldint, oldpipe;
1174 enum okay volatile rv = STOP;
1175 NYD_ENTER;
1177 if (mb.mb_type != MB_IMAP)
1178 goto jleave;
1180 imaplock = 1;
1181 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1182 safe_signal(SIGINT, &_imap_maincatch);
1183 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1184 if (sigsetjmp(imapjmp, 1) == 0) {
1185 if (oldpipe != SIG_IGN)
1186 safe_signal(SIGPIPE, imapcatch);
1188 rv = imap_noop1(&mb);
1190 safe_signal(SIGINT, oldint);
1191 safe_signal(SIGPIPE, oldpipe);
1192 imaplock = 0;
1193 jleave:
1194 NYD_LEAVE;
1195 if (interrupts)
1196 n_go_onintr_for_imap();
1197 return rv;
1200 static void
1201 rec_queue(enum rec_type rt, unsigned long cnt)
1203 struct record *rp;
1204 NYD_ENTER;
1206 rp = scalloc(1, sizeof *rp);
1207 rp->rec_type = rt;
1208 rp->rec_count = cnt;
1209 if (record && recend) {
1210 recend->rec_next = rp;
1211 recend = rp;
1212 } else
1213 record = recend = rp;
1214 NYD_LEAVE;
1217 static enum okay
1218 rec_dequeue(void)
1220 struct message *omessage;
1221 struct record *rp, *rq;
1222 uiz_t exists = 0, i;
1223 enum okay rv = STOP;
1224 NYD_ENTER;
1226 if (record == NULL)
1227 goto jleave;
1229 omessage = message;
1230 message = smalloc((msgCount+1) * sizeof *message);
1231 if (msgCount)
1232 memcpy(message, omessage, msgCount * sizeof *message);
1233 memset(&message[msgCount], 0, sizeof *message);
1235 rp = record, rq = NULL;
1236 rv = OKAY;
1237 while (rp != NULL) {
1238 switch (rp->rec_type) {
1239 case REC_EXISTS:
1240 exists = rp->rec_count;
1241 break;
1242 case REC_EXPUNGE:
1243 if (rp->rec_count == 0) {
1244 rv = STOP;
1245 break;
1247 if (rp->rec_count > (unsigned long)msgCount) {
1248 if (exists == 0 || rp->rec_count > exists--)
1249 rv = STOP;
1250 break;
1252 if (exists > 0)
1253 exists--;
1254 delcache(&mb, &message[rp->rec_count-1]);
1255 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1256 ((msgCount - rp->rec_count + 1) * sizeof *message));
1257 --msgCount;
1258 /* If the message was part of a collapsed thread,
1259 * the m_collapsed field of one of its ancestors
1260 * should be incremented. It seems hardly possible
1261 * to do this with the current message structure,
1262 * though. The result is that a '+' may be shown
1263 * in the header summary even if no collapsed
1264 * children exists */
1265 break;
1267 if (rq != NULL)
1268 free(rq);
1269 rq = rp;
1270 rp = rp->rec_next;
1272 if (rq != NULL)
1273 free(rq);
1275 record = recend = NULL;
1276 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1277 message = srealloc(message, (exists + 1) * sizeof *message);
1278 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1279 for (i = msgCount; i < exists; ++i)
1280 imap_init(&mb, i);
1281 imap_flags(&mb, msgCount+1, exists);
1282 msgCount = exists;
1285 if (rv == STOP) {
1286 free(message);
1287 message = omessage;
1289 jleave:
1290 NYD_LEAVE;
1291 return rv;
1294 static void
1295 rec_rmqueue(void)
1297 struct record *rp;
1298 NYD_ENTER;
1300 for (rp = record; rp != NULL;) {
1301 struct record *tmp = rp;
1302 rp = rp->rec_next;
1303 free(tmp);
1305 record = recend = NULL;
1306 NYD_LEAVE;
1309 /*ARGSUSED*/
1310 static void
1311 imapalarm(int s)
1313 sighandler_type volatile saveint, savepipe;
1314 NYD_X; /* Signal handler */
1315 n_UNUSED(s);
1317 if (imaplock++ == 0) {
1318 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1319 safe_signal(SIGINT, &_imap_maincatch);
1320 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1321 if (sigsetjmp(imapjmp, 1)) {
1322 safe_signal(SIGINT, saveint);
1323 safe_signal(SIGPIPE, savepipe);
1324 goto jbrk;
1326 if (savepipe != SIG_IGN)
1327 safe_signal(SIGPIPE, imapcatch);
1328 if (imap_noop1(&mb) != OKAY) {
1329 safe_signal(SIGINT, saveint);
1330 safe_signal(SIGPIPE, savepipe);
1331 goto jleave;
1333 safe_signal(SIGINT, saveint);
1334 safe_signal(SIGPIPE, savepipe);
1336 jbrk:
1337 alarm(imapkeepalive);
1338 jleave:
1339 --imaplock;
1342 static enum okay
1343 imap_preauth(struct mailbox *mp, struct url const *urlp)
1345 NYD_X;
1347 mp->mb_active |= MB_PREAUTH;
1348 imap_answer(mp, 1);
1350 #ifdef HAVE_SSL
1351 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1352 FILE *queuefp = NULL;
1353 char o[LINESIZE];
1355 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1356 IMAP_OUT(o, MB_COMD, return STOP)
1357 IMAP_ANSWER()
1358 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1359 return STOP;
1361 #else
1362 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1363 n_err(_("No SSL support compiled in\n"));
1364 return STOP;
1366 #endif
1368 imap_capability(mp);
1369 return OKAY;
1372 static enum okay
1373 imap_capability(struct mailbox *mp)
1375 char o[LINESIZE];
1376 FILE *queuefp = NULL;
1377 enum okay ok = STOP;
1378 const char *cp;
1379 NYD_X;
1381 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1382 IMAP_OUT(o, MB_COMD, return STOP)
1383 while (mp->mb_active & MB_COMD) {
1384 ok = imap_answer(mp, 0);
1385 if (response_status == RESPONSE_OTHER &&
1386 response_other == CAPABILITY_DATA) {
1387 cp = responded_other_text;
1388 while (*cp) {
1389 while (spacechar(*cp))
1390 ++cp;
1391 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1392 /* RFC 2359 */
1393 mp->mb_flags |= MB_UIDPLUS;
1394 while (*cp && !spacechar(*cp))
1395 ++cp;
1399 return ok;
1402 static enum okay
1403 imap_auth(struct mailbox *mp, struct ccred *ccred)
1405 enum okay rv;
1406 NYD_ENTER;
1408 if (!(mp->mb_active & MB_PREAUTH)) {
1409 rv = OKAY;
1410 goto jleave;
1413 switch (ccred->cc_authtype) {
1414 case AUTHTYPE_LOGIN:
1415 rv = imap_login(mp, ccred);
1416 break;
1417 #ifdef HAVE_MD5
1418 case AUTHTYPE_CRAM_MD5:
1419 rv = imap_cram_md5(mp, ccred);
1420 break;
1421 #endif
1422 #ifdef HAVE_GSSAPI
1423 case AUTHTYPE_GSSAPI:
1424 rv = _imap_gssapi(mp, ccred);
1425 break;
1426 #endif
1427 default:
1428 rv = STOP;
1429 break;
1431 jleave:
1432 NYD_LEAVE;
1433 return rv;
1436 #ifdef HAVE_MD5
1437 static enum okay
1438 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1440 char o[LINESIZE], *cp;
1441 FILE *queuefp = NULL;
1442 enum okay rv = STOP;
1443 NYD_ENTER;
1445 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1446 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1447 imap_answer(mp, 1);
1448 if (response_type != RESPONSE_CONT)
1449 goto jleave;
1451 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1452 if(cp == NULL)
1453 goto jleave;
1454 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1455 while (mp->mb_active & MB_COMD)
1456 rv = imap_answer(mp, 1);
1457 jleave:
1458 NYD_LEAVE;
1459 return rv;
1461 #endif /* HAVE_MD5 */
1463 static enum okay
1464 imap_login(struct mailbox *mp, struct ccred *ccred)
1466 char o[LINESIZE];
1467 FILE *queuefp = NULL;
1468 enum okay rv = STOP;
1469 NYD_ENTER;
1471 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1472 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1473 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1474 while (mp->mb_active & MB_COMD)
1475 rv = imap_answer(mp, 1);
1476 jleave:
1477 NYD_LEAVE;
1478 return rv;
1481 #ifdef HAVE_GSSAPI
1482 # include "obs-imap-gssapi.h"
1483 #endif
1485 FL enum okay
1486 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1487 enum fedit_mode fm)
1489 char o[LINESIZE];
1490 char const *qname, *cp;
1491 FILE *queuefp;
1492 enum okay ok;
1493 NYD_X;
1494 n_UNUSED(size);
1496 ok = STOP;
1497 queuefp = NULL;
1499 if((qname = imap_path_quote(mp, mbx)) == NULL)
1500 goto jleave;
1502 ok = OKAY;
1504 mp->mb_uidvalidity = 0;
1505 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1506 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1507 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1508 while (mp->mb_active & MB_COMD) {
1509 ok = imap_answer(mp, 1);
1510 if (response_status != RESPONSE_OTHER &&
1511 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1512 mp->mb_uidvalidity = atol(&cp[13]);
1514 *cnt = (had_exists > 0) ? had_exists : 0;
1515 if (response_status != RESPONSE_OTHER &&
1516 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1517 mp->mb_perm = 0;
1518 jleave:
1519 return ok;
1522 static enum okay
1523 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1525 char o[LINESIZE];
1526 FILE *queuefp = NULL;
1527 char const *cp;
1528 struct message *m;
1529 unsigned x = X, y = Y, n;
1530 NYD_X;
1532 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1533 IMAP_OUT(o, MB_COMD, return STOP)
1534 while (mp->mb_active & MB_COMD) {
1535 imap_answer(mp, 1);
1536 if (response_status == RESPONSE_OTHER &&
1537 response_other == MESSAGE_DATA_FETCH) {
1538 n = responded_other_number;
1539 if (n < x || n > y)
1540 continue;
1541 m = &message[n-1];
1542 m->m_xsize = 0;
1543 } else
1544 continue;
1546 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1547 cp += 5;
1548 while (*cp == ' ')
1549 cp++;
1550 if (*cp == '(')
1551 imap_getflags(cp, &cp, &m->m_flag);
1554 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1555 m->m_uid = strtoul(&cp[4], NULL, 10);
1556 getcache1(mp, m, NEED_UNSPEC, 1);
1557 m->m_flag &= ~MHIDDEN;
1560 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1561 x++;
1562 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1563 y--;
1564 if (x <= y) {
1565 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1566 tag(1), x, y);
1567 IMAP_OUT(o, MB_COMD, return STOP)
1568 while (mp->mb_active & MB_COMD) {
1569 imap_answer(mp, 1);
1570 if (response_status == RESPONSE_OTHER &&
1571 response_other == MESSAGE_DATA_FETCH) {
1572 n = responded_other_number;
1573 if (n < x || n > y)
1574 continue;
1575 m = &message[n-1];
1576 } else
1577 continue;
1578 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1579 m->m_xsize = strtol(&cp[12], NULL, 10);
1580 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1581 m->m_time = imap_read_date_time(&cp[13]);
1585 srelax_hold();
1586 for (n = X; n <= Y; ++n) {
1587 putcache(mp, &message[n-1]);
1588 srelax();
1590 srelax_rele();
1591 return OKAY;
1594 static void
1595 imap_init(struct mailbox *mp, int n)
1597 struct message *m;
1598 NYD_ENTER;
1599 n_UNUSED(mp);
1601 m = message + n;
1602 m->m_flag = MUSED | MNOFROM;
1603 m->m_block = 0;
1604 m->m_offset = 0;
1605 NYD_LEAVE;
1608 static void
1609 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1611 struct message *omessage = 0;
1612 int i, omsgCount = 0;
1613 enum okay dequeued = STOP;
1614 NYD_ENTER;
1616 if (nmail || transparent) {
1617 omessage = message;
1618 omsgCount = msgCount;
1620 if (nmail)
1621 dequeued = rec_dequeue();
1623 if (had_exists >= 0) {
1624 if (dequeued != OKAY)
1625 msgCount = had_exists;
1626 had_exists = -1;
1628 if (had_expunge >= 0) {
1629 if (dequeued != OKAY)
1630 msgCount -= had_expunge;
1631 had_expunge = -1;
1634 if (nmail && expunged_messages)
1635 printf("Expunged %ld message%s.\n", expunged_messages,
1636 (expunged_messages != 1 ? "s" : ""));
1637 *prevcount = omsgCount - expunged_messages;
1638 expunged_messages = 0;
1639 if (msgCount < 0) {
1640 fputs("IMAP error: Negative message count\n", stderr);
1641 msgCount = 0;
1644 if (dequeued != OKAY) {
1645 message = scalloc(msgCount + 1, sizeof *message);
1646 for (i = 0; i < msgCount; i++)
1647 imap_init(mp, i);
1648 if (!nmail && mp->mb_type == MB_IMAP)
1649 initcache(mp);
1650 if (msgCount > 0)
1651 imap_flags(mp, 1, msgCount);
1652 message[msgCount].m_size = 0;
1653 message[msgCount].m_lines = 0;
1654 rec_rmqueue();
1656 if (nmail || transparent)
1657 transflags(omessage, omsgCount, transparent);
1658 else
1659 setdot(message);
1660 NYD_LEAVE;
1663 FL int
1664 imap_setfile(const char *xserver, enum fedit_mode fm)
1666 struct url url;
1667 int rv;
1668 NYD_ENTER;
1670 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1671 rv = 1;
1672 goto jleave;
1674 if (!ok_blook(v15_compat) &&
1675 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1676 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1678 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1679 rv = _imap_setfile1(&url, fm, 0);
1680 jleave:
1681 NYD_LEAVE;
1682 return rv;
1685 static bool_t
1686 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1688 bool_t rv = FAL0;
1689 NYD_ENTER;
1691 if (ok_blook(v15_compat))
1692 rv = ccred_lookup(ccredp, urlp);
1693 else {
1694 char *var, *old,
1695 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1696 : urlp->url_u_h_p.s);
1698 if ((var = mbp->mb_imap_pass) != NULL) {
1699 var = savecat("password-", xuhp);
1700 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1701 old = sstrdup(old);
1702 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1704 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1705 if (var != NULL) {
1706 if (old != NULL) {
1707 n_var_vset(var, (uintptr_t)old);
1708 free(old);
1709 } else
1710 n_var_vclear(var);
1714 NYD_LEAVE;
1715 return rv;
1718 static int
1719 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1720 int volatile transparent)
1722 struct sock so;
1723 struct ccred ccred;
1724 sighandler_type volatile saveint, savepipe;
1725 char const *cp;
1726 int rv;
1727 int volatile prevcount = 0;
1728 enum mbflags same_flags;
1729 NYD_ENTER;
1731 if (fm & FEDIT_NEWMAIL) {
1732 saveint = safe_signal(SIGINT, SIG_IGN);
1733 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1734 if (saveint != SIG_IGN)
1735 safe_signal(SIGINT, imapcatch);
1736 if (savepipe != SIG_IGN)
1737 safe_signal(SIGPIPE, imapcatch);
1738 imaplock = 1;
1739 goto jnmail;
1742 same_flags = mb.mb_flags;
1743 same_imap_account = 0;
1744 if (mb.mb_imap_account != NULL &&
1745 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1746 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1747 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1748 disconnected(mb.mb_imap_account) == 0) {
1749 same_imap_account = 1;
1750 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1752 goto jduppass;
1753 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1754 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1755 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1756 jduppass:
1758 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1762 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1763 free(mb.mb_imap_pass);
1764 mb.mb_imap_pass = NULL;
1766 if (!_imap_getcred(&mb, &ccred, urlp)) {
1767 rv = -1;
1768 goto jleave;
1771 so.s_fd = -1;
1772 if (!same_imap_account) {
1773 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1774 rv = -1;
1775 goto jleave;
1777 } else
1778 so = mb.mb_sock;
1779 if (!transparent) {
1780 if(!quit(FAL0)){
1781 rv = -1;
1782 goto jleave;
1786 if (fm & FEDIT_SYSBOX)
1787 n_pstate &= ~n_PS_EDIT;
1788 else
1789 n_pstate |= n_PS_EDIT;
1790 if (mb.mb_imap_account != NULL)
1791 free(mb.mb_imap_account);
1792 if (mb.mb_imap_pass != NULL)
1793 free(mb.mb_imap_pass);
1794 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1795 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1796 * TODO object, and mailbox will naturally have an URL and credentials */
1797 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1799 if (!same_imap_account) {
1800 if (mb.mb_sock.s_fd >= 0)
1801 sclose(&mb.mb_sock);
1803 same_imap_account = 0;
1805 if (!transparent) {
1806 if (mb.mb_itf) {
1807 fclose(mb.mb_itf);
1808 mb.mb_itf = NULL;
1810 if (mb.mb_otf) {
1811 fclose(mb.mb_otf);
1812 mb.mb_otf = NULL;
1814 if (mb.mb_imap_mailbox != NULL)
1815 free(mb.mb_imap_mailbox);
1816 assert(urlp->url_path.s != NULL);
1817 imap_delim_init(&mb, urlp);
1818 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1819 initbox(savecatsep(urlp->url_p_eu_h_p,
1820 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1821 mb.mb_imap_mailbox));
1823 mb.mb_type = MB_VOID;
1824 mb.mb_active = MB_NONE;
1826 imaplock = 1;
1827 saveint = safe_signal(SIGINT, SIG_IGN);
1828 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1829 if (sigsetjmp(imapjmp, 1)) {
1830 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1831 sclose(&mb.mb_sock);
1832 safe_signal(SIGINT, saveint);
1833 safe_signal(SIGPIPE, savepipe);
1834 imaplock = 0;
1836 mb.mb_type = MB_VOID;
1837 mb.mb_active = MB_NONE;
1838 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1839 goto jleave;
1841 if (saveint != SIG_IGN)
1842 safe_signal(SIGINT, imapcatch);
1843 if (savepipe != SIG_IGN)
1844 safe_signal(SIGPIPE, imapcatch);
1846 if (mb.mb_sock.s_fd < 0) {
1847 if (disconnected(mb.mb_imap_account)) {
1848 if (cache_setptr(fm, transparent) == STOP)
1849 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1850 goto jdone;
1852 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1853 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1854 savealrm = safe_signal(SIGALRM, imapalarm);
1855 alarm(imapkeepalive);
1859 mb.mb_sock = so;
1860 mb.mb_sock.s_desc = "IMAP";
1861 mb.mb_sock.s_onclose = imap_timer_off;
1862 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1863 sclose(&mb.mb_sock);
1864 imap_timer_off();
1865 safe_signal(SIGINT, saveint);
1866 safe_signal(SIGPIPE, savepipe);
1867 imaplock = 0;
1868 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1869 goto jleave;
1871 } else /* same account */
1872 mb.mb_flags |= same_flags;
1874 if (n_poption & n_PO_R_FLAG)
1875 fm |= FEDIT_RDONLY;
1876 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1877 mb.mb_type = MB_IMAP;
1878 cache_dequeue(&mb);
1879 assert(urlp->url_path.s != NULL);
1880 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1881 /*sclose(&mb.mb_sock);
1882 imap_timer_off();*/
1883 safe_signal(SIGINT, saveint);
1884 safe_signal(SIGPIPE, savepipe);
1885 imaplock = 0;
1886 mb.mb_type = MB_VOID;
1887 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1888 goto jleave;
1891 jnmail:
1892 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1893 n_UNVOLATILE(&prevcount));
1894 jdone:
1895 setmsize(msgCount);
1896 safe_signal(SIGINT, saveint);
1897 safe_signal(SIGPIPE, savepipe);
1898 imaplock = 0;
1900 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1901 purgecache(&mb, message, msgCount);
1902 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1903 mb.mb_threaded = 0;
1904 c_sort((void*)-1);
1907 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1908 n_pstate &= ~n_PS_SAW_COMMAND;
1909 n_pstate |= n_PS_SETFILE_OPENED;
1912 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1913 mb.mb_type == MB_CACHE)) {
1914 rv = (msgCount == 0);
1915 goto jleave;
1918 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1919 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1920 !ok_blook(emptystart))
1921 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1922 rv = 1;
1923 goto jleave;
1926 if (fm & FEDIT_NEWMAIL)
1927 newmailinfo(prevcount);
1928 rv = 0;
1929 jleave:
1930 NYD_LEAVE;
1931 return rv;
1934 static int
1935 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1936 int need, const char *head, size_t headsize, long headlines)
1938 char *line = NULL, *lp;
1939 size_t linesize = 0, linelen, size = 0;
1940 int emptyline = 0, lines = 0, excess = 0;
1941 off_t offset;
1942 NYD_ENTER;
1944 fseek(mp->mb_otf, 0L, SEEK_END);
1945 offset = ftell(mp->mb_otf);
1947 if (head)
1948 fwrite(head, 1, headsize, mp->mb_otf);
1950 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1951 lp = line;
1952 if (linelen > expected) {
1953 excess = linelen - expected;
1954 linelen = expected;
1956 /* TODO >>
1957 * Need to mask 'From ' lines. This cannot be done properly
1958 * since some servers pass them as 'From ' and others as
1959 * '>From '. Although one could identify the first kind of
1960 * server in principle, it is not possible to identify the
1961 * second as '>From ' may also come from a server of the
1962 * first type as actual data. So do what is absolutely
1963 * necessary only - mask 'From '.
1965 * If the line is the first line of the message header, it
1966 * is likely a real 'From ' line. In this case, it is just
1967 * ignored since it violates all standards.
1968 * TODO can the latter *really* happen??
1969 * TODO <<
1971 /* Since we simply copy over data without doing any transfer
1972 * encoding reclassification/adjustment we *have* to perform
1973 * RFC 4155 compliant From_ quoting here */
1974 if (emptyline && is_head(lp, linelen, FAL0)) {
1975 fputc('>', mp->mb_otf);
1976 ++size;
1978 emptyline = 0;
1979 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1980 if (linelen > 2) {
1981 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1982 size += linelen - 1;
1983 } else {
1984 emptyline = 1;
1985 ++size;
1987 fputc('\n', mp->mb_otf);
1988 } else {
1989 fwrite(lp, 1, linelen, mp->mb_otf);
1990 size += linelen;
1992 ++lines;
1993 if ((expected -= linelen) <= 0)
1994 break;
1996 if (!emptyline) {
1997 /* This is very ugly; but some IMAP daemons don't end a
1998 * message with \r\n\r\n, and we need \n\n for mbox format */
1999 fputc('\n', mp->mb_otf);
2000 ++lines;
2001 ++size;
2003 fflush(mp->mb_otf);
2005 if (m != NULL) {
2006 m->m_size = size + headsize;
2007 m->m_lines = lines + headlines;
2008 m->m_block = mailx_blockof(offset);
2009 m->m_offset = mailx_offsetof(offset);
2010 switch (need) {
2011 case NEED_HEADER:
2012 m->m_content_info |= CI_HAVE_HEADER;
2013 break;
2014 case NEED_BODY:
2015 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2016 m->m_xlines = m->m_lines;
2017 m->m_xsize = m->m_size;
2018 break;
2021 free(line);
2022 NYD_LEAVE;
2023 return excess;
2026 static void
2027 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2028 const char *head, size_t headsize, long headlines)
2030 off_t offset;
2031 size_t len;
2032 NYD_ENTER;
2034 len = strlen(str);
2035 fseek(mp->mb_otf, 0L, SEEK_END);
2036 offset = ftell(mp->mb_otf);
2037 if (head)
2038 fwrite(head, 1, headsize, mp->mb_otf);
2039 if (len > 0) {
2040 fwrite(str, 1, len, mp->mb_otf);
2041 fputc('\n', mp->mb_otf);
2042 ++len;
2044 fflush(mp->mb_otf);
2046 if (m != NULL) {
2047 m->m_size = headsize + len;
2048 m->m_lines = headlines + 1;
2049 m->m_block = mailx_blockof(offset);
2050 m->m_offset = mailx_offsetof(offset);
2051 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2052 m->m_xlines = m->m_lines;
2053 m->m_xsize = m->m_size;
2055 NYD_LEAVE;
2058 static enum okay
2059 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2061 char o[LINESIZE];
2062 struct message mt;
2063 sighandler_type volatile saveint, savepipe;
2064 char * volatile head;
2065 char const *cp, *loc, * volatile item, * volatile resp;
2066 size_t expected;
2067 size_t volatile headsize;
2068 int number;
2069 FILE *queuefp;
2070 long volatile headlines;
2071 long n;
2072 ul_i volatile u;
2073 enum okay ok;
2074 NYD_X;
2076 saveint = savepipe = SIG_IGN;
2077 head = NULL;
2078 cp = loc = item = resp = NULL;
2079 headsize = 0;
2080 number = (int)PTR2SIZE(m - message + 1);
2081 queuefp = NULL;
2082 headlines = 0;
2083 n = -1;
2084 u = 0;
2085 ok = STOP;
2087 if (getcache(mp, m, need) == OKAY)
2088 return OKAY;
2089 if (mp->mb_type == MB_CACHE) {
2090 n_err(_("Message %lu not available\n"), (ul_i)number);
2091 return STOP;
2094 if (mp->mb_sock.s_fd < 0) {
2095 n_err(_("IMAP connection closed\n"));
2096 return STOP;
2099 switch (need) {
2100 case NEED_HEADER:
2101 resp = item = "RFC822.HEADER";
2102 break;
2103 case NEED_BODY:
2104 item = "BODY.PEEK[]";
2105 resp = "BODY[]";
2106 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2107 char *hdr = smalloc(m->m_size);
2108 fflush(mp->mb_otf);
2109 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2110 SEEK_SET) < 0 ||
2111 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2112 free(hdr);
2113 break;
2115 head = hdr;
2116 headsize = m->m_size;
2117 headlines = m->m_lines;
2118 item = "BODY.PEEK[TEXT]";
2119 resp = "BODY[TEXT]";
2121 break;
2122 case NEED_UNSPEC:
2123 return STOP;
2126 imaplock = 1;
2127 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2128 if (sigsetjmp(imapjmp, 1)) {
2129 safe_signal(SIGINT, saveint);
2130 safe_signal(SIGPIPE, savepipe);
2131 imaplock = 0;
2132 return STOP;
2134 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2135 safe_signal(SIGINT, &_imap_maincatch);
2136 if (savepipe != SIG_IGN)
2137 safe_signal(SIGPIPE, imapcatch);
2139 if (m->m_uid)
2140 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2141 tag(1), m->m_uid, item);
2142 else {
2143 if (check_expunged() == STOP)
2144 goto out;
2145 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2147 IMAP_OUT(o, MB_COMD, goto out)
2148 for (;;) {
2149 ok = imap_answer(mp, 1);
2150 if (ok == STOP)
2151 break;
2152 if (response_status != RESPONSE_OTHER ||
2153 response_other != MESSAGE_DATA_FETCH)
2154 continue;
2155 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2156 continue;
2157 if (m->m_uid) {
2158 if ((cp = asccasestr(responded_other_text, "UID "))) {
2159 u = atol(&cp[4]);
2160 n = 0;
2161 } else {
2162 n = -1;
2163 u = 0;
2165 } else
2166 n = responded_other_number;
2167 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2168 if (m->m_uid ? m->m_uid != u : n != number)
2169 continue;
2170 if ((cp = strchr(loc, '"')) != NULL) {
2171 cp = imap_unquotestr(cp);
2172 imap_putstr(mp, m, cp, head, headsize, headlines);
2173 } else {
2174 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2175 m->m_xlines = m->m_lines;
2176 m->m_xsize = m->m_size;
2178 goto out;
2180 expected = atol(&cp[1]);
2181 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2182 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2183 continue;
2185 mt = *m;
2186 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2187 if (n >= 0) {
2188 commitmsg(mp, m, &mt, mt.m_content_info);
2189 break;
2191 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2192 if (n_poption & n_PO_VERBVERB)
2193 fputs(imapbuf, stderr);
2194 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2195 u = atol(&cp[4]);
2196 if (u == m->m_uid) {
2197 commitmsg(mp, m, &mt, mt.m_content_info);
2198 break;
2203 out:
2204 while (mp->mb_active & MB_COMD)
2205 ok = imap_answer(mp, 1);
2207 if (saveint != SIG_IGN)
2208 safe_signal(SIGINT, saveint);
2209 if (savepipe != SIG_IGN)
2210 safe_signal(SIGPIPE, savepipe);
2211 imaplock--;
2213 if (ok == OKAY)
2214 putcache(mp, m);
2215 if (head != NULL)
2216 free(head);
2217 if (interrupts)
2218 n_go_onintr_for_imap();
2219 return ok;
2222 FL enum okay
2223 imap_header(struct message *m)
2225 enum okay rv;
2226 NYD_ENTER;
2228 rv = imap_get(&mb, m, NEED_HEADER);
2229 NYD_LEAVE;
2230 return rv;
2234 FL enum okay
2235 imap_body(struct message *m)
2237 enum okay rv;
2238 NYD_ENTER;
2240 rv = imap_get(&mb, m, NEED_BODY);
2241 NYD_LEAVE;
2242 return rv;
2245 static void
2246 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2247 enum content_info content_info)
2249 NYD_ENTER;
2250 tomp->m_size = frommp->m_size;
2251 tomp->m_lines = frommp->m_lines;
2252 tomp->m_block = frommp->m_block;
2253 tomp->m_offset = frommp->m_offset;
2254 tomp->m_content_info = content_info & CI_HAVE_MASK;
2255 if (content_info & CI_HAVE_BODY) {
2256 tomp->m_xlines = frommp->m_lines;
2257 tomp->m_xsize = frommp->m_size;
2259 putcache(mp, tomp);
2260 NYD_LEAVE;
2263 static enum okay
2264 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2266 /* bot > topp */
2267 char o[LINESIZE];
2268 char const *cp;
2269 struct message mt;
2270 size_t expected;
2271 int n = 0, u;
2272 FILE *queuefp = NULL;
2273 enum okay ok;
2274 NYD_X;
2276 if (m[bot].m_uid)
2277 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2278 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2279 else {
2280 if (check_expunged() == STOP)
2281 return STOP;
2282 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2283 tag(1), bot, topp);
2285 IMAP_OUT(o, MB_COMD, return STOP)
2287 srelax_hold();
2288 for (;;) {
2289 ok = imap_answer(mp, 1);
2290 if (response_status != RESPONSE_OTHER)
2291 break;
2292 if (response_other != MESSAGE_DATA_FETCH)
2293 continue;
2294 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2295 srelax_rele();
2296 return STOP;
2298 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2299 continue;
2300 expected = atol(&cp[1]);
2301 if (m[bot-1].m_uid) {
2302 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2303 u = atoi(&cp[4]);
2304 for (n = bot; n <= topp; n++)
2305 if ((unsigned long)u == m[n-1].m_uid)
2306 break;
2307 if (n > topp) {
2308 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2309 continue;
2311 } else
2312 n = -1;
2313 } else {
2314 n = responded_other_number;
2315 if (n <= 0 || n > msgCount) {
2316 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2317 continue;
2320 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2321 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2322 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2323 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2324 if (n_poption & n_PO_VERBVERB)
2325 fputs(imapbuf, stderr);
2326 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2327 u = atoi(&cp[4]);
2328 for (n = bot; n <= topp; n++)
2329 if ((unsigned long)u == m[n-1].m_uid)
2330 break;
2331 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2332 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2333 n = 0;
2336 srelax();
2338 srelax_rele();
2340 while (mp->mb_active & MB_COMD)
2341 ok = imap_answer(mp, 1);
2342 return ok;
2345 FL void
2346 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2348 sighandler_type saveint, savepipe;
2349 /*enum okay ok = STOP;*/
2350 int i, chunk = 256;
2351 NYD_X;
2353 if (mb.mb_type == MB_CACHE)
2354 return;
2355 if (bot < 1)
2356 bot = 1;
2357 if (topp > msgCount)
2358 topp = msgCount;
2359 for (i = bot; i < topp; i++) {
2360 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2361 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2362 bot = i+1;
2363 else
2364 break;
2366 for (i = topp; i > bot; i--) {
2367 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2368 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2369 topp = i-1;
2370 else
2371 break;
2373 if (bot >= topp)
2374 return;
2376 imaplock = 1;
2377 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2378 safe_signal(SIGINT, &_imap_maincatch);
2379 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2380 if (sigsetjmp(imapjmp, 1) == 0) {
2381 if (savepipe != SIG_IGN)
2382 safe_signal(SIGPIPE, imapcatch);
2384 for (i = bot; i <= topp; i += chunk) {
2385 int j = i + chunk - 1;
2386 j = n_MIN(j, topp);
2387 if (visible(message + j))
2388 /*ok = */imap_fetchheaders(&mb, message, i, j);
2389 if (interrupts)
2390 n_go_onintr_for_imap(); /* XXX imaplock? */
2393 safe_signal(SIGINT, saveint);
2394 safe_signal(SIGPIPE, savepipe);
2395 imaplock = 0;
2398 static enum okay
2399 __imap_exit(struct mailbox *mp)
2401 char o[LINESIZE];
2402 FILE *queuefp = NULL;
2403 NYD_X;
2405 mp->mb_active |= MB_BYE;
2406 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2407 IMAP_OUT(o, MB_COMD, return STOP)
2408 IMAP_ANSWER()
2409 return OKAY;
2412 static enum okay
2413 imap_exit(struct mailbox *mp)
2415 enum okay rv;
2416 NYD_ENTER;
2418 rv = __imap_exit(mp);
2419 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2420 free(mp->mb_imap_pass);
2421 free(mp->mb_imap_account);
2422 free(mp->mb_imap_mailbox);
2423 if (mp->mb_cache_directory != NULL)
2424 free(mp->mb_cache_directory);
2425 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2426 mp->mb_imap_account =
2427 mp->mb_imap_mailbox =
2428 mp->mb_cache_directory = "";
2429 #else
2430 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2431 mp->mb_imap_mailbox = NULL;
2432 mp->mb_cache_directory = NULL;
2433 #endif
2434 #endif
2435 sclose(&mp->mb_sock);
2436 NYD_LEAVE;
2437 return rv;
2440 static enum okay
2441 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2443 NYD_ENTER;
2444 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2445 if (mp->mb_type == MB_IMAP)
2446 delcache(mp, m);
2447 NYD_LEAVE;
2448 return OKAY;
2451 static enum okay
2452 imap_close(struct mailbox *mp)
2454 char o[LINESIZE];
2455 FILE *queuefp = NULL;
2456 NYD_X;
2458 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2459 IMAP_OUT(o, MB_COMD, return STOP)
2460 IMAP_ANSWER()
2461 return OKAY;
2464 static enum okay
2465 imap_update(struct mailbox *mp)
2467 struct message *m;
2468 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2469 NYD_ENTER;
2471 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2472 holdbits();
2473 c = 0;
2474 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2475 if (m->m_flag & MBOX)
2476 ++c;
2477 if (c > 0)
2478 if (makembox() == STOP)
2479 goto jbypass;
2482 gotcha = held = 0;
2483 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2484 if (mp->mb_perm == 0)
2485 dodel = 0;
2486 else if (n_pstate & n_PS_EDIT)
2487 dodel = ((m->m_flag & MDELETED) != 0);
2488 else
2489 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2491 /* Fetch the result after around each 800 STORE commands
2492 * sent (approx. 32k data sent). Otherwise, servers will
2493 * try to flush the return queue at some point, leading
2494 * to a deadlock if we are still writing commands but not
2495 * reading their results */
2496 needstat = stored > 0 && stored % 800 == 0;
2497 /* Even if this message has been deleted, continue
2498 * to set further flags. This is necessary to support
2499 * Gmail semantics, where "delete" actually means
2500 * "archive", and the flags are applied to the copy
2501 * in "All Mail" */
2502 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2503 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2504 stored++;
2506 if (m->m_flag & MFLAG) {
2507 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2508 stored++;
2510 if (m->m_flag & MUNFLAG) {
2511 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2512 stored++;
2514 if (m->m_flag & MANSWER) {
2515 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2516 stored++;
2518 if (m->m_flag & MUNANSWER) {
2519 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2520 stored++;
2522 if (m->m_flag & MDRAFT) {
2523 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2524 stored++;
2526 if (m->m_flag & MUNDRAFT) {
2527 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2528 stored++;
2531 if (dodel) {
2532 imap_delete(mp, m-message+1, m, needstat);
2533 stored++;
2534 gotcha++;
2535 } else if (mp->mb_type != MB_CACHE ||
2536 (!(n_pstate & n_PS_EDIT) &&
2537 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2538 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2539 (MPRESERVE | MTOUCH) ||
2540 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2541 held++;
2542 if (m->m_flag & MNEW) {
2543 m->m_flag &= ~MNEW;
2544 m->m_flag |= MSTATUS;
2547 jbypass:
2548 if (gotcha)
2549 imap_close(mp);
2551 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2552 if (!(m->m_flag & MUNLINKED) &&
2553 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2554 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2555 putcache(mp, m);
2556 modflags++;
2559 /* XXX should be readonly (but our IMAP code is weird...) */
2560 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2561 mb.mb_perm != 0) {
2562 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2563 printf(_("\"%s\" "), displayname);
2564 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2565 ? _("complete\n") : _("updated.\n"));
2566 } else if (held && !(n_pstate & n_PS_EDIT)) {
2567 if (held == 1)
2568 printf(_("Held 1 message in %s\n"), displayname);
2569 else
2570 printf(_("Held %d messages in %s\n"), held, displayname);
2572 fflush(stdout);
2574 NYD_LEAVE;
2575 return OKAY;
2578 FL bool_t
2579 imap_quit(bool_t hold_sigs_on)
2581 sighandler_type volatile saveint, savepipe;
2582 bool_t rv;
2583 NYD_ENTER;
2585 if(hold_sigs_on)
2586 rele_sigs();
2588 if (mb.mb_type == MB_CACHE) {
2589 rv = (imap_update(&mb) == OKAY);
2590 goto jleave;
2593 rv = FAL0;
2595 if (mb.mb_sock.s_fd < 0) {
2596 n_err(_("IMAP connection closed\n"));
2597 goto jleave;
2600 imaplock = 1;
2601 saveint = safe_signal(SIGINT, SIG_IGN);
2602 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2603 if (sigsetjmp(imapjmp, 1)) {
2604 safe_signal(SIGINT, saveint);
2605 safe_signal(SIGPIPE, saveint);
2606 imaplock = 0;
2607 goto jleave;
2609 if (saveint != SIG_IGN)
2610 safe_signal(SIGINT, imapcatch);
2611 if (savepipe != SIG_IGN)
2612 safe_signal(SIGPIPE, imapcatch);
2614 rv = (imap_update(&mb) == OKAY);
2615 if(!same_imap_account && imap_exit(&mb) != OKAY)
2616 rv = FAL0;
2618 safe_signal(SIGINT, saveint);
2619 safe_signal(SIGPIPE, savepipe);
2620 imaplock = 0;
2621 jleave:
2622 if(hold_sigs_on)
2623 hold_sigs();
2624 NYD_LEAVE;
2625 return rv;
2628 static enum okay
2629 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2630 int needstat)
2632 char o[LINESIZE];
2633 FILE *queuefp = NULL;
2634 NYD_X;
2636 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2637 return STOP;
2638 if (m->m_uid)
2639 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2640 tag(1), m->m_uid, c, sp);
2641 else {
2642 if (check_expunged() == STOP)
2643 return STOP;
2644 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2646 IMAP_OUT(o, MB_COMD, return STOP)
2647 if (needstat)
2648 IMAP_ANSWER()
2649 else
2650 mb.mb_active &= ~MB_COMD;
2651 if (queuefp != NULL)
2652 Fclose(queuefp);
2653 return OKAY;
2656 FL enum okay
2657 imap_undelete(struct message *m, int n)
2659 enum okay rv;
2660 NYD_ENTER;
2662 rv = imap_unstore(m, n, "\\Deleted");
2663 NYD_LEAVE;
2664 return rv;
2667 FL enum okay
2668 imap_unread(struct message *m, int n)
2670 enum okay rv;
2671 NYD_ENTER;
2673 rv = imap_unstore(m, n, "\\Seen");
2674 NYD_LEAVE;
2675 return rv;
2678 static enum okay
2679 imap_unstore(struct message *m, int n, const char *flag)
2681 sighandler_type saveint, savepipe;
2682 enum okay volatile rv = STOP;
2683 NYD_ENTER;
2685 imaplock = 1;
2686 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2687 safe_signal(SIGINT, &_imap_maincatch);
2688 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2689 if (sigsetjmp(imapjmp, 1) == 0) {
2690 if (savepipe != SIG_IGN)
2691 safe_signal(SIGPIPE, imapcatch);
2693 rv = imap_store(&mb, m, n, '-', flag, 1);
2695 safe_signal(SIGINT, saveint);
2696 safe_signal(SIGPIPE, savepipe);
2697 imaplock = 0;
2699 NYD_LEAVE;
2700 if (interrupts)
2701 n_go_onintr_for_imap();
2702 return rv;
2705 static const char *
2706 tag(int new)
2708 static char ts[20];
2709 static long n;
2710 NYD2_ENTER;
2712 if (new)
2713 ++n;
2714 snprintf(ts, sizeof ts, "T%lu", n);
2715 NYD2_LEAVE;
2716 return ts;
2719 FL int
2720 c_imapcodec(void *vp){
2721 bool_t err;
2722 size_t alen;
2723 char const **argv, *varname, *varres, *act, *cp;
2724 NYD_ENTER;
2726 argv = vp;
2727 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2729 act = *argv;
2730 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2732 if(act == cp)
2733 goto jesynopsis;
2734 alen = PTR2SIZE(cp - act);
2735 if(*cp != '\0')
2736 ++cp;
2738 n_pstate_err_no = n_ERR_NONE;
2739 varres = imap_path_normalize(NULL, cp);
2741 if(is_ascncaseprefix(act, "encode", alen))
2742 varres = imap_path_encode(varres, &err);
2743 else if(is_ascncaseprefix(act, "decode", alen))
2744 varres = imap_path_decode(varres, &err);
2745 else
2746 goto jesynopsis;
2748 if(err){
2749 n_pstate_err_no = n_ERR_CANCELED;
2750 varres = cp;
2751 vp = NULL;
2754 if(varname != NULL){
2755 if(!n_var_vset(varname, (uintptr_t)varres)){
2756 n_pstate_err_no = n_ERR_NOTSUP;
2757 vp = NULL;
2759 }else{
2760 struct str in, out;
2762 in.l = strlen(in.s = n_UNCONST(varres));
2763 makeprint(&in, &out);
2764 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2765 n_pstate_err_no = n_err_no;
2766 vp = NULL;
2768 free(out.s);
2771 jleave:
2772 NYD_LEAVE;
2773 return (vp != NULL ? 0 : 1);
2774 jesynopsis:
2775 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2776 n_pstate_err_no = n_ERR_INVAL;
2777 vp = NULL;
2778 goto jleave;
2781 FL int
2782 c_imap_imap(void *vp)
2784 char o[LINESIZE];
2785 sighandler_type saveint, savepipe;
2786 struct mailbox *mp = &mb;
2787 FILE *queuefp = NULL;
2788 enum okay volatile ok = STOP;
2789 NYD_X;
2791 if (mp->mb_type != MB_IMAP) {
2792 printf("Not operating on an IMAP mailbox.\n");
2793 return 1;
2795 imaplock = 1;
2796 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2797 safe_signal(SIGINT, &_imap_maincatch);
2798 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2799 if (sigsetjmp(imapjmp, 1) == 0) {
2800 if (savepipe != SIG_IGN)
2801 safe_signal(SIGPIPE, imapcatch);
2803 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2804 IMAP_OUT(o, MB_COMD, goto out)
2805 while (mp->mb_active & MB_COMD) {
2806 ok = imap_answer(mp, 0);
2807 fputs(responded_text, stdout);
2810 out:
2811 safe_signal(SIGINT, saveint);
2812 safe_signal(SIGPIPE, savepipe);
2813 imaplock = 0;
2815 if (interrupts)
2816 n_go_onintr_for_imap();
2817 return ok != OKAY;
2820 FL int
2821 imap_newmail(int nmail)
2823 NYD_ENTER;
2825 if (nmail && had_exists < 0 && had_expunge < 0) {
2826 imaplock = 1;
2827 imap_noop();
2828 imaplock = 0;
2831 if (had_exists == msgCount && had_expunge < 0)
2832 /* Some servers always respond with EXISTS to NOOP. If
2833 * the mailbox has been changed but the number of messages
2834 * has not, an EXPUNGE must also had been sent; otherwise,
2835 * nothing has changed */
2836 had_exists = -1;
2837 NYD_LEAVE;
2838 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2841 static char *
2842 imap_putflags(int f)
2844 const char *cp;
2845 char *buf, *bp;
2846 NYD2_ENTER;
2848 bp = buf = salloc(100);
2849 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2850 *bp++ = '(';
2851 if (f & MREAD) {
2852 if (bp[-1] != '(')
2853 *bp++ = ' ';
2854 for (cp = "\\Seen"; *cp; cp++)
2855 *bp++ = *cp;
2857 if (f & MFLAGGED) {
2858 if (bp[-1] != '(')
2859 *bp++ = ' ';
2860 for (cp = "\\Flagged"; *cp; cp++)
2861 *bp++ = *cp;
2863 if (f & MANSWERED) {
2864 if (bp[-1] != '(')
2865 *bp++ = ' ';
2866 for (cp = "\\Answered"; *cp; cp++)
2867 *bp++ = *cp;
2869 if (f & MDRAFT) {
2870 if (bp[-1] != '(')
2871 *bp++ = ' ';
2872 for (cp = "\\Draft"; *cp; cp++)
2873 *bp++ = *cp;
2875 *bp++ = ')';
2876 *bp++ = ' ';
2878 *bp = '\0';
2879 NYD2_LEAVE;
2880 return buf;
2883 static void
2884 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2886 NYD2_ENTER;
2887 while (*cp != ')') {
2888 if (*cp == '\\') {
2889 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2890 *f |= MREAD;
2891 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2892 *f |= MNEW;
2893 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2894 *f |= MDELETED;
2895 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2896 *f |= MFLAGGED;
2897 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2898 *f |= MANSWERED;
2899 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2900 *f |= MDRAFTED;
2902 cp++;
2905 if (xp != NULL)
2906 *xp = cp;
2907 NYD2_LEAVE;
2910 static enum okay
2911 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2912 long xsize, enum mflag flag, time_t t)
2914 char o[LINESIZE], *buf;
2915 size_t bufsize, buflen, cnt;
2916 long size, lines, ysize;
2917 char const *qname;
2918 bool_t twice;
2919 FILE *queuefp;
2920 enum okay rv;
2921 NYD_ENTER;
2923 rv = STOP;
2924 queuefp = NULL;
2925 twice = FAL0;
2926 buf = NULL;
2928 if((qname = imap_path_quote(mp, name)) == NULL)
2929 goto jleave;
2931 if (mp->mb_type == MB_CACHE) {
2932 queuefp = cache_queue(mp);
2933 if (queuefp == NULL) {
2934 buf = NULL;
2935 goto jleave;
2937 rv = OKAY;
2940 buf = smalloc(bufsize = LINESIZE);
2941 buflen = 0;
2942 jagain:
2943 size = xsize;
2944 cnt = fsize(fp);
2945 if (fseek(fp, off1, SEEK_SET) < 0) {
2946 rv = STOP;
2947 goto jleave;
2950 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2951 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2952 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2953 while (mp->mb_active & MB_COMD) {
2954 rv = imap_answer(mp, twice);
2955 if (response_type == RESPONSE_CONT)
2956 break;
2959 if (mp->mb_type != MB_CACHE && rv == STOP) {
2960 if (!twice)
2961 goto jtrycreate;
2962 else
2963 goto jleave;
2966 lines = ysize = 0;
2967 while (size > 0) {
2968 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2969 lines++;
2970 ysize += buflen;
2971 buf[buflen - 1] = '\r';
2972 buf[buflen] = '\n';
2973 if (mp->mb_type != MB_CACHE)
2974 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2975 else if (queuefp)
2976 fwrite(buf, 1, buflen+1, queuefp);
2977 size -= buflen + 1;
2979 if (mp->mb_type != MB_CACHE)
2980 swrite(&mp->mb_sock, "\r\n");
2981 else if (queuefp)
2982 fputs("\r\n", queuefp);
2983 while (mp->mb_active & MB_COMD) {
2984 rv = imap_answer(mp, 0);
2985 if (response_status == RESPONSE_NO /*&&
2986 ascncasecmp(responded_text,
2987 "[TRYCREATE] ", 12) == 0*/) {
2988 jtrycreate:
2989 if (twice) {
2990 rv = STOP;
2991 goto jleave;
2993 twice = TRU1;
2994 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2995 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2996 while (mp->mb_active & MB_COMD)
2997 rv = imap_answer(mp, 1);
2998 if (rv == STOP)
2999 goto jleave;
3000 imap_created_mailbox++;
3001 goto jagain;
3002 } else if (rv != OKAY)
3003 n_err(_("IMAP error: %s"), responded_text);
3004 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
3005 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
3007 jleave:
3008 if (queuefp != NULL)
3009 Fclose(queuefp);
3010 if (buf != NULL)
3011 free(buf);
3012 NYD_LEAVE;
3013 return rv;
3016 static enum okay
3017 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3019 char *buf, *bp, *lp;
3020 size_t bufsize, buflen, cnt;
3021 off_t off1 = -1, offs;
3022 int flag;
3023 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3024 time_t tim;
3025 long size;
3026 enum okay rv;
3027 NYD_ENTER;
3029 buf = smalloc(bufsize = LINESIZE);
3030 buflen = 0;
3031 cnt = fsize(fp);
3032 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3033 time(&tim);
3034 size = 0;
3036 for (flag = MNEW, state = _NLSEP;;) {
3037 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3039 if (bp == NULL ||
3040 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3041 is_head(buf, buflen, FAL0))) {
3042 if (off1 != (off_t)-1) {
3043 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3044 if (rv == STOP)
3045 goto jleave;
3046 fseek(fp, offs+buflen, SEEK_SET);
3048 off1 = offs + buflen;
3049 size = 0;
3050 flag = MNEW;
3051 state = _INHEAD;
3052 if (bp == NULL)
3053 break;
3054 tim = unixtime(buf);
3055 } else
3056 size += buflen+1;
3057 offs += buflen;
3059 state &= ~_NLSEP;
3060 if (buf[0] == '\n') {
3061 state &= ~_INHEAD;
3062 state |= _NLSEP;
3063 } else if (state & _INHEAD) {
3064 if (ascncasecmp(buf, "status", 6) == 0) {
3065 lp = &buf[6];
3066 while (whitechar(*lp))
3067 lp++;
3068 if (*lp == ':')
3069 while (*++lp != '\0')
3070 switch (*lp) {
3071 case 'R':
3072 flag |= MREAD;
3073 break;
3074 case 'O':
3075 flag &= ~MNEW;
3076 break;
3078 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3079 lp = &buf[8];
3080 while (whitechar(*lp))
3081 lp++;
3082 if (*lp == ':')
3083 while (*++lp != '\0')
3084 switch (*lp) {
3085 case 'F':
3086 flag |= MFLAGGED;
3087 break;
3088 case 'A':
3089 flag |= MANSWERED;
3090 break;
3091 case 'T':
3092 flag |= MDRAFTED;
3093 break;
3098 rv = OKAY;
3099 jleave:
3100 free(buf);
3101 NYD_LEAVE;
3102 return rv;
3105 FL enum okay
3106 imap_append(const char *xserver, FILE *fp, long offset)
3108 sighandler_type volatile saveint, savepipe;
3109 struct url url;
3110 struct ccred ccred;
3111 enum okay rv = STOP;
3112 NYD_ENTER;
3114 if (!url_parse(&url, CPROTO_IMAP, xserver))
3115 goto j_leave;
3116 if (!ok_blook(v15_compat) &&
3117 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3118 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3119 assert(url.url_path.s != NULL);
3121 imaplock = 1;
3122 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3123 safe_signal(SIGINT, &_imap_maincatch);
3124 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3125 if (sigsetjmp(imapjmp, 1))
3126 goto jleave;
3127 if (savepipe != SIG_IGN)
3128 safe_signal(SIGPIPE, imapcatch);
3130 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3131 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3132 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3133 } else {
3134 struct mailbox mx;
3136 memset(&mx, 0, sizeof mx);
3138 if (!_imap_getcred(&mx, &ccred, &url))
3139 goto jleave;
3141 imap_delim_init(&mx, &url);
3142 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3144 if (disconnected(url.url_p_eu_h_p) == 0) {
3145 if (!sopen(&mx.mb_sock, &url))
3146 goto jfail;
3147 mx.mb_sock.s_desc = "IMAP";
3148 mx.mb_type = MB_IMAP;
3149 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3150 /* TODO the code now did
3151 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3152 * TODO though imap_mailbox is sfree()d and mbx
3153 * TODO is possibly even a constant
3154 * TODO i changed this to sstrdup() sofar, as is used
3155 * TODO somewhere else in this file for this! */
3156 if (imap_preauth(&mx, &url) != OKAY ||
3157 imap_auth(&mx, &ccred) != OKAY) {
3158 sclose(&mx.mb_sock);
3159 goto jfail;
3161 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3162 imap_exit(&mx);
3163 } else {
3164 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3165 mx.mb_type = MB_CACHE;
3166 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3168 jfail:
3172 jleave:
3173 safe_signal(SIGINT, saveint);
3174 safe_signal(SIGPIPE, savepipe);
3175 imaplock = 0;
3176 j_leave:
3177 NYD_LEAVE;
3178 if (interrupts)
3179 n_go_onintr_for_imap();
3180 return rv;
3183 static enum okay
3184 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3185 struct list_item **lend, int level)
3187 char o[LINESIZE], *cp;
3188 struct list_item *lp;
3189 const char *qname, *bp;
3190 FILE *queuefp;
3191 enum okay ok;
3192 NYD_X;
3194 ok = STOP;
3195 queuefp = NULL;
3197 if((qname = imap_path_quote(mp, base)) == NULL)
3198 goto jleave;
3200 *list = *lend = NULL;
3201 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3202 IMAP_OUT(o, MB_COMD, goto jleave)
3203 while (mp->mb_active & MB_COMD) {
3204 ok = imap_answer(mp, 1);
3205 if (response_status == RESPONSE_OTHER &&
3206 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3207 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3208 lp = csalloc(1, sizeof *lp);
3209 lp->l_name = cp;
3210 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3211 ++cp;
3212 lp->l_base = *cp ? cp : savestr(base);
3213 lp->l_attr = list_attributes;
3214 lp->l_level = level+1;
3215 lp->l_delim = list_hierarchy_delimiter;
3216 if (*list && *lend) {
3217 (*lend)->l_next = lp;
3218 *lend = lp;
3219 } else
3220 *list = *lend = lp;
3223 jleave:
3224 return ok;
3227 static enum okay
3228 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3230 struct list_item *list, *lend, *lp, *lx, *ly;
3231 int n, depth;
3232 const char *bp;
3233 char *cp;
3234 enum okay rv;
3235 NYD_ENTER;
3237 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3238 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3239 goto jleave;
3240 rv = OKAY;
3241 if (list == NULL || lend == NULL)
3242 goto jleave;
3244 for (lp = list; lp; lp = lp->l_next)
3245 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3246 !(lp->l_attr & LIST_NOINFERIORS)) {
3247 cp = salloc((n = strlen(lp->l_name)) + 2);
3248 memcpy(cp, lp->l_name, n);
3249 cp[n] = lp->l_delim;
3250 cp[n+1] = '\0';
3251 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3252 lp->l_has_children = 1;
3253 if (strcmp(cp, lx->l_name) == 0)
3254 lx = lx->l_next;
3255 if (lx) {
3256 lend->l_next = lx;
3257 lend = ly;
3262 for (lp = list; lp; lp = lp->l_next) {
3263 if (strip) {
3264 cp = lp->l_name;
3265 for (bp = base; *bp && *bp == *cp; bp++)
3266 cp++;
3267 } else
3268 cp = lp->l_name;
3269 if (!(lp->l_attr & LIST_NOSELECT))
3270 fprintf(fp, "%s\n", *cp ? cp : base);
3271 else if (lp->l_has_children == 0)
3272 fprintf(fp, "%s%c\n", *cp ? cp : base,
3273 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3275 jleave:
3276 NYD_LEAVE;
3277 return rv;
3280 FL int
3281 imap_folders(const char * volatile name, int strip)
3283 sighandler_type saveint, savepipe;
3284 const char * volatile fold, *cp, *sp;
3285 FILE * volatile fp;
3286 int rv = 1;
3287 NYD_ENTER;
3289 cp = protbase(name);
3290 sp = mb.mb_imap_account;
3291 if (sp == NULL || strcmp(cp, sp)) {
3292 n_err(
3293 _("Cannot perform `folders' but when on the very IMAP "
3294 "account; the current one is\n `%s' -- "
3295 "try `folders @'\n"),
3296 (sp != NULL ? sp : _("[NONE]")));
3297 goto jleave;
3300 fold = imap_fileof(name);
3301 if (n_psonce & n_PSO_TTYOUT) {
3302 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3303 == NULL) {
3304 n_perr(_("tmpfile"), 0);
3305 goto jleave;
3307 } else
3308 fp = stdout;
3310 imaplock = 1;
3311 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3312 safe_signal(SIGINT, &_imap_maincatch);
3313 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3314 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3315 goto junroll;
3316 if (savepipe != SIG_IGN)
3317 safe_signal(SIGPIPE, imapcatch);
3319 if (mb.mb_type == MB_CACHE)
3320 cache_list(&mb, fold, strip, fp);
3321 else
3322 imap_list(&mb, fold, strip, fp);
3324 imaplock = 0;
3325 if (interrupts) {
3326 if (n_psonce & n_PSO_TTYOUT)
3327 Fclose(fp);
3328 rv = 0;
3329 goto jleave;
3331 fflush(fp);
3333 if (n_psonce & n_PSO_TTYOUT) {
3334 rewind(fp);
3335 if (fsize(fp) > 0){
3336 dopr(fp);
3337 rv = 0;
3338 }else
3339 n_err(_("Folder not found\n"));
3340 }else
3341 rv = 0;
3342 junroll:
3343 safe_signal(SIGINT, saveint);
3344 safe_signal(SIGPIPE, savepipe);
3345 if (n_psonce & n_PSO_TTYOUT)
3346 Fclose(fp);
3347 jleave:
3348 NYD_LEAVE;
3349 if (interrupts)
3350 n_go_onintr_for_imap();
3351 return rv;
3354 static void
3355 dopr(FILE *fp)
3357 char o[LINESIZE];
3358 int c;
3359 long n = 0, mx = 0, columns, width;
3360 FILE *out;
3361 NYD_ENTER;
3363 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER))
3364 == NULL) {
3365 n_perr(_("tmpfile"), 0);
3366 goto jleave;
3369 while ((c = getc(fp)) != EOF) {
3370 if (c == '\n') {
3371 if (n > mx)
3372 mx = n;
3373 n = 0;
3374 } else
3375 ++n;
3377 rewind(fp);
3379 width = n_scrnwidth;
3380 if (mx < width / 2) {
3381 columns = width / (mx+2);
3382 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3383 } else
3384 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3385 n_child_run(ok_vlook(SHELL), NULL, fileno(fp), fileno(out), "-c", o, NULL,
3386 NULL, NULL);
3387 page_or_print(out, 0);
3388 Fclose(out);
3389 jleave:
3390 NYD_LEAVE;
3393 static enum okay
3394 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3396 char o[LINESIZE];
3397 const char *qname;
3398 bool_t twice, stored;
3399 FILE *queuefp;
3400 enum okay ok;
3401 NYD_X;
3403 ok = STOP;
3404 queuefp = NULL;
3405 twice = stored = FAL0;
3407 /* C99 */{
3408 size_t i;
3410 i = strlen(name = imap_fileof(name));
3411 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3412 name = savecat(name, "INBOX");
3413 if((qname = imap_path_quote(mp, name)) == NULL)
3414 goto jleave;
3417 if (mp->mb_type == MB_CACHE) {
3418 if ((queuefp = cache_queue(mp)) == NULL)
3419 goto jleave;
3420 ok = OKAY;
3423 /* Since it is not possible to set flags on the copy, recently
3424 * set flags must be set on the original to include it in the copy */
3425 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3426 imap_store(mp, m, n, '+', "\\Seen", 0);
3427 if (m->m_flag&MFLAG)
3428 imap_store(mp, m, n, '+', "\\Flagged", 0);
3429 if (m->m_flag&MUNFLAG)
3430 imap_store(mp, m, n, '-', "\\Flagged", 0);
3431 if (m->m_flag&MANSWER)
3432 imap_store(mp, m, n, '+', "\\Answered", 0);
3433 if (m->m_flag&MUNANSWER)
3434 imap_store(mp, m, n, '-', "\\Flagged", 0);
3435 if (m->m_flag&MDRAFT)
3436 imap_store(mp, m, n, '+', "\\Draft", 0);
3437 if (m->m_flag&MUNDRAFT)
3438 imap_store(mp, m, n, '-', "\\Draft", 0);
3439 again:
3440 if (m->m_uid)
3441 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3442 else {
3443 if (check_expunged() == STOP)
3444 goto out;
3445 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3447 IMAP_OUT(o, MB_COMD, goto out)
3448 while (mp->mb_active & MB_COMD)
3449 ok = imap_answer(mp, twice);
3451 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3452 response_status == RESPONSE_OK)
3453 imap_copyuid(mp, m, name);
3455 if (response_status == RESPONSE_NO && !twice) {
3456 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3457 IMAP_OUT(o, MB_COMD, goto out)
3458 while (mp->mb_active & MB_COMD)
3459 ok = imap_answer(mp, 1);
3460 if (ok == OKAY) {
3461 imap_created_mailbox++;
3462 goto again;
3465 twice = TRU1;
3467 if (queuefp != NULL)
3468 Fclose(queuefp);
3470 /* ... and reset the flag to its initial value so that the 'exit'
3471 * command still leaves the message unread */
3472 out:
3473 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3474 imap_store(mp, m, n, '-', "\\Seen", 0);
3475 stored = TRU1;
3477 if (m->m_flag & MFLAG) {
3478 imap_store(mp, m, n, '-', "\\Flagged", 0);
3479 stored = TRU1;
3481 if (m->m_flag & MUNFLAG) {
3482 imap_store(mp, m, n, '+', "\\Flagged", 0);
3483 stored = TRU1;
3485 if (m->m_flag & MANSWER) {
3486 imap_store(mp, m, n, '-', "\\Answered", 0);
3487 stored = TRU1;
3489 if (m->m_flag & MUNANSWER) {
3490 imap_store(mp, m, n, '+', "\\Answered", 0);
3491 stored = TRU1;
3493 if (m->m_flag & MDRAFT) {
3494 imap_store(mp, m, n, '-', "\\Draft", 0);
3495 stored = TRU1;
3497 if (m->m_flag & MUNDRAFT) {
3498 imap_store(mp, m, n, '+', "\\Draft", 0);
3499 stored = TRU1;
3501 if (stored) {
3502 mp->mb_active |= MB_COMD;
3503 (void)imap_finish(mp);
3505 jleave:
3506 return ok;
3509 FL enum okay
3510 imap_copy(struct message *m, int n, const char *name)
3512 sighandler_type saveint, savepipe;
3513 enum okay volatile rv = STOP;
3514 NYD_ENTER;
3516 imaplock = 1;
3517 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3518 safe_signal(SIGINT, &_imap_maincatch);
3519 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3520 if (sigsetjmp(imapjmp, 1) == 0) {
3521 if (savepipe != SIG_IGN)
3522 safe_signal(SIGPIPE, imapcatch);
3524 rv = imap_copy1(&mb, m, n, name);
3526 safe_signal(SIGINT, saveint);
3527 safe_signal(SIGPIPE, savepipe);
3528 imaplock = 0;
3530 NYD_LEAVE;
3531 if (interrupts)
3532 n_go_onintr_for_imap();
3533 return rv;
3536 static enum okay
3537 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3538 unsigned long *olduid, unsigned long *newuid)
3540 char *xp, *yp, *zp;
3541 enum okay rv;
3542 NYD_ENTER;
3544 *uidvalidity = strtoul(cp, &xp, 10);
3545 *olduid = strtoul(xp, &yp, 10);
3546 *newuid = strtoul(yp, &zp, 10);
3547 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3548 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3549 NYD_LEAVE;
3550 return rv;
3553 static enum okay
3554 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3555 unsigned long *uid)
3557 char *xp, *yp;
3558 enum okay rv;
3559 NYD_ENTER;
3561 *uidvalidity = strtoul(cp, &xp, 10);
3562 *uid = strtoul(xp, &yp, 10);
3563 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3564 *yp == ']');
3565 NYD_LEAVE;
3566 return rv;
3569 static enum okay
3570 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3572 struct mailbox xmb;
3573 struct message xm;
3574 const char *cp;
3575 unsigned long uidvalidity, olduid, newuid;
3576 enum okay rv;
3577 NYD_ENTER;
3579 rv = STOP;
3581 memset(&xmb, 0, sizeof xmb);
3583 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3584 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3585 goto jleave;
3587 rv = OKAY;
3589 xmb = *mp;
3590 xmb.mb_cache_directory = NULL;
3591 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3592 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3593 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3594 sizeof(xmb.mb_imap_delim));
3595 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3596 if (mp->mb_cache_directory != NULL)
3597 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3598 xmb.mb_uidvalidity = uidvalidity;
3599 initcache(&xmb);
3601 if (m == NULL) {
3602 memset(&xm, 0, sizeof xm);
3603 xm.m_uid = olduid;
3604 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3605 goto jleave;
3606 getcache(mp, &xm, NEED_HEADER);
3607 getcache(mp, &xm, NEED_BODY);
3608 } else {
3609 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3610 getcache(mp, m, NEED_HEADER);
3611 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3612 getcache(mp, m, NEED_BODY);
3613 xm = *m;
3615 xm.m_uid = newuid;
3616 xm.m_flag &= ~MFULLYCACHED;
3617 putcache(&xmb, &xm);
3618 jleave:
3619 if (xmb.mb_cache_directory != NULL)
3620 free(xmb.mb_cache_directory);
3621 if (xmb.mb_imap_mailbox != NULL)
3622 free(xmb.mb_imap_mailbox);
3623 if (xmb.mb_imap_pass != NULL)
3624 free(xmb.mb_imap_pass);
3625 if (xmb.mb_imap_account != NULL)
3626 free(xmb.mb_imap_account);
3627 NYD_LEAVE;
3628 return rv;
3631 static enum okay
3632 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3633 long size, long lines, int flag, const char *name)
3635 struct mailbox xmb;
3636 struct message xm;
3637 const char *cp;
3638 unsigned long uidvalidity, uid;
3639 enum okay rv;
3640 NYD_ENTER;
3642 rv = STOP;
3644 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3645 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3646 goto jleave;
3648 rv = OKAY;
3650 xmb = *mp;
3651 xmb.mb_cache_directory = NULL;
3652 /* XXX mb_imap_delim reused */
3653 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3654 xmb.mb_uidvalidity = uidvalidity;
3655 xmb.mb_otf = xmb.mb_itf = fp;
3656 initcache(&xmb);
3657 memset(&xm, 0, sizeof xm);
3658 xm.m_flag = (flag & MREAD) | MNEW;
3659 xm.m_time = t;
3660 xm.m_block = mailx_blockof(off1);
3661 xm.m_offset = mailx_offsetof(off1);
3662 xm.m_size = size;
3663 xm.m_xsize = xsize;
3664 xm.m_lines = xm.m_xlines = lines;
3665 xm.m_uid = uid;
3666 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3667 putcache(&xmb, &xm);
3669 free(xmb.mb_imap_mailbox);
3670 jleave:
3671 NYD_LEAVE;
3672 return rv;
3675 static enum okay
3676 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3678 FILE *tp = NULL;
3679 time_t t;
3680 long size, xsize, ysize, lines;
3681 enum mflag flag = MNEW;
3682 char *name, *buf, *bp;
3683 char const *cp;
3684 size_t bufsize, buflen, cnt;
3685 enum okay rv = STOP;
3686 NYD_ENTER;
3688 buf = smalloc(bufsize = LINESIZE);
3689 buflen = 0;
3690 cnt = fsize(fp);
3691 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3692 goto jstop;
3694 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3696 while (*bp == ' ')
3697 ++bp;
3699 if ((cp = strrchr(bp, '{')) == NULL)
3700 goto jstop;
3702 xsize = atol(&cp[1]) + 2;
3703 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3704 goto jstop;
3705 while (*cp == ' ')
3706 cp++;
3708 if (*cp == '(') {
3709 imap_getflags(cp, &cp, &flag);
3710 while (*++cp == ' ')
3713 t = imap_read_date_time(cp);
3715 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3716 == NULL)
3717 goto jstop;
3719 size = xsize;
3720 ysize = lines = 0;
3721 while (size > 0) {
3722 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3723 goto jstop;
3724 size -= buflen;
3725 buf[--buflen] = '\0';
3726 buf[buflen-1] = '\n';
3727 fwrite(buf, 1, buflen, tp);
3728 ysize += buflen;
3729 ++lines;
3731 fflush(tp);
3732 rewind(tp);
3734 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3735 imap_unquotestr(name));
3736 rv = OKAY;
3737 jstop:
3738 free(buf);
3739 if (tp)
3740 Fclose(tp);
3741 NYD_LEAVE;
3742 return rv;
3745 #ifdef HAVE_IMAP_SEARCH
3746 static enum okay
3747 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3748 int f)
3750 char *o, *xp, *cs, c;
3751 size_t osize;
3752 FILE *queuefp = NULL;
3753 int i;
3754 unsigned long n;
3755 const char *cp;
3756 enum okay ok = STOP;
3757 NYD_X;
3759 c = 0;
3760 for (cp = spec; *cp; cp++)
3761 c |= *cp;
3762 if (c & 0200) {
3763 cp = ok_vlook(ttycharset);
3764 # ifdef HAVE_ICONV
3765 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3766 iconv_t it;
3767 char *nsp, *nspec;
3768 size_t sz, nsz;
3770 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3771 sz = strlen(spec) + 1;
3772 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3773 if (n_iconv_buf(it, n_ICONV_DEFAULT,
3774 (char const**)&spec, &sz, &nsp, &nsz) == 0 &&
3775 sz == 0) {
3776 spec = nspec;
3777 cp = "utf-8";
3779 n_iconv_close(it);
3782 # endif
3783 cp = imap_quotestr(cp);
3784 cs = salloc(n = strlen(cp) + 10);
3785 snprintf(cs, n, "CHARSET %s ", cp);
3786 } else
3787 cs = n_UNCONST("");
3789 o = ac_alloc(osize = strlen(spec) + 60);
3790 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3791 IMAP_OUT(o, MB_COMD, goto out)
3792 while (mp->mb_active & MB_COMD) {
3793 ok = imap_answer(mp, 0);
3794 if (response_status == RESPONSE_OTHER &&
3795 response_other == MAILBOX_DATA_SEARCH) {
3796 xp = responded_other_text;
3797 while (*xp && *xp != '\r') {
3798 n = strtoul(xp, &xp, 10);
3799 for (i = 0; i < cnt; i++)
3800 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3801 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3802 mark(i+1, f);
3806 out:
3807 ac_free(o);
3808 return ok;
3811 FL enum okay
3812 imap_search1(const char * volatile spec, int f)
3814 sighandler_type saveint, savepipe;
3815 enum okay volatile rv = STOP;
3816 NYD_ENTER;
3818 if (mb.mb_type != MB_IMAP)
3819 goto jleave;
3821 imaplock = 1;
3822 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3823 safe_signal(SIGINT, &_imap_maincatch);
3824 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3825 if (sigsetjmp(imapjmp, 1) == 0) {
3826 if (savepipe != SIG_IGN)
3827 safe_signal(SIGPIPE, imapcatch);
3829 rv = imap_search2(&mb, message, msgCount, spec, f);
3831 safe_signal(SIGINT, saveint);
3832 safe_signal(SIGPIPE, savepipe);
3833 imaplock = 0;
3834 jleave:
3835 NYD_LEAVE;
3836 if (interrupts)
3837 n_go_onintr_for_imap();
3838 return rv;
3840 #endif /* HAVE_IMAP_SEARCH */
3842 FL int
3843 imap_thisaccount(const char *cp)
3845 int rv;
3846 NYD_ENTER;
3848 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3849 rv = 0;
3850 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3851 mb.mb_imap_account == NULL)
3852 rv = 0;
3853 else
3854 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3855 NYD_LEAVE;
3856 return rv;
3859 FL enum okay
3860 imap_remove(const char * volatile name)
3862 sighandler_type volatile saveint, savepipe;
3863 enum okay volatile rv = STOP;
3864 NYD_ENTER;
3866 if (mb.mb_type != MB_IMAP) {
3867 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3868 goto jleave;
3871 if (!imap_thisaccount(name)) {
3872 n_err(_("Can only remove mailboxes on current IMAP server: "
3873 "\"%s\" not removed\n"), name);
3874 goto jleave;
3877 imaplock = 1;
3878 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3879 safe_signal(SIGINT, &_imap_maincatch);
3880 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3881 if (sigsetjmp(imapjmp, 1) == 0) {
3882 if (savepipe != SIG_IGN)
3883 safe_signal(SIGPIPE, imapcatch);
3885 rv = imap_remove1(&mb, imap_fileof(name));
3887 safe_signal(SIGINT, saveint);
3888 safe_signal(SIGPIPE, savepipe);
3889 imaplock = 0;
3891 if (rv == OKAY)
3892 rv = cache_remove(name);
3893 jleave:
3894 NYD_LEAVE;
3895 if (interrupts)
3896 n_go_onintr_for_imap();
3897 return rv;
3900 static enum okay
3901 imap_remove1(struct mailbox *mp, const char *name)
3903 char *o;
3904 int os;
3905 char const *qname;
3906 FILE *queuefp;
3907 enum okay ok;
3908 NYD_X;
3910 ok = STOP;
3911 queuefp = NULL;
3913 if((qname = imap_path_quote(mp, name)) != NULL){
3914 o = ac_alloc(os = strlen(qname) + 100);
3915 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3916 IMAP_OUT(o, MB_COMD, goto out)
3917 while (mp->mb_active & MB_COMD)
3918 ok = imap_answer(mp, 1);
3919 out:
3920 ac_free(o);
3922 return ok;
3925 FL enum okay
3926 imap_rename(const char *old, const char *new)
3928 sighandler_type saveint, savepipe;
3929 enum okay volatile rv = STOP;
3930 NYD_ENTER;
3932 if (mb.mb_type != MB_IMAP) {
3933 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3934 goto jleave;
3937 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3938 n_err(_("Can only rename mailboxes on current IMAP "
3939 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3940 goto jleave;
3943 imaplock = 1;
3944 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3945 safe_signal(SIGINT, &_imap_maincatch);
3946 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3947 if (sigsetjmp(imapjmp, 1) == 0) {
3948 if (savepipe != SIG_IGN)
3949 safe_signal(SIGPIPE, imapcatch);
3951 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3953 safe_signal(SIGINT, saveint);
3954 safe_signal(SIGPIPE, savepipe);
3955 imaplock = 0;
3957 if (rv == OKAY)
3958 rv = cache_rename(old, new);
3959 jleave:
3960 NYD_LEAVE;
3961 if (interrupts)
3962 n_go_onintr_for_imap();
3963 return rv;
3966 static enum okay
3967 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3969 char *o;
3970 int os;
3971 char const *qoname, *qnname;
3972 FILE *queuefp;
3973 enum okay ok;
3974 NYD_X;
3976 ok = STOP;
3977 queuefp = NULL;
3979 if((qoname = imap_path_quote(mp, old)) != NULL &&
3980 (qnname = imap_path_quote(mp, new)) != NULL){
3981 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3982 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3983 IMAP_OUT(o, MB_COMD, goto out)
3984 while (mp->mb_active & MB_COMD)
3985 ok = imap_answer(mp, 1);
3986 out:
3987 ac_free(o);
3989 return ok;
3992 FL enum okay
3993 imap_dequeue(struct mailbox *mp, FILE *fp)
3995 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3996 size_t bufsize, buflen, cnt;
3997 long offs, offs1, offs2, octets;
3998 int twice, gotcha = 0;
3999 FILE *queuefp = NULL;
4000 enum okay ok = OKAY, rok = OKAY;
4001 NYD_X;
4003 buf = smalloc(bufsize = LINESIZE);
4004 buflen = 0;
4005 cnt = fsize(fp);
4006 while ((offs1 = ftell(fp)) >= 0 &&
4007 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
4008 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
4010 while (*bp == ' ')
4011 ++bp;
4012 twice = 0;
4013 if ((offs = ftell(fp)) < 0)
4014 goto fail;
4015 again:
4016 snprintf(o, sizeof o, "%s %s", tag(1), bp);
4017 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
4018 cp = &bp[9];
4019 while (digitchar(*cp))
4020 cp++;
4021 if (*cp != ' ')
4022 goto fail;
4023 while (*cp == ' ')
4024 cp++;
4025 if ((newname = imap_strex(cp, NULL)) == NULL)
4026 goto fail;
4027 IMAP_OUT(o, MB_COMD, continue)
4028 while (mp->mb_active & MB_COMD)
4029 ok = imap_answer(mp, twice);
4030 if (response_status == RESPONSE_NO && twice++ == 0)
4031 goto trycreate;
4032 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4033 imap_copyuid(mp, NULL, imap_unquotestr(newname));
4035 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
4036 IMAP_OUT(o, MB_COMD, continue)
4037 while (mp->mb_active & MB_COMD)
4038 ok = imap_answer(mp, 1);
4039 if (ok == OKAY)
4040 gotcha++;
4041 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
4042 if ((cp = strrchr(bp, '{')) == NULL)
4043 goto fail;
4044 octets = atol(&cp[1]) + 2;
4045 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
4046 goto fail;
4047 IMAP_OUT(o, MB_COMD, continue)
4048 while (mp->mb_active & MB_COMD) {
4049 ok = imap_answer(mp, twice);
4050 if (response_type == RESPONSE_CONT)
4051 break;
4053 if (ok == STOP) {
4054 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
4055 goto trycreate;
4056 goto fail;
4058 while (octets > 0) {
4059 size_t n = (UICMP(z, octets, >, sizeof iob)
4060 ? sizeof iob : (size_t)octets);
4061 octets -= n;
4062 if (n != fread(iob, 1, n, fp))
4063 goto fail;
4064 swrite1(&mp->mb_sock, iob, n, 1);
4066 swrite(&mp->mb_sock, "");
4067 while (mp->mb_active & MB_COMD) {
4068 ok = imap_answer(mp, 0);
4069 if (response_status == RESPONSE_NO && twice++ == 0) {
4070 if (fseek(fp, offs, SEEK_SET) < 0)
4071 goto fail;
4072 goto trycreate;
4075 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4076 if ((offs2 = ftell(fp)) < 0)
4077 goto fail;
4078 fseek(fp, offs1, SEEK_SET);
4079 if (imap_appenduid_cached(mp, fp) == STOP) {
4080 (void)fseek(fp, offs2, SEEK_SET);
4081 goto fail;
4084 } else {
4085 fail:
4086 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4087 rok = STOP;
4089 continue;
4090 trycreate:
4091 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4092 IMAP_OUT(o, MB_COMD, continue)
4093 while (mp->mb_active & MB_COMD)
4094 ok = imap_answer(mp, 1);
4095 if (ok == OKAY)
4096 goto again;
4098 fflush(fp);
4099 rewind(fp);
4100 ftruncate(fileno(fp), 0);
4101 if (gotcha)
4102 imap_close(mp);
4103 free(buf);
4104 return rok;
4107 static char *
4108 imap_strex(char const *cp, char const **xp)
4110 char const *cq;
4111 char *n = NULL;
4112 NYD_ENTER;
4114 if (*cp != '"')
4115 goto jleave;
4117 for (cq = cp + 1; *cq != '\0'; ++cq) {
4118 if (*cq == '\\')
4119 cq++;
4120 else if (*cq == '"')
4121 break;
4123 if (*cq != '"')
4124 goto jleave;
4126 n = salloc(cq - cp + 2);
4127 memcpy(n, cp, cq - cp +1);
4128 n[cq - cp + 1] = '\0';
4129 if (xp != NULL)
4130 *xp = cq + 1;
4131 jleave:
4132 NYD_LEAVE;
4133 return n;
4136 static enum okay
4137 check_expunged(void)
4139 enum okay rv;
4140 NYD_ENTER;
4142 if (expunged_messages > 0) {
4143 n_err(_("Command not executed - messages have been expunged\n"));
4144 rv = STOP;
4145 } else
4146 rv = OKAY;
4147 NYD_LEAVE;
4148 return rv;
4151 FL int
4152 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4154 struct url url;
4155 int rv, omsgCount = msgCount;
4156 NYD_ENTER;
4157 n_UNUSED(vp);
4159 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4160 n_err(_("Already connected\n"));
4161 rv = 1;
4162 goto jleave;
4165 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4166 rv = 1;
4167 goto jleave;
4169 ok_bclear(disconnected);
4170 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4172 if (mb.mb_type == MB_CACHE) {
4173 enum fedit_mode fm = FEDIT_NONE;
4174 if (_imap_rdonly)
4175 fm |= FEDIT_RDONLY;
4176 if (!(n_pstate & n_PS_EDIT))
4177 fm |= FEDIT_SYSBOX;
4178 _imap_setfile1(&url, fm, 1);
4179 if (msgCount > omsgCount)
4180 newmailinfo(omsgCount);
4182 rv = 0;
4183 jleave:
4184 NYD_LEAVE;
4185 return rv;
4188 FL int
4189 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4191 struct url url;
4192 int rv = 1, *msgvec = vp;
4193 NYD_ENTER;
4195 if (mb.mb_type == MB_CACHE) {
4196 n_err(_("Not connected\n"));
4197 goto jleave;
4199 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4200 n_err(_("The current mailbox is not cached\n"));
4201 goto jleave;
4204 if (!url_parse(&url, CPROTO_IMAP, mailname))
4205 goto jleave;
4207 if (*msgvec)
4208 c_cache(vp);
4209 ok_bset(disconnected);
4210 if (mb.mb_type == MB_IMAP) {
4211 enum fedit_mode fm = FEDIT_NONE;
4212 if (_imap_rdonly)
4213 fm |= FEDIT_RDONLY;
4214 if (!(n_pstate & n_PS_EDIT))
4215 fm |= FEDIT_SYSBOX;
4216 sclose(&mb.mb_sock);
4217 _imap_setfile1(&url, fm, 1);
4219 rv = 0;
4220 jleave:
4221 NYD_LEAVE;
4222 return rv;
4225 FL int
4226 c_cache(void *vp)
4228 int rv = 1, *msgvec = vp, *ip;
4229 struct message *mp;
4230 NYD_ENTER;
4232 if (mb.mb_type != MB_IMAP) {
4233 n_err(_("Not connected to an IMAP server\n"));
4234 goto jleave;
4236 if (cached_uidvalidity(&mb) == 0) {
4237 n_err(_("The current mailbox is not cached\n"));
4238 goto jleave;
4241 srelax_hold();
4242 for (ip = msgvec; *ip; ++ip) {
4243 mp = &message[*ip - 1];
4244 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4245 get_body(mp);
4246 srelax();
4249 srelax_rele();
4250 rv = 0;
4251 jleave:
4252 NYD_LEAVE;
4253 return rv;
4256 FL int
4257 disconnected(const char *file)
4259 struct url url;
4260 int rv = 1;
4261 NYD_ENTER;
4263 if (ok_blook(disconnected)) {
4264 rv = 1;
4265 goto jleave;
4268 if (!url_parse(&url, CPROTO_IMAP, file)) {
4269 rv = 0;
4270 goto jleave;
4272 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4274 jleave:
4275 NYD_LEAVE;
4276 return rv;
4279 FL void
4280 transflags(struct message *omessage, long omsgCount, int transparent)
4282 struct message *omp, *nmp, *newdot, *newprevdot;
4283 int hf;
4284 NYD_ENTER;
4286 omp = omessage;
4287 nmp = message;
4288 newdot = message;
4289 newprevdot = NULL;
4290 while (PTRCMP(omp, <, omessage + omsgCount) &&
4291 PTRCMP(nmp, <, message + msgCount)) {
4292 if (dot && nmp->m_uid == dot->m_uid)
4293 newdot = nmp;
4294 if (prevdot && nmp->m_uid == prevdot->m_uid)
4295 newprevdot = nmp;
4296 if (omp->m_uid == nmp->m_uid) {
4297 hf = nmp->m_flag & MHIDDEN;
4298 if (transparent && mb.mb_type == MB_IMAP)
4299 omp->m_flag &= ~MHIDDEN;
4300 *nmp++ = *omp++;
4301 if (transparent && mb.mb_type == MB_CACHE)
4302 nmp[-1].m_flag |= hf;
4303 } else if (omp->m_uid < nmp->m_uid)
4304 ++omp;
4305 else
4306 ++nmp;
4308 dot = newdot;
4309 setdot(newdot);
4310 prevdot = newprevdot;
4311 free(omessage);
4312 NYD_LEAVE;
4315 FL time_t
4316 imap_read_date_time(const char *cp)
4318 char buf[3];
4319 time_t t;
4320 int i, year, month, day, hour, minute, second, sign = -1;
4321 NYD2_ENTER;
4323 /* "25-Jul-2004 15:33:44 +0200"
4324 * | | | | | |
4325 * 0 5 10 15 20 25 */
4326 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4327 goto jinvalid;
4328 day = strtol(&cp[1], NULL, 10);
4329 for (i = 0;;) {
4330 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4331 break;
4332 if (n_month_names[++i][0] == '\0')
4333 goto jinvalid;
4335 month = i + 1;
4336 year = strtol(&cp[8], NULL, 10);
4337 hour = strtol(&cp[13], NULL, 10);
4338 minute = strtol(&cp[16], NULL, 10);
4339 second = strtol(&cp[19], NULL, 10);
4340 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4341 goto jinvalid;
4342 switch (cp[22]) {
4343 case '-':
4344 sign = 1;
4345 break;
4346 case '+':
4347 break;
4348 default:
4349 goto jinvalid;
4351 buf[2] = '\0';
4352 buf[0] = cp[23];
4353 buf[1] = cp[24];
4354 t += strtol(buf, NULL, 10) * sign * 3600;
4355 buf[0] = cp[25];
4356 buf[1] = cp[26];
4357 t += strtol(buf, NULL, 10) * sign * 60;
4358 jleave:
4359 NYD2_LEAVE;
4360 return t;
4361 jinvalid:
4362 time(&t);
4363 goto jleave;
4366 FL const char *
4367 imap_make_date_time(time_t t)
4369 static char s[40];
4370 struct tm *tmptr;
4371 int tzdiff, tzdiff_hour, tzdiff_min;
4372 NYD2_ENTER;
4374 tzdiff = t - mktime(gmtime(&t));
4375 tzdiff_hour = (int)(tzdiff / 60);
4376 tzdiff_min = tzdiff_hour % 60;
4377 tzdiff_hour /= 60;
4378 tmptr = localtime(&t);
4379 if (tmptr->tm_isdst > 0)
4380 tzdiff_hour++;
4381 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4382 tmptr->tm_mday, n_month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4383 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4384 NYD2_LEAVE;
4385 return s;
4388 FL char *
4389 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4391 char *n, *np;
4392 NYD2_ENTER;
4394 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4395 n_MEMORY_DEBUG_ARGSCALL);
4397 /* Just ignore the `is-system-mailbox' prefix XXX */
4398 if (cp[0] == '%' && cp[1] == ':')
4399 cp += 2;
4401 while (*cp != '\0') {
4402 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4403 *np++ = *cp++;
4404 *np++ = *cp++;
4405 *np++ = *cp++;
4406 } else if (cp[0] == '/')
4407 break;
4408 else
4409 *np++ = *cp++;
4411 *np = '\0';
4412 NYD2_LEAVE;
4413 return n;
4415 #endif /* HAVE_IMAP */
4417 /* s-it-mode */