gen-okeys.h: regenerate
[s-mailx.git] / obs-imap.c
blob52fed33e29c0d952fa2cd6b54687f3867765d730
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE obs_imap
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_IMAP
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 EMPTY_FILE()
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() \
62 if (mp->mb_type != MB_CACHE) {\
63 enum okay ok = OKAY;\
64 while (mp->mb_active & MB_COMD)\
65 ok = imap_answer(mp, 1);\
66 if (ok == STOP)\
67 return STOP;\
71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
72 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
73 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
74 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
75 * TODO I.e., that should be a function, not a macro ... or so.
76 * TODO This entire module needs MASSIVE work! */
77 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
80 if (mp->mb_type != MB_CACHE) {\
81 if (imap_finish(mp) == STOP) {\
82 ACTIONBAIL;\
84 if (n_poption & n_PO_VERBVERB)\
85 n_err(">>> %s", X);\
86 mp->mb_active |= Y;\
87 if (swrite(&mp->mb_sock, X) == STOP) {\
88 ACTIONERR;\
90 } else {\
91 if (queuefp != NULL)\
92 fputs(X, queuefp);\
96 static struct record {
97 struct record *rec_next;
98 unsigned long rec_count;
99 enum rec_type {
100 REC_EXISTS,
101 REC_EXPUNGE
102 } rec_type;
103 } *record, *recend;
105 static enum {
106 RESPONSE_TAGGED,
107 RESPONSE_DATA,
108 RESPONSE_FATAL,
109 RESPONSE_CONT,
110 RESPONSE_ILLEGAL
111 } response_type;
113 static enum {
114 RESPONSE_OK,
115 RESPONSE_NO,
116 RESPONSE_BAD,
117 RESPONSE_PREAUTH,
118 RESPONSE_BYE,
119 RESPONSE_OTHER,
120 RESPONSE_UNKNOWN
121 } response_status;
123 static char *responded_tag;
124 static char *responded_text;
125 static char *responded_other_text;
126 static long responded_other_number;
128 static enum {
129 MAILBOX_DATA_FLAGS,
130 MAILBOX_DATA_LIST,
131 MAILBOX_DATA_LSUB,
132 MAILBOX_DATA_MAILBOX,
133 MAILBOX_DATA_SEARCH,
134 MAILBOX_DATA_STATUS,
135 MAILBOX_DATA_EXISTS,
136 MAILBOX_DATA_RECENT,
137 MESSAGE_DATA_EXPUNGE,
138 MESSAGE_DATA_FETCH,
139 CAPABILITY_DATA,
140 RESPONSE_OTHER_UNKNOWN
141 } response_other;
143 static enum list_attributes {
144 LIST_NONE = 000,
145 LIST_NOINFERIORS = 001,
146 LIST_NOSELECT = 002,
147 LIST_MARKED = 004,
148 LIST_UNMARKED = 010
149 } list_attributes;
151 static int list_hierarchy_delimiter;
152 static char *list_name;
154 struct list_item {
155 struct list_item *l_next;
156 char *l_name;
157 char *l_base;
158 enum list_attributes l_attr;
159 int l_delim;
160 int l_level;
161 int l_has_children;
164 static char *imapbuf; /* TODO not static, use pool */
165 static size_t imapbufsize;
166 static sigjmp_buf imapjmp;
167 static sighandler_type savealrm;
168 static int imapkeepalive;
169 static long had_exists = -1;
170 static long had_expunge = -1;
171 static long expunged_messages;
172 static int volatile imaplock;
173 static int same_imap_account;
174 static bool_t _imap_rdonly;
176 static char *imap_quotestr(char const *s);
177 static char *imap_unquotestr(char const *s);
178 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
179 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
180 /* Returns NULL on error */
181 static char *imap_path_quote(struct mailbox *mp, char const *cp);
182 static void imap_other_get(char *pp);
183 static void imap_response_get(const char **cp);
184 static void imap_response_parse(void);
185 static enum okay imap_answer(struct mailbox *mp, int errprnt);
186 static enum okay imap_parse_list(void);
187 static enum okay imap_finish(struct mailbox *mp);
188 static void imap_timer_off(void);
189 static void imapcatch(int s);
190 static void _imap_maincatch(int s);
191 static enum okay imap_noop1(struct mailbox *mp);
192 static void rec_queue(enum rec_type type, unsigned long cnt);
193 static enum okay rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s);
196 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
197 static enum okay imap_capability(struct mailbox *mp);
198 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
199 #ifdef HAVE_MD5
200 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
201 #endif
202 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
203 #ifdef HAVE_GSSAPI
204 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
205 #endif
206 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
207 static void imap_init(struct mailbox *mp, int n);
208 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
209 int *prevcount);
210 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
211 struct url *urlp);
212 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
213 int transparent);
214 static int imap_fetchdata(struct mailbox *mp, struct message *m,
215 size_t expected, int need, const char *head,
216 size_t headsize, long headlines);
217 static void imap_putstr(struct mailbox *mp, struct message *m,
218 const char *str, const char *head, size_t headsize,
219 long headlines);
220 static enum okay imap_get(struct mailbox *mp, struct message *m,
221 enum needspec need);
222 static void commitmsg(struct mailbox *mp, struct message *to,
223 struct message *from, enum content_info content_info);
224 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
225 int bot, int top);
226 static enum okay imap_exit(struct mailbox *mp);
227 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
228 int needstat);
229 static enum okay imap_close(struct mailbox *mp);
230 static enum okay imap_update(struct mailbox *mp);
231 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
232 int c, const char *sp, int needstat);
233 static enum okay imap_unstore(struct message *m, int n, const char *flag);
234 static const char *tag(int new);
235 static char * imap_putflags(int f);
236 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
237 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
238 off_t off1, long xsize, enum mflag flag, time_t t);
239 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
240 long offset);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend,
243 int level);
244 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
245 FILE *fp);
246 static 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 + 1)
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 complete |= 2;
992 if (errprnt)
993 n_err(_("IMAP error: %s"), responded_text);
994 break;
995 case RESPONSE_UNKNOWN: /* does not happen */
996 case RESPONSE_BYE:
997 i = mp->mb_active;
998 mp->mb_active = MB_NONE;
999 if (i & MB_BYE)
1000 goto jokay;
1001 goto jstop;
1002 case RESPONSE_OTHER:
1003 rv = OKAY;
1004 break;
1006 if (response_status != RESPONSE_OTHER &&
1007 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
1008 n_err(_("IMAP alert: %s"), &responded_text[8]);
1009 if (complete == 3)
1010 mp->mb_active &= ~MB_COMD;
1011 } else
1012 mp->mb_active = MB_NONE;
1013 jleave:
1014 NYD2_LEAVE;
1015 return rv;
1018 static enum okay
1019 imap_parse_list(void)
1021 char *cp;
1022 enum okay rv;
1023 NYD2_ENTER;
1025 rv = STOP;
1027 cp = responded_other_text;
1028 list_attributes = LIST_NONE;
1029 if (*cp == '(') {
1030 while (*cp && *cp != ')') {
1031 if (*cp == '\\') {
1032 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1033 list_attributes |= LIST_NOINFERIORS;
1034 cp += 12;
1035 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1036 list_attributes |= LIST_NOSELECT;
1037 cp += 9;
1038 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1039 list_attributes |= LIST_MARKED;
1040 cp += 7;
1041 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1042 list_attributes |= LIST_UNMARKED;
1043 cp += 9;
1046 cp++;
1048 if (*++cp != ' ')
1049 goto jleave;
1050 while (*cp == ' ')
1051 cp++;
1054 list_hierarchy_delimiter = EOF;
1055 if (*cp == '"') {
1056 if (*++cp == '\\')
1057 cp++;
1058 list_hierarchy_delimiter = *cp++ & 0377;
1059 if (cp[0] != '"' || cp[1] != ' ')
1060 goto jleave;
1061 cp++;
1062 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1063 list_hierarchy_delimiter = EOF;
1064 cp += 3;
1067 while (*cp == ' ')
1068 cp++;
1069 list_name = cp;
1070 while (*cp && *cp != '\r')
1071 cp++;
1072 *cp = '\0';
1073 rv = OKAY;
1074 jleave:
1075 NYD2_LEAVE;
1076 return rv;
1079 static enum okay
1080 imap_finish(struct mailbox *mp)
1082 NYD_ENTER;
1083 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1084 imap_answer(mp, 1);
1085 NYD_LEAVE;
1086 return OKAY;
1089 static void
1090 imap_timer_off(void)
1092 NYD_ENTER;
1093 if (imapkeepalive > 0) {
1094 alarm(0);
1095 safe_signal(SIGALRM, savealrm);
1097 NYD_LEAVE;
1100 static void
1101 imapcatch(int s)
1103 NYD_X; /* Signal handler */
1104 switch (s) {
1105 case SIGINT:
1106 n_err_sighdl(_("Interrupt\n"));
1107 siglongjmp(imapjmp, 1);
1108 /*NOTREACHED*/
1109 case SIGPIPE:
1110 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1111 break;
1115 static void
1116 _imap_maincatch(int s)
1118 NYD_X; /* Signal handler */
1119 n_UNUSED(s);
1120 if (interrupts++ == 0) {
1121 n_err_sighdl(_("Interrupt\n"));
1122 return;
1124 n_go_onintr_for_imap();
1127 static enum okay
1128 imap_noop1(struct mailbox *mp)
1130 char o[LINESIZE];
1131 FILE *queuefp = NULL;
1132 NYD_X;
1134 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1135 IMAP_OUT(o, MB_COMD, return STOP)
1136 IMAP_ANSWER()
1137 return OKAY;
1140 FL char const *
1141 imap_fileof(char const *xcp)
1143 char const *cp = xcp;
1144 int state = 0;
1145 NYD_ENTER;
1147 while (*cp) {
1148 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1149 cp += 3;
1150 state = 1;
1152 if (cp[0] == '/' && state == 1) {
1153 ++cp;
1154 goto jleave;
1156 if (cp[0] == '/') {
1157 cp = xcp;
1158 goto jleave;
1160 ++cp;
1162 jleave:
1163 NYD_LEAVE;
1164 return cp;
1167 FL enum okay
1168 imap_noop(void)
1170 sighandler_type volatile oldint, oldpipe;
1171 enum okay volatile rv = STOP;
1172 NYD_ENTER;
1174 if (mb.mb_type != MB_IMAP)
1175 goto jleave;
1177 imaplock = 1;
1178 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1179 safe_signal(SIGINT, &_imap_maincatch);
1180 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1181 if (sigsetjmp(imapjmp, 1) == 0) {
1182 if (oldpipe != SIG_IGN)
1183 safe_signal(SIGPIPE, imapcatch);
1185 rv = imap_noop1(&mb);
1187 safe_signal(SIGINT, oldint);
1188 safe_signal(SIGPIPE, oldpipe);
1189 imaplock = 0;
1190 jleave:
1191 NYD_LEAVE;
1192 if (interrupts)
1193 n_go_onintr_for_imap();
1194 return rv;
1197 static void
1198 rec_queue(enum rec_type rt, unsigned long cnt)
1200 struct record *rp;
1201 NYD_ENTER;
1203 rp = scalloc(1, sizeof *rp);
1204 rp->rec_type = rt;
1205 rp->rec_count = cnt;
1206 if (record && recend) {
1207 recend->rec_next = rp;
1208 recend = rp;
1209 } else
1210 record = recend = rp;
1211 NYD_LEAVE;
1214 static enum okay
1215 rec_dequeue(void)
1217 struct message *omessage;
1218 struct record *rp, *rq;
1219 uiz_t exists = 0, i;
1220 enum okay rv = STOP;
1221 NYD_ENTER;
1223 if (record == NULL)
1224 goto jleave;
1226 omessage = message;
1227 message = smalloc((msgCount+1) * sizeof *message);
1228 if (msgCount)
1229 memcpy(message, omessage, msgCount * sizeof *message);
1230 memset(&message[msgCount], 0, sizeof *message);
1232 rp = record, rq = NULL;
1233 rv = OKAY;
1234 while (rp != NULL) {
1235 switch (rp->rec_type) {
1236 case REC_EXISTS:
1237 exists = rp->rec_count;
1238 break;
1239 case REC_EXPUNGE:
1240 if (rp->rec_count == 0) {
1241 rv = STOP;
1242 break;
1244 if (rp->rec_count > (unsigned long)msgCount) {
1245 if (exists == 0 || rp->rec_count > exists--)
1246 rv = STOP;
1247 break;
1249 if (exists > 0)
1250 exists--;
1251 delcache(&mb, &message[rp->rec_count-1]);
1252 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1253 ((msgCount - rp->rec_count + 1) * sizeof *message));
1254 --msgCount;
1255 /* If the message was part of a collapsed thread,
1256 * the m_collapsed field of one of its ancestors
1257 * should be incremented. It seems hardly possible
1258 * to do this with the current message structure,
1259 * though. The result is that a '+' may be shown
1260 * in the header summary even if no collapsed
1261 * children exists */
1262 break;
1264 if (rq != NULL)
1265 free(rq);
1266 rq = rp;
1267 rp = rp->rec_next;
1269 if (rq != NULL)
1270 free(rq);
1272 record = recend = NULL;
1273 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1274 message = srealloc(message, (exists + 1) * sizeof *message);
1275 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1276 for (i = msgCount; i < exists; ++i)
1277 imap_init(&mb, i);
1278 imap_flags(&mb, msgCount+1, exists);
1279 msgCount = exists;
1282 if (rv == STOP) {
1283 free(message);
1284 message = omessage;
1286 jleave:
1287 NYD_LEAVE;
1288 return rv;
1291 static void
1292 rec_rmqueue(void)
1294 struct record *rp;
1295 NYD_ENTER;
1297 for (rp = record; rp != NULL;) {
1298 struct record *tmp = rp;
1299 rp = rp->rec_next;
1300 free(tmp);
1302 record = recend = NULL;
1303 NYD_LEAVE;
1306 /*ARGSUSED*/
1307 static void
1308 imapalarm(int s)
1310 sighandler_type volatile saveint, savepipe;
1311 NYD_X; /* Signal handler */
1312 n_UNUSED(s);
1314 if (imaplock++ == 0) {
1315 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1316 safe_signal(SIGINT, &_imap_maincatch);
1317 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1318 if (sigsetjmp(imapjmp, 1)) {
1319 safe_signal(SIGINT, saveint);
1320 safe_signal(SIGPIPE, savepipe);
1321 goto jbrk;
1323 if (savepipe != SIG_IGN)
1324 safe_signal(SIGPIPE, imapcatch);
1325 if (imap_noop1(&mb) != OKAY) {
1326 safe_signal(SIGINT, saveint);
1327 safe_signal(SIGPIPE, savepipe);
1328 goto jleave;
1330 safe_signal(SIGINT, saveint);
1331 safe_signal(SIGPIPE, savepipe);
1333 jbrk:
1334 alarm(imapkeepalive);
1335 jleave:
1336 --imaplock;
1339 static enum okay
1340 imap_preauth(struct mailbox *mp, struct url const *urlp)
1342 NYD_X;
1344 mp->mb_active |= MB_PREAUTH;
1345 imap_answer(mp, 1);
1347 #ifdef HAVE_SSL
1348 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1349 FILE *queuefp = NULL;
1350 char o[LINESIZE];
1352 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1353 IMAP_OUT(o, MB_COMD, return STOP)
1354 IMAP_ANSWER()
1355 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1356 return STOP;
1358 #else
1359 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1360 n_err(_("No SSL support compiled in\n"));
1361 return STOP;
1363 #endif
1365 imap_capability(mp);
1366 return OKAY;
1369 static enum okay
1370 imap_capability(struct mailbox *mp)
1372 char o[LINESIZE];
1373 FILE *queuefp = NULL;
1374 enum okay ok = STOP;
1375 const char *cp;
1376 NYD_X;
1378 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1379 IMAP_OUT(o, MB_COMD, return STOP)
1380 while (mp->mb_active & MB_COMD) {
1381 ok = imap_answer(mp, 0);
1382 if (response_status == RESPONSE_OTHER &&
1383 response_other == CAPABILITY_DATA) {
1384 cp = responded_other_text;
1385 while (*cp) {
1386 while (spacechar(*cp))
1387 ++cp;
1388 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1389 /* RFC 2359 */
1390 mp->mb_flags |= MB_UIDPLUS;
1391 while (*cp && !spacechar(*cp))
1392 ++cp;
1396 return ok;
1399 static enum okay
1400 imap_auth(struct mailbox *mp, struct ccred *ccred)
1402 enum okay rv;
1403 NYD_ENTER;
1405 if (!(mp->mb_active & MB_PREAUTH)) {
1406 rv = OKAY;
1407 goto jleave;
1410 switch (ccred->cc_authtype) {
1411 case AUTHTYPE_LOGIN:
1412 rv = imap_login(mp, ccred);
1413 break;
1414 #ifdef HAVE_MD5
1415 case AUTHTYPE_CRAM_MD5:
1416 rv = imap_cram_md5(mp, ccred);
1417 break;
1418 #endif
1419 #ifdef HAVE_GSSAPI
1420 case AUTHTYPE_GSSAPI:
1421 rv = _imap_gssapi(mp, ccred);
1422 break;
1423 #endif
1424 default:
1425 rv = STOP;
1426 break;
1428 jleave:
1429 NYD_LEAVE;
1430 return rv;
1433 #ifdef HAVE_MD5
1434 static enum okay
1435 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1437 char o[LINESIZE], *cp;
1438 FILE *queuefp = NULL;
1439 enum okay rv = STOP;
1440 NYD_ENTER;
1442 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1443 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1444 imap_answer(mp, 1);
1445 if (response_type != RESPONSE_CONT)
1446 goto jleave;
1448 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1449 if(cp == NULL)
1450 goto jleave;
1451 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1452 while (mp->mb_active & MB_COMD)
1453 rv = imap_answer(mp, 1);
1454 jleave:
1455 NYD_LEAVE;
1456 return rv;
1458 #endif /* HAVE_MD5 */
1460 static enum okay
1461 imap_login(struct mailbox *mp, struct ccred *ccred)
1463 char o[LINESIZE];
1464 FILE *queuefp = NULL;
1465 enum okay rv = STOP;
1466 NYD_ENTER;
1468 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1469 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1470 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1471 while (mp->mb_active & MB_COMD)
1472 rv = imap_answer(mp, 1);
1473 jleave:
1474 NYD_LEAVE;
1475 return rv;
1478 #ifdef HAVE_GSSAPI
1479 # include "obs-imap-gssapi.h"
1480 #endif
1482 FL enum okay
1483 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1484 enum fedit_mode fm)
1486 char o[LINESIZE];
1487 char const *qname, *cp;
1488 FILE *queuefp;
1489 enum okay ok;
1490 NYD_X;
1491 n_UNUSED(size);
1493 ok = STOP;
1494 queuefp = NULL;
1496 if((qname = imap_path_quote(mp, mbx)) == NULL)
1497 goto jleave;
1499 ok = OKAY;
1501 mp->mb_uidvalidity = 0;
1502 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1503 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1504 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1505 while (mp->mb_active & MB_COMD) {
1506 ok = imap_answer(mp, 1);
1507 if (response_status != RESPONSE_OTHER &&
1508 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1509 mp->mb_uidvalidity = atol(&cp[13]);
1511 *cnt = (had_exists > 0) ? had_exists : 0;
1512 if (response_status != RESPONSE_OTHER &&
1513 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1514 mp->mb_perm = 0;
1515 jleave:
1516 return ok;
1519 static enum okay
1520 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1522 char o[LINESIZE];
1523 FILE *queuefp = NULL;
1524 char const *cp;
1525 struct message *m;
1526 unsigned x = X, y = Y, n;
1527 NYD_X;
1529 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1530 IMAP_OUT(o, MB_COMD, return STOP)
1531 while (mp->mb_active & MB_COMD) {
1532 imap_answer(mp, 1);
1533 if (response_status == RESPONSE_OTHER &&
1534 response_other == MESSAGE_DATA_FETCH) {
1535 n = responded_other_number;
1536 if (n < x || n > y)
1537 continue;
1538 m = &message[n-1];
1539 m->m_xsize = 0;
1540 } else
1541 continue;
1543 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1544 cp += 5;
1545 while (*cp == ' ')
1546 cp++;
1547 if (*cp == '(')
1548 imap_getflags(cp, &cp, &m->m_flag);
1551 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1552 m->m_uid = strtoul(&cp[4], NULL, 10);
1553 getcache1(mp, m, NEED_UNSPEC, 1);
1554 m->m_flag &= ~MHIDDEN;
1557 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1558 x++;
1559 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1560 y--;
1561 if (x <= y) {
1562 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1563 tag(1), x, y);
1564 IMAP_OUT(o, MB_COMD, return STOP)
1565 while (mp->mb_active & MB_COMD) {
1566 imap_answer(mp, 1);
1567 if (response_status == RESPONSE_OTHER &&
1568 response_other == MESSAGE_DATA_FETCH) {
1569 n = responded_other_number;
1570 if (n < x || n > y)
1571 continue;
1572 m = &message[n-1];
1573 } else
1574 continue;
1575 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1576 m->m_xsize = strtol(&cp[12], NULL, 10);
1577 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1578 m->m_time = imap_read_date_time(&cp[13]);
1582 srelax_hold();
1583 for (n = X; n <= Y; ++n) {
1584 putcache(mp, &message[n-1]);
1585 srelax();
1587 srelax_rele();
1588 return OKAY;
1591 static void
1592 imap_init(struct mailbox *mp, int n)
1594 struct message *m;
1595 NYD_ENTER;
1596 n_UNUSED(mp);
1598 m = message + n;
1599 m->m_flag = MUSED | MNOFROM;
1600 m->m_block = 0;
1601 m->m_offset = 0;
1602 NYD_LEAVE;
1605 static void
1606 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1608 struct message *omessage = 0;
1609 int i, omsgCount = 0;
1610 enum okay dequeued = STOP;
1611 NYD_ENTER;
1613 if (nmail || transparent) {
1614 omessage = message;
1615 omsgCount = msgCount;
1617 if (nmail)
1618 dequeued = rec_dequeue();
1620 if (had_exists >= 0) {
1621 if (dequeued != OKAY)
1622 msgCount = had_exists;
1623 had_exists = -1;
1625 if (had_expunge >= 0) {
1626 if (dequeued != OKAY)
1627 msgCount -= had_expunge;
1628 had_expunge = -1;
1631 if (nmail && expunged_messages)
1632 printf("Expunged %ld message%s.\n", expunged_messages,
1633 (expunged_messages != 1 ? "s" : ""));
1634 *prevcount = omsgCount - expunged_messages;
1635 expunged_messages = 0;
1636 if (msgCount < 0) {
1637 fputs("IMAP error: Negative message count\n", stderr);
1638 msgCount = 0;
1641 if (dequeued != OKAY) {
1642 message = scalloc(msgCount + 1, sizeof *message);
1643 for (i = 0; i < msgCount; i++)
1644 imap_init(mp, i);
1645 if (!nmail && mp->mb_type == MB_IMAP)
1646 initcache(mp);
1647 if (msgCount > 0)
1648 imap_flags(mp, 1, msgCount);
1649 message[msgCount].m_size = 0;
1650 message[msgCount].m_lines = 0;
1651 rec_rmqueue();
1653 if (nmail || transparent)
1654 transflags(omessage, omsgCount, transparent);
1655 else
1656 setdot(message);
1657 NYD_LEAVE;
1660 FL int
1661 imap_setfile(const char *xserver, enum fedit_mode fm)
1663 struct url url;
1664 int rv;
1665 NYD_ENTER;
1667 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1668 rv = 1;
1669 goto jleave;
1671 if (!ok_blook(v15_compat) &&
1672 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1673 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1675 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1676 rv = _imap_setfile1(&url, fm, 0);
1677 jleave:
1678 NYD_LEAVE;
1679 return rv;
1682 static bool_t
1683 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1685 bool_t rv = FAL0;
1686 NYD_ENTER;
1688 if (ok_blook(v15_compat))
1689 rv = ccred_lookup(ccredp, urlp);
1690 else {
1691 char *var, *old,
1692 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1693 : urlp->url_u_h_p.s);
1695 if ((var = mbp->mb_imap_pass) != NULL) {
1696 var = savecat("password-", xuhp);
1697 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1698 old = sstrdup(old);
1699 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1701 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1702 if (var != NULL) {
1703 if (old != NULL) {
1704 n_var_vset(var, (uintptr_t)old);
1705 free(old);
1706 } else
1707 n_var_vclear(var);
1711 NYD_LEAVE;
1712 return rv;
1715 static int
1716 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1717 int volatile transparent)
1719 struct sock so;
1720 struct ccred ccred;
1721 sighandler_type volatile saveint, savepipe;
1722 char const *cp;
1723 int rv;
1724 int volatile prevcount = 0;
1725 enum mbflags same_flags;
1726 NYD_ENTER;
1728 if (fm & FEDIT_NEWMAIL) {
1729 saveint = safe_signal(SIGINT, SIG_IGN);
1730 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1731 if (saveint != SIG_IGN)
1732 safe_signal(SIGINT, imapcatch);
1733 if (savepipe != SIG_IGN)
1734 safe_signal(SIGPIPE, imapcatch);
1735 imaplock = 1;
1736 goto jnmail;
1739 same_flags = mb.mb_flags;
1740 same_imap_account = 0;
1741 if (mb.mb_imap_account != NULL &&
1742 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1743 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1744 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1745 disconnected(mb.mb_imap_account) == 0) {
1746 same_imap_account = 1;
1747 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1749 goto jduppass;
1750 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1751 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1752 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1753 jduppass:
1755 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1759 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1760 free(mb.mb_imap_pass);
1761 mb.mb_imap_pass = NULL;
1763 if (!_imap_getcred(&mb, &ccred, urlp)) {
1764 rv = -1;
1765 goto jleave;
1768 memset(&so, 0, sizeof so);
1769 so.s_fd = -1;
1770 if (!same_imap_account) {
1771 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1772 rv = -1;
1773 goto jleave;
1775 } else
1776 so = mb.mb_sock;
1777 if (!transparent) {
1778 if(!quit(FAL0)){
1779 rv = -1;
1780 goto jleave;
1784 if (fm & FEDIT_SYSBOX)
1785 n_pstate &= ~n_PS_EDIT;
1786 else
1787 n_pstate |= n_PS_EDIT;
1788 if (mb.mb_imap_account != NULL)
1789 free(mb.mb_imap_account);
1790 if (mb.mb_imap_pass != NULL)
1791 free(mb.mb_imap_pass);
1792 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1793 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1794 * TODO object, and mailbox will naturally have an URL and credentials */
1795 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1797 if (!same_imap_account) {
1798 if (mb.mb_sock.s_fd >= 0)
1799 sclose(&mb.mb_sock);
1801 same_imap_account = 0;
1803 if (!transparent) {
1804 if (mb.mb_itf) {
1805 fclose(mb.mb_itf);
1806 mb.mb_itf = NULL;
1808 if (mb.mb_otf) {
1809 fclose(mb.mb_otf);
1810 mb.mb_otf = NULL;
1812 if (mb.mb_imap_mailbox != NULL)
1813 free(mb.mb_imap_mailbox);
1814 assert(urlp->url_path.s != NULL);
1815 imap_delim_init(&mb, urlp);
1816 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1817 initbox(savecatsep(urlp->url_p_eu_h_p,
1818 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1819 mb.mb_imap_mailbox));
1821 mb.mb_type = MB_VOID;
1822 mb.mb_active = MB_NONE;
1824 imaplock = 1;
1825 saveint = safe_signal(SIGINT, SIG_IGN);
1826 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1827 if (sigsetjmp(imapjmp, 1)) {
1828 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1829 sclose(&mb.mb_sock);
1830 safe_signal(SIGINT, saveint);
1831 safe_signal(SIGPIPE, savepipe);
1832 imaplock = 0;
1834 mb.mb_type = MB_VOID;
1835 mb.mb_active = MB_NONE;
1836 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1837 goto jleave;
1839 if (saveint != SIG_IGN)
1840 safe_signal(SIGINT, imapcatch);
1841 if (savepipe != SIG_IGN)
1842 safe_signal(SIGPIPE, imapcatch);
1844 if (mb.mb_sock.s_fd < 0) {
1845 if (disconnected(mb.mb_imap_account)) {
1846 if (cache_setptr(fm, transparent) == STOP)
1847 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1848 goto jdone;
1850 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1851 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1852 savealrm = safe_signal(SIGALRM, imapalarm);
1853 alarm(imapkeepalive);
1857 mb.mb_sock = so;
1858 mb.mb_sock.s_desc = "IMAP";
1859 mb.mb_sock.s_onclose = imap_timer_off;
1860 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1861 sclose(&mb.mb_sock);
1862 imap_timer_off();
1863 safe_signal(SIGINT, saveint);
1864 safe_signal(SIGPIPE, savepipe);
1865 imaplock = 0;
1866 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1867 goto jleave;
1869 } else /* same account */
1870 mb.mb_flags |= same_flags;
1872 if (n_poption & n_PO_R_FLAG)
1873 fm |= FEDIT_RDONLY;
1874 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1875 mb.mb_type = MB_IMAP;
1876 cache_dequeue(&mb);
1877 assert(urlp->url_path.s != NULL);
1878 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1879 /*sclose(&mb.mb_sock);
1880 imap_timer_off();*/
1881 safe_signal(SIGINT, saveint);
1882 safe_signal(SIGPIPE, savepipe);
1883 imaplock = 0;
1884 mb.mb_type = MB_VOID;
1885 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1886 goto jleave;
1889 jnmail:
1890 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1891 n_UNVOLATILE(&prevcount));
1892 jdone:
1893 setmsize(msgCount);
1894 safe_signal(SIGINT, saveint);
1895 safe_signal(SIGPIPE, savepipe);
1896 imaplock = 0;
1898 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1899 purgecache(&mb, message, msgCount);
1900 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1901 mb.mb_threaded = 0;
1902 c_sort((void*)-1);
1905 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1906 n_pstate &= ~n_PS_SAW_COMMAND;
1907 n_pstate |= n_PS_SETFILE_OPENED;
1910 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1911 mb.mb_type == MB_CACHE)) {
1912 rv = (msgCount == 0);
1913 goto jleave;
1916 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1917 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1918 !ok_blook(emptystart))
1919 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1920 rv = 1;
1921 goto jleave;
1924 if (fm & FEDIT_NEWMAIL)
1925 newmailinfo(prevcount);
1926 rv = 0;
1927 jleave:
1928 NYD_LEAVE;
1929 return rv;
1932 static int
1933 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1934 int need, const char *head, size_t headsize, long headlines)
1936 char *line = NULL, *lp;
1937 size_t linesize = 0, linelen, size = 0;
1938 int emptyline = 0, lines = 0, excess = 0;
1939 off_t offset;
1940 NYD_ENTER;
1942 fseek(mp->mb_otf, 0L, SEEK_END);
1943 offset = ftell(mp->mb_otf);
1945 if (head)
1946 fwrite(head, 1, headsize, mp->mb_otf);
1948 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1949 lp = line;
1950 if (linelen > expected) {
1951 excess = linelen - expected;
1952 linelen = expected;
1954 /* TODO >>
1955 * Need to mask 'From ' lines. This cannot be done properly
1956 * since some servers pass them as 'From ' and others as
1957 * '>From '. Although one could identify the first kind of
1958 * server in principle, it is not possible to identify the
1959 * second as '>From ' may also come from a server of the
1960 * first type as actual data. So do what is absolutely
1961 * necessary only - mask 'From '.
1963 * If the line is the first line of the message header, it
1964 * is likely a real 'From ' line. In this case, it is just
1965 * ignored since it violates all standards.
1966 * TODO can the latter *really* happen??
1967 * TODO <<
1969 /* Since we simply copy over data without doing any transfer
1970 * encoding reclassification/adjustment we *have* to perform
1971 * RFC 4155 compliant From_ quoting here */
1972 if (emptyline && is_head(lp, linelen, FAL0)) {
1973 fputc('>', mp->mb_otf);
1974 ++size;
1976 emptyline = 0;
1977 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1978 if (linelen > 2) {
1979 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1980 size += linelen - 1;
1981 } else {
1982 emptyline = 1;
1983 ++size;
1985 fputc('\n', mp->mb_otf);
1986 } else {
1987 fwrite(lp, 1, linelen, mp->mb_otf);
1988 size += linelen;
1990 ++lines;
1991 if ((expected -= linelen) <= 0)
1992 break;
1994 if (!emptyline) {
1995 /* This is very ugly; but some IMAP daemons don't end a
1996 * message with \r\n\r\n, and we need \n\n for mbox format */
1997 fputc('\n', mp->mb_otf);
1998 ++lines;
1999 ++size;
2001 fflush(mp->mb_otf);
2003 if (m != NULL) {
2004 m->m_size = size + headsize;
2005 m->m_lines = lines + headlines;
2006 m->m_block = mailx_blockof(offset);
2007 m->m_offset = mailx_offsetof(offset);
2008 switch (need) {
2009 case NEED_HEADER:
2010 m->m_content_info = CI_HAVE_HEADER;
2011 break;
2012 case NEED_BODY:
2013 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
2014 m->m_xlines = m->m_lines;
2015 m->m_xsize = m->m_size;
2016 break;
2019 free(line);
2020 NYD_LEAVE;
2021 return excess;
2024 static void
2025 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2026 const char *head, size_t headsize, long headlines)
2028 off_t offset;
2029 size_t len;
2030 NYD_ENTER;
2032 len = strlen(str);
2033 fseek(mp->mb_otf, 0L, SEEK_END);
2034 offset = ftell(mp->mb_otf);
2035 if (head)
2036 fwrite(head, 1, headsize, mp->mb_otf);
2037 if (len > 0) {
2038 fwrite(str, 1, len, mp->mb_otf);
2039 fputc('\n', mp->mb_otf);
2040 ++len;
2042 fflush(mp->mb_otf);
2044 if (m != NULL) {
2045 m->m_size = headsize + len;
2046 m->m_lines = headlines + 1;
2047 m->m_block = mailx_blockof(offset);
2048 m->m_offset = mailx_offsetof(offset);
2049 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2050 m->m_xlines = m->m_lines;
2051 m->m_xsize = m->m_size;
2053 NYD_LEAVE;
2056 static enum okay
2057 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2059 char o[LINESIZE];
2060 struct message mt;
2061 sighandler_type volatile saveint, savepipe;
2062 char * volatile head;
2063 char const *cp, *loc, * volatile item, * volatile resp;
2064 size_t expected;
2065 size_t volatile headsize;
2066 int number;
2067 FILE *queuefp;
2068 long volatile headlines;
2069 long n;
2070 ul_i volatile u;
2071 enum okay ok;
2072 NYD_X;
2074 saveint = savepipe = SIG_IGN;
2075 head = NULL;
2076 cp = loc = item = resp = NULL;
2077 headsize = 0;
2078 number = (int)PTR2SIZE(m - message + 1);
2079 queuefp = NULL;
2080 headlines = 0;
2081 u = 0;
2082 ok = STOP;
2084 if (getcache(mp, m, need) == OKAY)
2085 return OKAY;
2086 if (mp->mb_type == MB_CACHE) {
2087 n_err(_("Message %lu not available\n"), (ul_i)number);
2088 return STOP;
2091 if (mp->mb_sock.s_fd < 0) {
2092 n_err(_("IMAP connection closed\n"));
2093 return STOP;
2096 switch (need) {
2097 case NEED_HEADER:
2098 resp = item = "RFC822.HEADER";
2099 break;
2100 case NEED_BODY:
2101 item = "BODY.PEEK[]";
2102 resp = "BODY[]";
2103 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2104 char *hdr = smalloc(m->m_size);
2105 fflush(mp->mb_otf);
2106 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2107 SEEK_SET) < 0 ||
2108 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2109 free(hdr);
2110 break;
2112 head = hdr;
2113 headsize = m->m_size;
2114 headlines = m->m_lines;
2115 item = "BODY.PEEK[TEXT]";
2116 resp = "BODY[TEXT]";
2118 break;
2119 case NEED_UNSPEC:
2120 return STOP;
2123 imaplock = 1;
2124 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2125 if (sigsetjmp(imapjmp, 1)) {
2126 safe_signal(SIGINT, saveint);
2127 safe_signal(SIGPIPE, savepipe);
2128 imaplock = 0;
2129 return STOP;
2131 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2132 safe_signal(SIGINT, &_imap_maincatch);
2133 if (savepipe != SIG_IGN)
2134 safe_signal(SIGPIPE, imapcatch);
2136 if (m->m_uid)
2137 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2138 tag(1), m->m_uid, item);
2139 else {
2140 if (check_expunged() == STOP)
2141 goto out;
2142 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2144 IMAP_OUT(o, MB_COMD, goto out)
2145 for (;;) {
2146 ok = imap_answer(mp, 1);
2147 if (ok == STOP)
2148 break;
2149 if (response_status != RESPONSE_OTHER ||
2150 response_other != MESSAGE_DATA_FETCH)
2151 continue;
2152 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2153 continue;
2154 if (m->m_uid) {
2155 if ((cp = asccasestr(responded_other_text, "UID "))) {
2156 u = atol(&cp[4]);
2157 n = 0;
2158 } else {
2159 u = 0;
2160 n = -1;
2162 } else
2163 n = responded_other_number;
2164 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2165 if (m->m_uid ? m->m_uid != u : n != number)
2166 continue;
2167 if ((cp = strchr(loc, '"')) != NULL) {
2168 cp = imap_unquotestr(cp);
2169 imap_putstr(mp, m, cp, head, headsize, headlines);
2170 } else {
2171 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2172 m->m_xlines = m->m_lines;
2173 m->m_xsize = m->m_size;
2175 goto out;
2177 expected = atol(&cp[1]);
2178 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2179 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2180 continue;
2182 mt = *m;
2183 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2184 if (n >= 0) {
2185 commitmsg(mp, m, &mt, mt.m_content_info);
2186 break;
2188 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2189 if (n_poption & n_PO_VERBVERB)
2190 fputs(imapbuf, stderr);
2191 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2192 u = atol(&cp[4]);
2193 if (u == m->m_uid) {
2194 commitmsg(mp, m, &mt, mt.m_content_info);
2195 break;
2200 out:
2201 while (mp->mb_active & MB_COMD)
2202 ok = imap_answer(mp, 1);
2204 if (saveint != SIG_IGN)
2205 safe_signal(SIGINT, saveint);
2206 if (savepipe != SIG_IGN)
2207 safe_signal(SIGPIPE, savepipe);
2208 imaplock--;
2210 if (ok == OKAY)
2211 putcache(mp, m);
2212 if (head != NULL)
2213 free(head);
2214 if (interrupts)
2215 n_go_onintr_for_imap();
2216 return ok;
2219 FL enum okay
2220 imap_header(struct message *m)
2222 enum okay rv;
2223 NYD_ENTER;
2225 rv = imap_get(&mb, m, NEED_HEADER);
2226 NYD_LEAVE;
2227 return rv;
2231 FL enum okay
2232 imap_body(struct message *m)
2234 enum okay rv;
2235 NYD_ENTER;
2237 rv = imap_get(&mb, m, NEED_BODY);
2238 NYD_LEAVE;
2239 return rv;
2242 static void
2243 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2244 enum content_info content_info)
2246 NYD_ENTER;
2247 tomp->m_size = frommp->m_size;
2248 tomp->m_lines = frommp->m_lines;
2249 tomp->m_block = frommp->m_block;
2250 tomp->m_offset = frommp->m_offset;
2251 tomp->m_content_info = content_info & CI_HAVE_MASK;
2252 if (content_info & CI_HAVE_BODY) {
2253 tomp->m_xlines = frommp->m_lines;
2254 tomp->m_xsize = frommp->m_size;
2256 putcache(mp, tomp);
2257 NYD_LEAVE;
2260 static enum okay
2261 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2263 /* bot > topp */
2264 char o[LINESIZE];
2265 char const *cp;
2266 struct message mt;
2267 size_t expected;
2268 int n = 0, u;
2269 FILE *queuefp = NULL;
2270 enum okay ok;
2271 NYD_X;
2273 if (m[bot].m_uid)
2274 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2275 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2276 else {
2277 if (check_expunged() == STOP)
2278 return STOP;
2279 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2280 tag(1), bot, topp);
2282 IMAP_OUT(o, MB_COMD, return STOP)
2284 srelax_hold();
2285 for (;;) {
2286 ok = imap_answer(mp, 1);
2287 if (response_status != RESPONSE_OTHER)
2288 break;
2289 if (response_other != MESSAGE_DATA_FETCH)
2290 continue;
2291 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2292 srelax_rele();
2293 return STOP;
2295 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2296 continue;
2297 expected = atol(&cp[1]);
2298 if (m[bot-1].m_uid) {
2299 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2300 u = atoi(&cp[4]);
2301 for (n = bot; n <= topp; n++)
2302 if ((unsigned long)u == m[n-1].m_uid)
2303 break;
2304 if (n > topp) {
2305 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2306 continue;
2308 } else
2309 n = -1;
2310 } else {
2311 n = responded_other_number;
2312 if (n <= 0 || n > msgCount) {
2313 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2314 continue;
2317 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2318 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2319 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2320 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2321 if (n_poption & n_PO_VERBVERB)
2322 fputs(imapbuf, stderr);
2323 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2324 u = atoi(&cp[4]);
2325 for (n = bot; n <= topp; n++)
2326 if ((unsigned long)u == m[n-1].m_uid)
2327 break;
2328 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2329 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2332 srelax();
2334 srelax_rele();
2336 while (mp->mb_active & MB_COMD)
2337 ok = imap_answer(mp, 1);
2338 return ok;
2341 FL void
2342 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2344 sighandler_type saveint, savepipe;
2345 /*enum okay ok = STOP;*/
2346 int i, chunk = 256;
2347 NYD_X;
2349 if (mb.mb_type == MB_CACHE)
2350 return;
2351 if (bot < 1)
2352 bot = 1;
2353 if (topp > msgCount)
2354 topp = msgCount;
2355 for (i = bot; i < topp; i++) {
2356 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2357 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2358 bot = i+1;
2359 else
2360 break;
2362 for (i = topp; i > bot; i--) {
2363 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2364 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2365 topp = i-1;
2366 else
2367 break;
2369 if (bot >= topp)
2370 return;
2372 imaplock = 1;
2373 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2374 safe_signal(SIGINT, &_imap_maincatch);
2375 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2376 if (sigsetjmp(imapjmp, 1) == 0) {
2377 if (savepipe != SIG_IGN)
2378 safe_signal(SIGPIPE, imapcatch);
2380 for (i = bot; i <= topp; i += chunk) {
2381 int j = i + chunk - 1;
2382 j = n_MIN(j, topp);
2383 if (visible(message + j))
2384 /*ok = */imap_fetchheaders(&mb, message, i, j);
2385 if (interrupts)
2386 n_go_onintr_for_imap(); /* XXX imaplock? */
2389 safe_signal(SIGINT, saveint);
2390 safe_signal(SIGPIPE, savepipe);
2391 imaplock = 0;
2394 static enum okay
2395 __imap_exit(struct mailbox *mp)
2397 char o[LINESIZE];
2398 FILE *queuefp = NULL;
2399 NYD_X;
2401 mp->mb_active |= MB_BYE;
2402 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2403 IMAP_OUT(o, MB_COMD, return STOP)
2404 IMAP_ANSWER()
2405 return OKAY;
2408 static enum okay
2409 imap_exit(struct mailbox *mp)
2411 enum okay rv;
2412 NYD_ENTER;
2414 rv = __imap_exit(mp);
2415 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2416 free(mp->mb_imap_pass);
2417 free(mp->mb_imap_account);
2418 free(mp->mb_imap_mailbox);
2419 if (mp->mb_cache_directory != NULL)
2420 free(mp->mb_cache_directory);
2421 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2422 mp->mb_imap_account =
2423 mp->mb_imap_mailbox =
2424 mp->mb_cache_directory = "";
2425 #else
2426 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2427 mp->mb_imap_mailbox = NULL;
2428 mp->mb_cache_directory = NULL;
2429 #endif
2430 #endif
2431 sclose(&mp->mb_sock);
2432 NYD_LEAVE;
2433 return rv;
2436 static enum okay
2437 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2439 NYD_ENTER;
2440 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2441 if (mp->mb_type == MB_IMAP)
2442 delcache(mp, m);
2443 NYD_LEAVE;
2444 return OKAY;
2447 static enum okay
2448 imap_close(struct mailbox *mp)
2450 char o[LINESIZE];
2451 FILE *queuefp = NULL;
2452 NYD_X;
2454 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2455 IMAP_OUT(o, MB_COMD, return STOP)
2456 IMAP_ANSWER()
2457 return OKAY;
2460 static enum okay
2461 imap_update(struct mailbox *mp)
2463 struct message *m;
2464 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2465 NYD_ENTER;
2467 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2468 holdbits();
2469 c = 0;
2470 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2471 if (m->m_flag & MBOX)
2472 ++c;
2473 if (c > 0)
2474 if (makembox() == STOP)
2475 goto jbypass;
2478 gotcha = held = 0;
2479 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2480 if (mp->mb_perm == 0)
2481 dodel = 0;
2482 else if (n_pstate & n_PS_EDIT)
2483 dodel = ((m->m_flag & MDELETED) != 0);
2484 else
2485 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2487 /* Fetch the result after around each 800 STORE commands
2488 * sent (approx. 32k data sent). Otherwise, servers will
2489 * try to flush the return queue at some point, leading
2490 * to a deadlock if we are still writing commands but not
2491 * reading their results */
2492 needstat = stored > 0 && stored % 800 == 0;
2493 /* Even if this message has been deleted, continue
2494 * to set further flags. This is necessary to support
2495 * Gmail semantics, where "delete" actually means
2496 * "archive", and the flags are applied to the copy
2497 * in "All Mail" */
2498 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2499 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2500 stored++;
2502 if (m->m_flag & MFLAG) {
2503 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2504 stored++;
2506 if (m->m_flag & MUNFLAG) {
2507 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2508 stored++;
2510 if (m->m_flag & MANSWER) {
2511 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2512 stored++;
2514 if (m->m_flag & MUNANSWER) {
2515 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2516 stored++;
2518 if (m->m_flag & MDRAFT) {
2519 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2520 stored++;
2522 if (m->m_flag & MUNDRAFT) {
2523 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2524 stored++;
2527 if (dodel) {
2528 imap_delete(mp, m-message+1, m, needstat);
2529 stored++;
2530 gotcha++;
2531 } else if (mp->mb_type != MB_CACHE ||
2532 (!(n_pstate & n_PS_EDIT) &&
2533 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2534 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2535 (MPRESERVE | MTOUCH) ||
2536 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2537 held++;
2538 if (m->m_flag & MNEW) {
2539 m->m_flag &= ~MNEW;
2540 m->m_flag |= MSTATUS;
2543 jbypass:
2544 if (gotcha)
2545 imap_close(mp);
2547 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2548 if (!(m->m_flag & MUNLINKED) &&
2549 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2550 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2551 putcache(mp, m);
2552 modflags++;
2555 /* XXX should be readonly (but our IMAP code is weird...) */
2556 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2557 mb.mb_perm != 0) {
2558 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2559 printf(_("\"%s\" "), displayname);
2560 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2561 ? _("complete\n") : _("updated.\n"));
2562 } else if (held && !(n_pstate & n_PS_EDIT)) {
2563 if (held == 1)
2564 printf(_("Held 1 message in %s\n"), displayname);
2565 else
2566 printf(_("Held %d messages in %s\n"), held, displayname);
2568 fflush(stdout);
2570 NYD_LEAVE;
2571 return OKAY;
2574 FL bool_t
2575 imap_quit(bool_t hold_sigs_on)
2577 sighandler_type volatile saveint, savepipe;
2578 bool_t rv;
2579 NYD_ENTER;
2581 if(hold_sigs_on)
2582 rele_sigs();
2584 if (mb.mb_type == MB_CACHE) {
2585 rv = (imap_update(&mb) == OKAY);
2586 goto jleave;
2589 rv = FAL0;
2591 if (mb.mb_sock.s_fd < 0) {
2592 n_err(_("IMAP connection closed\n"));
2593 goto jleave;
2596 imaplock = 1;
2597 saveint = safe_signal(SIGINT, SIG_IGN);
2598 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2599 if (sigsetjmp(imapjmp, 1)) {
2600 safe_signal(SIGINT, saveint);
2601 safe_signal(SIGPIPE, saveint);
2602 imaplock = 0;
2603 goto jleave;
2605 if (saveint != SIG_IGN)
2606 safe_signal(SIGINT, imapcatch);
2607 if (savepipe != SIG_IGN)
2608 safe_signal(SIGPIPE, imapcatch);
2610 rv = (imap_update(&mb) == OKAY);
2611 if(!same_imap_account && imap_exit(&mb) != OKAY)
2612 rv = FAL0;
2614 safe_signal(SIGINT, saveint);
2615 safe_signal(SIGPIPE, savepipe);
2616 imaplock = 0;
2617 jleave:
2618 if(hold_sigs_on)
2619 hold_sigs();
2620 NYD_LEAVE;
2621 return rv;
2624 static enum okay
2625 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2626 int needstat)
2628 char o[LINESIZE];
2629 FILE *queuefp = NULL;
2630 NYD_X;
2632 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2633 return STOP;
2634 if (m->m_uid)
2635 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2636 tag(1), m->m_uid, c, sp);
2637 else {
2638 if (check_expunged() == STOP)
2639 return STOP;
2640 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2642 IMAP_OUT(o, MB_COMD, return STOP)
2643 if (needstat)
2644 IMAP_ANSWER()
2645 else
2646 mb.mb_active &= ~MB_COMD;
2647 if (queuefp != NULL)
2648 Fclose(queuefp);
2649 return OKAY;
2652 FL enum okay
2653 imap_undelete(struct message *m, int n)
2655 enum okay rv;
2656 NYD_ENTER;
2658 rv = imap_unstore(m, n, "\\Deleted");
2659 NYD_LEAVE;
2660 return rv;
2663 FL enum okay
2664 imap_unread(struct message *m, int n)
2666 enum okay rv;
2667 NYD_ENTER;
2669 rv = imap_unstore(m, n, "\\Seen");
2670 NYD_LEAVE;
2671 return rv;
2674 static enum okay
2675 imap_unstore(struct message *m, int n, const char *flag)
2677 sighandler_type saveint, savepipe;
2678 enum okay volatile rv = STOP;
2679 NYD_ENTER;
2681 imaplock = 1;
2682 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2683 safe_signal(SIGINT, &_imap_maincatch);
2684 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2685 if (sigsetjmp(imapjmp, 1) == 0) {
2686 if (savepipe != SIG_IGN)
2687 safe_signal(SIGPIPE, imapcatch);
2689 rv = imap_store(&mb, m, n, '-', flag, 1);
2691 safe_signal(SIGINT, saveint);
2692 safe_signal(SIGPIPE, savepipe);
2693 imaplock = 0;
2695 NYD_LEAVE;
2696 if (interrupts)
2697 n_go_onintr_for_imap();
2698 return rv;
2701 static const char *
2702 tag(int new)
2704 static char ts[20];
2705 static long n;
2706 NYD2_ENTER;
2708 if (new)
2709 ++n;
2710 snprintf(ts, sizeof ts, "T%lu", n);
2711 NYD2_LEAVE;
2712 return ts;
2715 FL int
2716 c_imapcodec(void *vp){
2717 bool_t err;
2718 size_t alen;
2719 char const **argv, *varname, *varres, *act, *cp;
2720 NYD_ENTER;
2722 argv = vp;
2723 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2725 act = *argv;
2726 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2728 if(act == cp)
2729 goto jesynopsis;
2730 alen = PTR2SIZE(cp - act);
2731 if(*cp != '\0')
2732 ++cp;
2734 n_pstate_err_no = n_ERR_NONE;
2735 varres = imap_path_normalize(NULL, cp);
2737 if(is_ascncaseprefix(act, "encode", alen))
2738 varres = imap_path_encode(varres, &err);
2739 else if(is_ascncaseprefix(act, "decode", alen))
2740 varres = imap_path_decode(varres, &err);
2741 else
2742 goto jesynopsis;
2744 if(err){
2745 n_pstate_err_no = n_ERR_CANCELED;
2746 varres = cp;
2747 vp = NULL;
2750 if(varname != NULL){
2751 if(!n_var_vset(varname, (uintptr_t)varres)){
2752 n_pstate_err_no = n_ERR_NOTSUP;
2753 vp = NULL;
2755 }else{
2756 struct str in, out;
2758 in.l = strlen(in.s = n_UNCONST(varres));
2759 makeprint(&in, &out);
2760 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2761 n_pstate_err_no = n_err_no;
2762 vp = NULL;
2764 free(out.s);
2767 jleave:
2768 NYD_LEAVE;
2769 return (vp != NULL ? 0 : 1);
2770 jesynopsis:
2771 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2772 n_pstate_err_no = n_ERR_INVAL;
2773 vp = NULL;
2774 goto jleave;
2777 FL int
2778 c_imap_imap(void *vp)
2780 char o[LINESIZE];
2781 sighandler_type saveint, savepipe;
2782 struct mailbox *mp = &mb;
2783 FILE *queuefp = NULL;
2784 enum okay volatile ok = STOP;
2785 NYD_X;
2787 if (mp->mb_type != MB_IMAP) {
2788 printf("Not operating on an IMAP mailbox.\n");
2789 return 1;
2791 imaplock = 1;
2792 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2793 safe_signal(SIGINT, &_imap_maincatch);
2794 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2795 if (sigsetjmp(imapjmp, 1) == 0) {
2796 if (savepipe != SIG_IGN)
2797 safe_signal(SIGPIPE, imapcatch);
2799 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2800 IMAP_OUT(o, MB_COMD, goto out)
2801 while (mp->mb_active & MB_COMD) {
2802 ok = imap_answer(mp, 0);
2803 fputs(responded_text, stdout);
2806 out:
2807 safe_signal(SIGINT, saveint);
2808 safe_signal(SIGPIPE, savepipe);
2809 imaplock = 0;
2811 if (interrupts)
2812 n_go_onintr_for_imap();
2813 return ok != OKAY;
2816 FL int
2817 imap_newmail(int nmail)
2819 NYD_ENTER;
2821 if (nmail && had_exists < 0 && had_expunge < 0) {
2822 imaplock = 1;
2823 imap_noop();
2824 imaplock = 0;
2827 if (had_exists == msgCount && had_expunge < 0)
2828 /* Some servers always respond with EXISTS to NOOP. If
2829 * the mailbox has been changed but the number of messages
2830 * has not, an EXPUNGE must also had been sent; otherwise,
2831 * nothing has changed */
2832 had_exists = -1;
2833 NYD_LEAVE;
2834 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2837 static char *
2838 imap_putflags(int f)
2840 const char *cp;
2841 char *buf, *bp;
2842 NYD2_ENTER;
2844 bp = buf = salloc(100);
2845 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2846 *bp++ = '(';
2847 if (f & MREAD) {
2848 if (bp[-1] != '(')
2849 *bp++ = ' ';
2850 for (cp = "\\Seen"; *cp; cp++)
2851 *bp++ = *cp;
2853 if (f & MFLAGGED) {
2854 if (bp[-1] != '(')
2855 *bp++ = ' ';
2856 for (cp = "\\Flagged"; *cp; cp++)
2857 *bp++ = *cp;
2859 if (f & MANSWERED) {
2860 if (bp[-1] != '(')
2861 *bp++ = ' ';
2862 for (cp = "\\Answered"; *cp; cp++)
2863 *bp++ = *cp;
2865 if (f & MDRAFT) {
2866 if (bp[-1] != '(')
2867 *bp++ = ' ';
2868 for (cp = "\\Draft"; *cp; cp++)
2869 *bp++ = *cp;
2871 *bp++ = ')';
2872 *bp++ = ' ';
2874 *bp = '\0';
2875 NYD2_LEAVE;
2876 return buf;
2879 static void
2880 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2882 NYD2_ENTER;
2883 while (*cp != ')') {
2884 if (*cp == '\\') {
2885 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2886 *f |= MREAD;
2887 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2888 *f |= MNEW;
2889 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2890 *f |= MDELETED;
2891 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2892 *f |= MFLAGGED;
2893 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2894 *f |= MANSWERED;
2895 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2896 *f |= MDRAFTED;
2898 cp++;
2901 if (xp != NULL)
2902 *xp = cp;
2903 NYD2_LEAVE;
2906 static enum okay
2907 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2908 long xsize, enum mflag flag, time_t t)
2910 char o[LINESIZE], *buf;
2911 size_t bufsize, buflen, cnt;
2912 long size, lines, ysize;
2913 char const *qname;
2914 bool_t twice;
2915 FILE *queuefp;
2916 enum okay rv;
2917 NYD_ENTER;
2919 rv = STOP;
2920 queuefp = NULL;
2921 twice = FAL0;
2922 buf = NULL;
2924 if((qname = imap_path_quote(mp, name)) == NULL)
2925 goto jleave;
2927 if (mp->mb_type == MB_CACHE) {
2928 queuefp = cache_queue(mp);
2929 if (queuefp == NULL) {
2930 buf = NULL;
2931 goto jleave;
2933 rv = OKAY;
2936 buf = smalloc(bufsize = LINESIZE);
2937 buflen = 0;
2938 jagain:
2939 size = xsize;
2940 cnt = fsize(fp);
2941 if (fseek(fp, off1, SEEK_SET) < 0) {
2942 rv = STOP;
2943 goto jleave;
2946 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2947 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2948 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2949 while (mp->mb_active & MB_COMD) {
2950 rv = imap_answer(mp, twice);
2951 if (response_type == RESPONSE_CONT)
2952 break;
2955 if (mp->mb_type != MB_CACHE && rv == STOP) {
2956 if (!twice)
2957 goto jtrycreate;
2958 else
2959 goto jleave;
2962 lines = ysize = 0;
2963 while (size > 0) {
2964 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2965 lines++;
2966 ysize += buflen;
2967 buf[buflen - 1] = '\r';
2968 buf[buflen] = '\n';
2969 if (mp->mb_type != MB_CACHE)
2970 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2971 else if (queuefp)
2972 fwrite(buf, 1, buflen+1, queuefp);
2973 size -= buflen + 1;
2975 if (mp->mb_type != MB_CACHE)
2976 swrite(&mp->mb_sock, "\r\n");
2977 else if (queuefp)
2978 fputs("\r\n", queuefp);
2979 while (mp->mb_active & MB_COMD) {
2980 rv = imap_answer(mp, 0);
2981 if (response_status == RESPONSE_NO /*&&
2982 ascncasecmp(responded_text,
2983 "[TRYCREATE] ", 12) == 0*/) {
2984 jtrycreate:
2985 if (twice) {
2986 rv = STOP;
2987 goto jleave;
2989 twice = TRU1;
2990 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2991 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2992 while (mp->mb_active & MB_COMD)
2993 rv = imap_answer(mp, 1);
2994 if (rv == STOP)
2995 goto jleave;
2996 imap_created_mailbox++;
2997 goto jagain;
2998 } else if (rv != OKAY)
2999 n_err(_("IMAP error: %s"), responded_text);
3000 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
3001 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
3003 jleave:
3004 if (queuefp != NULL)
3005 Fclose(queuefp);
3006 if (buf != NULL)
3007 free(buf);
3008 NYD_LEAVE;
3009 return rv;
3012 static enum okay
3013 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3015 char *buf, *bp, *lp;
3016 size_t bufsize, buflen, cnt;
3017 off_t off1 = -1, offs;
3018 int flag;
3019 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3020 time_t tim;
3021 long size;
3022 enum okay rv;
3023 NYD_ENTER;
3025 buf = smalloc(bufsize = LINESIZE);
3026 buflen = 0;
3027 cnt = fsize(fp);
3028 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3029 time(&tim);
3030 size = 0;
3032 for (flag = MNEW, state = _NLSEP;;) {
3033 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3035 if (bp == NULL ||
3036 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3037 is_head(buf, buflen, FAL0))) {
3038 if (off1 != (off_t)-1) {
3039 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3040 if (rv == STOP)
3041 goto jleave;
3042 fseek(fp, offs+buflen, SEEK_SET);
3044 off1 = offs + buflen;
3045 size = 0;
3046 flag = MNEW;
3047 state = _INHEAD;
3048 if (bp == NULL)
3049 break;
3050 tim = unixtime(buf);
3051 } else
3052 size += buflen+1;
3053 offs += buflen;
3055 state &= ~_NLSEP;
3056 if (buf[0] == '\n') {
3057 state &= ~_INHEAD;
3058 state |= _NLSEP;
3059 } else if (state & _INHEAD) {
3060 if (ascncasecmp(buf, "status", 6) == 0) {
3061 lp = &buf[6];
3062 while (whitechar(*lp))
3063 lp++;
3064 if (*lp == ':')
3065 while (*++lp != '\0')
3066 switch (*lp) {
3067 case 'R':
3068 flag |= MREAD;
3069 break;
3070 case 'O':
3071 flag &= ~MNEW;
3072 break;
3074 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3075 lp = &buf[8];
3076 while (whitechar(*lp))
3077 lp++;
3078 if (*lp == ':')
3079 while (*++lp != '\0')
3080 switch (*lp) {
3081 case 'F':
3082 flag |= MFLAGGED;
3083 break;
3084 case 'A':
3085 flag |= MANSWERED;
3086 break;
3087 case 'T':
3088 flag |= MDRAFTED;
3089 break;
3094 rv = OKAY;
3095 jleave:
3096 free(buf);
3097 NYD_LEAVE;
3098 return rv;
3101 FL enum okay
3102 imap_append(const char *xserver, FILE *fp, long offset)
3104 sighandler_type volatile saveint, savepipe;
3105 struct url url;
3106 struct ccred ccred;
3107 enum okay rv = STOP;
3108 NYD_ENTER;
3110 if (!url_parse(&url, CPROTO_IMAP, xserver))
3111 goto j_leave;
3112 if (!ok_blook(v15_compat) &&
3113 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3114 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3115 assert(url.url_path.s != NULL);
3117 imaplock = 1;
3118 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3119 safe_signal(SIGINT, &_imap_maincatch);
3120 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3121 if (sigsetjmp(imapjmp, 1))
3122 goto jleave;
3123 if (savepipe != SIG_IGN)
3124 safe_signal(SIGPIPE, imapcatch);
3126 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3127 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3128 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3129 } else {
3130 struct mailbox mx;
3132 memset(&mx, 0, sizeof mx);
3134 if (!_imap_getcred(&mx, &ccred, &url))
3135 goto jleave;
3137 imap_delim_init(&mx, &url);
3138 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3140 if (disconnected(url.url_p_eu_h_p) == 0) {
3141 if (!sopen(&mx.mb_sock, &url))
3142 goto jfail;
3143 mx.mb_sock.s_desc = "IMAP";
3144 mx.mb_type = MB_IMAP;
3145 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3146 /* TODO the code now did
3147 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3148 * TODO though imap_mailbox is sfree()d and mbx
3149 * TODO is possibly even a constant
3150 * TODO i changed this to sstrdup() sofar, as is used
3151 * TODO somewhere else in this file for this! */
3152 if (imap_preauth(&mx, &url) != OKAY ||
3153 imap_auth(&mx, &ccred) != OKAY) {
3154 sclose(&mx.mb_sock);
3155 goto jfail;
3157 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3158 imap_exit(&mx);
3159 } else {
3160 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3161 mx.mb_type = MB_CACHE;
3162 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3164 jfail:
3168 jleave:
3169 safe_signal(SIGINT, saveint);
3170 safe_signal(SIGPIPE, savepipe);
3171 imaplock = 0;
3172 j_leave:
3173 NYD_LEAVE;
3174 if (interrupts)
3175 n_go_onintr_for_imap();
3176 return rv;
3179 static enum okay
3180 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3181 struct list_item **lend, int level)
3183 char o[LINESIZE], *cp;
3184 struct list_item *lp;
3185 const char *qname, *bp;
3186 FILE *queuefp;
3187 enum okay ok;
3188 NYD_X;
3190 ok = STOP;
3191 queuefp = NULL;
3193 if((qname = imap_path_quote(mp, base)) == NULL)
3194 goto jleave;
3196 *list = *lend = NULL;
3197 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3198 IMAP_OUT(o, MB_COMD, goto jleave)
3199 while (mp->mb_active & MB_COMD) {
3200 ok = imap_answer(mp, 1);
3201 if (response_status == RESPONSE_OTHER &&
3202 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3203 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3204 lp = csalloc(1, sizeof *lp);
3205 lp->l_name = cp;
3206 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3207 ++cp;
3208 lp->l_base = *cp ? cp : savestr(base);
3209 lp->l_attr = list_attributes;
3210 lp->l_level = level+1;
3211 lp->l_delim = list_hierarchy_delimiter;
3212 if (*list && *lend) {
3213 (*lend)->l_next = lp;
3214 *lend = lp;
3215 } else
3216 *list = *lend = lp;
3219 jleave:
3220 return ok;
3223 static enum okay
3224 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3226 struct list_item *list, *lend, *lp, *lx, *ly;
3227 int n, depth;
3228 const char *bp;
3229 char *cp;
3230 enum okay rv;
3231 NYD_ENTER;
3233 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3234 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3235 goto jleave;
3236 rv = OKAY;
3237 if (list == NULL || lend == NULL)
3238 goto jleave;
3240 for (lp = list; lp; lp = lp->l_next)
3241 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3242 !(lp->l_attr & LIST_NOINFERIORS)) {
3243 cp = salloc((n = strlen(lp->l_name)) + 2);
3244 memcpy(cp, lp->l_name, n);
3245 cp[n] = lp->l_delim;
3246 cp[n+1] = '\0';
3247 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3248 lp->l_has_children = 1;
3249 if (strcmp(cp, lx->l_name) == 0)
3250 lx = lx->l_next;
3251 if (lx) {
3252 lend->l_next = lx;
3253 lend = ly;
3258 for (lp = list; lp; lp = lp->l_next) {
3259 if (strip) {
3260 cp = lp->l_name;
3261 for (bp = base; *bp && *bp == *cp; bp++)
3262 cp++;
3263 } else
3264 cp = lp->l_name;
3265 if (!(lp->l_attr & LIST_NOSELECT))
3266 fprintf(fp, "%s\n", *cp ? cp : base);
3267 else if (lp->l_has_children == 0)
3268 fprintf(fp, "%s%c\n", *cp ? cp : base,
3269 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3271 jleave:
3272 NYD_LEAVE;
3273 return rv;
3276 FL int
3277 imap_folders(const char * volatile name, int strip)
3279 sighandler_type saveint, savepipe;
3280 const char * volatile fold, *cp, *sp;
3281 FILE * volatile fp;
3282 int rv = 1;
3283 NYD_ENTER;
3285 cp = protbase(name);
3286 sp = mb.mb_imap_account;
3287 if (sp == NULL || strcmp(cp, sp)) {
3288 n_err(
3289 _("Cannot perform `folders' but when on the very IMAP "
3290 "account; the current one is\n `%s' -- "
3291 "try `folders @'\n"),
3292 (sp != NULL ? sp : _("[NONE]")));
3293 goto jleave;
3296 fold = imap_fileof(name);
3297 if (n_psonce & n_PSO_TTYOUT) {
3298 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3299 == NULL) {
3300 n_perr(_("tmpfile"), 0);
3301 goto jleave;
3303 } else
3304 fp = stdout;
3306 imaplock = 1;
3307 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3308 safe_signal(SIGINT, &_imap_maincatch);
3309 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3310 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3311 goto junroll;
3312 if (savepipe != SIG_IGN)
3313 safe_signal(SIGPIPE, imapcatch);
3315 if (mb.mb_type == MB_CACHE)
3316 cache_list(&mb, fold, strip, fp);
3317 else
3318 imap_list(&mb, fold, strip, fp);
3320 imaplock = 0;
3321 if (interrupts) {
3322 if (n_psonce & n_PSO_TTYOUT)
3323 Fclose(fp);
3324 rv = 0;
3325 goto jleave;
3327 fflush(fp);
3329 if (n_psonce & n_PSO_TTYOUT) {
3330 rewind(fp);
3331 if (fsize(fp) > 0){
3332 dopr(fp);
3333 rv = 0;
3334 }else
3335 n_err(_("Folder not found\n"));
3336 }else
3337 rv = 0;
3338 junroll:
3339 safe_signal(SIGINT, saveint);
3340 safe_signal(SIGPIPE, savepipe);
3341 if (n_psonce & n_PSO_TTYOUT)
3342 Fclose(fp);
3343 jleave:
3344 NYD_LEAVE;
3345 if (interrupts)
3346 n_go_onintr_for_imap();
3347 return rv;
3350 static void
3351 dopr(FILE *fp)
3353 char o[LINESIZE];
3354 int c;
3355 long n = 0, mx = 0, columns, width;
3356 FILE *out;
3357 NYD_ENTER;
3359 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER))
3360 == NULL) {
3361 n_perr(_("tmpfile"), 0);
3362 goto jleave;
3365 while ((c = getc(fp)) != EOF) {
3366 if (c == '\n') {
3367 if (n > mx)
3368 mx = n;
3369 n = 0;
3370 } else
3371 ++n;
3373 rewind(fp);
3375 width = n_scrnwidth;
3376 if (mx < width / 2) {
3377 columns = width / (mx+2);
3378 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3379 } else
3380 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3381 n_child_run(ok_vlook(SHELL), NULL, fileno(fp), fileno(out), "-c", o, NULL,
3382 NULL, NULL);
3383 page_or_print(out, 0);
3384 Fclose(out);
3385 jleave:
3386 NYD_LEAVE;
3389 static enum okay
3390 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3392 char o[LINESIZE];
3393 const char *qname;
3394 bool_t twice, stored;
3395 FILE *queuefp;
3396 enum okay ok;
3397 NYD_X;
3399 ok = STOP;
3400 queuefp = NULL;
3401 twice = stored = FAL0;
3403 /* C99 */{
3404 size_t i;
3406 i = strlen(name = imap_fileof(name));
3407 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3408 name = savecat(name, "INBOX");
3409 if((qname = imap_path_quote(mp, name)) == NULL)
3410 goto jleave;
3413 if (mp->mb_type == MB_CACHE) {
3414 if ((queuefp = cache_queue(mp)) == NULL)
3415 goto jleave;
3416 ok = OKAY;
3419 /* Since it is not possible to set flags on the copy, recently
3420 * set flags must be set on the original to include it in the copy */
3421 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3422 imap_store(mp, m, n, '+', "\\Seen", 0);
3423 if (m->m_flag&MFLAG)
3424 imap_store(mp, m, n, '+', "\\Flagged", 0);
3425 if (m->m_flag&MUNFLAG)
3426 imap_store(mp, m, n, '-', "\\Flagged", 0);
3427 if (m->m_flag&MANSWER)
3428 imap_store(mp, m, n, '+', "\\Answered", 0);
3429 if (m->m_flag&MUNANSWER)
3430 imap_store(mp, m, n, '-', "\\Flagged", 0);
3431 if (m->m_flag&MDRAFT)
3432 imap_store(mp, m, n, '+', "\\Draft", 0);
3433 if (m->m_flag&MUNDRAFT)
3434 imap_store(mp, m, n, '-', "\\Draft", 0);
3435 again:
3436 if (m->m_uid)
3437 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3438 else {
3439 if (check_expunged() == STOP)
3440 goto out;
3441 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3443 IMAP_OUT(o, MB_COMD, goto out)
3444 while (mp->mb_active & MB_COMD)
3445 ok = imap_answer(mp, twice);
3447 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3448 response_status == RESPONSE_OK)
3449 imap_copyuid(mp, m, name);
3451 if (response_status == RESPONSE_NO && !twice) {
3452 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3453 IMAP_OUT(o, MB_COMD, goto out)
3454 while (mp->mb_active & MB_COMD)
3455 ok = imap_answer(mp, 1);
3456 if (ok == OKAY) {
3457 imap_created_mailbox++;
3458 goto again;
3462 if (queuefp != NULL)
3463 Fclose(queuefp);
3465 /* ... and reset the flag to its initial value so that the 'exit'
3466 * command still leaves the message unread */
3467 out:
3468 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3469 imap_store(mp, m, n, '-', "\\Seen", 0);
3470 stored = TRU1;
3472 if (m->m_flag & MFLAG) {
3473 imap_store(mp, m, n, '-', "\\Flagged", 0);
3474 stored = TRU1;
3476 if (m->m_flag & MUNFLAG) {
3477 imap_store(mp, m, n, '+', "\\Flagged", 0);
3478 stored = TRU1;
3480 if (m->m_flag & MANSWER) {
3481 imap_store(mp, m, n, '-', "\\Answered", 0);
3482 stored = TRU1;
3484 if (m->m_flag & MUNANSWER) {
3485 imap_store(mp, m, n, '+', "\\Answered", 0);
3486 stored = TRU1;
3488 if (m->m_flag & MDRAFT) {
3489 imap_store(mp, m, n, '-', "\\Draft", 0);
3490 stored = TRU1;
3492 if (m->m_flag & MUNDRAFT) {
3493 imap_store(mp, m, n, '+', "\\Draft", 0);
3494 stored = TRU1;
3496 if (stored) {
3497 mp->mb_active |= MB_COMD;
3498 (void)imap_finish(mp);
3500 jleave:
3501 return ok;
3504 FL enum okay
3505 imap_copy(struct message *m, int n, const char *name)
3507 sighandler_type saveint, savepipe;
3508 enum okay volatile rv = STOP;
3509 NYD_ENTER;
3511 imaplock = 1;
3512 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3513 safe_signal(SIGINT, &_imap_maincatch);
3514 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3515 if (sigsetjmp(imapjmp, 1) == 0) {
3516 if (savepipe != SIG_IGN)
3517 safe_signal(SIGPIPE, imapcatch);
3519 rv = imap_copy1(&mb, m, n, name);
3521 safe_signal(SIGINT, saveint);
3522 safe_signal(SIGPIPE, savepipe);
3523 imaplock = 0;
3525 NYD_LEAVE;
3526 if (interrupts)
3527 n_go_onintr_for_imap();
3528 return rv;
3531 static enum okay
3532 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3533 unsigned long *olduid, unsigned long *newuid)
3535 char *xp, *yp, *zp;
3536 enum okay rv;
3537 NYD_ENTER;
3539 *uidvalidity = strtoul(cp, &xp, 10);
3540 *olduid = strtoul(xp, &yp, 10);
3541 *newuid = strtoul(yp, &zp, 10);
3542 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3543 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3544 NYD_LEAVE;
3545 return rv;
3548 static enum okay
3549 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3550 unsigned long *uid)
3552 char *xp, *yp;
3553 enum okay rv;
3554 NYD_ENTER;
3556 *uidvalidity = strtoul(cp, &xp, 10);
3557 *uid = strtoul(xp, &yp, 10);
3558 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3559 *yp == ']');
3560 NYD_LEAVE;
3561 return rv;
3564 static enum okay
3565 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3567 struct mailbox xmb;
3568 struct message xm;
3569 const char *cp;
3570 unsigned long uidvalidity, olduid, newuid;
3571 enum okay rv;
3572 NYD_ENTER;
3574 rv = STOP;
3576 memset(&xmb, 0, sizeof xmb);
3578 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3579 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3580 goto jleave;
3582 rv = OKAY;
3584 xmb = *mp;
3585 xmb.mb_cache_directory = NULL;
3586 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3587 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3588 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3589 sizeof(xmb.mb_imap_delim));
3590 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3591 if (mp->mb_cache_directory != NULL)
3592 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3593 xmb.mb_uidvalidity = uidvalidity;
3594 initcache(&xmb);
3596 if (m == NULL) {
3597 memset(&xm, 0, sizeof xm);
3598 xm.m_uid = olduid;
3599 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3600 goto jleave;
3601 getcache(mp, &xm, NEED_HEADER);
3602 getcache(mp, &xm, NEED_BODY);
3603 } else {
3604 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3605 getcache(mp, m, NEED_HEADER);
3606 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3607 getcache(mp, m, NEED_BODY);
3608 xm = *m;
3610 xm.m_uid = newuid;
3611 xm.m_flag &= ~MFULLYCACHED;
3612 putcache(&xmb, &xm);
3613 jleave:
3614 if (xmb.mb_cache_directory != NULL)
3615 free(xmb.mb_cache_directory);
3616 if (xmb.mb_imap_mailbox != NULL)
3617 free(xmb.mb_imap_mailbox);
3618 if (xmb.mb_imap_pass != NULL)
3619 free(xmb.mb_imap_pass);
3620 if (xmb.mb_imap_account != NULL)
3621 free(xmb.mb_imap_account);
3622 NYD_LEAVE;
3623 return rv;
3626 static enum okay
3627 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3628 long size, long lines, int flag, const char *name)
3630 struct mailbox xmb;
3631 struct message xm;
3632 const char *cp;
3633 unsigned long uidvalidity, uid;
3634 enum okay rv;
3635 NYD_ENTER;
3637 rv = STOP;
3639 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3640 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3641 goto jleave;
3643 rv = OKAY;
3645 xmb = *mp;
3646 xmb.mb_cache_directory = NULL;
3647 /* XXX mb_imap_delim reused */
3648 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3649 xmb.mb_uidvalidity = uidvalidity;
3650 xmb.mb_otf = xmb.mb_itf = fp;
3651 initcache(&xmb);
3652 memset(&xm, 0, sizeof xm);
3653 xm.m_flag = (flag & MREAD) | MNEW;
3654 xm.m_time = t;
3655 xm.m_block = mailx_blockof(off1);
3656 xm.m_offset = mailx_offsetof(off1);
3657 xm.m_size = size;
3658 xm.m_xsize = xsize;
3659 xm.m_lines = xm.m_xlines = lines;
3660 xm.m_uid = uid;
3661 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3662 putcache(&xmb, &xm);
3664 free(xmb.mb_imap_mailbox);
3665 jleave:
3666 NYD_LEAVE;
3667 return rv;
3670 static enum okay
3671 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3673 FILE *tp = NULL;
3674 time_t t;
3675 long size, xsize, ysize, lines;
3676 enum mflag flag = MNEW;
3677 char *name, *buf, *bp;
3678 char const *cp;
3679 size_t bufsize, buflen, cnt;
3680 enum okay rv = STOP;
3681 NYD_ENTER;
3683 buf = smalloc(bufsize = LINESIZE);
3684 buflen = 0;
3685 cnt = fsize(fp);
3686 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3687 goto jstop;
3689 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3691 while (*bp == ' ')
3692 ++bp;
3694 if ((cp = strrchr(bp, '{')) == NULL)
3695 goto jstop;
3697 xsize = atol(&cp[1]) + 2;
3698 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3699 goto jstop;
3700 while (*cp == ' ')
3701 cp++;
3703 if (*cp == '(') {
3704 imap_getflags(cp, &cp, &flag);
3705 while (*++cp == ' ')
3708 t = imap_read_date_time(cp);
3710 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3711 == NULL)
3712 goto jstop;
3714 size = xsize;
3715 ysize = lines = 0;
3716 while (size > 0) {
3717 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3718 goto jstop;
3719 size -= buflen;
3720 buf[--buflen] = '\0';
3721 buf[buflen-1] = '\n';
3722 fwrite(buf, 1, buflen, tp);
3723 ysize += buflen;
3724 ++lines;
3726 fflush(tp);
3727 rewind(tp);
3729 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3730 imap_unquotestr(name));
3731 rv = OKAY;
3732 jstop:
3733 free(buf);
3734 if (tp)
3735 Fclose(tp);
3736 NYD_LEAVE;
3737 return rv;
3740 #ifdef HAVE_IMAP_SEARCH
3741 static enum okay
3742 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3743 int f)
3745 char *o, *xp, *cs, c;
3746 size_t osize;
3747 FILE *queuefp = NULL;
3748 int i;
3749 unsigned long n;
3750 const char *cp;
3751 enum okay ok = STOP;
3752 NYD_X;
3754 c = 0;
3755 for (cp = spec; *cp; cp++)
3756 c |= *cp;
3757 if (c & 0200) {
3758 cp = ok_vlook(ttycharset);
3759 # ifdef HAVE_ICONV
3760 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3761 iconv_t it;
3762 char *nsp, *nspec;
3763 size_t sz, nsz;
3765 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3766 sz = strlen(spec) + 1;
3767 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3768 if (n_iconv_buf(it, n_ICONV_DEFAULT,
3769 (char const**)&spec, &sz, &nsp, &nsz) == 0 &&
3770 sz == 0) {
3771 spec = nspec;
3772 cp = "utf-8";
3774 n_iconv_close(it);
3777 # endif
3778 cp = imap_quotestr(cp);
3779 cs = salloc(n = strlen(cp) + 10);
3780 snprintf(cs, n, "CHARSET %s ", cp);
3781 } else
3782 cs = n_UNCONST("");
3784 o = ac_alloc(osize = strlen(spec) + 60);
3785 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3786 IMAP_OUT(o, MB_COMD, goto out)
3787 while (mp->mb_active & MB_COMD) {
3788 ok = imap_answer(mp, 0);
3789 if (response_status == RESPONSE_OTHER &&
3790 response_other == MAILBOX_DATA_SEARCH) {
3791 xp = responded_other_text;
3792 while (*xp && *xp != '\r') {
3793 n = strtoul(xp, &xp, 10);
3794 for (i = 0; i < cnt; i++)
3795 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3796 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3797 mark(i+1, f);
3801 out:
3802 ac_free(o);
3803 return ok;
3806 FL enum okay
3807 imap_search1(const char * volatile spec, int f)
3809 sighandler_type saveint, savepipe;
3810 enum okay volatile rv = STOP;
3811 NYD_ENTER;
3813 if (mb.mb_type != MB_IMAP)
3814 goto jleave;
3816 imaplock = 1;
3817 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3818 safe_signal(SIGINT, &_imap_maincatch);
3819 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3820 if (sigsetjmp(imapjmp, 1) == 0) {
3821 if (savepipe != SIG_IGN)
3822 safe_signal(SIGPIPE, imapcatch);
3824 rv = imap_search2(&mb, message, msgCount, spec, f);
3826 safe_signal(SIGINT, saveint);
3827 safe_signal(SIGPIPE, savepipe);
3828 imaplock = 0;
3829 jleave:
3830 NYD_LEAVE;
3831 if (interrupts)
3832 n_go_onintr_for_imap();
3833 return rv;
3835 #endif /* HAVE_IMAP_SEARCH */
3837 FL int
3838 imap_thisaccount(const char *cp)
3840 int rv;
3841 NYD_ENTER;
3843 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3844 rv = 0;
3845 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3846 mb.mb_imap_account == NULL)
3847 rv = 0;
3848 else
3849 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3850 NYD_LEAVE;
3851 return rv;
3854 FL enum okay
3855 imap_remove(const char * volatile name)
3857 sighandler_type volatile saveint, savepipe;
3858 enum okay volatile rv = STOP;
3859 NYD_ENTER;
3861 if (mb.mb_type != MB_IMAP) {
3862 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3863 goto jleave;
3866 if (!imap_thisaccount(name)) {
3867 n_err(_("Can only remove mailboxes on current IMAP server: "
3868 "\"%s\" not removed\n"), name);
3869 goto jleave;
3872 imaplock = 1;
3873 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3874 safe_signal(SIGINT, &_imap_maincatch);
3875 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3876 if (sigsetjmp(imapjmp, 1) == 0) {
3877 if (savepipe != SIG_IGN)
3878 safe_signal(SIGPIPE, imapcatch);
3880 rv = imap_remove1(&mb, imap_fileof(name));
3882 safe_signal(SIGINT, saveint);
3883 safe_signal(SIGPIPE, savepipe);
3884 imaplock = 0;
3886 if (rv == OKAY)
3887 rv = cache_remove(name);
3888 jleave:
3889 NYD_LEAVE;
3890 if (interrupts)
3891 n_go_onintr_for_imap();
3892 return rv;
3895 static enum okay
3896 imap_remove1(struct mailbox *mp, const char *name)
3898 char *o;
3899 int os;
3900 char const *qname;
3901 FILE *queuefp;
3902 enum okay ok;
3903 NYD_X;
3905 ok = STOP;
3906 queuefp = NULL;
3908 if((qname = imap_path_quote(mp, name)) != NULL){
3909 o = ac_alloc(os = strlen(qname) + 100);
3910 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3911 IMAP_OUT(o, MB_COMD, goto out)
3912 while (mp->mb_active & MB_COMD)
3913 ok = imap_answer(mp, 1);
3914 out:
3915 ac_free(o);
3917 return ok;
3920 FL enum okay
3921 imap_rename(const char *old, const char *new)
3923 sighandler_type saveint, savepipe;
3924 enum okay volatile rv = STOP;
3925 NYD_ENTER;
3927 if (mb.mb_type != MB_IMAP) {
3928 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3929 goto jleave;
3932 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3933 n_err(_("Can only rename mailboxes on current IMAP "
3934 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3935 goto jleave;
3938 imaplock = 1;
3939 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3940 safe_signal(SIGINT, &_imap_maincatch);
3941 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3942 if (sigsetjmp(imapjmp, 1) == 0) {
3943 if (savepipe != SIG_IGN)
3944 safe_signal(SIGPIPE, imapcatch);
3946 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3948 safe_signal(SIGINT, saveint);
3949 safe_signal(SIGPIPE, savepipe);
3950 imaplock = 0;
3952 if (rv == OKAY)
3953 rv = cache_rename(old, new);
3954 jleave:
3955 NYD_LEAVE;
3956 if (interrupts)
3957 n_go_onintr_for_imap();
3958 return rv;
3961 static enum okay
3962 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3964 char *o;
3965 int os;
3966 char const *qoname, *qnname;
3967 FILE *queuefp;
3968 enum okay ok;
3969 NYD_X;
3971 ok = STOP;
3972 queuefp = NULL;
3974 if((qoname = imap_path_quote(mp, old)) != NULL &&
3975 (qnname = imap_path_quote(mp, new)) != NULL){
3976 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3977 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3978 IMAP_OUT(o, MB_COMD, goto out)
3979 while (mp->mb_active & MB_COMD)
3980 ok = imap_answer(mp, 1);
3981 out:
3982 ac_free(o);
3984 return ok;
3987 FL enum okay
3988 imap_dequeue(struct mailbox *mp, FILE *fp)
3990 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3991 size_t bufsize, buflen, cnt;
3992 long offs, offs1, offs2, octets;
3993 int twice, gotcha = 0;
3994 FILE *queuefp = NULL;
3995 enum okay ok = OKAY, rok = OKAY;
3996 NYD_X;
3998 buf = smalloc(bufsize = LINESIZE);
3999 buflen = 0;
4000 cnt = fsize(fp);
4001 while ((offs1 = ftell(fp)) >= 0 &&
4002 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
4003 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
4005 while (*bp == ' ')
4006 ++bp;
4007 twice = 0;
4008 if ((offs = ftell(fp)) < 0)
4009 goto fail;
4010 again:
4011 snprintf(o, sizeof o, "%s %s", tag(1), bp);
4012 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
4013 cp = &bp[9];
4014 while (digitchar(*cp))
4015 cp++;
4016 if (*cp != ' ')
4017 goto fail;
4018 while (*cp == ' ')
4019 cp++;
4020 if ((newname = imap_strex(cp, NULL)) == NULL)
4021 goto fail;
4022 IMAP_OUT(o, MB_COMD, continue)
4023 while (mp->mb_active & MB_COMD)
4024 ok = imap_answer(mp, twice);
4025 if (response_status == RESPONSE_NO && twice++ == 0)
4026 goto trycreate;
4027 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4028 imap_copyuid(mp, NULL, imap_unquotestr(newname));
4030 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
4031 IMAP_OUT(o, MB_COMD, continue)
4032 while (mp->mb_active & MB_COMD)
4033 ok = imap_answer(mp, 1);
4034 if (ok == OKAY)
4035 gotcha++;
4036 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
4037 if ((cp = strrchr(bp, '{')) == NULL)
4038 goto fail;
4039 octets = atol(&cp[1]) + 2;
4040 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
4041 goto fail;
4042 IMAP_OUT(o, MB_COMD, continue)
4043 while (mp->mb_active & MB_COMD) {
4044 ok = imap_answer(mp, twice);
4045 if (response_type == RESPONSE_CONT)
4046 break;
4048 if (ok == STOP) {
4049 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
4050 goto trycreate;
4051 goto fail;
4053 while (octets > 0) {
4054 size_t n = (UICMP(z, octets, >, sizeof iob)
4055 ? sizeof iob : (size_t)octets);
4056 octets -= n;
4057 if (n != fread(iob, 1, n, fp))
4058 goto fail;
4059 swrite1(&mp->mb_sock, iob, n, 1);
4061 swrite(&mp->mb_sock, "");
4062 while (mp->mb_active & MB_COMD) {
4063 ok = imap_answer(mp, 0);
4064 if (response_status == RESPONSE_NO && twice++ == 0) {
4065 if (fseek(fp, offs, SEEK_SET) < 0)
4066 goto fail;
4067 goto trycreate;
4070 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4071 if ((offs2 = ftell(fp)) < 0)
4072 goto fail;
4073 fseek(fp, offs1, SEEK_SET);
4074 if (imap_appenduid_cached(mp, fp) == STOP) {
4075 (void)fseek(fp, offs2, SEEK_SET);
4076 goto fail;
4079 } else {
4080 fail:
4081 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4082 rok = STOP;
4084 continue;
4085 trycreate:
4086 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4087 IMAP_OUT(o, MB_COMD, continue)
4088 while (mp->mb_active & MB_COMD)
4089 ok = imap_answer(mp, 1);
4090 if (ok == OKAY)
4091 goto again;
4093 fflush(fp);
4094 rewind(fp);
4095 ftruncate(fileno(fp), 0);
4096 if (gotcha)
4097 imap_close(mp);
4098 free(buf);
4099 return rok;
4102 static char *
4103 imap_strex(char const *cp, char const **xp)
4105 char const *cq;
4106 char *n = NULL;
4107 NYD_ENTER;
4109 if (*cp != '"')
4110 goto jleave;
4112 for (cq = cp + 1; *cq != '\0'; ++cq) {
4113 if (*cq == '\\')
4114 cq++;
4115 else if (*cq == '"')
4116 break;
4118 if (*cq != '"')
4119 goto jleave;
4121 n = salloc(cq - cp + 2);
4122 memcpy(n, cp, cq - cp +1);
4123 n[cq - cp + 1] = '\0';
4124 if (xp != NULL)
4125 *xp = cq + 1;
4126 jleave:
4127 NYD_LEAVE;
4128 return n;
4131 static enum okay
4132 check_expunged(void)
4134 enum okay rv;
4135 NYD_ENTER;
4137 if (expunged_messages > 0) {
4138 n_err(_("Command not executed - messages have been expunged\n"));
4139 rv = STOP;
4140 } else
4141 rv = OKAY;
4142 NYD_LEAVE;
4143 return rv;
4146 FL int
4147 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4149 struct url url;
4150 int rv, omsgCount = msgCount;
4151 NYD_ENTER;
4152 n_UNUSED(vp);
4154 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4155 n_err(_("Already connected\n"));
4156 rv = 1;
4157 goto jleave;
4160 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4161 rv = 1;
4162 goto jleave;
4164 ok_bclear(disconnected);
4165 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4167 if (mb.mb_type == MB_CACHE) {
4168 enum fedit_mode fm = FEDIT_NONE;
4169 if (_imap_rdonly)
4170 fm |= FEDIT_RDONLY;
4171 if (!(n_pstate & n_PS_EDIT))
4172 fm |= FEDIT_SYSBOX;
4173 _imap_setfile1(&url, fm, 1);
4174 if (msgCount > omsgCount)
4175 newmailinfo(omsgCount);
4177 rv = 0;
4178 jleave:
4179 NYD_LEAVE;
4180 return rv;
4183 FL int
4184 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4186 struct url url;
4187 int rv = 1, *msgvec = vp;
4188 NYD_ENTER;
4190 if (mb.mb_type == MB_CACHE) {
4191 n_err(_("Not connected\n"));
4192 goto jleave;
4194 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4195 n_err(_("The current mailbox is not cached\n"));
4196 goto jleave;
4199 if (!url_parse(&url, CPROTO_IMAP, mailname))
4200 goto jleave;
4202 if (*msgvec)
4203 c_cache(vp);
4204 ok_bset(disconnected);
4205 if (mb.mb_type == MB_IMAP) {
4206 enum fedit_mode fm = FEDIT_NONE;
4207 if (_imap_rdonly)
4208 fm |= FEDIT_RDONLY;
4209 if (!(n_pstate & n_PS_EDIT))
4210 fm |= FEDIT_SYSBOX;
4211 sclose(&mb.mb_sock);
4212 _imap_setfile1(&url, fm, 1);
4214 rv = 0;
4215 jleave:
4216 NYD_LEAVE;
4217 return rv;
4220 FL int
4221 c_cache(void *vp)
4223 int rv = 1, *msgvec = vp, *ip;
4224 struct message *mp;
4225 NYD_ENTER;
4227 if (mb.mb_type != MB_IMAP) {
4228 n_err(_("Not connected to an IMAP server\n"));
4229 goto jleave;
4231 if (cached_uidvalidity(&mb) == 0) {
4232 n_err(_("The current mailbox is not cached\n"));
4233 goto jleave;
4236 srelax_hold();
4237 for (ip = msgvec; *ip; ++ip) {
4238 mp = &message[*ip - 1];
4239 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4240 get_body(mp);
4241 srelax();
4244 srelax_rele();
4245 rv = 0;
4246 jleave:
4247 NYD_LEAVE;
4248 return rv;
4251 FL int
4252 disconnected(const char *file)
4254 struct url url;
4255 int rv = 1;
4256 NYD_ENTER;
4258 if (ok_blook(disconnected)) {
4259 rv = 1;
4260 goto jleave;
4263 if (!url_parse(&url, CPROTO_IMAP, file)) {
4264 rv = 0;
4265 goto jleave;
4267 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4269 jleave:
4270 NYD_LEAVE;
4271 return rv;
4274 FL void
4275 transflags(struct message *omessage, long omsgCount, int transparent)
4277 struct message *omp, *nmp, *newdot, *newprevdot;
4278 int hf;
4279 NYD_ENTER;
4281 omp = omessage;
4282 nmp = message;
4283 newdot = message;
4284 newprevdot = NULL;
4285 while (PTRCMP(omp, <, omessage + omsgCount) &&
4286 PTRCMP(nmp, <, message + msgCount)) {
4287 if (dot && nmp->m_uid == dot->m_uid)
4288 newdot = nmp;
4289 if (prevdot && nmp->m_uid == prevdot->m_uid)
4290 newprevdot = nmp;
4291 if (omp->m_uid == nmp->m_uid) {
4292 hf = nmp->m_flag & MHIDDEN;
4293 if (transparent && mb.mb_type == MB_IMAP)
4294 omp->m_flag &= ~MHIDDEN;
4295 *nmp++ = *omp++;
4296 if (transparent && mb.mb_type == MB_CACHE)
4297 nmp[-1].m_flag |= hf;
4298 } else if (omp->m_uid < nmp->m_uid)
4299 ++omp;
4300 else
4301 ++nmp;
4303 dot = newdot;
4304 setdot(newdot);
4305 prevdot = newprevdot;
4306 free(omessage);
4307 NYD_LEAVE;
4310 FL time_t
4311 imap_read_date_time(const char *cp)
4313 char buf[3];
4314 time_t t;
4315 int i, year, month, day, hour, minute, second, sign = -1;
4316 NYD2_ENTER;
4318 /* "25-Jul-2004 15:33:44 +0200"
4319 * | | | | | |
4320 * 0 5 10 15 20 25 */
4321 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4322 goto jinvalid;
4323 day = strtol(&cp[1], NULL, 10);
4324 for (i = 0;;) {
4325 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4326 break;
4327 if (n_month_names[++i][0] == '\0')
4328 goto jinvalid;
4330 month = i + 1;
4331 year = strtol(&cp[8], NULL, 10);
4332 hour = strtol(&cp[13], NULL, 10);
4333 minute = strtol(&cp[16], NULL, 10);
4334 second = strtol(&cp[19], NULL, 10);
4335 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4336 goto jinvalid;
4337 switch (cp[22]) {
4338 case '-':
4339 sign = 1;
4340 break;
4341 case '+':
4342 break;
4343 default:
4344 goto jinvalid;
4346 buf[2] = '\0';
4347 buf[0] = cp[23];
4348 buf[1] = cp[24];
4349 t += strtol(buf, NULL, 10) * sign * 3600;
4350 buf[0] = cp[25];
4351 buf[1] = cp[26];
4352 t += strtol(buf, NULL, 10) * sign * 60;
4353 jleave:
4354 NYD2_LEAVE;
4355 return t;
4356 jinvalid:
4357 time(&t);
4358 goto jleave;
4361 FL const char *
4362 imap_make_date_time(time_t t)
4364 static char s[40];
4365 struct tm *tmptr;
4366 int tzdiff, tzdiff_hour, tzdiff_min;
4367 NYD2_ENTER;
4369 tzdiff = t - mktime(gmtime(&t));
4370 tzdiff_hour = (int)(tzdiff / 60);
4371 tzdiff_min = tzdiff_hour % 60;
4372 tzdiff_hour /= 60;
4373 tmptr = localtime(&t);
4374 if (tmptr->tm_isdst > 0)
4375 tzdiff_hour++;
4376 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4377 tmptr->tm_mday, n_month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4378 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4379 NYD2_LEAVE;
4380 return s;
4383 FL char *
4384 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4386 char *n, *np;
4387 NYD2_ENTER;
4389 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4390 n_MEMORY_DEBUG_ARGSCALL);
4392 /* Just ignore the `is-system-mailbox' prefix XXX */
4393 if (cp[0] == '%' && cp[1] == ':')
4394 cp += 2;
4396 while (*cp != '\0') {
4397 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4398 *np++ = *cp++;
4399 *np++ = *cp++;
4400 *np++ = *cp++;
4401 } else if (cp[0] == '/')
4402 break;
4403 else
4404 *np++ = *cp++;
4406 *np = '\0';
4407 NYD2_LEAVE;
4408 return n;
4410 #endif /* HAVE_IMAP */
4412 /* s-it-mode */