a_message_match_at(): CID 1377027 !
[s-mailx.git] / obs-imap.c
blobfb6b0f74e98e08d6ec33d34e2f07a2f077dfd948
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE obs_imap
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_IMAP
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 EMPTY_FILE()
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() \
62 if (mp->mb_type != MB_CACHE) {\
63 enum okay ok = OKAY;\
64 while (mp->mb_active & MB_COMD)\
65 ok = imap_answer(mp, 1);\
66 if (ok == STOP)\
67 return STOP;\
71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
72 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
73 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
74 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
75 * TODO I.e., that should be a function, not a macro ... or so.
76 * TODO This entire module needs MASSIVE work! */
77 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
79 do {\
80 if (mp->mb_type != MB_CACHE) {\
81 if (imap_finish(mp) == STOP) {\
82 ACTIONBAIL;\
84 if (n_poption & n_PO_VERBVERB)\
85 n_err(">>> %s", X);\
86 mp->mb_active |= Y;\
87 if (swrite(&mp->mb_sock, X) == STOP) {\
88 ACTIONERR;\
90 } else {\
91 if (queuefp != NULL)\
92 fputs(X, queuefp);\
94 } while (0);
96 static struct record {
97 struct record *rec_next;
98 unsigned long rec_count;
99 enum rec_type {
100 REC_EXISTS,
101 REC_EXPUNGE
102 } rec_type;
103 } *record, *recend;
105 static enum {
106 RESPONSE_TAGGED,
107 RESPONSE_DATA,
108 RESPONSE_FATAL,
109 RESPONSE_CONT,
110 RESPONSE_ILLEGAL
111 } response_type;
113 static enum {
114 RESPONSE_OK,
115 RESPONSE_NO,
116 RESPONSE_BAD,
117 RESPONSE_PREAUTH,
118 RESPONSE_BYE,
119 RESPONSE_OTHER,
120 RESPONSE_UNKNOWN
121 } response_status;
123 static char *responded_tag;
124 static char *responded_text;
125 static char *responded_other_text;
126 static long responded_other_number;
128 static enum {
129 MAILBOX_DATA_FLAGS,
130 MAILBOX_DATA_LIST,
131 MAILBOX_DATA_LSUB,
132 MAILBOX_DATA_MAILBOX,
133 MAILBOX_DATA_SEARCH,
134 MAILBOX_DATA_STATUS,
135 MAILBOX_DATA_EXISTS,
136 MAILBOX_DATA_RECENT,
137 MESSAGE_DATA_EXPUNGE,
138 MESSAGE_DATA_FETCH,
139 CAPABILITY_DATA,
140 RESPONSE_OTHER_UNKNOWN
141 } response_other;
143 static enum list_attributes {
144 LIST_NONE = 000,
145 LIST_NOINFERIORS = 001,
146 LIST_NOSELECT = 002,
147 LIST_MARKED = 004,
148 LIST_UNMARKED = 010
149 } list_attributes;
151 static int list_hierarchy_delimiter;
152 static char *list_name;
154 struct list_item {
155 struct list_item *l_next;
156 char *l_name;
157 char *l_base;
158 enum list_attributes l_attr;
159 int l_delim;
160 int l_level;
161 int l_has_children;
164 static char *imapbuf; /* TODO not static, use pool */
165 static size_t imapbufsize;
166 static sigjmp_buf imapjmp;
167 static sighandler_type savealrm;
168 static int imapkeepalive;
169 static long had_exists = -1;
170 static long had_expunge = -1;
171 static long expunged_messages;
172 static int volatile imaplock;
173 static int same_imap_account;
174 static bool_t _imap_rdonly;
176 static char *imap_quotestr(char const *s);
177 static char *imap_unquotestr(char const *s);
178 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
179 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
180 /* Returns NULL on error */
181 static char *imap_path_quote(struct mailbox *mp, char const *cp);
182 static void imap_other_get(char *pp);
183 static void imap_response_get(const char **cp);
184 static void imap_response_parse(void);
185 static enum okay imap_answer(struct mailbox *mp, int errprnt);
186 static enum okay imap_parse_list(void);
187 static enum okay imap_finish(struct mailbox *mp);
188 static void imap_timer_off(void);
189 static void imapcatch(int s);
190 static void _imap_maincatch(int s);
191 static enum okay imap_noop1(struct mailbox *mp);
192 static void rec_queue(enum rec_type type, unsigned long cnt);
193 static enum okay rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s);
196 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
197 static enum okay imap_capability(struct mailbox *mp);
198 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
199 #ifdef HAVE_MD5
200 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
201 #endif
202 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
203 #ifdef HAVE_GSSAPI
204 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
205 #endif
206 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
207 static void imap_init(struct mailbox *mp, int n);
208 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
209 int *prevcount);
210 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
211 struct url *urlp);
212 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
213 int transparent);
214 static int imap_fetchdata(struct mailbox *mp, struct message *m,
215 size_t expected, int need, const char *head,
216 size_t headsize, long headlines);
217 static void imap_putstr(struct mailbox *mp, struct message *m,
218 const char *str, const char *head, size_t headsize,
219 long headlines);
220 static enum okay imap_get(struct mailbox *mp, struct message *m,
221 enum needspec need);
222 static void commitmsg(struct mailbox *mp, struct message *to,
223 struct message *from, enum content_info content_info);
224 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
225 int bot, int top);
226 static enum okay imap_exit(struct mailbox *mp);
227 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
228 int needstat);
229 static enum okay imap_close(struct mailbox *mp);
230 static enum okay imap_update(struct mailbox *mp);
231 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
232 int c, const char *sp, int needstat);
233 static enum okay imap_unstore(struct message *m, int n, const char *flag);
234 static const char *tag(int new);
235 static char * imap_putflags(int f);
236 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
237 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
238 off_t off1, long xsize, enum mflag flag, time_t t);
239 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
240 long offset);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend,
243 int level);
244 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
245 FILE *fp);
246 static void dopr(FILE *fp);
247 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
248 const char *name);
249 static enum okay imap_copyuid_parse(const char *cp,
250 unsigned long *uidvalidity, unsigned long *olduid,
251 unsigned long *newuid);
252 static enum okay imap_appenduid_parse(const char *cp,
253 unsigned long *uidvalidity, unsigned long *uid);
254 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
255 const char *name);
256 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
257 long off1, long xsize, long size, long lines, int flag,
258 const char *name);
259 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
260 #ifdef HAVE_IMAP_SEARCH
261 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
262 const char *spec, int f);
263 #endif
264 static enum okay imap_remove1(struct mailbox *mp, const char *name);
265 static enum okay imap_rename1(struct mailbox *mp, const char *old,
266 const char *new);
267 static char * imap_strex(char const *cp, char const **xp);
268 static enum okay check_expunged(void);
270 static char *
271 imap_quotestr(char const *s)
273 char *n, *np;
274 NYD2_ENTER;
276 np = n = salloc(2 * strlen(s) + 3);
277 *np++ = '"';
278 while (*s) {
279 if (*s == '"' || *s == '\\')
280 *np++ = '\\';
281 *np++ = *s++;
283 *np++ = '"';
284 *np = '\0';
285 NYD2_LEAVE;
286 return n;
289 static char *
290 imap_unquotestr(char const *s)
292 char *n, *np;
293 NYD2_ENTER;
295 if (*s != '"') {
296 n = savestr(s);
297 goto jleave;
300 np = n = salloc(strlen(s) + 1);
301 while (*++s) {
302 if (*s == '\\')
303 s++;
304 else if (*s == '"')
305 break;
306 *np++ = *s;
308 *np = '\0';
309 jleave:
310 NYD2_LEAVE;
311 return n;
314 static void
315 imap_delim_init(struct mailbox *mp, struct url const *urlp){
316 size_t i;
317 char const *cp;
318 NYD2_ENTER;
320 mp->mb_imap_delim[0] = '\0';
322 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
323 i = strlen(cp);
325 if(i == 0){
326 cp = n_IMAP_DELIM;
327 i = sizeof(n_IMAP_DELIM) -1;
328 goto jcopy;
331 if(i < n_NELEM(mp->mb_imap_delim))
332 jcopy:
333 memcpy(&mb.mb_imap_delim[0], cp, i +1);
334 else
335 n_err(_("*imap-delim* for %s is too long: %s\n"),
336 urlp->url_input, cp);
338 NYD2_LEAVE;
341 static char const *
342 imap_path_normalize(struct mailbox *mp, char const *cp){
343 char *rv_base, *rv, dc2, dc, c, lc;
344 char const *dcp;
345 NYD2_ENTER;
347 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
348 * exactly what i have specified" */
349 if(mp == NULL || mp->mb_imap_delim[0] == '\0')
350 dcp = &n_IMAP_DELIM[0];
351 else
352 dcp = &mp->mb_imap_delim[0];
353 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
355 /* Plain names don't need path quoting */
356 /* C99 */{
357 size_t i, j;
358 char const *cpx;
360 for(cpx = cp;; ++cpx)
361 if((c = *cpx) == '\0')
362 goto jleave;
363 else if(dc == '\0'){
364 if(strchr(n_IMAP_DELIM, c)){
365 dc = c;
366 break;
368 }else if(c == dc)
369 break;
370 else if(dc2 && strchr(dcp, c) != NULL)
371 break;
373 /* And we don't need to reevaluate what we have seen yet */
374 i = PTR2SIZE(cpx - cp);
375 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
376 if(i > 0)
377 memcpy(rv, cp, i);
378 memcpy(&rv[i], cpx, j);
379 rv += i;
380 cp = cpx;
383 /* Squeeze adjacent delimiters, convert remain to dc */
384 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
385 if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
386 c = dc;
387 if(c != dc || lc != dc)
388 *rv++ = c;
390 *rv = '\0';
392 cp = rv_base;
393 jleave:
394 NYD2_LEAVE;
395 return cp;
398 FL char const *
399 imap_path_encode(char const *cp, bool_t *err_or_null){
400 /* To a large extend inspired by dovecot(1) */
401 struct str in, out;
402 bool_t err_def;
403 ui8_t *be16p_base, *be16p;
404 char const *emsg;
405 char c;
406 size_t l, l_plain;
407 NYD2_ENTER;
409 if(err_or_null == NULL)
410 err_or_null = &err_def;
411 *err_or_null = FAL0;
413 /* Is this a string that works out as "plain US-ASCII"? */
414 for(l = 0;; ++l)
415 if((c = cp[l]) == '\0')
416 goto jleave;
417 else if(c <= 0x1F || c >= 0x7F || c == '&')
418 break;
420 *err_or_null = TRU1;
422 /* We need to encode in mUTF-7! For that, we first have to convert the
423 * local charset to UTF-8, then convert all characters which need to be
424 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
425 * We can skip the UTF-8 conversion occasionally, however */
426 if(!(n_psonce & n_PSO_UNICODE)){
427 int ir;
428 iconv_t icd;
430 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
432 if((icd = iconv_open("utf-8", ok_vlook(ttycharset))) == (iconv_t)-1)
433 goto jerr;
435 out.s = NULL, out.l = 0;
436 in.s = n_UNCONST(cp); /* logical */
437 l += strlen(&cp[l]);
438 in.l = l;
439 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
440 cp = savestrbuf(out.s, out.l);
442 if(out.s != NULL)
443 free(out.s);
444 iconv_close(icd);
446 if(ir != 0)
447 goto jerr;
450 * So: Why not start all over again?
453 /* Is this a string that works out as "plain US-ASCII"? */
454 for(l = 0;; ++l)
455 if((c = cp[l]) == '\0')
456 goto jleave;
457 else if(c <= 0x1F || c >= 0x7F || c == '&')
458 break;
461 /* We need to encode, save what we have, encode the rest */
462 l_plain = l;
464 for(cp += l, l = 0; cp[l] != '\0'; ++l)
466 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
468 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
469 if(l_plain > 0)
470 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
471 else
472 out.l = 0;
473 DBG( l_plain += (l << 2); )
475 while(l > 0){
476 c = *cp++;
477 --l;
479 if(c == '&'){
480 out.s[out.l + 0] = '&';
481 out.s[out.l + 1] = '-';
482 out.l += 2;
483 }else if(c > 0x1F && c < 0x7F)
484 out.s[out.l++] = c;
485 else{
486 static char const mb64ct[] =
487 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
488 ui32_t utf32;
490 /* Convert consecutive non-representables */
491 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
493 for(be16p = be16p_base, --cp, ++l;;){
494 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
495 goto jerr;
497 /* TODO S-CText: magic utf16 conversions */
498 if(utf32 < 0x10000){
499 be16p[1] = utf32 & 0xFF;
500 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
501 be16p += 2;
502 }else{
503 ui16_t s7e;
505 utf32 -= 0x10000;
506 s7e = 0xD800u | (utf32 >> 10);
507 be16p[1] = s7e & 0xFF;
508 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
509 s7e = 0xDC00u | (utf32 &= 0x03FF);
510 be16p[3] = s7e & 0xFF;
511 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
512 be16p += 4;
515 if(l == 0)
516 break;
517 if((c = *cp) > 0x1F && c < 0x7F)
518 break;
521 /* And then warp that UTF-16BE to mUTF-7 */
522 out.s[out.l++] = '&';
523 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
524 be16p = be16p_base;
526 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
527 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
528 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
529 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
530 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
531 out.l += 4;
533 if(utf32 > 0){
534 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
535 if(--utf32 == 0){
536 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
537 out.l += 2;
538 }else{
539 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
540 (be16p[1] >> 4)];
541 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
542 out.l += 3;
545 out.s[out.l++] = '-';
548 out.s[out.l] = '\0';
549 assert(out.l <= l_plain);
550 *err_or_null = FAL0;
551 cp = out.s;
552 jleave:
553 NYD2_LEAVE;
554 return cp;
555 jerr:
556 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
557 goto jleave;
560 FL char *
561 imap_path_decode(char const *path, bool_t *err_or_null){
562 /* To a large extend inspired by dovecot(1) TODO use string */
563 struct str in, out;
564 bool_t err_def;
565 ui8_t *mb64p_base, *mb64p, *mb64xp;
566 char const *emsg, *cp;
567 char *rv_base, *rv, c;
568 size_t l_orig, l, i;
569 NYD2_ENTER;
571 if(err_or_null == NULL)
572 err_or_null = &err_def;
573 *err_or_null = FAL0;
575 l = l_orig = strlen(path);
576 rv = rv_base = salloc(l << 1);
577 memcpy(rv, path, l +1);
579 /* xxx Don't check for invalid characters from malicious servers */
580 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
581 goto jleave;
583 *err_or_null = TRU1;
585 emsg = N_("Invalid mUTF-7 encoding");
586 i = PTR2SIZE(cp - path);
587 rv += i;
588 l -= i;
589 mb64p_base = NULL;
591 while(l > 0){
592 if((c = *cp) != '&'){
593 if(c <= 0x1F || c >= 0x7F){
594 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
595 goto jerr;
597 *rv++ = c;
598 ++cp;
599 --l;
600 }else if(--l == 0)
601 goto jeincpl;
602 else if(*++cp == '-'){
603 *rv++ = '&';
604 ++cp;
605 --l;
606 }else if(l < 3){
607 jeincpl:
608 emsg = N_("Invalid mUTF-7: incomplete input");
609 goto jerr;
610 }else{
611 /* mUTF-7 -> UTF-16BE -> UTF-8 */
612 static ui8_t const mb64dt[256] = {
613 #undef XX
614 #define XX 0xFFu
615 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
616 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
617 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
618 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
619 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
620 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
621 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
622 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
623 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
624 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
625 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
626 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
627 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
628 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
629 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
630 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
633 if(mb64p_base == NULL)
634 mb64p_base = salloc(l);
636 /* Decode the mUTF-7 to what is indeed UTF-16BE */
637 for(mb64p = mb64p_base;;){
638 assert(l >= 3);
639 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
640 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
641 goto jerr;
642 mb64p += 2;
644 c = cp[2];
645 cp += 3;
646 l -= 3;
647 if(c == '-')
648 break;
649 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
650 goto jerr;
652 if(l == 0)
653 goto jerr;
654 --l;
655 if((c = *cp++) == '-')
656 break;
657 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
658 goto jerr;
660 if(l < 3){
661 if(l > 0 && *cp == '-'){
662 --l;
663 ++cp;
664 break;
666 goto jerr;
669 #undef XX
671 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
672 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
673 goto jerr;
676 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
677 i = PTR2SIZE(mb64p - mb64p_base);
678 mb64p = mb64xp = mb64p_base;
680 while(i > 0){
681 ui8_t ul, u0, u1, u2, u3;
683 ul = (i >= 4) ? 4 : i & 0x3;
684 i -= ul;
685 u0 = mb64xp[0];
686 u1 = mb64xp[1];
687 u2 = (ul < 3) ? 0 : mb64xp[2];
688 u3 = (ul < 4) ? 0 : mb64xp[3];
689 mb64xp += ul;
690 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
691 if(ul < 3)
692 break;
693 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
694 if(ul < 4)
695 break;
696 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
699 /* UTF-16BE we convert to UTF-8 */
700 i = PTR2SIZE(mb64p - mb64p_base);
701 if(i & 1){
702 emsg = N_("Odd bytecount for UTF-16BE input");
703 goto jerr;
706 /* TODO S-CText: magic utf16 conversions */
707 emsg = N_("Invalid UTF-16BE encoding");
709 for(mb64p = mb64p_base; i > 0;){
710 ui32_t utf32;
711 ui16_t uhi, ulo;
713 uhi = mb64p[0];
714 uhi <<= 8;
715 uhi |= mb64p[1];
717 /* Not a surrogate? */
718 if(uhi < 0xD800 || uhi > 0xDFFF){
719 utf32 = uhi;
720 mb64p += 2;
721 i -= 2;
722 }else if(uhi > 0xDBFF)
723 goto jerr;
724 else if(i < 4){
725 emsg = N_("Incomplete UTF-16BE surrogate pair");
726 goto jerr;
727 }else{
728 ulo = mb64p[2];
729 ulo <<= 8;
730 ulo |= mb64p[3];
731 if(ulo < 0xDC00 || ulo > 0xDFFF)
732 goto jerr;
734 utf32 = (uhi &= 0x03FF);
735 utf32 <<= 10;
736 utf32 += 0x10000;
737 utf32 |= (ulo &= 0x03FF);
738 mb64p += 4;
739 i -= 4;
742 utf32 = n_utf32_to_utf8(utf32, rv);
743 rv += utf32;
747 *rv = '\0';
749 /* We can skip the UTF-8 conversion occasionally */
750 if(!(n_psonce & n_PSO_UNICODE)){
751 int ir;
752 iconv_t icd;
754 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
756 if((icd = iconv_open(ok_vlook(ttycharset), "utf-8")) == (iconv_t)-1)
757 goto jerr;
759 out.s = NULL, out.l = 0;
760 in.l = strlen(in.s = rv_base);
761 if((ir = n_iconv_str(icd, n_ICONV_NONE, &out, &in, NULL)) == 0)
762 /* Because the length of this is unpredictable, copy */
763 rv_base = savestrbuf(out.s, out.l);
765 if(out.s != NULL)
766 free(out.s);
767 iconv_close(icd);
769 if(ir != 0)
770 goto jerr;
773 *err_or_null = FAL0;
774 rv = rv_base;
775 jleave:
776 NYD2_LEAVE;
777 return rv;
778 jerr:
779 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
780 memcpy(rv = rv_base, path, ++l_orig);
781 goto jleave;
784 static char *
785 imap_path_quote(struct mailbox *mp, char const *cp){
786 bool_t err;
787 char *rv;
788 NYD2_ENTER;
790 cp = imap_path_normalize(mp, cp);
791 cp = imap_path_encode(cp, &err);
792 rv = err ? NULL : imap_quotestr(cp);
793 NYD2_LEAVE;
794 return rv;
797 static void
798 imap_other_get(char *pp)
800 char *xp;
801 NYD2_ENTER;
803 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
804 pp += 6;
805 response_other = MAILBOX_DATA_FLAGS;
806 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
807 pp += 5;
808 response_other = MAILBOX_DATA_LIST;
809 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
810 pp += 5;
811 response_other = MAILBOX_DATA_LSUB;
812 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
813 pp += 8;
814 response_other = MAILBOX_DATA_MAILBOX;
815 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
816 pp += 7;
817 response_other = MAILBOX_DATA_SEARCH;
818 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
819 pp += 7;
820 response_other = MAILBOX_DATA_STATUS;
821 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
822 pp += 11;
823 response_other = CAPABILITY_DATA;
824 } else {
825 responded_other_number = strtol(pp, &xp, 10);
826 while (*xp == ' ')
827 ++xp;
828 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
829 response_other = MAILBOX_DATA_EXISTS;
830 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
831 response_other = MAILBOX_DATA_RECENT;
832 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
833 response_other = MESSAGE_DATA_EXPUNGE;
834 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
835 pp = &xp[6];
836 response_other = MESSAGE_DATA_FETCH;
837 } else
838 response_other = RESPONSE_OTHER_UNKNOWN;
840 responded_other_text = pp;
841 NYD2_LEAVE;
844 static void
845 imap_response_get(const char **cp)
847 NYD2_ENTER;
848 if (ascncasecmp(*cp, "OK ", 3) == 0) {
849 *cp += 3;
850 response_status = RESPONSE_OK;
851 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
852 *cp += 3;
853 response_status = RESPONSE_NO;
854 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
855 *cp += 4;
856 response_status = RESPONSE_BAD;
857 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
858 *cp += 8;
859 response_status = RESPONSE_PREAUTH;
860 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
861 *cp += 4;
862 response_status = RESPONSE_BYE;
863 } else
864 response_status = RESPONSE_OTHER;
865 NYD2_LEAVE;
868 static void
869 imap_response_parse(void)
871 static char *parsebuf; /* TODO Use pool */
872 static size_t parsebufsize;
874 const char *ip = imapbuf;
875 char *pp;
876 NYD2_ENTER;
878 if (parsebufsize < imapbufsize + 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 so.s_fd = -1;
1769 if (!same_imap_account) {
1770 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1771 rv = -1;
1772 goto jleave;
1774 } else
1775 so = mb.mb_sock;
1776 if (!transparent) {
1777 if(!quit(FAL0)){
1778 rv = -1;
1779 goto jleave;
1783 if (fm & FEDIT_SYSBOX)
1784 n_pstate &= ~n_PS_EDIT;
1785 else
1786 n_pstate |= n_PS_EDIT;
1787 if (mb.mb_imap_account != NULL)
1788 free(mb.mb_imap_account);
1789 if (mb.mb_imap_pass != NULL)
1790 free(mb.mb_imap_pass);
1791 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1792 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1793 * TODO object, and mailbox will naturally have an URL and credentials */
1794 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1796 if (!same_imap_account) {
1797 if (mb.mb_sock.s_fd >= 0)
1798 sclose(&mb.mb_sock);
1800 same_imap_account = 0;
1802 if (!transparent) {
1803 if (mb.mb_itf) {
1804 fclose(mb.mb_itf);
1805 mb.mb_itf = NULL;
1807 if (mb.mb_otf) {
1808 fclose(mb.mb_otf);
1809 mb.mb_otf = NULL;
1811 if (mb.mb_imap_mailbox != NULL)
1812 free(mb.mb_imap_mailbox);
1813 assert(urlp->url_path.s != NULL);
1814 imap_delim_init(&mb, urlp);
1815 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1816 initbox(savecatsep(urlp->url_p_eu_h_p,
1817 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1818 mb.mb_imap_mailbox));
1820 mb.mb_type = MB_VOID;
1821 mb.mb_active = MB_NONE;
1823 imaplock = 1;
1824 saveint = safe_signal(SIGINT, SIG_IGN);
1825 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1826 if (sigsetjmp(imapjmp, 1)) {
1827 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1828 sclose(&mb.mb_sock);
1829 safe_signal(SIGINT, saveint);
1830 safe_signal(SIGPIPE, savepipe);
1831 imaplock = 0;
1833 mb.mb_type = MB_VOID;
1834 mb.mb_active = MB_NONE;
1835 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1836 goto jleave;
1838 if (saveint != SIG_IGN)
1839 safe_signal(SIGINT, imapcatch);
1840 if (savepipe != SIG_IGN)
1841 safe_signal(SIGPIPE, imapcatch);
1843 if (mb.mb_sock.s_fd < 0) {
1844 if (disconnected(mb.mb_imap_account)) {
1845 if (cache_setptr(fm, transparent) == STOP)
1846 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1847 goto jdone;
1849 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1850 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1851 savealrm = safe_signal(SIGALRM, imapalarm);
1852 alarm(imapkeepalive);
1856 mb.mb_sock = so;
1857 mb.mb_sock.s_desc = "IMAP";
1858 mb.mb_sock.s_onclose = imap_timer_off;
1859 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1860 sclose(&mb.mb_sock);
1861 imap_timer_off();
1862 safe_signal(SIGINT, saveint);
1863 safe_signal(SIGPIPE, savepipe);
1864 imaplock = 0;
1865 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1866 goto jleave;
1868 } else /* same account */
1869 mb.mb_flags |= same_flags;
1871 if (n_poption & n_PO_R_FLAG)
1872 fm |= FEDIT_RDONLY;
1873 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1874 mb.mb_type = MB_IMAP;
1875 cache_dequeue(&mb);
1876 assert(urlp->url_path.s != NULL);
1877 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1878 /*sclose(&mb.mb_sock);
1879 imap_timer_off();*/
1880 safe_signal(SIGINT, saveint);
1881 safe_signal(SIGPIPE, savepipe);
1882 imaplock = 0;
1883 mb.mb_type = MB_VOID;
1884 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1885 goto jleave;
1888 jnmail:
1889 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1890 n_UNVOLATILE(&prevcount));
1891 jdone:
1892 setmsize(msgCount);
1893 safe_signal(SIGINT, saveint);
1894 safe_signal(SIGPIPE, savepipe);
1895 imaplock = 0;
1897 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1898 purgecache(&mb, message, msgCount);
1899 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1900 mb.mb_threaded = 0;
1901 c_sort((void*)-1);
1904 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1905 n_pstate &= ~n_PS_SAW_COMMAND;
1906 n_pstate |= n_PS_SETFILE_OPENED;
1909 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1910 mb.mb_type == MB_CACHE)) {
1911 rv = (msgCount == 0);
1912 goto jleave;
1915 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1916 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1917 !ok_blook(emptystart))
1918 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1919 rv = 1;
1920 goto jleave;
1923 if (fm & FEDIT_NEWMAIL)
1924 newmailinfo(prevcount);
1925 rv = 0;
1926 jleave:
1927 NYD_LEAVE;
1928 return rv;
1931 static int
1932 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1933 int need, const char *head, size_t headsize, long headlines)
1935 char *line = NULL, *lp;
1936 size_t linesize = 0, linelen, size = 0;
1937 int emptyline = 0, lines = 0, excess = 0;
1938 off_t offset;
1939 NYD_ENTER;
1941 fseek(mp->mb_otf, 0L, SEEK_END);
1942 offset = ftell(mp->mb_otf);
1944 if (head)
1945 fwrite(head, 1, headsize, mp->mb_otf);
1947 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1948 lp = line;
1949 if (linelen > expected) {
1950 excess = linelen - expected;
1951 linelen = expected;
1953 /* TODO >>
1954 * Need to mask 'From ' lines. This cannot be done properly
1955 * since some servers pass them as 'From ' and others as
1956 * '>From '. Although one could identify the first kind of
1957 * server in principle, it is not possible to identify the
1958 * second as '>From ' may also come from a server of the
1959 * first type as actual data. So do what is absolutely
1960 * necessary only - mask 'From '.
1962 * If the line is the first line of the message header, it
1963 * is likely a real 'From ' line. In this case, it is just
1964 * ignored since it violates all standards.
1965 * TODO can the latter *really* happen??
1966 * TODO <<
1968 /* Since we simply copy over data without doing any transfer
1969 * encoding reclassification/adjustment we *have* to perform
1970 * RFC 4155 compliant From_ quoting here */
1971 if (emptyline && is_head(lp, linelen, FAL0)) {
1972 fputc('>', mp->mb_otf);
1973 ++size;
1975 emptyline = 0;
1976 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1977 if (linelen > 2) {
1978 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1979 size += linelen - 1;
1980 } else {
1981 emptyline = 1;
1982 ++size;
1984 fputc('\n', mp->mb_otf);
1985 } else {
1986 fwrite(lp, 1, linelen, mp->mb_otf);
1987 size += linelen;
1989 ++lines;
1990 if ((expected -= linelen) <= 0)
1991 break;
1993 if (!emptyline) {
1994 /* This is very ugly; but some IMAP daemons don't end a
1995 * message with \r\n\r\n, and we need \n\n for mbox format */
1996 fputc('\n', mp->mb_otf);
1997 ++lines;
1998 ++size;
2000 fflush(mp->mb_otf);
2002 if (m != NULL) {
2003 m->m_size = size + headsize;
2004 m->m_lines = lines + headlines;
2005 m->m_block = mailx_blockof(offset);
2006 m->m_offset = mailx_offsetof(offset);
2007 switch (need) {
2008 case NEED_HEADER:
2009 m->m_content_info = CI_HAVE_HEADER;
2010 break;
2011 case NEED_BODY:
2012 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
2013 m->m_xlines = m->m_lines;
2014 m->m_xsize = m->m_size;
2015 break;
2018 free(line);
2019 NYD_LEAVE;
2020 return excess;
2023 static void
2024 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2025 const char *head, size_t headsize, long headlines)
2027 off_t offset;
2028 size_t len;
2029 NYD_ENTER;
2031 len = strlen(str);
2032 fseek(mp->mb_otf, 0L, SEEK_END);
2033 offset = ftell(mp->mb_otf);
2034 if (head)
2035 fwrite(head, 1, headsize, mp->mb_otf);
2036 if (len > 0) {
2037 fwrite(str, 1, len, mp->mb_otf);
2038 fputc('\n', mp->mb_otf);
2039 ++len;
2041 fflush(mp->mb_otf);
2043 if (m != NULL) {
2044 m->m_size = headsize + len;
2045 m->m_lines = headlines + 1;
2046 m->m_block = mailx_blockof(offset);
2047 m->m_offset = mailx_offsetof(offset);
2048 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2049 m->m_xlines = m->m_lines;
2050 m->m_xsize = m->m_size;
2052 NYD_LEAVE;
2055 static enum okay
2056 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2058 char o[LINESIZE];
2059 struct message mt;
2060 sighandler_type volatile saveint, savepipe;
2061 char * volatile head;
2062 char const *cp, *loc, * volatile item, * volatile resp;
2063 size_t expected;
2064 size_t volatile headsize;
2065 int number;
2066 FILE *queuefp;
2067 long volatile headlines;
2068 long n;
2069 ul_i volatile u;
2070 enum okay ok;
2071 NYD_X;
2073 saveint = savepipe = SIG_IGN;
2074 head = NULL;
2075 cp = loc = item = resp = NULL;
2076 headsize = 0;
2077 number = (int)PTR2SIZE(m - message + 1);
2078 queuefp = NULL;
2079 headlines = 0;
2080 u = 0;
2081 ok = STOP;
2083 if (getcache(mp, m, need) == OKAY)
2084 return OKAY;
2085 if (mp->mb_type == MB_CACHE) {
2086 n_err(_("Message %lu not available\n"), (ul_i)number);
2087 return STOP;
2090 if (mp->mb_sock.s_fd < 0) {
2091 n_err(_("IMAP connection closed\n"));
2092 return STOP;
2095 switch (need) {
2096 case NEED_HEADER:
2097 resp = item = "RFC822.HEADER";
2098 break;
2099 case NEED_BODY:
2100 item = "BODY.PEEK[]";
2101 resp = "BODY[]";
2102 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2103 char *hdr = smalloc(m->m_size);
2104 fflush(mp->mb_otf);
2105 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2106 SEEK_SET) < 0 ||
2107 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2108 free(hdr);
2109 break;
2111 head = hdr;
2112 headsize = m->m_size;
2113 headlines = m->m_lines;
2114 item = "BODY.PEEK[TEXT]";
2115 resp = "BODY[TEXT]";
2117 break;
2118 case NEED_UNSPEC:
2119 return STOP;
2122 imaplock = 1;
2123 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2124 if (sigsetjmp(imapjmp, 1)) {
2125 safe_signal(SIGINT, saveint);
2126 safe_signal(SIGPIPE, savepipe);
2127 imaplock = 0;
2128 return STOP;
2130 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2131 safe_signal(SIGINT, &_imap_maincatch);
2132 if (savepipe != SIG_IGN)
2133 safe_signal(SIGPIPE, imapcatch);
2135 if (m->m_uid)
2136 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2137 tag(1), m->m_uid, item);
2138 else {
2139 if (check_expunged() == STOP)
2140 goto out;
2141 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2143 IMAP_OUT(o, MB_COMD, goto out)
2144 for (;;) {
2145 ok = imap_answer(mp, 1);
2146 if (ok == STOP)
2147 break;
2148 if (response_status != RESPONSE_OTHER ||
2149 response_other != MESSAGE_DATA_FETCH)
2150 continue;
2151 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2152 continue;
2153 if (m->m_uid) {
2154 if ((cp = asccasestr(responded_other_text, "UID "))) {
2155 u = atol(&cp[4]);
2156 n = 0;
2157 } else {
2158 u = 0;
2159 n = -1;
2161 } else
2162 n = responded_other_number;
2163 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2164 if (m->m_uid ? m->m_uid != u : n != number)
2165 continue;
2166 if ((cp = strchr(loc, '"')) != NULL) {
2167 cp = imap_unquotestr(cp);
2168 imap_putstr(mp, m, cp, head, headsize, headlines);
2169 } else {
2170 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2171 m->m_xlines = m->m_lines;
2172 m->m_xsize = m->m_size;
2174 goto out;
2176 expected = atol(&cp[1]);
2177 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2178 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2179 continue;
2181 mt = *m;
2182 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2183 if (n >= 0) {
2184 commitmsg(mp, m, &mt, mt.m_content_info);
2185 break;
2187 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2188 if (n_poption & n_PO_VERBVERB)
2189 fputs(imapbuf, stderr);
2190 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2191 u = atol(&cp[4]);
2192 if (u == m->m_uid) {
2193 commitmsg(mp, m, &mt, mt.m_content_info);
2194 break;
2199 out:
2200 while (mp->mb_active & MB_COMD)
2201 ok = imap_answer(mp, 1);
2203 if (saveint != SIG_IGN)
2204 safe_signal(SIGINT, saveint);
2205 if (savepipe != SIG_IGN)
2206 safe_signal(SIGPIPE, savepipe);
2207 imaplock--;
2209 if (ok == OKAY)
2210 putcache(mp, m);
2211 if (head != NULL)
2212 free(head);
2213 if (interrupts)
2214 n_go_onintr_for_imap();
2215 return ok;
2218 FL enum okay
2219 imap_header(struct message *m)
2221 enum okay rv;
2222 NYD_ENTER;
2224 rv = imap_get(&mb, m, NEED_HEADER);
2225 NYD_LEAVE;
2226 return rv;
2230 FL enum okay
2231 imap_body(struct message *m)
2233 enum okay rv;
2234 NYD_ENTER;
2236 rv = imap_get(&mb, m, NEED_BODY);
2237 NYD_LEAVE;
2238 return rv;
2241 static void
2242 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2243 enum content_info content_info)
2245 NYD_ENTER;
2246 tomp->m_size = frommp->m_size;
2247 tomp->m_lines = frommp->m_lines;
2248 tomp->m_block = frommp->m_block;
2249 tomp->m_offset = frommp->m_offset;
2250 tomp->m_content_info = content_info & CI_HAVE_MASK;
2251 if (content_info & CI_HAVE_BODY) {
2252 tomp->m_xlines = frommp->m_lines;
2253 tomp->m_xsize = frommp->m_size;
2255 putcache(mp, tomp);
2256 NYD_LEAVE;
2259 static enum okay
2260 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2262 /* bot > topp */
2263 char o[LINESIZE];
2264 char const *cp;
2265 struct message mt;
2266 size_t expected;
2267 int n = 0, u;
2268 FILE *queuefp = NULL;
2269 enum okay ok;
2270 NYD_X;
2272 if (m[bot].m_uid)
2273 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2274 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2275 else {
2276 if (check_expunged() == STOP)
2277 return STOP;
2278 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2279 tag(1), bot, topp);
2281 IMAP_OUT(o, MB_COMD, return STOP)
2283 srelax_hold();
2284 for (;;) {
2285 ok = imap_answer(mp, 1);
2286 if (response_status != RESPONSE_OTHER)
2287 break;
2288 if (response_other != MESSAGE_DATA_FETCH)
2289 continue;
2290 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2291 srelax_rele();
2292 return STOP;
2294 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2295 continue;
2296 expected = atol(&cp[1]);
2297 if (m[bot-1].m_uid) {
2298 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2299 u = atoi(&cp[4]);
2300 for (n = bot; n <= topp; n++)
2301 if ((unsigned long)u == m[n-1].m_uid)
2302 break;
2303 if (n > topp) {
2304 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2305 continue;
2307 } else
2308 n = -1;
2309 } else {
2310 n = responded_other_number;
2311 if (n <= 0 || n > msgCount) {
2312 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2313 continue;
2316 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2317 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2318 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2319 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2320 if (n_poption & n_PO_VERBVERB)
2321 fputs(imapbuf, stderr);
2322 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2323 u = atoi(&cp[4]);
2324 for (n = bot; n <= topp; n++)
2325 if ((unsigned long)u == m[n-1].m_uid)
2326 break;
2327 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2328 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2331 srelax();
2333 srelax_rele();
2335 while (mp->mb_active & MB_COMD)
2336 ok = imap_answer(mp, 1);
2337 return ok;
2340 FL void
2341 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2343 sighandler_type saveint, savepipe;
2344 /*enum okay ok = STOP;*/
2345 int i, chunk = 256;
2346 NYD_X;
2348 if (mb.mb_type == MB_CACHE)
2349 return;
2350 if (bot < 1)
2351 bot = 1;
2352 if (topp > msgCount)
2353 topp = msgCount;
2354 for (i = bot; i < topp; i++) {
2355 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2356 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2357 bot = i+1;
2358 else
2359 break;
2361 for (i = topp; i > bot; i--) {
2362 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2363 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2364 topp = i-1;
2365 else
2366 break;
2368 if (bot >= topp)
2369 return;
2371 imaplock = 1;
2372 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2373 safe_signal(SIGINT, &_imap_maincatch);
2374 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2375 if (sigsetjmp(imapjmp, 1) == 0) {
2376 if (savepipe != SIG_IGN)
2377 safe_signal(SIGPIPE, imapcatch);
2379 for (i = bot; i <= topp; i += chunk) {
2380 int j = i + chunk - 1;
2381 j = n_MIN(j, topp);
2382 if (visible(message + j))
2383 /*ok = */imap_fetchheaders(&mb, message, i, j);
2384 if (interrupts)
2385 n_go_onintr_for_imap(); /* XXX imaplock? */
2388 safe_signal(SIGINT, saveint);
2389 safe_signal(SIGPIPE, savepipe);
2390 imaplock = 0;
2393 static enum okay
2394 __imap_exit(struct mailbox *mp)
2396 char o[LINESIZE];
2397 FILE *queuefp = NULL;
2398 NYD_X;
2400 mp->mb_active |= MB_BYE;
2401 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2402 IMAP_OUT(o, MB_COMD, return STOP)
2403 IMAP_ANSWER()
2404 return OKAY;
2407 static enum okay
2408 imap_exit(struct mailbox *mp)
2410 enum okay rv;
2411 NYD_ENTER;
2413 rv = __imap_exit(mp);
2414 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2415 free(mp->mb_imap_pass);
2416 free(mp->mb_imap_account);
2417 free(mp->mb_imap_mailbox);
2418 if (mp->mb_cache_directory != NULL)
2419 free(mp->mb_cache_directory);
2420 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2421 mp->mb_imap_account =
2422 mp->mb_imap_mailbox =
2423 mp->mb_cache_directory = "";
2424 #else
2425 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2426 mp->mb_imap_mailbox = NULL;
2427 mp->mb_cache_directory = NULL;
2428 #endif
2429 #endif
2430 sclose(&mp->mb_sock);
2431 NYD_LEAVE;
2432 return rv;
2435 static enum okay
2436 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2438 NYD_ENTER;
2439 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2440 if (mp->mb_type == MB_IMAP)
2441 delcache(mp, m);
2442 NYD_LEAVE;
2443 return OKAY;
2446 static enum okay
2447 imap_close(struct mailbox *mp)
2449 char o[LINESIZE];
2450 FILE *queuefp = NULL;
2451 NYD_X;
2453 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2454 IMAP_OUT(o, MB_COMD, return STOP)
2455 IMAP_ANSWER()
2456 return OKAY;
2459 static enum okay
2460 imap_update(struct mailbox *mp)
2462 struct message *m;
2463 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2464 NYD_ENTER;
2466 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2467 holdbits();
2468 c = 0;
2469 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2470 if (m->m_flag & MBOX)
2471 ++c;
2472 if (c > 0)
2473 if (makembox() == STOP)
2474 goto jbypass;
2477 gotcha = held = 0;
2478 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2479 if (mp->mb_perm == 0)
2480 dodel = 0;
2481 else if (n_pstate & n_PS_EDIT)
2482 dodel = ((m->m_flag & MDELETED) != 0);
2483 else
2484 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2486 /* Fetch the result after around each 800 STORE commands
2487 * sent (approx. 32k data sent). Otherwise, servers will
2488 * try to flush the return queue at some point, leading
2489 * to a deadlock if we are still writing commands but not
2490 * reading their results */
2491 needstat = stored > 0 && stored % 800 == 0;
2492 /* Even if this message has been deleted, continue
2493 * to set further flags. This is necessary to support
2494 * Gmail semantics, where "delete" actually means
2495 * "archive", and the flags are applied to the copy
2496 * in "All Mail" */
2497 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2498 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2499 stored++;
2501 if (m->m_flag & MFLAG) {
2502 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2503 stored++;
2505 if (m->m_flag & MUNFLAG) {
2506 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2507 stored++;
2509 if (m->m_flag & MANSWER) {
2510 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2511 stored++;
2513 if (m->m_flag & MUNANSWER) {
2514 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2515 stored++;
2517 if (m->m_flag & MDRAFT) {
2518 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2519 stored++;
2521 if (m->m_flag & MUNDRAFT) {
2522 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2523 stored++;
2526 if (dodel) {
2527 imap_delete(mp, m-message+1, m, needstat);
2528 stored++;
2529 gotcha++;
2530 } else if (mp->mb_type != MB_CACHE ||
2531 (!(n_pstate & n_PS_EDIT) &&
2532 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2533 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2534 (MPRESERVE | MTOUCH) ||
2535 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2536 held++;
2537 if (m->m_flag & MNEW) {
2538 m->m_flag &= ~MNEW;
2539 m->m_flag |= MSTATUS;
2542 jbypass:
2543 if (gotcha)
2544 imap_close(mp);
2546 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2547 if (!(m->m_flag & MUNLINKED) &&
2548 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2549 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2550 putcache(mp, m);
2551 modflags++;
2554 /* XXX should be readonly (but our IMAP code is weird...) */
2555 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2556 mb.mb_perm != 0) {
2557 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2558 printf(_("\"%s\" "), displayname);
2559 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2560 ? _("complete\n") : _("updated.\n"));
2561 } else if (held && !(n_pstate & n_PS_EDIT)) {
2562 if (held == 1)
2563 printf(_("Held 1 message in %s\n"), displayname);
2564 else
2565 printf(_("Held %d messages in %s\n"), held, displayname);
2567 fflush(stdout);
2569 NYD_LEAVE;
2570 return OKAY;
2573 FL bool_t
2574 imap_quit(bool_t hold_sigs_on)
2576 sighandler_type volatile saveint, savepipe;
2577 bool_t rv;
2578 NYD_ENTER;
2580 if(hold_sigs_on)
2581 rele_sigs();
2583 if (mb.mb_type == MB_CACHE) {
2584 rv = (imap_update(&mb) == OKAY);
2585 goto jleave;
2588 rv = FAL0;
2590 if (mb.mb_sock.s_fd < 0) {
2591 n_err(_("IMAP connection closed\n"));
2592 goto jleave;
2595 imaplock = 1;
2596 saveint = safe_signal(SIGINT, SIG_IGN);
2597 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2598 if (sigsetjmp(imapjmp, 1)) {
2599 safe_signal(SIGINT, saveint);
2600 safe_signal(SIGPIPE, saveint);
2601 imaplock = 0;
2602 goto jleave;
2604 if (saveint != SIG_IGN)
2605 safe_signal(SIGINT, imapcatch);
2606 if (savepipe != SIG_IGN)
2607 safe_signal(SIGPIPE, imapcatch);
2609 rv = (imap_update(&mb) == OKAY);
2610 if(!same_imap_account && imap_exit(&mb) != OKAY)
2611 rv = FAL0;
2613 safe_signal(SIGINT, saveint);
2614 safe_signal(SIGPIPE, savepipe);
2615 imaplock = 0;
2616 jleave:
2617 if(hold_sigs_on)
2618 hold_sigs();
2619 NYD_LEAVE;
2620 return rv;
2623 static enum okay
2624 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2625 int needstat)
2627 char o[LINESIZE];
2628 FILE *queuefp = NULL;
2629 NYD_X;
2631 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2632 return STOP;
2633 if (m->m_uid)
2634 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2635 tag(1), m->m_uid, c, sp);
2636 else {
2637 if (check_expunged() == STOP)
2638 return STOP;
2639 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2641 IMAP_OUT(o, MB_COMD, return STOP)
2642 if (needstat)
2643 IMAP_ANSWER()
2644 else
2645 mb.mb_active &= ~MB_COMD;
2646 if (queuefp != NULL)
2647 Fclose(queuefp);
2648 return OKAY;
2651 FL enum okay
2652 imap_undelete(struct message *m, int n)
2654 enum okay rv;
2655 NYD_ENTER;
2657 rv = imap_unstore(m, n, "\\Deleted");
2658 NYD_LEAVE;
2659 return rv;
2662 FL enum okay
2663 imap_unread(struct message *m, int n)
2665 enum okay rv;
2666 NYD_ENTER;
2668 rv = imap_unstore(m, n, "\\Seen");
2669 NYD_LEAVE;
2670 return rv;
2673 static enum okay
2674 imap_unstore(struct message *m, int n, const char *flag)
2676 sighandler_type saveint, savepipe;
2677 enum okay volatile rv = STOP;
2678 NYD_ENTER;
2680 imaplock = 1;
2681 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2682 safe_signal(SIGINT, &_imap_maincatch);
2683 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2684 if (sigsetjmp(imapjmp, 1) == 0) {
2685 if (savepipe != SIG_IGN)
2686 safe_signal(SIGPIPE, imapcatch);
2688 rv = imap_store(&mb, m, n, '-', flag, 1);
2690 safe_signal(SIGINT, saveint);
2691 safe_signal(SIGPIPE, savepipe);
2692 imaplock = 0;
2694 NYD_LEAVE;
2695 if (interrupts)
2696 n_go_onintr_for_imap();
2697 return rv;
2700 static const char *
2701 tag(int new)
2703 static char ts[20];
2704 static long n;
2705 NYD2_ENTER;
2707 if (new)
2708 ++n;
2709 snprintf(ts, sizeof ts, "T%lu", n);
2710 NYD2_LEAVE;
2711 return ts;
2714 FL int
2715 c_imapcodec(void *vp){
2716 bool_t err;
2717 size_t alen;
2718 char const **argv, *varname, *varres, *act, *cp;
2719 NYD_ENTER;
2721 argv = vp;
2722 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2724 act = *argv;
2725 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2727 if(act == cp)
2728 goto jesynopsis;
2729 alen = PTR2SIZE(cp - act);
2730 if(*cp != '\0')
2731 ++cp;
2733 n_pstate_err_no = n_ERR_NONE;
2734 varres = imap_path_normalize(NULL, cp);
2736 if(is_ascncaseprefix(act, "encode", alen))
2737 varres = imap_path_encode(varres, &err);
2738 else if(is_ascncaseprefix(act, "decode", alen))
2739 varres = imap_path_decode(varres, &err);
2740 else
2741 goto jesynopsis;
2743 if(err){
2744 n_pstate_err_no = n_ERR_CANCELED;
2745 varres = cp;
2746 vp = NULL;
2749 if(varname != NULL){
2750 if(!n_var_vset(varname, (uintptr_t)varres)){
2751 n_pstate_err_no = n_ERR_NOTSUP;
2752 vp = NULL;
2754 }else{
2755 struct str in, out;
2757 in.l = strlen(in.s = n_UNCONST(varres));
2758 makeprint(&in, &out);
2759 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2760 n_pstate_err_no = n_err_no;
2761 vp = NULL;
2763 free(out.s);
2766 jleave:
2767 NYD_LEAVE;
2768 return (vp != NULL ? 0 : 1);
2769 jesynopsis:
2770 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2771 n_pstate_err_no = n_ERR_INVAL;
2772 vp = NULL;
2773 goto jleave;
2776 FL int
2777 c_imap_imap(void *vp)
2779 char o[LINESIZE];
2780 sighandler_type saveint, savepipe;
2781 struct mailbox *mp = &mb;
2782 FILE *queuefp = NULL;
2783 enum okay volatile ok = STOP;
2784 NYD_X;
2786 if (mp->mb_type != MB_IMAP) {
2787 printf("Not operating on an IMAP mailbox.\n");
2788 return 1;
2790 imaplock = 1;
2791 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2792 safe_signal(SIGINT, &_imap_maincatch);
2793 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2794 if (sigsetjmp(imapjmp, 1) == 0) {
2795 if (savepipe != SIG_IGN)
2796 safe_signal(SIGPIPE, imapcatch);
2798 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2799 IMAP_OUT(o, MB_COMD, goto out)
2800 while (mp->mb_active & MB_COMD) {
2801 ok = imap_answer(mp, 0);
2802 fputs(responded_text, stdout);
2805 out:
2806 safe_signal(SIGINT, saveint);
2807 safe_signal(SIGPIPE, savepipe);
2808 imaplock = 0;
2810 if (interrupts)
2811 n_go_onintr_for_imap();
2812 return ok != OKAY;
2815 FL int
2816 imap_newmail(int nmail)
2818 NYD_ENTER;
2820 if (nmail && had_exists < 0 && had_expunge < 0) {
2821 imaplock = 1;
2822 imap_noop();
2823 imaplock = 0;
2826 if (had_exists == msgCount && had_expunge < 0)
2827 /* Some servers always respond with EXISTS to NOOP. If
2828 * the mailbox has been changed but the number of messages
2829 * has not, an EXPUNGE must also had been sent; otherwise,
2830 * nothing has changed */
2831 had_exists = -1;
2832 NYD_LEAVE;
2833 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2836 static char *
2837 imap_putflags(int f)
2839 const char *cp;
2840 char *buf, *bp;
2841 NYD2_ENTER;
2843 bp = buf = salloc(100);
2844 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2845 *bp++ = '(';
2846 if (f & MREAD) {
2847 if (bp[-1] != '(')
2848 *bp++ = ' ';
2849 for (cp = "\\Seen"; *cp; cp++)
2850 *bp++ = *cp;
2852 if (f & MFLAGGED) {
2853 if (bp[-1] != '(')
2854 *bp++ = ' ';
2855 for (cp = "\\Flagged"; *cp; cp++)
2856 *bp++ = *cp;
2858 if (f & MANSWERED) {
2859 if (bp[-1] != '(')
2860 *bp++ = ' ';
2861 for (cp = "\\Answered"; *cp; cp++)
2862 *bp++ = *cp;
2864 if (f & MDRAFT) {
2865 if (bp[-1] != '(')
2866 *bp++ = ' ';
2867 for (cp = "\\Draft"; *cp; cp++)
2868 *bp++ = *cp;
2870 *bp++ = ')';
2871 *bp++ = ' ';
2873 *bp = '\0';
2874 NYD2_LEAVE;
2875 return buf;
2878 static void
2879 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2881 NYD2_ENTER;
2882 while (*cp != ')') {
2883 if (*cp == '\\') {
2884 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2885 *f |= MREAD;
2886 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2887 *f |= MNEW;
2888 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2889 *f |= MDELETED;
2890 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2891 *f |= MFLAGGED;
2892 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2893 *f |= MANSWERED;
2894 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2895 *f |= MDRAFTED;
2897 cp++;
2900 if (xp != NULL)
2901 *xp = cp;
2902 NYD2_LEAVE;
2905 static enum okay
2906 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2907 long xsize, enum mflag flag, time_t t)
2909 char o[LINESIZE], *buf;
2910 size_t bufsize, buflen, cnt;
2911 long size, lines, ysize;
2912 char const *qname;
2913 bool_t twice;
2914 FILE *queuefp;
2915 enum okay rv;
2916 NYD_ENTER;
2918 rv = STOP;
2919 queuefp = NULL;
2920 twice = FAL0;
2921 buf = NULL;
2923 if((qname = imap_path_quote(mp, name)) == NULL)
2924 goto jleave;
2926 if (mp->mb_type == MB_CACHE) {
2927 queuefp = cache_queue(mp);
2928 if (queuefp == NULL) {
2929 buf = NULL;
2930 goto jleave;
2932 rv = OKAY;
2935 buf = smalloc(bufsize = LINESIZE);
2936 buflen = 0;
2937 jagain:
2938 size = xsize;
2939 cnt = fsize(fp);
2940 if (fseek(fp, off1, SEEK_SET) < 0) {
2941 rv = STOP;
2942 goto jleave;
2945 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2946 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2947 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2948 while (mp->mb_active & MB_COMD) {
2949 rv = imap_answer(mp, twice);
2950 if (response_type == RESPONSE_CONT)
2951 break;
2954 if (mp->mb_type != MB_CACHE && rv == STOP) {
2955 if (!twice)
2956 goto jtrycreate;
2957 else
2958 goto jleave;
2961 lines = ysize = 0;
2962 while (size > 0) {
2963 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2964 lines++;
2965 ysize += buflen;
2966 buf[buflen - 1] = '\r';
2967 buf[buflen] = '\n';
2968 if (mp->mb_type != MB_CACHE)
2969 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2970 else if (queuefp)
2971 fwrite(buf, 1, buflen+1, queuefp);
2972 size -= buflen + 1;
2974 if (mp->mb_type != MB_CACHE)
2975 swrite(&mp->mb_sock, "\r\n");
2976 else if (queuefp)
2977 fputs("\r\n", queuefp);
2978 while (mp->mb_active & MB_COMD) {
2979 rv = imap_answer(mp, 0);
2980 if (response_status == RESPONSE_NO /*&&
2981 ascncasecmp(responded_text,
2982 "[TRYCREATE] ", 12) == 0*/) {
2983 jtrycreate:
2984 if (twice) {
2985 rv = STOP;
2986 goto jleave;
2988 twice = TRU1;
2989 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2990 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2991 while (mp->mb_active & MB_COMD)
2992 rv = imap_answer(mp, 1);
2993 if (rv == STOP)
2994 goto jleave;
2995 imap_created_mailbox++;
2996 goto jagain;
2997 } else if (rv != OKAY)
2998 n_err(_("IMAP error: %s"), responded_text);
2999 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
3000 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
3002 jleave:
3003 if (queuefp != NULL)
3004 Fclose(queuefp);
3005 if (buf != NULL)
3006 free(buf);
3007 NYD_LEAVE;
3008 return rv;
3011 static enum okay
3012 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3014 char *buf, *bp, *lp;
3015 size_t bufsize, buflen, cnt;
3016 off_t off1 = -1, offs;
3017 int flag;
3018 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3019 time_t tim;
3020 long size;
3021 enum okay rv;
3022 NYD_ENTER;
3024 buf = smalloc(bufsize = LINESIZE);
3025 buflen = 0;
3026 cnt = fsize(fp);
3027 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3028 time(&tim);
3029 size = 0;
3031 for (flag = MNEW, state = _NLSEP;;) {
3032 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3034 if (bp == NULL ||
3035 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3036 is_head(buf, buflen, FAL0))) {
3037 if (off1 != (off_t)-1) {
3038 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3039 if (rv == STOP)
3040 goto jleave;
3041 fseek(fp, offs+buflen, SEEK_SET);
3043 off1 = offs + buflen;
3044 size = 0;
3045 flag = MNEW;
3046 state = _INHEAD;
3047 if (bp == NULL)
3048 break;
3049 tim = unixtime(buf);
3050 } else
3051 size += buflen+1;
3052 offs += buflen;
3054 state &= ~_NLSEP;
3055 if (buf[0] == '\n') {
3056 state &= ~_INHEAD;
3057 state |= _NLSEP;
3058 } else if (state & _INHEAD) {
3059 if (ascncasecmp(buf, "status", 6) == 0) {
3060 lp = &buf[6];
3061 while (whitechar(*lp))
3062 lp++;
3063 if (*lp == ':')
3064 while (*++lp != '\0')
3065 switch (*lp) {
3066 case 'R':
3067 flag |= MREAD;
3068 break;
3069 case 'O':
3070 flag &= ~MNEW;
3071 break;
3073 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3074 lp = &buf[8];
3075 while (whitechar(*lp))
3076 lp++;
3077 if (*lp == ':')
3078 while (*++lp != '\0')
3079 switch (*lp) {
3080 case 'F':
3081 flag |= MFLAGGED;
3082 break;
3083 case 'A':
3084 flag |= MANSWERED;
3085 break;
3086 case 'T':
3087 flag |= MDRAFTED;
3088 break;
3093 rv = OKAY;
3094 jleave:
3095 free(buf);
3096 NYD_LEAVE;
3097 return rv;
3100 FL enum okay
3101 imap_append(const char *xserver, FILE *fp, long offset)
3103 sighandler_type volatile saveint, savepipe;
3104 struct url url;
3105 struct ccred ccred;
3106 enum okay rv = STOP;
3107 NYD_ENTER;
3109 if (!url_parse(&url, CPROTO_IMAP, xserver))
3110 goto j_leave;
3111 if (!ok_blook(v15_compat) &&
3112 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3113 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3114 assert(url.url_path.s != NULL);
3116 imaplock = 1;
3117 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3118 safe_signal(SIGINT, &_imap_maincatch);
3119 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3120 if (sigsetjmp(imapjmp, 1))
3121 goto jleave;
3122 if (savepipe != SIG_IGN)
3123 safe_signal(SIGPIPE, imapcatch);
3125 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3126 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3127 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3128 } else {
3129 struct mailbox mx;
3131 memset(&mx, 0, sizeof mx);
3133 if (!_imap_getcred(&mx, &ccred, &url))
3134 goto jleave;
3136 imap_delim_init(&mx, &url);
3137 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3139 if (disconnected(url.url_p_eu_h_p) == 0) {
3140 if (!sopen(&mx.mb_sock, &url))
3141 goto jfail;
3142 mx.mb_sock.s_desc = "IMAP";
3143 mx.mb_type = MB_IMAP;
3144 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3145 /* TODO the code now did
3146 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3147 * TODO though imap_mailbox is sfree()d and mbx
3148 * TODO is possibly even a constant
3149 * TODO i changed this to sstrdup() sofar, as is used
3150 * TODO somewhere else in this file for this! */
3151 if (imap_preauth(&mx, &url) != OKAY ||
3152 imap_auth(&mx, &ccred) != OKAY) {
3153 sclose(&mx.mb_sock);
3154 goto jfail;
3156 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3157 imap_exit(&mx);
3158 } else {
3159 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3160 mx.mb_type = MB_CACHE;
3161 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3163 jfail:
3167 jleave:
3168 safe_signal(SIGINT, saveint);
3169 safe_signal(SIGPIPE, savepipe);
3170 imaplock = 0;
3171 j_leave:
3172 NYD_LEAVE;
3173 if (interrupts)
3174 n_go_onintr_for_imap();
3175 return rv;
3178 static enum okay
3179 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3180 struct list_item **lend, int level)
3182 char o[LINESIZE], *cp;
3183 struct list_item *lp;
3184 const char *qname, *bp;
3185 FILE *queuefp;
3186 enum okay ok;
3187 NYD_X;
3189 ok = STOP;
3190 queuefp = NULL;
3192 if((qname = imap_path_quote(mp, base)) == NULL)
3193 goto jleave;
3195 *list = *lend = NULL;
3196 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3197 IMAP_OUT(o, MB_COMD, goto jleave)
3198 while (mp->mb_active & MB_COMD) {
3199 ok = imap_answer(mp, 1);
3200 if (response_status == RESPONSE_OTHER &&
3201 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3202 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3203 lp = csalloc(1, sizeof *lp);
3204 lp->l_name = cp;
3205 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3206 ++cp;
3207 lp->l_base = *cp ? cp : savestr(base);
3208 lp->l_attr = list_attributes;
3209 lp->l_level = level+1;
3210 lp->l_delim = list_hierarchy_delimiter;
3211 if (*list && *lend) {
3212 (*lend)->l_next = lp;
3213 *lend = lp;
3214 } else
3215 *list = *lend = lp;
3218 jleave:
3219 return ok;
3222 static enum okay
3223 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3225 struct list_item *list, *lend, *lp, *lx, *ly;
3226 int n, depth;
3227 const char *bp;
3228 char *cp;
3229 enum okay rv;
3230 NYD_ENTER;
3232 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3233 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3234 goto jleave;
3235 rv = OKAY;
3236 if (list == NULL || lend == NULL)
3237 goto jleave;
3239 for (lp = list; lp; lp = lp->l_next)
3240 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3241 !(lp->l_attr & LIST_NOINFERIORS)) {
3242 cp = salloc((n = strlen(lp->l_name)) + 2);
3243 memcpy(cp, lp->l_name, n);
3244 cp[n] = lp->l_delim;
3245 cp[n+1] = '\0';
3246 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3247 lp->l_has_children = 1;
3248 if (strcmp(cp, lx->l_name) == 0)
3249 lx = lx->l_next;
3250 if (lx) {
3251 lend->l_next = lx;
3252 lend = ly;
3257 for (lp = list; lp; lp = lp->l_next) {
3258 if (strip) {
3259 cp = lp->l_name;
3260 for (bp = base; *bp && *bp == *cp; bp++)
3261 cp++;
3262 } else
3263 cp = lp->l_name;
3264 if (!(lp->l_attr & LIST_NOSELECT))
3265 fprintf(fp, "%s\n", *cp ? cp : base);
3266 else if (lp->l_has_children == 0)
3267 fprintf(fp, "%s%c\n", *cp ? cp : base,
3268 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3270 jleave:
3271 NYD_LEAVE;
3272 return rv;
3275 FL int
3276 imap_folders(const char * volatile name, int strip)
3278 sighandler_type saveint, savepipe;
3279 const char * volatile fold, *cp, *sp;
3280 FILE * volatile fp;
3281 int rv = 1;
3282 NYD_ENTER;
3284 cp = protbase(name);
3285 sp = mb.mb_imap_account;
3286 if (sp == NULL || strcmp(cp, sp)) {
3287 n_err(
3288 _("Cannot perform `folders' but when on the very IMAP "
3289 "account; the current one is\n `%s' -- "
3290 "try `folders @'\n"),
3291 (sp != NULL ? sp : _("[NONE]")));
3292 goto jleave;
3295 fold = imap_fileof(name);
3296 if (n_psonce & n_PSO_TTYOUT) {
3297 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3298 == NULL) {
3299 n_perr(_("tmpfile"), 0);
3300 goto jleave;
3302 } else
3303 fp = stdout;
3305 imaplock = 1;
3306 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3307 safe_signal(SIGINT, &_imap_maincatch);
3308 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3309 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3310 goto junroll;
3311 if (savepipe != SIG_IGN)
3312 safe_signal(SIGPIPE, imapcatch);
3314 if (mb.mb_type == MB_CACHE)
3315 cache_list(&mb, fold, strip, fp);
3316 else
3317 imap_list(&mb, fold, strip, fp);
3319 imaplock = 0;
3320 if (interrupts) {
3321 if (n_psonce & n_PSO_TTYOUT)
3322 Fclose(fp);
3323 rv = 0;
3324 goto jleave;
3326 fflush(fp);
3328 if (n_psonce & n_PSO_TTYOUT) {
3329 rewind(fp);
3330 if (fsize(fp) > 0){
3331 dopr(fp);
3332 rv = 0;
3333 }else
3334 n_err(_("Folder not found\n"));
3335 }else
3336 rv = 0;
3337 junroll:
3338 safe_signal(SIGINT, saveint);
3339 safe_signal(SIGPIPE, savepipe);
3340 if (n_psonce & n_PSO_TTYOUT)
3341 Fclose(fp);
3342 jleave:
3343 NYD_LEAVE;
3344 if (interrupts)
3345 n_go_onintr_for_imap();
3346 return rv;
3349 static void
3350 dopr(FILE *fp)
3352 char o[LINESIZE];
3353 int c;
3354 long n = 0, mx = 0, columns, width;
3355 FILE *out;
3356 NYD_ENTER;
3358 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER))
3359 == NULL) {
3360 n_perr(_("tmpfile"), 0);
3361 goto jleave;
3364 while ((c = getc(fp)) != EOF) {
3365 if (c == '\n') {
3366 if (n > mx)
3367 mx = n;
3368 n = 0;
3369 } else
3370 ++n;
3372 rewind(fp);
3374 width = n_scrnwidth;
3375 if (mx < width / 2) {
3376 columns = width / (mx+2);
3377 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3378 } else
3379 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3380 n_child_run(ok_vlook(SHELL), NULL, fileno(fp), fileno(out), "-c", o, NULL,
3381 NULL, NULL);
3382 page_or_print(out, 0);
3383 Fclose(out);
3384 jleave:
3385 NYD_LEAVE;
3388 static enum okay
3389 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3391 char o[LINESIZE];
3392 const char *qname;
3393 bool_t twice, stored;
3394 FILE *queuefp;
3395 enum okay ok;
3396 NYD_X;
3398 ok = STOP;
3399 queuefp = NULL;
3400 twice = stored = FAL0;
3402 /* C99 */{
3403 size_t i;
3405 i = strlen(name = imap_fileof(name));
3406 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3407 name = savecat(name, "INBOX");
3408 if((qname = imap_path_quote(mp, name)) == NULL)
3409 goto jleave;
3412 if (mp->mb_type == MB_CACHE) {
3413 if ((queuefp = cache_queue(mp)) == NULL)
3414 goto jleave;
3415 ok = OKAY;
3418 /* Since it is not possible to set flags on the copy, recently
3419 * set flags must be set on the original to include it in the copy */
3420 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3421 imap_store(mp, m, n, '+', "\\Seen", 0);
3422 if (m->m_flag&MFLAG)
3423 imap_store(mp, m, n, '+', "\\Flagged", 0);
3424 if (m->m_flag&MUNFLAG)
3425 imap_store(mp, m, n, '-', "\\Flagged", 0);
3426 if (m->m_flag&MANSWER)
3427 imap_store(mp, m, n, '+', "\\Answered", 0);
3428 if (m->m_flag&MUNANSWER)
3429 imap_store(mp, m, n, '-', "\\Flagged", 0);
3430 if (m->m_flag&MDRAFT)
3431 imap_store(mp, m, n, '+', "\\Draft", 0);
3432 if (m->m_flag&MUNDRAFT)
3433 imap_store(mp, m, n, '-', "\\Draft", 0);
3434 again:
3435 if (m->m_uid)
3436 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3437 else {
3438 if (check_expunged() == STOP)
3439 goto out;
3440 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3442 IMAP_OUT(o, MB_COMD, goto out)
3443 while (mp->mb_active & MB_COMD)
3444 ok = imap_answer(mp, twice);
3446 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3447 response_status == RESPONSE_OK)
3448 imap_copyuid(mp, m, name);
3450 if (response_status == RESPONSE_NO && !twice) {
3451 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3452 IMAP_OUT(o, MB_COMD, goto out)
3453 while (mp->mb_active & MB_COMD)
3454 ok = imap_answer(mp, 1);
3455 if (ok == OKAY) {
3456 imap_created_mailbox++;
3457 goto again;
3461 if (queuefp != NULL)
3462 Fclose(queuefp);
3464 /* ... and reset the flag to its initial value so that the 'exit'
3465 * command still leaves the message unread */
3466 out:
3467 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3468 imap_store(mp, m, n, '-', "\\Seen", 0);
3469 stored = TRU1;
3471 if (m->m_flag & MFLAG) {
3472 imap_store(mp, m, n, '-', "\\Flagged", 0);
3473 stored = TRU1;
3475 if (m->m_flag & MUNFLAG) {
3476 imap_store(mp, m, n, '+', "\\Flagged", 0);
3477 stored = TRU1;
3479 if (m->m_flag & MANSWER) {
3480 imap_store(mp, m, n, '-', "\\Answered", 0);
3481 stored = TRU1;
3483 if (m->m_flag & MUNANSWER) {
3484 imap_store(mp, m, n, '+', "\\Answered", 0);
3485 stored = TRU1;
3487 if (m->m_flag & MDRAFT) {
3488 imap_store(mp, m, n, '-', "\\Draft", 0);
3489 stored = TRU1;
3491 if (m->m_flag & MUNDRAFT) {
3492 imap_store(mp, m, n, '+', "\\Draft", 0);
3493 stored = TRU1;
3495 if (stored) {
3496 mp->mb_active |= MB_COMD;
3497 (void)imap_finish(mp);
3499 jleave:
3500 return ok;
3503 FL enum okay
3504 imap_copy(struct message *m, int n, const char *name)
3506 sighandler_type saveint, savepipe;
3507 enum okay volatile rv = STOP;
3508 NYD_ENTER;
3510 imaplock = 1;
3511 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3512 safe_signal(SIGINT, &_imap_maincatch);
3513 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3514 if (sigsetjmp(imapjmp, 1) == 0) {
3515 if (savepipe != SIG_IGN)
3516 safe_signal(SIGPIPE, imapcatch);
3518 rv = imap_copy1(&mb, m, n, name);
3520 safe_signal(SIGINT, saveint);
3521 safe_signal(SIGPIPE, savepipe);
3522 imaplock = 0;
3524 NYD_LEAVE;
3525 if (interrupts)
3526 n_go_onintr_for_imap();
3527 return rv;
3530 static enum okay
3531 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3532 unsigned long *olduid, unsigned long *newuid)
3534 char *xp, *yp, *zp;
3535 enum okay rv;
3536 NYD_ENTER;
3538 *uidvalidity = strtoul(cp, &xp, 10);
3539 *olduid = strtoul(xp, &yp, 10);
3540 *newuid = strtoul(yp, &zp, 10);
3541 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3542 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3543 NYD_LEAVE;
3544 return rv;
3547 static enum okay
3548 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3549 unsigned long *uid)
3551 char *xp, *yp;
3552 enum okay rv;
3553 NYD_ENTER;
3555 *uidvalidity = strtoul(cp, &xp, 10);
3556 *uid = strtoul(xp, &yp, 10);
3557 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3558 *yp == ']');
3559 NYD_LEAVE;
3560 return rv;
3563 static enum okay
3564 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3566 struct mailbox xmb;
3567 struct message xm;
3568 const char *cp;
3569 unsigned long uidvalidity, olduid, newuid;
3570 enum okay rv;
3571 NYD_ENTER;
3573 rv = STOP;
3575 memset(&xmb, 0, sizeof xmb);
3577 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3578 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3579 goto jleave;
3581 rv = OKAY;
3583 xmb = *mp;
3584 xmb.mb_cache_directory = NULL;
3585 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3586 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3587 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3588 sizeof(xmb.mb_imap_delim));
3589 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3590 if (mp->mb_cache_directory != NULL)
3591 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3592 xmb.mb_uidvalidity = uidvalidity;
3593 initcache(&xmb);
3595 if (m == NULL) {
3596 memset(&xm, 0, sizeof xm);
3597 xm.m_uid = olduid;
3598 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3599 goto jleave;
3600 getcache(mp, &xm, NEED_HEADER);
3601 getcache(mp, &xm, NEED_BODY);
3602 } else {
3603 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3604 getcache(mp, m, NEED_HEADER);
3605 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3606 getcache(mp, m, NEED_BODY);
3607 xm = *m;
3609 xm.m_uid = newuid;
3610 xm.m_flag &= ~MFULLYCACHED;
3611 putcache(&xmb, &xm);
3612 jleave:
3613 if (xmb.mb_cache_directory != NULL)
3614 free(xmb.mb_cache_directory);
3615 if (xmb.mb_imap_mailbox != NULL)
3616 free(xmb.mb_imap_mailbox);
3617 if (xmb.mb_imap_pass != NULL)
3618 free(xmb.mb_imap_pass);
3619 if (xmb.mb_imap_account != NULL)
3620 free(xmb.mb_imap_account);
3621 NYD_LEAVE;
3622 return rv;
3625 static enum okay
3626 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3627 long size, long lines, int flag, const char *name)
3629 struct mailbox xmb;
3630 struct message xm;
3631 const char *cp;
3632 unsigned long uidvalidity, uid;
3633 enum okay rv;
3634 NYD_ENTER;
3636 rv = STOP;
3638 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3639 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3640 goto jleave;
3642 rv = OKAY;
3644 xmb = *mp;
3645 xmb.mb_cache_directory = NULL;
3646 /* XXX mb_imap_delim reused */
3647 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3648 xmb.mb_uidvalidity = uidvalidity;
3649 xmb.mb_otf = xmb.mb_itf = fp;
3650 initcache(&xmb);
3651 memset(&xm, 0, sizeof xm);
3652 xm.m_flag = (flag & MREAD) | MNEW;
3653 xm.m_time = t;
3654 xm.m_block = mailx_blockof(off1);
3655 xm.m_offset = mailx_offsetof(off1);
3656 xm.m_size = size;
3657 xm.m_xsize = xsize;
3658 xm.m_lines = xm.m_xlines = lines;
3659 xm.m_uid = uid;
3660 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3661 putcache(&xmb, &xm);
3663 free(xmb.mb_imap_mailbox);
3664 jleave:
3665 NYD_LEAVE;
3666 return rv;
3669 static enum okay
3670 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3672 FILE *tp = NULL;
3673 time_t t;
3674 long size, xsize, ysize, lines;
3675 enum mflag flag = MNEW;
3676 char *name, *buf, *bp;
3677 char const *cp;
3678 size_t bufsize, buflen, cnt;
3679 enum okay rv = STOP;
3680 NYD_ENTER;
3682 buf = smalloc(bufsize = LINESIZE);
3683 buflen = 0;
3684 cnt = fsize(fp);
3685 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3686 goto jstop;
3688 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3690 while (*bp == ' ')
3691 ++bp;
3693 if ((cp = strrchr(bp, '{')) == NULL)
3694 goto jstop;
3696 xsize = atol(&cp[1]) + 2;
3697 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3698 goto jstop;
3699 while (*cp == ' ')
3700 cp++;
3702 if (*cp == '(') {
3703 imap_getflags(cp, &cp, &flag);
3704 while (*++cp == ' ')
3707 t = imap_read_date_time(cp);
3709 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3710 == NULL)
3711 goto jstop;
3713 size = xsize;
3714 ysize = lines = 0;
3715 while (size > 0) {
3716 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3717 goto jstop;
3718 size -= buflen;
3719 buf[--buflen] = '\0';
3720 buf[buflen-1] = '\n';
3721 fwrite(buf, 1, buflen, tp);
3722 ysize += buflen;
3723 ++lines;
3725 fflush(tp);
3726 rewind(tp);
3728 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3729 imap_unquotestr(name));
3730 rv = OKAY;
3731 jstop:
3732 free(buf);
3733 if (tp)
3734 Fclose(tp);
3735 NYD_LEAVE;
3736 return rv;
3739 #ifdef HAVE_IMAP_SEARCH
3740 static enum okay
3741 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3742 int f)
3744 char *o, *xp, *cs, c;
3745 size_t osize;
3746 FILE *queuefp = NULL;
3747 int i;
3748 unsigned long n;
3749 const char *cp;
3750 enum okay ok = STOP;
3751 NYD_X;
3753 c = 0;
3754 for (cp = spec; *cp; cp++)
3755 c |= *cp;
3756 if (c & 0200) {
3757 cp = ok_vlook(ttycharset);
3758 # ifdef HAVE_ICONV
3759 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3760 iconv_t it;
3761 char *nsp, *nspec;
3762 size_t sz, nsz;
3764 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3765 sz = strlen(spec) + 1;
3766 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3767 if (n_iconv_buf(it, n_ICONV_DEFAULT,
3768 (char const**)&spec, &sz, &nsp, &nsz) == 0 &&
3769 sz == 0) {
3770 spec = nspec;
3771 cp = "utf-8";
3773 n_iconv_close(it);
3776 # endif
3777 cp = imap_quotestr(cp);
3778 cs = salloc(n = strlen(cp) + 10);
3779 snprintf(cs, n, "CHARSET %s ", cp);
3780 } else
3781 cs = n_UNCONST("");
3783 o = ac_alloc(osize = strlen(spec) + 60);
3784 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3785 IMAP_OUT(o, MB_COMD, goto out)
3786 while (mp->mb_active & MB_COMD) {
3787 ok = imap_answer(mp, 0);
3788 if (response_status == RESPONSE_OTHER &&
3789 response_other == MAILBOX_DATA_SEARCH) {
3790 xp = responded_other_text;
3791 while (*xp && *xp != '\r') {
3792 n = strtoul(xp, &xp, 10);
3793 for (i = 0; i < cnt; i++)
3794 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3795 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3796 mark(i+1, f);
3800 out:
3801 ac_free(o);
3802 return ok;
3805 FL enum okay
3806 imap_search1(const char * volatile spec, int f)
3808 sighandler_type saveint, savepipe;
3809 enum okay volatile rv = STOP;
3810 NYD_ENTER;
3812 if (mb.mb_type != MB_IMAP)
3813 goto jleave;
3815 imaplock = 1;
3816 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3817 safe_signal(SIGINT, &_imap_maincatch);
3818 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3819 if (sigsetjmp(imapjmp, 1) == 0) {
3820 if (savepipe != SIG_IGN)
3821 safe_signal(SIGPIPE, imapcatch);
3823 rv = imap_search2(&mb, message, msgCount, spec, f);
3825 safe_signal(SIGINT, saveint);
3826 safe_signal(SIGPIPE, savepipe);
3827 imaplock = 0;
3828 jleave:
3829 NYD_LEAVE;
3830 if (interrupts)
3831 n_go_onintr_for_imap();
3832 return rv;
3834 #endif /* HAVE_IMAP_SEARCH */
3836 FL int
3837 imap_thisaccount(const char *cp)
3839 int rv;
3840 NYD_ENTER;
3842 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3843 rv = 0;
3844 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3845 mb.mb_imap_account == NULL)
3846 rv = 0;
3847 else
3848 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3849 NYD_LEAVE;
3850 return rv;
3853 FL enum okay
3854 imap_remove(const char * volatile name)
3856 sighandler_type volatile saveint, savepipe;
3857 enum okay volatile rv = STOP;
3858 NYD_ENTER;
3860 if (mb.mb_type != MB_IMAP) {
3861 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3862 goto jleave;
3865 if (!imap_thisaccount(name)) {
3866 n_err(_("Can only remove mailboxes on current IMAP server: "
3867 "\"%s\" not removed\n"), name);
3868 goto jleave;
3871 imaplock = 1;
3872 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3873 safe_signal(SIGINT, &_imap_maincatch);
3874 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3875 if (sigsetjmp(imapjmp, 1) == 0) {
3876 if (savepipe != SIG_IGN)
3877 safe_signal(SIGPIPE, imapcatch);
3879 rv = imap_remove1(&mb, imap_fileof(name));
3881 safe_signal(SIGINT, saveint);
3882 safe_signal(SIGPIPE, savepipe);
3883 imaplock = 0;
3885 if (rv == OKAY)
3886 rv = cache_remove(name);
3887 jleave:
3888 NYD_LEAVE;
3889 if (interrupts)
3890 n_go_onintr_for_imap();
3891 return rv;
3894 static enum okay
3895 imap_remove1(struct mailbox *mp, const char *name)
3897 char *o;
3898 int os;
3899 char const *qname;
3900 FILE *queuefp;
3901 enum okay ok;
3902 NYD_X;
3904 ok = STOP;
3905 queuefp = NULL;
3907 if((qname = imap_path_quote(mp, name)) != NULL){
3908 o = ac_alloc(os = strlen(qname) + 100);
3909 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3910 IMAP_OUT(o, MB_COMD, goto out)
3911 while (mp->mb_active & MB_COMD)
3912 ok = imap_answer(mp, 1);
3913 out:
3914 ac_free(o);
3916 return ok;
3919 FL enum okay
3920 imap_rename(const char *old, const char *new)
3922 sighandler_type saveint, savepipe;
3923 enum okay volatile rv = STOP;
3924 NYD_ENTER;
3926 if (mb.mb_type != MB_IMAP) {
3927 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3928 goto jleave;
3931 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3932 n_err(_("Can only rename mailboxes on current IMAP "
3933 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3934 goto jleave;
3937 imaplock = 1;
3938 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3939 safe_signal(SIGINT, &_imap_maincatch);
3940 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3941 if (sigsetjmp(imapjmp, 1) == 0) {
3942 if (savepipe != SIG_IGN)
3943 safe_signal(SIGPIPE, imapcatch);
3945 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3947 safe_signal(SIGINT, saveint);
3948 safe_signal(SIGPIPE, savepipe);
3949 imaplock = 0;
3951 if (rv == OKAY)
3952 rv = cache_rename(old, new);
3953 jleave:
3954 NYD_LEAVE;
3955 if (interrupts)
3956 n_go_onintr_for_imap();
3957 return rv;
3960 static enum okay
3961 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3963 char *o;
3964 int os;
3965 char const *qoname, *qnname;
3966 FILE *queuefp;
3967 enum okay ok;
3968 NYD_X;
3970 ok = STOP;
3971 queuefp = NULL;
3973 if((qoname = imap_path_quote(mp, old)) != NULL &&
3974 (qnname = imap_path_quote(mp, new)) != NULL){
3975 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3976 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3977 IMAP_OUT(o, MB_COMD, goto out)
3978 while (mp->mb_active & MB_COMD)
3979 ok = imap_answer(mp, 1);
3980 out:
3981 ac_free(o);
3983 return ok;
3986 FL enum okay
3987 imap_dequeue(struct mailbox *mp, FILE *fp)
3989 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3990 size_t bufsize, buflen, cnt;
3991 long offs, offs1, offs2, octets;
3992 int twice, gotcha = 0;
3993 FILE *queuefp = NULL;
3994 enum okay ok = OKAY, rok = OKAY;
3995 NYD_X;
3997 buf = smalloc(bufsize = LINESIZE);
3998 buflen = 0;
3999 cnt = fsize(fp);
4000 while ((offs1 = ftell(fp)) >= 0 &&
4001 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
4002 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
4004 while (*bp == ' ')
4005 ++bp;
4006 twice = 0;
4007 if ((offs = ftell(fp)) < 0)
4008 goto fail;
4009 again:
4010 snprintf(o, sizeof o, "%s %s", tag(1), bp);
4011 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
4012 cp = &bp[9];
4013 while (digitchar(*cp))
4014 cp++;
4015 if (*cp != ' ')
4016 goto fail;
4017 while (*cp == ' ')
4018 cp++;
4019 if ((newname = imap_strex(cp, NULL)) == NULL)
4020 goto fail;
4021 IMAP_OUT(o, MB_COMD, continue)
4022 while (mp->mb_active & MB_COMD)
4023 ok = imap_answer(mp, twice);
4024 if (response_status == RESPONSE_NO && twice++ == 0)
4025 goto trycreate;
4026 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4027 imap_copyuid(mp, NULL, imap_unquotestr(newname));
4029 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
4030 IMAP_OUT(o, MB_COMD, continue)
4031 while (mp->mb_active & MB_COMD)
4032 ok = imap_answer(mp, 1);
4033 if (ok == OKAY)
4034 gotcha++;
4035 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
4036 if ((cp = strrchr(bp, '{')) == NULL)
4037 goto fail;
4038 octets = atol(&cp[1]) + 2;
4039 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
4040 goto fail;
4041 IMAP_OUT(o, MB_COMD, continue)
4042 while (mp->mb_active & MB_COMD) {
4043 ok = imap_answer(mp, twice);
4044 if (response_type == RESPONSE_CONT)
4045 break;
4047 if (ok == STOP) {
4048 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
4049 goto trycreate;
4050 goto fail;
4052 while (octets > 0) {
4053 size_t n = (UICMP(z, octets, >, sizeof iob)
4054 ? sizeof iob : (size_t)octets);
4055 octets -= n;
4056 if (n != fread(iob, 1, n, fp))
4057 goto fail;
4058 swrite1(&mp->mb_sock, iob, n, 1);
4060 swrite(&mp->mb_sock, "");
4061 while (mp->mb_active & MB_COMD) {
4062 ok = imap_answer(mp, 0);
4063 if (response_status == RESPONSE_NO && twice++ == 0) {
4064 if (fseek(fp, offs, SEEK_SET) < 0)
4065 goto fail;
4066 goto trycreate;
4069 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4070 if ((offs2 = ftell(fp)) < 0)
4071 goto fail;
4072 fseek(fp, offs1, SEEK_SET);
4073 if (imap_appenduid_cached(mp, fp) == STOP) {
4074 (void)fseek(fp, offs2, SEEK_SET);
4075 goto fail;
4078 } else {
4079 fail:
4080 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4081 rok = STOP;
4083 continue;
4084 trycreate:
4085 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4086 IMAP_OUT(o, MB_COMD, continue)
4087 while (mp->mb_active & MB_COMD)
4088 ok = imap_answer(mp, 1);
4089 if (ok == OKAY)
4090 goto again;
4092 fflush(fp);
4093 rewind(fp);
4094 ftruncate(fileno(fp), 0);
4095 if (gotcha)
4096 imap_close(mp);
4097 free(buf);
4098 return rok;
4101 static char *
4102 imap_strex(char const *cp, char const **xp)
4104 char const *cq;
4105 char *n = NULL;
4106 NYD_ENTER;
4108 if (*cp != '"')
4109 goto jleave;
4111 for (cq = cp + 1; *cq != '\0'; ++cq) {
4112 if (*cq == '\\')
4113 cq++;
4114 else if (*cq == '"')
4115 break;
4117 if (*cq != '"')
4118 goto jleave;
4120 n = salloc(cq - cp + 2);
4121 memcpy(n, cp, cq - cp +1);
4122 n[cq - cp + 1] = '\0';
4123 if (xp != NULL)
4124 *xp = cq + 1;
4125 jleave:
4126 NYD_LEAVE;
4127 return n;
4130 static enum okay
4131 check_expunged(void)
4133 enum okay rv;
4134 NYD_ENTER;
4136 if (expunged_messages > 0) {
4137 n_err(_("Command not executed - messages have been expunged\n"));
4138 rv = STOP;
4139 } else
4140 rv = OKAY;
4141 NYD_LEAVE;
4142 return rv;
4145 FL int
4146 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4148 struct url url;
4149 int rv, omsgCount = msgCount;
4150 NYD_ENTER;
4151 n_UNUSED(vp);
4153 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4154 n_err(_("Already connected\n"));
4155 rv = 1;
4156 goto jleave;
4159 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4160 rv = 1;
4161 goto jleave;
4163 ok_bclear(disconnected);
4164 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4166 if (mb.mb_type == MB_CACHE) {
4167 enum fedit_mode fm = FEDIT_NONE;
4168 if (_imap_rdonly)
4169 fm |= FEDIT_RDONLY;
4170 if (!(n_pstate & n_PS_EDIT))
4171 fm |= FEDIT_SYSBOX;
4172 _imap_setfile1(&url, fm, 1);
4173 if (msgCount > omsgCount)
4174 newmailinfo(omsgCount);
4176 rv = 0;
4177 jleave:
4178 NYD_LEAVE;
4179 return rv;
4182 FL int
4183 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4185 struct url url;
4186 int rv = 1, *msgvec = vp;
4187 NYD_ENTER;
4189 if (mb.mb_type == MB_CACHE) {
4190 n_err(_("Not connected\n"));
4191 goto jleave;
4193 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4194 n_err(_("The current mailbox is not cached\n"));
4195 goto jleave;
4198 if (!url_parse(&url, CPROTO_IMAP, mailname))
4199 goto jleave;
4201 if (*msgvec)
4202 c_cache(vp);
4203 ok_bset(disconnected);
4204 if (mb.mb_type == MB_IMAP) {
4205 enum fedit_mode fm = FEDIT_NONE;
4206 if (_imap_rdonly)
4207 fm |= FEDIT_RDONLY;
4208 if (!(n_pstate & n_PS_EDIT))
4209 fm |= FEDIT_SYSBOX;
4210 sclose(&mb.mb_sock);
4211 _imap_setfile1(&url, fm, 1);
4213 rv = 0;
4214 jleave:
4215 NYD_LEAVE;
4216 return rv;
4219 FL int
4220 c_cache(void *vp)
4222 int rv = 1, *msgvec = vp, *ip;
4223 struct message *mp;
4224 NYD_ENTER;
4226 if (mb.mb_type != MB_IMAP) {
4227 n_err(_("Not connected to an IMAP server\n"));
4228 goto jleave;
4230 if (cached_uidvalidity(&mb) == 0) {
4231 n_err(_("The current mailbox is not cached\n"));
4232 goto jleave;
4235 srelax_hold();
4236 for (ip = msgvec; *ip; ++ip) {
4237 mp = &message[*ip - 1];
4238 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4239 get_body(mp);
4240 srelax();
4243 srelax_rele();
4244 rv = 0;
4245 jleave:
4246 NYD_LEAVE;
4247 return rv;
4250 FL int
4251 disconnected(const char *file)
4253 struct url url;
4254 int rv = 1;
4255 NYD_ENTER;
4257 if (ok_blook(disconnected)) {
4258 rv = 1;
4259 goto jleave;
4262 if (!url_parse(&url, CPROTO_IMAP, file)) {
4263 rv = 0;
4264 goto jleave;
4266 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4268 jleave:
4269 NYD_LEAVE;
4270 return rv;
4273 FL void
4274 transflags(struct message *omessage, long omsgCount, int transparent)
4276 struct message *omp, *nmp, *newdot, *newprevdot;
4277 int hf;
4278 NYD_ENTER;
4280 omp = omessage;
4281 nmp = message;
4282 newdot = message;
4283 newprevdot = NULL;
4284 while (PTRCMP(omp, <, omessage + omsgCount) &&
4285 PTRCMP(nmp, <, message + msgCount)) {
4286 if (dot && nmp->m_uid == dot->m_uid)
4287 newdot = nmp;
4288 if (prevdot && nmp->m_uid == prevdot->m_uid)
4289 newprevdot = nmp;
4290 if (omp->m_uid == nmp->m_uid) {
4291 hf = nmp->m_flag & MHIDDEN;
4292 if (transparent && mb.mb_type == MB_IMAP)
4293 omp->m_flag &= ~MHIDDEN;
4294 *nmp++ = *omp++;
4295 if (transparent && mb.mb_type == MB_CACHE)
4296 nmp[-1].m_flag |= hf;
4297 } else if (omp->m_uid < nmp->m_uid)
4298 ++omp;
4299 else
4300 ++nmp;
4302 dot = newdot;
4303 setdot(newdot);
4304 prevdot = newprevdot;
4305 free(omessage);
4306 NYD_LEAVE;
4309 FL time_t
4310 imap_read_date_time(const char *cp)
4312 char buf[3];
4313 time_t t;
4314 int i, year, month, day, hour, minute, second, sign = -1;
4315 NYD2_ENTER;
4317 /* "25-Jul-2004 15:33:44 +0200"
4318 * | | | | | |
4319 * 0 5 10 15 20 25 */
4320 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4321 goto jinvalid;
4322 day = strtol(&cp[1], NULL, 10);
4323 for (i = 0;;) {
4324 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4325 break;
4326 if (n_month_names[++i][0] == '\0')
4327 goto jinvalid;
4329 month = i + 1;
4330 year = strtol(&cp[8], NULL, 10);
4331 hour = strtol(&cp[13], NULL, 10);
4332 minute = strtol(&cp[16], NULL, 10);
4333 second = strtol(&cp[19], NULL, 10);
4334 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4335 goto jinvalid;
4336 switch (cp[22]) {
4337 case '-':
4338 sign = 1;
4339 break;
4340 case '+':
4341 break;
4342 default:
4343 goto jinvalid;
4345 buf[2] = '\0';
4346 buf[0] = cp[23];
4347 buf[1] = cp[24];
4348 t += strtol(buf, NULL, 10) * sign * 3600;
4349 buf[0] = cp[25];
4350 buf[1] = cp[26];
4351 t += strtol(buf, NULL, 10) * sign * 60;
4352 jleave:
4353 NYD2_LEAVE;
4354 return t;
4355 jinvalid:
4356 time(&t);
4357 goto jleave;
4360 FL const char *
4361 imap_make_date_time(time_t t)
4363 static char s[40];
4364 struct tm *tmptr;
4365 int tzdiff, tzdiff_hour, tzdiff_min;
4366 NYD2_ENTER;
4368 tzdiff = t - mktime(gmtime(&t));
4369 tzdiff_hour = (int)(tzdiff / 60);
4370 tzdiff_min = tzdiff_hour % 60;
4371 tzdiff_hour /= 60;
4372 tmptr = localtime(&t);
4373 if (tmptr->tm_isdst > 0)
4374 tzdiff_hour++;
4375 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4376 tmptr->tm_mday, n_month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4377 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4378 NYD2_LEAVE;
4379 return s;
4382 FL char *
4383 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4385 char *n, *np;
4386 NYD2_ENTER;
4388 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4389 n_MEMORY_DEBUG_ARGSCALL);
4391 /* Just ignore the `is-system-mailbox' prefix XXX */
4392 if (cp[0] == '%' && cp[1] == ':')
4393 cp += 2;
4395 while (*cp != '\0') {
4396 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4397 *np++ = *cp++;
4398 *np++ = *cp++;
4399 *np++ = *cp++;
4400 } else if (cp[0] == '/')
4401 break;
4402 else
4403 *np++ = *cp++;
4405 *np = '\0';
4406 NYD2_LEAVE;
4407 return n;
4409 #endif /* HAVE_IMAP */
4411 /* s-it-mode */