collect(): ~[FfMmUu]: should default to the "dot" (Andrew Gee)
[s-mailx.git] / obs-imap.c
blobd403078bb41ce206e547438c2a57fdbd07663f22
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 #undef n_FILE
40 #define n_FILE obs_imap
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #ifdef HAVE_IMAP
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 EMPTY_FILE()
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() \
62 if (mp->mb_type != MB_CACHE) {\
63 enum okay ok = OKAY;\
64 while (mp->mb_active & MB_COMD)\
65 ok = imap_answer(mp, 1);\
66 if (ok == STOP)\
67 return STOP;\
71 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
72 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
73 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
74 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
75 * TODO I.e., that should be a function, not a macro ... or so.
76 * TODO This entire module needs MASSIVE work! */
77 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
78 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
80 if (mp->mb_type != MB_CACHE) {\
81 if (imap_finish(mp) == STOP) {\
82 ACTIONBAIL;\
84 if (n_poption & n_PO_D_VV)\
85 n_err(">>> %s", X);\
86 mp->mb_active |= Y;\
87 if (swrite(&mp->mb_sock, X) == STOP) {\
88 ACTIONERR;\
90 } else {\
91 if (queuefp != NULL)\
92 fputs(X, queuefp);\
96 static struct record {
97 struct record *rec_next;
98 unsigned long rec_count;
99 enum rec_type {
100 REC_EXISTS,
101 REC_EXPUNGE
102 } rec_type;
103 } *record, *recend;
105 static enum {
106 RESPONSE_TAGGED,
107 RESPONSE_DATA,
108 RESPONSE_FATAL,
109 RESPONSE_CONT,
110 RESPONSE_ILLEGAL
111 } response_type;
113 static enum {
114 RESPONSE_OK,
115 RESPONSE_NO,
116 RESPONSE_BAD,
117 RESPONSE_PREAUTH,
118 RESPONSE_BYE,
119 RESPONSE_OTHER,
120 RESPONSE_UNKNOWN
121 } response_status;
123 static char *responded_tag;
124 static char *responded_text;
125 static char *responded_other_text;
126 static long responded_other_number;
128 static enum {
129 MAILBOX_DATA_FLAGS,
130 MAILBOX_DATA_LIST,
131 MAILBOX_DATA_LSUB,
132 MAILBOX_DATA_MAILBOX,
133 MAILBOX_DATA_SEARCH,
134 MAILBOX_DATA_STATUS,
135 MAILBOX_DATA_EXISTS,
136 MAILBOX_DATA_RECENT,
137 MESSAGE_DATA_EXPUNGE,
138 MESSAGE_DATA_FETCH,
139 CAPABILITY_DATA,
140 RESPONSE_OTHER_UNKNOWN
141 } response_other;
143 static enum list_attributes {
144 LIST_NONE = 000,
145 LIST_NOINFERIORS = 001,
146 LIST_NOSELECT = 002,
147 LIST_MARKED = 004,
148 LIST_UNMARKED = 010
149 } list_attributes;
151 static int list_hierarchy_delimiter;
152 static char *list_name;
154 struct list_item {
155 struct list_item *l_next;
156 char *l_name;
157 char *l_base;
158 enum list_attributes l_attr;
159 int l_delim;
160 int l_level;
161 int l_has_children;
164 static char *imapbuf; /* TODO not static, use pool */
165 static size_t imapbufsize;
166 static sigjmp_buf imapjmp;
167 static sighandler_type savealrm;
168 static int imapkeepalive;
169 static long had_exists = -1;
170 static long had_expunge = -1;
171 static long expunged_messages;
172 static int volatile imaplock;
173 static int same_imap_account;
174 static bool_t _imap_rdonly;
176 static char *imap_quotestr(char const *s);
177 static char *imap_unquotestr(char const *s);
178 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
179 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
180 /* Returns NULL on error */
181 static char *imap_path_quote(struct mailbox *mp, char const *cp);
182 static void imap_other_get(char *pp);
183 static void imap_response_get(const char **cp);
184 static void imap_response_parse(void);
185 static enum okay imap_answer(struct mailbox *mp, int errprnt);
186 static enum okay imap_parse_list(void);
187 static enum okay imap_finish(struct mailbox *mp);
188 static void imap_timer_off(void);
189 static void imapcatch(int s);
190 static void _imap_maincatch(int s);
191 static enum okay imap_noop1(struct mailbox *mp);
192 static void rec_queue(enum rec_type type, unsigned long cnt);
193 static enum okay rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s);
196 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
197 static enum okay imap_capability(struct mailbox *mp);
198 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
199 #ifdef HAVE_MD5
200 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
201 #endif
202 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
203 #ifdef HAVE_GSSAPI
204 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
205 #endif
206 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
207 static void imap_init(struct mailbox *mp, int n);
208 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
209 int *prevcount);
210 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
211 struct url *urlp);
212 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
213 int transparent);
214 static int imap_fetchdata(struct mailbox *mp, struct message *m,
215 size_t expected, int need, const char *head,
216 size_t headsize, long headlines);
217 static void imap_putstr(struct mailbox *mp, struct message *m,
218 const char *str, const char *head, size_t headsize,
219 long headlines);
220 static enum okay imap_get(struct mailbox *mp, struct message *m,
221 enum needspec need);
222 static void commitmsg(struct mailbox *mp, struct message *to,
223 struct message *from, enum content_info content_info);
224 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
225 int bot, int top);
226 static enum okay imap_exit(struct mailbox *mp);
227 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
228 int needstat);
229 static enum okay imap_close(struct mailbox *mp);
230 static enum okay imap_update(struct mailbox *mp);
231 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
232 int c, const char *sp, int needstat);
233 static enum okay imap_unstore(struct message *m, int n, const char *flag);
234 static const char *tag(int new);
235 static char * imap_putflags(int f);
236 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
237 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
238 off_t off1, long xsize, enum mflag flag, time_t t);
239 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
240 long offset);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend,
243 int level);
244 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
245 FILE *fp);
246 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
247 const char *name);
248 static enum okay imap_copyuid_parse(const char *cp,
249 ui64_t *uidvalidity, ui64_t *olduid, ui64_t *newuid);
250 static enum okay imap_appenduid_parse(const char *cp,
251 ui64_t *uidvalidity, ui64_t *uid);
252 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
253 const char *name);
254 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
255 long off1, long xsize, long size, long lines, int flag,
256 const char *name);
257 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
258 #ifdef HAVE_IMAP_SEARCH
259 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
260 const char *spec, int f);
261 #endif
262 static enum okay imap_remove1(struct mailbox *mp, const char *name);
263 static enum okay imap_rename1(struct mailbox *mp, const char *old,
264 const char *new);
265 static char * imap_strex(char const *cp, char const **xp);
266 static enum okay check_expunged(void);
268 static char *
269 imap_quotestr(char const *s)
271 char *n, *np;
272 NYD2_ENTER;
274 np = n = salloc(2 * strlen(s) + 3);
275 *np++ = '"';
276 while (*s) {
277 if (*s == '"' || *s == '\\')
278 *np++ = '\\';
279 *np++ = *s++;
281 *np++ = '"';
282 *np = '\0';
283 NYD2_LEAVE;
284 return n;
287 static char *
288 imap_unquotestr(char const *s)
290 char *n, *np;
291 NYD2_ENTER;
293 if (*s != '"') {
294 n = savestr(s);
295 goto jleave;
298 np = n = salloc(strlen(s) + 1);
299 while (*++s) {
300 if (*s == '\\')
301 s++;
302 else if (*s == '"')
303 break;
304 *np++ = *s;
306 *np = '\0';
307 jleave:
308 NYD2_LEAVE;
309 return n;
312 static void
313 imap_delim_init(struct mailbox *mp, struct url const *urlp){
314 size_t i;
315 char const *cp;
316 NYD2_ENTER;
318 mp->mb_imap_delim[0] = '\0';
320 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
321 i = strlen(cp);
323 if(i == 0){
324 cp = n_IMAP_DELIM;
325 i = sizeof(n_IMAP_DELIM) -1;
326 goto jcopy;
329 if(i < n_NELEM(mp->mb_imap_delim))
330 jcopy:
331 memcpy(&mb.mb_imap_delim[0], cp, i +1);
332 else
333 n_err(_("*imap-delim* for %s is too long: %s\n"),
334 urlp->url_input, cp);
336 NYD2_LEAVE;
339 static char const *
340 imap_path_normalize(struct mailbox *mp, char const *cp){
341 char *rv_base, *rv, dc2, dc, c, lc;
342 char const *dcp;
343 NYD2_ENTER;
345 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
346 * exactly what i have specified" */
347 if(mp == NULL || mp->mb_imap_delim[0] == '\0')
348 dcp = &n_IMAP_DELIM[0];
349 else
350 dcp = &mp->mb_imap_delim[0];
351 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
353 /* Plain names don't need path quoting */
354 /* C99 */{
355 size_t i, j;
356 char const *cpx;
358 for(cpx = cp;; ++cpx)
359 if((c = *cpx) == '\0')
360 goto jleave;
361 else if(dc == '\0'){
362 if(strchr(n_IMAP_DELIM, c)){
363 dc = c;
364 break;
366 }else if(c == dc)
367 break;
368 else if(dc2 && strchr(dcp, c) != NULL)
369 break;
371 /* And we don't need to reevaluate what we have seen yet */
372 i = PTR2SIZE(cpx - cp);
373 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
374 if(i > 0)
375 memcpy(rv, cp, i);
376 memcpy(&rv[i], cpx, j);
377 rv += i;
378 cp = cpx;
381 /* Squeeze adjacent delimiters, convert remain to dc */
382 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
383 if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
384 c = dc;
385 if(c != dc || lc != dc)
386 *rv++ = c;
388 *rv = '\0';
390 cp = rv_base;
391 jleave:
392 NYD2_LEAVE;
393 return cp;
396 FL char const *
397 imap_path_encode(char const *cp, bool_t *err_or_null){
398 /* To a large extend inspired by dovecot(1) */
399 struct str out;
400 bool_t err_def;
401 ui8_t *be16p_base, *be16p;
402 char const *emsg;
403 char c;
404 size_t l, l_plain;
405 NYD2_ENTER;
407 if(err_or_null == NULL)
408 err_or_null = &err_def;
409 *err_or_null = FAL0;
411 /* Is this a string that works out as "plain US-ASCII"? */
412 for(l = 0;; ++l)
413 if((c = cp[l]) == '\0')
414 goto jleave;
415 else if(c <= 0x1F || c >= 0x7F || c == '&')
416 break;
418 *err_or_null = TRU1;
420 /* We need to encode in mUTF-7! For that, we first have to convert the
421 * local charset to UTF-8, then convert all characters which need to be
422 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
423 * We can skip the UTF-8 conversion occasionally, however */
424 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
425 !defined HAVE_ALWAYS_UNICODE_LOCALE
426 if(!(n_psonce & n_PSO_UNICODE)){
427 char const *x;
429 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
430 if((x = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
431 cp)) == NULL)
432 goto jerr;
433 cp = x;
435 /* So: Why not start all over again?
436 * Is this a string that works out as "plain US-ASCII"? */
437 for(l = 0;; ++l)
438 if((c = cp[l]) == '\0')
439 goto jleave;
440 else if(c <= 0x1F || c >= 0x7F || c == '&')
441 break;
443 #endif
445 /* We need to encode, save what we have, encode the rest */
446 l_plain = l;
448 for(cp += l, l = 0; cp[l] != '\0'; ++l)
450 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
452 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
453 if(l_plain > 0)
454 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
455 else
456 out.l = 0;
457 DBG( l_plain += (l << 2); )
459 while(l > 0){
460 c = *cp++;
461 --l;
463 if(c == '&'){
464 out.s[out.l + 0] = '&';
465 out.s[out.l + 1] = '-';
466 out.l += 2;
467 }else if(c > 0x1F && c < 0x7F)
468 out.s[out.l++] = c;
469 else{
470 static char const mb64ct[] =
471 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
472 ui32_t utf32;
474 /* Convert consecutive non-representables */
475 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
477 for(be16p = be16p_base, --cp, ++l;;){
478 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
479 goto jerr;
481 /* TODO S-CText: magic utf16 conversions */
482 if(utf32 < 0x10000){
483 be16p[1] = utf32 & 0xFF;
484 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
485 be16p += 2;
486 }else{
487 ui16_t s7e;
489 utf32 -= 0x10000;
490 s7e = 0xD800u | (utf32 >> 10);
491 be16p[1] = s7e & 0xFF;
492 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
493 s7e = 0xDC00u | (utf32 &= 0x03FF);
494 be16p[3] = s7e & 0xFF;
495 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
496 be16p += 4;
499 if(l == 0)
500 break;
501 if((c = *cp) > 0x1F && c < 0x7F)
502 break;
505 /* And then warp that UTF-16BE to mUTF-7 */
506 out.s[out.l++] = '&';
507 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
508 be16p = be16p_base;
510 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
511 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
512 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
513 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
514 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
515 out.l += 4;
517 if(utf32 > 0){
518 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
519 if(--utf32 == 0){
520 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
521 out.l += 2;
522 }else{
523 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
524 (be16p[1] >> 4)];
525 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
526 out.l += 3;
529 out.s[out.l++] = '-';
532 out.s[out.l] = '\0';
533 assert(out.l <= l_plain);
534 *err_or_null = FAL0;
535 cp = out.s;
536 jleave:
537 NYD2_LEAVE;
538 return cp;
539 jerr:
540 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
541 goto jleave;
544 FL char *
545 imap_path_decode(char const *path, bool_t *err_or_null){
546 /* To a large extend inspired by dovecot(1) TODO use string */
547 bool_t err_def;
548 ui8_t *mb64p_base, *mb64p, *mb64xp;
549 char const *emsg, *cp;
550 char *rv_base, *rv, c;
551 size_t l_orig, l, i;
552 NYD2_ENTER;
554 if(err_or_null == NULL)
555 err_or_null = &err_def;
556 *err_or_null = FAL0;
558 l = l_orig = strlen(path);
559 rv = rv_base = salloc(l << 1);
560 memcpy(rv, path, l +1);
562 /* xxx Don't check for invalid characters from malicious servers */
563 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
564 goto jleave;
566 *err_or_null = TRU1;
568 emsg = N_("Invalid mUTF-7 encoding");
569 i = PTR2SIZE(cp - path);
570 rv += i;
571 l -= i;
572 mb64p_base = NULL;
574 while(l > 0){
575 if((c = *cp) != '&'){
576 if(c <= 0x1F || c >= 0x7F){
577 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
578 goto jerr;
580 *rv++ = c;
581 ++cp;
582 --l;
583 }else if(--l == 0)
584 goto jeincpl;
585 else if(*++cp == '-'){
586 *rv++ = '&';
587 ++cp;
588 --l;
589 }else if(l < 3){
590 jeincpl:
591 emsg = N_("Invalid mUTF-7: incomplete input");
592 goto jerr;
593 }else{
594 /* mUTF-7 -> UTF-16BE -> UTF-8 */
595 static ui8_t const mb64dt[256] = {
596 #undef XX
597 #define XX 0xFFu
598 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
599 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
600 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
601 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
602 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
603 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
604 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
605 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
606 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
607 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
608 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
609 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
610 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
611 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
612 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
613 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
616 if(mb64p_base == NULL)
617 mb64p_base = salloc(l);
619 /* Decode the mUTF-7 to what is indeed UTF-16BE */
620 for(mb64p = mb64p_base;;){
621 assert(l >= 3);
622 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
623 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
624 goto jerr;
625 mb64p += 2;
627 c = cp[2];
628 cp += 3;
629 l -= 3;
630 if(c == '-')
631 break;
632 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
633 goto jerr;
635 if(l == 0)
636 goto jerr;
637 --l;
638 if((c = *cp++) == '-')
639 break;
640 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
641 goto jerr;
643 if(l < 3){
644 if(l > 0 && *cp == '-'){
645 --l;
646 ++cp;
647 break;
649 goto jerr;
652 #undef XX
654 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
655 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
656 goto jerr;
659 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
660 i = PTR2SIZE(mb64p - mb64p_base);
661 mb64p = mb64xp = mb64p_base;
663 while(i > 0){
664 ui8_t ul, u0, u1, u2, u3;
666 ul = (i >= 4) ? 4 : i & 0x3;
667 i -= ul;
668 u0 = mb64xp[0];
669 u1 = mb64xp[1];
670 u2 = (ul < 3) ? 0 : mb64xp[2];
671 u3 = (ul < 4) ? 0 : mb64xp[3];
672 mb64xp += ul;
673 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
674 if(ul < 3)
675 break;
676 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
677 if(ul < 4)
678 break;
679 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
682 /* UTF-16BE we convert to UTF-8 */
683 i = PTR2SIZE(mb64p - mb64p_base);
684 if(i & 1){
685 emsg = N_("Odd bytecount for UTF-16BE input");
686 goto jerr;
689 /* TODO S-CText: magic utf16 conversions */
690 emsg = N_("Invalid UTF-16BE encoding");
692 for(mb64p = mb64p_base; i > 0;){
693 ui32_t utf32;
694 ui16_t uhi, ulo;
696 uhi = mb64p[0];
697 uhi <<= 8;
698 uhi |= mb64p[1];
700 /* Not a surrogate? */
701 if(uhi < 0xD800 || uhi > 0xDFFF){
702 utf32 = uhi;
703 mb64p += 2;
704 i -= 2;
705 }else if(uhi > 0xDBFF)
706 goto jerr;
707 else if(i < 4){
708 emsg = N_("Incomplete UTF-16BE surrogate pair");
709 goto jerr;
710 }else{
711 ulo = mb64p[2];
712 ulo <<= 8;
713 ulo |= mb64p[3];
714 if(ulo < 0xDC00 || ulo > 0xDFFF)
715 goto jerr;
717 utf32 = (uhi &= 0x03FF);
718 utf32 <<= 10;
719 utf32 += 0x10000;
720 utf32 |= (ulo &= 0x03FF);
721 mb64p += 4;
722 i -= 4;
725 utf32 = n_utf32_to_utf8(utf32, rv);
726 rv += utf32;
730 *rv = '\0';
732 /* We can skip the UTF-8 conversion occasionally */
733 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
734 !defined HAVE_ALWAYS_UNICODE_LOCALE
735 if(!(n_psonce & n_PSO_UNICODE)){
736 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
737 if((rv = n_iconv_onetime_cp(n_ICONV_NONE, NULL, NULL, rv_base)) == NULL)
738 goto jerr;
740 #endif
742 *err_or_null = FAL0;
743 rv = rv_base;
744 jleave:
745 NYD2_LEAVE;
746 return rv;
747 jerr:
748 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
749 memcpy(rv = rv_base, path, ++l_orig);
750 goto jleave;
753 static char *
754 imap_path_quote(struct mailbox *mp, char const *cp){
755 bool_t err;
756 char *rv;
757 NYD2_ENTER;
759 cp = imap_path_normalize(mp, cp);
760 cp = imap_path_encode(cp, &err);
761 rv = err ? NULL : imap_quotestr(cp);
762 NYD2_LEAVE;
763 return rv;
766 static void
767 imap_other_get(char *pp)
769 char *xp;
770 NYD2_ENTER;
772 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
773 pp += 6;
774 response_other = MAILBOX_DATA_FLAGS;
775 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
776 pp += 5;
777 response_other = MAILBOX_DATA_LIST;
778 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
779 pp += 5;
780 response_other = MAILBOX_DATA_LSUB;
781 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
782 pp += 8;
783 response_other = MAILBOX_DATA_MAILBOX;
784 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
785 pp += 7;
786 response_other = MAILBOX_DATA_SEARCH;
787 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
788 pp += 7;
789 response_other = MAILBOX_DATA_STATUS;
790 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
791 pp += 11;
792 response_other = CAPABILITY_DATA;
793 } else {
794 responded_other_number = strtol(pp, &xp, 10);
795 while (*xp == ' ')
796 ++xp;
797 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
798 response_other = MAILBOX_DATA_EXISTS;
799 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
800 response_other = MAILBOX_DATA_RECENT;
801 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
802 response_other = MESSAGE_DATA_EXPUNGE;
803 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
804 pp = &xp[6];
805 response_other = MESSAGE_DATA_FETCH;
806 } else
807 response_other = RESPONSE_OTHER_UNKNOWN;
809 responded_other_text = pp;
810 NYD2_LEAVE;
813 static void
814 imap_response_get(const char **cp)
816 NYD2_ENTER;
817 if (ascncasecmp(*cp, "OK ", 3) == 0) {
818 *cp += 3;
819 response_status = RESPONSE_OK;
820 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
821 *cp += 3;
822 response_status = RESPONSE_NO;
823 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
824 *cp += 4;
825 response_status = RESPONSE_BAD;
826 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
827 *cp += 8;
828 response_status = RESPONSE_PREAUTH;
829 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
830 *cp += 4;
831 response_status = RESPONSE_BYE;
832 } else
833 response_status = RESPONSE_OTHER;
834 NYD2_LEAVE;
837 static void
838 imap_response_parse(void)
840 static char *parsebuf; /* TODO Use pool */
841 static size_t parsebufsize;
843 const char *ip = imapbuf;
844 char *pp;
845 NYD2_ENTER;
847 if (parsebufsize < imapbufsize + 1)
848 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
849 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
850 pp = parsebuf;
851 switch (*ip) {
852 case '+':
853 response_type = RESPONSE_CONT;
854 ip++;
855 pp++;
856 while (*ip == ' ') {
857 ip++;
858 pp++;
860 break;
861 case '*':
862 ip++;
863 pp++;
864 while (*ip == ' ') {
865 ip++;
866 pp++;
868 imap_response_get(&ip);
869 pp = &parsebuf[ip - imapbuf];
870 switch (response_status) {
871 case RESPONSE_BYE:
872 response_type = RESPONSE_FATAL;
873 break;
874 default:
875 response_type = RESPONSE_DATA;
877 break;
878 default:
879 responded_tag = parsebuf;
880 while (*pp && *pp != ' ')
881 pp++;
882 if (*pp == '\0') {
883 response_type = RESPONSE_ILLEGAL;
884 break;
886 *pp++ = '\0';
887 while (*pp && *pp == ' ')
888 pp++;
889 if (*pp == '\0') {
890 response_type = RESPONSE_ILLEGAL;
891 break;
893 ip = &imapbuf[pp - parsebuf];
894 response_type = RESPONSE_TAGGED;
895 imap_response_get(&ip);
896 pp = &parsebuf[ip - imapbuf];
898 responded_text = pp;
899 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
900 response_status == RESPONSE_OTHER)
901 imap_other_get(pp);
902 NYD2_LEAVE;
905 static enum okay
906 imap_answer(struct mailbox *mp, int errprnt)
908 int i, complete;
909 enum okay rv;
910 NYD2_ENTER;
912 rv = OKAY;
913 if (mp->mb_type == MB_CACHE)
914 goto jleave;
915 rv = STOP;
916 jagain:
917 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
918 if (n_poption & n_PO_D_VV)
919 n_err(">>> SERVER: %s", imapbuf);
920 imap_response_parse();
921 if (response_type == RESPONSE_ILLEGAL)
922 goto jagain;
923 if (response_type == RESPONSE_CONT) {
924 rv = OKAY;
925 goto jleave;
927 if (response_status == RESPONSE_OTHER) {
928 if (response_other == MAILBOX_DATA_EXISTS) {
929 had_exists = responded_other_number;
930 rec_queue(REC_EXISTS, responded_other_number);
931 if (had_expunge > 0)
932 had_expunge = 0;
933 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
934 rec_queue(REC_EXPUNGE, responded_other_number);
935 if (had_expunge < 0)
936 had_expunge = 0;
937 had_expunge++;
938 expunged_messages++;
941 complete = 0;
942 if (response_type == RESPONSE_TAGGED) {
943 if (asccasecmp(responded_tag, tag(0)) == 0)
944 complete |= 1;
945 else
946 goto jagain;
948 switch (response_status) {
949 case RESPONSE_PREAUTH:
950 mp->mb_active &= ~MB_PREAUTH;
951 /*FALLTHRU*/
952 case RESPONSE_OK:
953 jokay:
954 rv = OKAY;
955 complete |= 2;
956 break;
957 case RESPONSE_NO:
958 case RESPONSE_BAD:
959 jstop:
960 complete |= 2;
961 if (errprnt)
962 n_err(_("IMAP error: %s"), responded_text);
963 break;
964 case RESPONSE_UNKNOWN: /* does not happen */
965 case RESPONSE_BYE:
966 i = mp->mb_active;
967 mp->mb_active = MB_NONE;
968 if (i & MB_BYE)
969 goto jokay;
970 goto jstop;
971 case RESPONSE_OTHER:
972 rv = OKAY;
973 break;
975 if (response_status != RESPONSE_OTHER &&
976 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
977 n_err(_("IMAP alert: %s"), &responded_text[8]);
978 if (complete == 3)
979 mp->mb_active &= ~MB_COMD;
980 } else
981 mp->mb_active = MB_NONE;
982 jleave:
983 NYD2_LEAVE;
984 return rv;
987 static enum okay
988 imap_parse_list(void)
990 char *cp;
991 enum okay rv;
992 NYD2_ENTER;
994 rv = STOP;
996 cp = responded_other_text;
997 list_attributes = LIST_NONE;
998 if (*cp == '(') {
999 while (*cp && *cp != ')') {
1000 if (*cp == '\\') {
1001 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1002 list_attributes |= LIST_NOINFERIORS;
1003 cp += 12;
1004 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1005 list_attributes |= LIST_NOSELECT;
1006 cp += 9;
1007 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1008 list_attributes |= LIST_MARKED;
1009 cp += 7;
1010 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1011 list_attributes |= LIST_UNMARKED;
1012 cp += 9;
1015 cp++;
1017 if (*++cp != ' ')
1018 goto jleave;
1019 while (*cp == ' ')
1020 cp++;
1023 list_hierarchy_delimiter = EOF;
1024 if (*cp == '"') {
1025 if (*++cp == '\\')
1026 cp++;
1027 list_hierarchy_delimiter = *cp++ & 0377;
1028 if (cp[0] != '"' || cp[1] != ' ')
1029 goto jleave;
1030 cp++;
1031 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1032 list_hierarchy_delimiter = EOF;
1033 cp += 3;
1036 while (*cp == ' ')
1037 cp++;
1038 list_name = cp;
1039 while (*cp && *cp != '\r')
1040 cp++;
1041 *cp = '\0';
1042 rv = OKAY;
1043 jleave:
1044 NYD2_LEAVE;
1045 return rv;
1048 static enum okay
1049 imap_finish(struct mailbox *mp)
1051 NYD_ENTER;
1052 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1053 imap_answer(mp, 1);
1054 NYD_LEAVE;
1055 return OKAY;
1058 static void
1059 imap_timer_off(void)
1061 NYD_ENTER;
1062 if (imapkeepalive > 0) {
1063 alarm(0);
1064 safe_signal(SIGALRM, savealrm);
1066 NYD_LEAVE;
1069 static void
1070 imapcatch(int s)
1072 NYD_X; /* Signal handler */
1073 switch (s) {
1074 case SIGINT:
1075 n_err_sighdl(_("Interrupt\n"));
1076 siglongjmp(imapjmp, 1);
1077 /*NOTREACHED*/
1078 case SIGPIPE:
1079 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1080 break;
1084 static void
1085 _imap_maincatch(int s)
1087 NYD_X; /* Signal handler */
1088 n_UNUSED(s);
1089 if (interrupts++ == 0) {
1090 n_err_sighdl(_("Interrupt\n"));
1091 return;
1093 n_go_onintr_for_imap();
1096 static enum okay
1097 imap_noop1(struct mailbox *mp)
1099 char o[LINESIZE];
1100 FILE *queuefp = NULL;
1101 NYD_X;
1103 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1104 IMAP_OUT(o, MB_COMD, return STOP)
1105 IMAP_ANSWER()
1106 return OKAY;
1109 FL char const *
1110 imap_fileof(char const *xcp)
1112 char const *cp = xcp;
1113 int state = 0;
1114 NYD_ENTER;
1116 while (*cp) {
1117 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1118 cp += 3;
1119 state = 1;
1121 if (cp[0] == '/' && state == 1) {
1122 ++cp;
1123 goto jleave;
1125 if (cp[0] == '/') {
1126 cp = xcp;
1127 goto jleave;
1129 ++cp;
1131 jleave:
1132 NYD_LEAVE;
1133 return cp;
1136 FL enum okay
1137 imap_noop(void)
1139 sighandler_type volatile oldint, oldpipe;
1140 enum okay volatile rv = STOP;
1141 NYD_ENTER;
1143 if (mb.mb_type != MB_IMAP)
1144 goto jleave;
1146 imaplock = 1;
1147 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1148 safe_signal(SIGINT, &_imap_maincatch);
1149 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1150 if (sigsetjmp(imapjmp, 1) == 0) {
1151 if (oldpipe != SIG_IGN)
1152 safe_signal(SIGPIPE, imapcatch);
1154 rv = imap_noop1(&mb);
1156 safe_signal(SIGINT, oldint);
1157 safe_signal(SIGPIPE, oldpipe);
1158 imaplock = 0;
1159 jleave:
1160 NYD_LEAVE;
1161 if (interrupts)
1162 n_go_onintr_for_imap();
1163 return rv;
1166 static void
1167 rec_queue(enum rec_type rt, unsigned long cnt)
1169 struct record *rp;
1170 NYD_ENTER;
1172 rp = scalloc(1, sizeof *rp);
1173 rp->rec_type = rt;
1174 rp->rec_count = cnt;
1175 if (record && recend) {
1176 recend->rec_next = rp;
1177 recend = rp;
1178 } else
1179 record = recend = rp;
1180 NYD_LEAVE;
1183 static enum okay
1184 rec_dequeue(void)
1186 struct message *omessage;
1187 struct record *rp, *rq;
1188 uiz_t exists = 0, i;
1189 enum okay rv = STOP;
1190 NYD_ENTER;
1192 if (record == NULL)
1193 goto jleave;
1195 omessage = message;
1196 message = smalloc((msgCount+1) * sizeof *message);
1197 if (msgCount)
1198 memcpy(message, omessage, msgCount * sizeof *message);
1199 memset(&message[msgCount], 0, sizeof *message);
1201 rp = record, rq = NULL;
1202 rv = OKAY;
1203 while (rp != NULL) {
1204 switch (rp->rec_type) {
1205 case REC_EXISTS:
1206 exists = rp->rec_count;
1207 break;
1208 case REC_EXPUNGE:
1209 if (rp->rec_count == 0) {
1210 rv = STOP;
1211 break;
1213 if (rp->rec_count > (unsigned long)msgCount) {
1214 if (exists == 0 || rp->rec_count > exists--)
1215 rv = STOP;
1216 break;
1218 if (exists > 0)
1219 exists--;
1220 delcache(&mb, &message[rp->rec_count-1]);
1221 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1222 ((msgCount - rp->rec_count + 1) * sizeof *message));
1223 --msgCount;
1224 /* If the message was part of a collapsed thread,
1225 * the m_collapsed field of one of its ancestors
1226 * should be incremented. It seems hardly possible
1227 * to do this with the current message structure,
1228 * though. The result is that a '+' may be shown
1229 * in the header summary even if no collapsed
1230 * children exists */
1231 break;
1233 if (rq != NULL)
1234 free(rq);
1235 rq = rp;
1236 rp = rp->rec_next;
1238 if (rq != NULL)
1239 free(rq);
1241 record = recend = NULL;
1242 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1243 message = srealloc(message, (exists + 1) * sizeof *message);
1244 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1245 for (i = msgCount; i < exists; ++i)
1246 imap_init(&mb, i);
1247 imap_flags(&mb, msgCount+1, exists);
1248 msgCount = exists;
1251 if (rv == STOP) {
1252 free(message);
1253 message = omessage;
1255 jleave:
1256 NYD_LEAVE;
1257 return rv;
1260 static void
1261 rec_rmqueue(void)
1263 struct record *rp;
1264 NYD_ENTER;
1266 for (rp = record; rp != NULL;) {
1267 struct record *tmp = rp;
1268 rp = rp->rec_next;
1269 free(tmp);
1271 record = recend = NULL;
1272 NYD_LEAVE;
1275 /*ARGSUSED*/
1276 static void
1277 imapalarm(int s)
1279 sighandler_type volatile saveint, savepipe;
1280 NYD_X; /* Signal handler */
1281 n_UNUSED(s);
1283 if (imaplock++ == 0) {
1284 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1285 safe_signal(SIGINT, &_imap_maincatch);
1286 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1287 if (sigsetjmp(imapjmp, 1)) {
1288 safe_signal(SIGINT, saveint);
1289 safe_signal(SIGPIPE, savepipe);
1290 goto jbrk;
1292 if (savepipe != SIG_IGN)
1293 safe_signal(SIGPIPE, imapcatch);
1294 if (imap_noop1(&mb) != OKAY) {
1295 safe_signal(SIGINT, saveint);
1296 safe_signal(SIGPIPE, savepipe);
1297 goto jleave;
1299 safe_signal(SIGINT, saveint);
1300 safe_signal(SIGPIPE, savepipe);
1302 jbrk:
1303 alarm(imapkeepalive);
1304 jleave:
1305 --imaplock;
1308 static enum okay
1309 imap_preauth(struct mailbox *mp, struct url const *urlp)
1311 NYD_X;
1313 mp->mb_active |= MB_PREAUTH;
1314 imap_answer(mp, 1);
1316 #ifdef HAVE_SSL
1317 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1318 FILE *queuefp = NULL;
1319 char o[LINESIZE];
1321 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1322 IMAP_OUT(o, MB_COMD, return STOP)
1323 IMAP_ANSWER()
1324 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1325 return STOP;
1327 #else
1328 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1329 n_err(_("No SSL support compiled in\n"));
1330 return STOP;
1332 #endif
1334 imap_capability(mp);
1335 return OKAY;
1338 static enum okay
1339 imap_capability(struct mailbox *mp)
1341 char o[LINESIZE];
1342 FILE *queuefp = NULL;
1343 enum okay ok = STOP;
1344 const char *cp;
1345 NYD_X;
1347 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1348 IMAP_OUT(o, MB_COMD, return STOP)
1349 while (mp->mb_active & MB_COMD) {
1350 ok = imap_answer(mp, 0);
1351 if (response_status == RESPONSE_OTHER &&
1352 response_other == CAPABILITY_DATA) {
1353 cp = responded_other_text;
1354 while (*cp) {
1355 while (spacechar(*cp))
1356 ++cp;
1357 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1358 /* RFC 2359 */
1359 mp->mb_flags |= MB_UIDPLUS;
1360 while (*cp && !spacechar(*cp))
1361 ++cp;
1365 return ok;
1368 static enum okay
1369 imap_auth(struct mailbox *mp, struct ccred *ccred)
1371 enum okay rv;
1372 NYD_ENTER;
1374 if (!(mp->mb_active & MB_PREAUTH)) {
1375 rv = OKAY;
1376 goto jleave;
1379 switch (ccred->cc_authtype) {
1380 case AUTHTYPE_LOGIN:
1381 rv = imap_login(mp, ccred);
1382 break;
1383 #ifdef HAVE_MD5
1384 case AUTHTYPE_CRAM_MD5:
1385 rv = imap_cram_md5(mp, ccred);
1386 break;
1387 #endif
1388 #ifdef HAVE_GSSAPI
1389 case AUTHTYPE_GSSAPI:
1390 rv = _imap_gssapi(mp, ccred);
1391 break;
1392 #endif
1393 default:
1394 rv = STOP;
1395 break;
1397 jleave:
1398 NYD_LEAVE;
1399 return rv;
1402 #ifdef HAVE_MD5
1403 static enum okay
1404 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1406 char o[LINESIZE], *cp;
1407 FILE *queuefp = NULL;
1408 enum okay rv = STOP;
1409 NYD_ENTER;
1411 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1412 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1413 imap_answer(mp, 1);
1414 if (response_type != RESPONSE_CONT)
1415 goto jleave;
1417 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1418 if(cp == NULL)
1419 goto jleave;
1420 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1421 while (mp->mb_active & MB_COMD)
1422 rv = imap_answer(mp, 1);
1423 jleave:
1424 NYD_LEAVE;
1425 return rv;
1427 #endif /* HAVE_MD5 */
1429 static enum okay
1430 imap_login(struct mailbox *mp, struct ccred *ccred)
1432 char o[LINESIZE];
1433 FILE *queuefp = NULL;
1434 enum okay rv = STOP;
1435 NYD_ENTER;
1437 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1438 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1439 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1440 while (mp->mb_active & MB_COMD)
1441 rv = imap_answer(mp, 1);
1442 jleave:
1443 NYD_LEAVE;
1444 return rv;
1447 #ifdef HAVE_GSSAPI
1448 # include "obs-imap-gssapi.h"
1449 #endif
1451 FL enum okay
1452 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1453 enum fedit_mode fm)
1455 char o[LINESIZE];
1456 char const *qname, *cp;
1457 FILE *queuefp;
1458 enum okay ok;
1459 NYD_X;
1460 n_UNUSED(size);
1462 ok = STOP;
1463 queuefp = NULL;
1465 if((qname = imap_path_quote(mp, mbx)) == NULL)
1466 goto jleave;
1468 ok = OKAY;
1470 mp->mb_uidvalidity = 0;
1471 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1472 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1473 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1474 while (mp->mb_active & MB_COMD) {
1475 ok = imap_answer(mp, 1);
1476 if (response_status != RESPONSE_OTHER &&
1477 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1478 mp->mb_uidvalidity = atol(&cp[13]);
1480 *cnt = (had_exists > 0) ? had_exists : 0;
1481 if (response_status != RESPONSE_OTHER &&
1482 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1483 mp->mb_perm = 0;
1484 jleave:
1485 return ok;
1488 static enum okay
1489 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1491 char o[LINESIZE];
1492 FILE *queuefp = NULL;
1493 char const *cp;
1494 struct message *m;
1495 unsigned x = X, y = Y, n;
1496 NYD_X;
1498 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1499 IMAP_OUT(o, MB_COMD, return STOP)
1500 while (mp->mb_active & MB_COMD) {
1501 imap_answer(mp, 1);
1502 if (response_status == RESPONSE_OTHER &&
1503 response_other == MESSAGE_DATA_FETCH) {
1504 n = responded_other_number;
1505 if (n < x || n > y)
1506 continue;
1507 m = &message[n-1];
1508 m->m_xsize = 0;
1509 } else
1510 continue;
1512 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1513 cp += 5;
1514 while (*cp == ' ')
1515 cp++;
1516 if (*cp == '(')
1517 imap_getflags(cp, &cp, &m->m_flag);
1520 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1521 n_idec_ui64_cp(&m->m_uid, &cp[4], 10, NULL);/* TODO errors? */
1522 getcache1(mp, m, NEED_UNSPEC, 1);
1523 m->m_flag &= ~MHIDDEN;
1526 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1527 x++;
1528 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1529 y--;
1530 if (x <= y) {
1531 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1532 tag(1), x, y);
1533 IMAP_OUT(o, MB_COMD, return STOP)
1534 while (mp->mb_active & MB_COMD) {
1535 imap_answer(mp, 1);
1536 if (response_status == RESPONSE_OTHER &&
1537 response_other == MESSAGE_DATA_FETCH) {
1538 n = responded_other_number;
1539 if (n < x || n > y)
1540 continue;
1541 m = &message[n-1];
1542 } else
1543 continue;
1544 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1545 m->m_xsize = strtol(&cp[12], NULL, 10);
1546 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1547 m->m_time = imap_read_date_time(&cp[13]);
1551 srelax_hold();
1552 for (n = X; n <= Y; ++n) {
1553 putcache(mp, &message[n-1]);
1554 srelax();
1556 srelax_rele();
1557 return OKAY;
1560 static void
1561 imap_init(struct mailbox *mp, int n)
1563 struct message *m;
1564 NYD_ENTER;
1565 n_UNUSED(mp);
1567 m = message + n;
1568 m->m_flag = MUSED | MNOFROM;
1569 m->m_block = 0;
1570 m->m_offset = 0;
1571 NYD_LEAVE;
1574 static void
1575 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1577 struct message *omessage = 0;
1578 int i, omsgCount = 0;
1579 enum okay dequeued = STOP;
1580 NYD_ENTER;
1582 if (nmail || transparent) {
1583 omessage = message;
1584 omsgCount = msgCount;
1586 if (nmail)
1587 dequeued = rec_dequeue();
1589 if (had_exists >= 0) {
1590 if (dequeued != OKAY)
1591 msgCount = had_exists;
1592 had_exists = -1;
1594 if (had_expunge >= 0) {
1595 if (dequeued != OKAY)
1596 msgCount -= had_expunge;
1597 had_expunge = -1;
1600 if (nmail && expunged_messages)
1601 printf("Expunged %ld message%s.\n", expunged_messages,
1602 (expunged_messages != 1 ? "s" : ""));
1603 *prevcount = omsgCount - expunged_messages;
1604 expunged_messages = 0;
1605 if (msgCount < 0) {
1606 fputs("IMAP error: Negative message count\n", stderr);
1607 msgCount = 0;
1610 if (dequeued != OKAY) {
1611 message = scalloc(msgCount + 1, sizeof *message);
1612 for (i = 0; i < msgCount; i++)
1613 imap_init(mp, i);
1614 if (!nmail && mp->mb_type == MB_IMAP)
1615 initcache(mp);
1616 if (msgCount > 0)
1617 imap_flags(mp, 1, msgCount);
1618 message[msgCount].m_size = 0;
1619 message[msgCount].m_lines = 0;
1620 rec_rmqueue();
1622 if (nmail || transparent)
1623 transflags(omessage, omsgCount, transparent);
1624 else
1625 setdot(message);
1626 NYD_LEAVE;
1629 FL int
1630 imap_setfile(const char *xserver, enum fedit_mode fm)
1632 struct url url;
1633 int rv;
1634 NYD_ENTER;
1636 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1637 rv = 1;
1638 goto jleave;
1640 if (!ok_blook(v15_compat) &&
1641 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1642 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1644 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1645 rv = _imap_setfile1(&url, fm, 0);
1646 jleave:
1647 NYD_LEAVE;
1648 return rv;
1651 static bool_t
1652 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1654 bool_t rv = FAL0;
1655 NYD_ENTER;
1657 if (ok_blook(v15_compat))
1658 rv = ccred_lookup(ccredp, urlp);
1659 else {
1660 char *var, *old,
1661 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1662 : urlp->url_u_h_p.s);
1664 if ((var = mbp->mb_imap_pass) != NULL) {
1665 var = savecat("password-", xuhp);
1666 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1667 old = sstrdup(old);
1668 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1670 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1671 if (var != NULL) {
1672 if (old != NULL) {
1673 n_var_vset(var, (uintptr_t)old);
1674 free(old);
1675 } else
1676 n_var_vclear(var);
1680 NYD_LEAVE;
1681 return rv;
1684 static int
1685 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1686 int volatile transparent)
1688 struct sock so;
1689 struct ccred ccred;
1690 sighandler_type volatile saveint, savepipe;
1691 char const *cp;
1692 int rv;
1693 int volatile prevcount = 0;
1694 enum mbflags same_flags;
1695 NYD_ENTER;
1697 if (fm & FEDIT_NEWMAIL) {
1698 saveint = safe_signal(SIGINT, SIG_IGN);
1699 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1700 if (saveint != SIG_IGN)
1701 safe_signal(SIGINT, imapcatch);
1702 if (savepipe != SIG_IGN)
1703 safe_signal(SIGPIPE, imapcatch);
1704 imaplock = 1;
1705 goto jnmail;
1708 same_flags = mb.mb_flags;
1709 same_imap_account = 0;
1710 if (mb.mb_imap_account != NULL &&
1711 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1712 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1713 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1714 disconnected(mb.mb_imap_account) == 0) {
1715 same_imap_account = 1;
1716 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1718 goto jduppass;
1719 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1720 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1721 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1722 jduppass:
1724 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1728 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1729 free(mb.mb_imap_pass);
1730 mb.mb_imap_pass = NULL;
1732 if (!_imap_getcred(&mb, &ccred, urlp)) {
1733 rv = -1;
1734 goto jleave;
1737 memset(&so, 0, sizeof so);
1738 so.s_fd = -1;
1739 if (!same_imap_account) {
1740 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1741 rv = -1;
1742 goto jleave;
1744 } else
1745 so = mb.mb_sock;
1746 if (!transparent) {
1747 if(!quit(FAL0)){
1748 rv = -1;
1749 goto jleave;
1753 if (fm & FEDIT_SYSBOX)
1754 n_pstate &= ~n_PS_EDIT;
1755 else
1756 n_pstate |= n_PS_EDIT;
1757 if (mb.mb_imap_account != NULL)
1758 free(mb.mb_imap_account);
1759 if (mb.mb_imap_pass != NULL)
1760 free(mb.mb_imap_pass);
1761 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1762 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1763 * TODO object, and mailbox will naturally have an URL and credentials */
1764 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1766 if (!same_imap_account) {
1767 if (mb.mb_sock.s_fd >= 0)
1768 sclose(&mb.mb_sock);
1770 same_imap_account = 0;
1772 if (!transparent) {
1773 if (mb.mb_itf) {
1774 fclose(mb.mb_itf);
1775 mb.mb_itf = NULL;
1777 if (mb.mb_otf) {
1778 fclose(mb.mb_otf);
1779 mb.mb_otf = NULL;
1781 if (mb.mb_imap_mailbox != NULL)
1782 free(mb.mb_imap_mailbox);
1783 assert(urlp->url_path.s != NULL);
1784 imap_delim_init(&mb, urlp);
1785 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1786 initbox(savecatsep(urlp->url_p_eu_h_p,
1787 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1788 mb.mb_imap_mailbox));
1790 mb.mb_type = MB_VOID;
1791 mb.mb_active = MB_NONE;
1793 imaplock = 1;
1794 saveint = safe_signal(SIGINT, SIG_IGN);
1795 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1796 if (sigsetjmp(imapjmp, 1)) {
1797 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1798 sclose(&mb.mb_sock);
1799 safe_signal(SIGINT, saveint);
1800 safe_signal(SIGPIPE, savepipe);
1801 imaplock = 0;
1803 mb.mb_type = MB_VOID;
1804 mb.mb_active = MB_NONE;
1805 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1806 goto jleave;
1808 if (saveint != SIG_IGN)
1809 safe_signal(SIGINT, imapcatch);
1810 if (savepipe != SIG_IGN)
1811 safe_signal(SIGPIPE, imapcatch);
1813 if (mb.mb_sock.s_fd < 0) {
1814 if (disconnected(mb.mb_imap_account)) {
1815 if (cache_setptr(fm, transparent) == STOP)
1816 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1817 goto jdone;
1819 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1820 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1821 savealrm = safe_signal(SIGALRM, imapalarm);
1822 alarm(imapkeepalive);
1826 mb.mb_sock = so;
1827 mb.mb_sock.s_desc = "IMAP";
1828 mb.mb_sock.s_onclose = imap_timer_off;
1829 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1830 sclose(&mb.mb_sock);
1831 imap_timer_off();
1832 safe_signal(SIGINT, saveint);
1833 safe_signal(SIGPIPE, savepipe);
1834 imaplock = 0;
1835 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1836 goto jleave;
1838 } else /* same account */
1839 mb.mb_flags |= same_flags;
1841 if (n_poption & n_PO_R_FLAG)
1842 fm |= FEDIT_RDONLY;
1843 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1844 mb.mb_type = MB_IMAP;
1845 cache_dequeue(&mb);
1846 assert(urlp->url_path.s != NULL);
1847 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1848 /*sclose(&mb.mb_sock);
1849 imap_timer_off();*/
1850 safe_signal(SIGINT, saveint);
1851 safe_signal(SIGPIPE, savepipe);
1852 imaplock = 0;
1853 mb.mb_type = MB_VOID;
1854 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1855 goto jleave;
1858 jnmail:
1859 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1860 n_UNVOLATILE(&prevcount));
1861 jdone:
1862 setmsize(msgCount);
1863 safe_signal(SIGINT, saveint);
1864 safe_signal(SIGPIPE, savepipe);
1865 imaplock = 0;
1867 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1868 purgecache(&mb, message, msgCount);
1869 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1870 mb.mb_threaded = 0;
1871 c_sort((void*)-1);
1874 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1875 n_pstate &= ~n_PS_SAW_COMMAND;
1876 n_pstate |= n_PS_SETFILE_OPENED;
1879 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1880 mb.mb_type == MB_CACHE)) {
1881 rv = (msgCount == 0);
1882 goto jleave;
1885 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1886 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1887 !ok_blook(emptystart))
1888 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1889 rv = 1;
1890 goto jleave;
1893 if (fm & FEDIT_NEWMAIL)
1894 newmailinfo(prevcount);
1895 rv = 0;
1896 jleave:
1897 NYD_LEAVE;
1898 return rv;
1901 static int
1902 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1903 int need, const char *head, size_t headsize, long headlines)
1905 char *line = NULL, *lp;
1906 size_t linesize = 0, linelen, size = 0;
1907 int emptyline = 0, lines = 0, excess = 0;
1908 off_t offset;
1909 NYD_ENTER;
1911 fseek(mp->mb_otf, 0L, SEEK_END);
1912 offset = ftell(mp->mb_otf);
1914 if (head)
1915 fwrite(head, 1, headsize, mp->mb_otf);
1917 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1918 lp = line;
1919 if (linelen > expected) {
1920 excess = linelen - expected;
1921 linelen = expected;
1923 /* TODO >>
1924 * Need to mask 'From ' lines. This cannot be done properly
1925 * since some servers pass them as 'From ' and others as
1926 * '>From '. Although one could identify the first kind of
1927 * server in principle, it is not possible to identify the
1928 * second as '>From ' may also come from a server of the
1929 * first type as actual data. So do what is absolutely
1930 * necessary only - mask 'From '.
1932 * If the line is the first line of the message header, it
1933 * is likely a real 'From ' line. In this case, it is just
1934 * ignored since it violates all standards.
1935 * TODO can the latter *really* happen??
1936 * TODO <<
1938 /* Since we simply copy over data without doing any transfer
1939 * encoding reclassification/adjustment we *have* to perform
1940 * RFC 4155 compliant From_ quoting here */
1941 if (emptyline && is_head(lp, linelen, FAL0)) {
1942 fputc('>', mp->mb_otf);
1943 ++size;
1945 emptyline = 0;
1946 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1947 if (linelen > 2) {
1948 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1949 size += linelen - 1;
1950 } else {
1951 emptyline = 1;
1952 ++size;
1954 fputc('\n', mp->mb_otf);
1955 } else {
1956 fwrite(lp, 1, linelen, mp->mb_otf);
1957 size += linelen;
1959 ++lines;
1960 if ((expected -= linelen) <= 0)
1961 break;
1963 if (!emptyline) {
1964 /* This is very ugly; but some IMAP daemons don't end a
1965 * message with \r\n\r\n, and we need \n\n for mbox format */
1966 fputc('\n', mp->mb_otf);
1967 ++lines;
1968 ++size;
1970 fflush(mp->mb_otf);
1972 if (m != NULL) {
1973 m->m_size = size + headsize;
1974 m->m_lines = lines + headlines;
1975 m->m_block = mailx_blockof(offset);
1976 m->m_offset = mailx_offsetof(offset);
1977 switch (need) {
1978 case NEED_HEADER:
1979 m->m_content_info = CI_HAVE_HEADER;
1980 break;
1981 case NEED_BODY:
1982 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
1983 m->m_xlines = m->m_lines;
1984 m->m_xsize = m->m_size;
1985 break;
1988 free(line);
1989 NYD_LEAVE;
1990 return excess;
1993 static void
1994 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1995 const char *head, size_t headsize, long headlines)
1997 off_t offset;
1998 size_t len;
1999 NYD_ENTER;
2001 len = strlen(str);
2002 fseek(mp->mb_otf, 0L, SEEK_END);
2003 offset = ftell(mp->mb_otf);
2004 if (head)
2005 fwrite(head, 1, headsize, mp->mb_otf);
2006 if (len > 0) {
2007 fwrite(str, 1, len, mp->mb_otf);
2008 fputc('\n', mp->mb_otf);
2009 ++len;
2011 fflush(mp->mb_otf);
2013 if (m != NULL) {
2014 m->m_size = headsize + len;
2015 m->m_lines = headlines + 1;
2016 m->m_block = mailx_blockof(offset);
2017 m->m_offset = mailx_offsetof(offset);
2018 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2019 m->m_xlines = m->m_lines;
2020 m->m_xsize = m->m_size;
2022 NYD_LEAVE;
2025 static enum okay
2026 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2028 char o[LINESIZE];
2029 struct message mt;
2030 sighandler_type volatile saveint, savepipe;
2031 char * volatile head;
2032 char const *cp, *loc, * volatile item, * volatile resp;
2033 size_t expected;
2034 size_t volatile headsize;
2035 int number;
2036 FILE *queuefp;
2037 long volatile headlines;
2038 long n;
2039 enum okay ok;
2040 NYD_X;
2042 saveint = savepipe = SIG_IGN;
2043 head = NULL;
2044 cp = loc = item = resp = NULL;
2045 headsize = 0;
2046 number = (int)PTR2SIZE(m - message + 1);
2047 queuefp = NULL;
2048 headlines = 0;
2049 ok = STOP;
2051 if (getcache(mp, m, need) == OKAY)
2052 return OKAY;
2053 if (mp->mb_type == MB_CACHE) {
2054 n_err(_("Message %lu not available\n"), (ul_i)number);
2055 return STOP;
2058 if (mp->mb_sock.s_fd < 0) {
2059 n_err(_("IMAP connection closed\n"));
2060 return STOP;
2063 switch (need) {
2064 case NEED_HEADER:
2065 resp = item = "RFC822.HEADER";
2066 break;
2067 case NEED_BODY:
2068 item = "BODY.PEEK[]";
2069 resp = "BODY[]";
2070 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2071 char *hdr = smalloc(m->m_size);
2072 fflush(mp->mb_otf);
2073 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2074 SEEK_SET) < 0 ||
2075 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2076 free(hdr);
2077 break;
2079 head = hdr;
2080 headsize = m->m_size;
2081 headlines = m->m_lines;
2082 item = "BODY.PEEK[TEXT]";
2083 resp = "BODY[TEXT]";
2085 break;
2086 case NEED_UNSPEC:
2087 return STOP;
2090 imaplock = 1;
2091 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2092 if (sigsetjmp(imapjmp, 1)) {
2093 safe_signal(SIGINT, saveint);
2094 safe_signal(SIGPIPE, savepipe);
2095 imaplock = 0;
2096 return STOP;
2098 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2099 safe_signal(SIGINT, &_imap_maincatch);
2100 if (savepipe != SIG_IGN)
2101 safe_signal(SIGPIPE, imapcatch);
2103 if (m->m_uid)
2104 snprintf(o, sizeof o, "%s UID FETCH %" PRIu64 " (%s)\r\n",
2105 tag(1), m->m_uid, item);
2106 else {
2107 if (check_expunged() == STOP)
2108 goto out;
2109 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2111 IMAP_OUT(o, MB_COMD, goto out)
2112 for (;;) {
2113 ui64_t uid;
2115 ok = imap_answer(mp, 1);
2116 if (ok == STOP)
2117 break;
2118 if (response_status != RESPONSE_OTHER ||
2119 response_other != MESSAGE_DATA_FETCH)
2120 continue;
2121 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2122 continue;
2123 uid = 0;
2124 if (m->m_uid) {
2125 if ((cp = asccasestr(responded_other_text, "UID "))) {
2126 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2127 n = 0;
2128 } else
2129 n = -1;
2130 } else
2131 n = responded_other_number;
2132 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2133 if (m->m_uid ? m->m_uid != uid : n != number)
2134 continue;
2135 if ((cp = strchr(loc, '"')) != NULL) {
2136 cp = imap_unquotestr(cp);
2137 imap_putstr(mp, m, cp, head, headsize, headlines);
2138 } else {
2139 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2140 m->m_xlines = m->m_lines;
2141 m->m_xsize = m->m_size;
2143 goto out;
2145 expected = atol(&cp[1]);
2146 if (m->m_uid ? n == 0 && m->m_uid != uid : n != number) {
2147 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2148 continue;
2150 mt = *m;
2151 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2152 if (n >= 0) {
2153 commitmsg(mp, m, &mt, mt.m_content_info);
2154 break;
2156 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2157 if (n_poption & n_PO_VERBVERB)
2158 fputs(imapbuf, stderr);
2159 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2160 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2161 if (uid == m->m_uid) {
2162 commitmsg(mp, m, &mt, mt.m_content_info);
2163 break;
2168 out:
2169 while (mp->mb_active & MB_COMD)
2170 ok = imap_answer(mp, 1);
2172 if (saveint != SIG_IGN)
2173 safe_signal(SIGINT, saveint);
2174 if (savepipe != SIG_IGN)
2175 safe_signal(SIGPIPE, savepipe);
2176 imaplock--;
2178 if (ok == OKAY)
2179 putcache(mp, m);
2180 if (head != NULL)
2181 free(head);
2182 if (interrupts)
2183 n_go_onintr_for_imap();
2184 return ok;
2187 FL enum okay
2188 imap_header(struct message *m)
2190 enum okay rv;
2191 NYD_ENTER;
2193 rv = imap_get(&mb, m, NEED_HEADER);
2194 NYD_LEAVE;
2195 return rv;
2199 FL enum okay
2200 imap_body(struct message *m)
2202 enum okay rv;
2203 NYD_ENTER;
2205 rv = imap_get(&mb, m, NEED_BODY);
2206 NYD_LEAVE;
2207 return rv;
2210 static void
2211 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2212 enum content_info content_info)
2214 NYD_ENTER;
2215 tomp->m_size = frommp->m_size;
2216 tomp->m_lines = frommp->m_lines;
2217 tomp->m_block = frommp->m_block;
2218 tomp->m_offset = frommp->m_offset;
2219 tomp->m_content_info = content_info & CI_HAVE_MASK;
2220 if (content_info & CI_HAVE_BODY) {
2221 tomp->m_xlines = frommp->m_lines;
2222 tomp->m_xsize = frommp->m_size;
2224 putcache(mp, tomp);
2225 NYD_LEAVE;
2228 static enum okay
2229 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2231 /* bot > topp */
2232 char o[LINESIZE];
2233 char const *cp;
2234 struct message mt;
2235 size_t expected;
2236 int n = 0;
2237 FILE *queuefp = NULL;
2238 enum okay ok;
2239 NYD_X;
2241 if (m[bot].m_uid)
2242 snprintf(o, sizeof o,
2243 "%s UID FETCH %" PRIu64 ":%" PRIu64 " (RFC822.HEADER)\r\n",
2244 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2245 else {
2246 if (check_expunged() == STOP)
2247 return STOP;
2248 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2249 tag(1), bot, topp);
2251 IMAP_OUT(o, MB_COMD, return STOP)
2253 srelax_hold();
2254 for (;;) {
2255 ok = imap_answer(mp, 1);
2256 if (response_status != RESPONSE_OTHER)
2257 break;
2258 if (response_other != MESSAGE_DATA_FETCH)
2259 continue;
2260 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2261 srelax_rele();
2262 return STOP;
2264 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2265 continue;
2266 expected = atol(&cp[1]);
2267 if (m[bot-1].m_uid) {
2268 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2269 ui64_t uid;
2271 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2272 for (n = bot; n <= topp; n++)
2273 if (uid == m[n-1].m_uid)
2274 break;
2275 if (n > topp) {
2276 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2277 continue;
2279 } else
2280 n = -1;
2281 } else {
2282 n = responded_other_number;
2283 if (n <= 0 || n > msgCount) {
2284 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2285 continue;
2288 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2289 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2290 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2291 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2292 if (n_poption & n_PO_VERBVERB)
2293 fputs(imapbuf, stderr);
2294 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2295 ui64_t uid;
2297 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2298 for (n = bot; n <= topp; n++)
2299 if (uid == m[n-1].m_uid)
2300 break;
2301 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2302 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2305 srelax();
2307 srelax_rele();
2309 while (mp->mb_active & MB_COMD)
2310 ok = imap_answer(mp, 1);
2311 return ok;
2314 FL void
2315 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2317 sighandler_type saveint, savepipe;
2318 /*enum okay ok = STOP;*/
2319 int i, chunk = 256;
2320 NYD_X;
2322 if (mb.mb_type == MB_CACHE)
2323 return;
2324 if (bot < 1)
2325 bot = 1;
2326 if (topp > msgCount)
2327 topp = msgCount;
2328 for (i = bot; i < topp; i++) {
2329 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2330 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2331 bot = i+1;
2332 else
2333 break;
2335 for (i = topp; i > bot; i--) {
2336 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2337 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2338 topp = i-1;
2339 else
2340 break;
2342 if (bot >= topp)
2343 return;
2345 imaplock = 1;
2346 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2347 safe_signal(SIGINT, &_imap_maincatch);
2348 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2349 if (sigsetjmp(imapjmp, 1) == 0) {
2350 if (savepipe != SIG_IGN)
2351 safe_signal(SIGPIPE, imapcatch);
2353 for (i = bot; i <= topp; i += chunk) {
2354 int j = i + chunk - 1;
2355 j = n_MIN(j, topp);
2356 if (visible(message + j))
2357 /*ok = */imap_fetchheaders(&mb, message, i, j);
2358 if (interrupts)
2359 n_go_onintr_for_imap(); /* XXX imaplock? */
2362 safe_signal(SIGINT, saveint);
2363 safe_signal(SIGPIPE, savepipe);
2364 imaplock = 0;
2367 static enum okay
2368 __imap_exit(struct mailbox *mp)
2370 char o[LINESIZE];
2371 FILE *queuefp = NULL;
2372 NYD_X;
2374 mp->mb_active |= MB_BYE;
2375 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2376 IMAP_OUT(o, MB_COMD, return STOP)
2377 IMAP_ANSWER()
2378 return OKAY;
2381 static enum okay
2382 imap_exit(struct mailbox *mp)
2384 enum okay rv;
2385 NYD_ENTER;
2387 rv = __imap_exit(mp);
2388 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2389 free(mp->mb_imap_pass);
2390 free(mp->mb_imap_account);
2391 free(mp->mb_imap_mailbox);
2392 if (mp->mb_cache_directory != NULL)
2393 free(mp->mb_cache_directory);
2394 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2395 mp->mb_imap_account =
2396 mp->mb_imap_mailbox =
2397 mp->mb_cache_directory = "";
2398 #else
2399 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2400 mp->mb_imap_mailbox = NULL;
2401 mp->mb_cache_directory = NULL;
2402 #endif
2403 #endif
2404 sclose(&mp->mb_sock);
2405 NYD_LEAVE;
2406 return rv;
2409 static enum okay
2410 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2412 NYD_ENTER;
2413 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2414 if (mp->mb_type == MB_IMAP)
2415 delcache(mp, m);
2416 NYD_LEAVE;
2417 return OKAY;
2420 static enum okay
2421 imap_close(struct mailbox *mp)
2423 char o[LINESIZE];
2424 FILE *queuefp = NULL;
2425 NYD_X;
2427 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2428 IMAP_OUT(o, MB_COMD, return STOP)
2429 IMAP_ANSWER()
2430 return OKAY;
2433 static enum okay
2434 imap_update(struct mailbox *mp)
2436 struct message *m;
2437 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2438 NYD_ENTER;
2440 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2441 holdbits();
2442 c = 0;
2443 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2444 if (m->m_flag & MBOX)
2445 ++c;
2446 if (c > 0)
2447 if (makembox() == STOP)
2448 goto jbypass;
2451 gotcha = held = 0;
2452 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2453 if (mp->mb_perm == 0)
2454 dodel = 0;
2455 else if (n_pstate & n_PS_EDIT)
2456 dodel = ((m->m_flag & MDELETED) != 0);
2457 else
2458 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2460 /* Fetch the result after around each 800 STORE commands
2461 * sent (approx. 32k data sent). Otherwise, servers will
2462 * try to flush the return queue at some point, leading
2463 * to a deadlock if we are still writing commands but not
2464 * reading their results */
2465 needstat = stored > 0 && stored % 800 == 0;
2466 /* Even if this message has been deleted, continue
2467 * to set further flags. This is necessary to support
2468 * Gmail semantics, where "delete" actually means
2469 * "archive", and the flags are applied to the copy
2470 * in "All Mail" */
2471 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2472 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2473 stored++;
2475 if (m->m_flag & MFLAG) {
2476 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2477 stored++;
2479 if (m->m_flag & MUNFLAG) {
2480 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2481 stored++;
2483 if (m->m_flag & MANSWER) {
2484 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2485 stored++;
2487 if (m->m_flag & MUNANSWER) {
2488 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2489 stored++;
2491 if (m->m_flag & MDRAFT) {
2492 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2493 stored++;
2495 if (m->m_flag & MUNDRAFT) {
2496 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2497 stored++;
2500 if (dodel) {
2501 imap_delete(mp, m-message+1, m, needstat);
2502 stored++;
2503 gotcha++;
2504 } else if (mp->mb_type != MB_CACHE ||
2505 (!(n_pstate & n_PS_EDIT) &&
2506 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2507 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2508 (MPRESERVE | MTOUCH) ||
2509 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2510 held++;
2511 if (m->m_flag & MNEW) {
2512 m->m_flag &= ~MNEW;
2513 m->m_flag |= MSTATUS;
2516 jbypass:
2517 if (gotcha)
2518 imap_close(mp);
2520 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2521 if (!(m->m_flag & MUNLINKED) &&
2522 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2523 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2524 putcache(mp, m);
2525 modflags++;
2528 /* XXX should be readonly (but our IMAP code is weird...) */
2529 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2530 mb.mb_perm != 0) {
2531 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2532 printf(_("\"%s\" "), displayname);
2533 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2534 ? _("complete\n") : _("updated.\n"));
2535 } else if (held && !(n_pstate & n_PS_EDIT)) {
2536 if (held == 1)
2537 printf(_("Held 1 message in %s\n"), displayname);
2538 else
2539 printf(_("Held %d messages in %s\n"), held, displayname);
2541 fflush(stdout);
2543 NYD_LEAVE;
2544 return OKAY;
2547 FL bool_t
2548 imap_quit(bool_t hold_sigs_on)
2550 sighandler_type volatile saveint, savepipe;
2551 bool_t rv;
2552 NYD_ENTER;
2554 if(hold_sigs_on)
2555 rele_sigs();
2557 if (mb.mb_type == MB_CACHE) {
2558 rv = (imap_update(&mb) == OKAY);
2559 goto jleave;
2562 rv = FAL0;
2564 if (mb.mb_sock.s_fd < 0) {
2565 n_err(_("IMAP connection closed\n"));
2566 goto jleave;
2569 imaplock = 1;
2570 saveint = safe_signal(SIGINT, SIG_IGN);
2571 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2572 if (sigsetjmp(imapjmp, 1)) {
2573 safe_signal(SIGINT, saveint);
2574 safe_signal(SIGPIPE, saveint);
2575 imaplock = 0;
2576 goto jleave;
2578 if (saveint != SIG_IGN)
2579 safe_signal(SIGINT, imapcatch);
2580 if (savepipe != SIG_IGN)
2581 safe_signal(SIGPIPE, imapcatch);
2583 rv = (imap_update(&mb) == OKAY);
2584 if(!same_imap_account && imap_exit(&mb) != OKAY)
2585 rv = FAL0;
2587 safe_signal(SIGINT, saveint);
2588 safe_signal(SIGPIPE, savepipe);
2589 imaplock = 0;
2590 jleave:
2591 if(hold_sigs_on)
2592 hold_sigs();
2593 NYD_LEAVE;
2594 return rv;
2597 static enum okay
2598 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2599 int needstat)
2601 char o[LINESIZE];
2602 FILE *queuefp = NULL;
2603 NYD_X;
2605 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2606 return STOP;
2607 if (m->m_uid)
2608 snprintf(o, sizeof o, "%s UID STORE %" PRIu64 " %cFLAGS (%s)\r\n",
2609 tag(1), m->m_uid, c, sp);
2610 else {
2611 if (check_expunged() == STOP)
2612 return STOP;
2613 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2615 IMAP_OUT(o, MB_COMD, return STOP)
2616 if (needstat)
2617 IMAP_ANSWER()
2618 else
2619 mb.mb_active &= ~MB_COMD;
2620 if (queuefp != NULL)
2621 Fclose(queuefp);
2622 return OKAY;
2625 FL enum okay
2626 imap_undelete(struct message *m, int n)
2628 enum okay rv;
2629 NYD_ENTER;
2631 rv = imap_unstore(m, n, "\\Deleted");
2632 NYD_LEAVE;
2633 return rv;
2636 FL enum okay
2637 imap_unread(struct message *m, int n)
2639 enum okay rv;
2640 NYD_ENTER;
2642 rv = imap_unstore(m, n, "\\Seen");
2643 NYD_LEAVE;
2644 return rv;
2647 static enum okay
2648 imap_unstore(struct message *m, int n, const char *flag)
2650 sighandler_type saveint, savepipe;
2651 enum okay volatile rv = STOP;
2652 NYD_ENTER;
2654 imaplock = 1;
2655 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2656 safe_signal(SIGINT, &_imap_maincatch);
2657 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2658 if (sigsetjmp(imapjmp, 1) == 0) {
2659 if (savepipe != SIG_IGN)
2660 safe_signal(SIGPIPE, imapcatch);
2662 rv = imap_store(&mb, m, n, '-', flag, 1);
2664 safe_signal(SIGINT, saveint);
2665 safe_signal(SIGPIPE, savepipe);
2666 imaplock = 0;
2668 NYD_LEAVE;
2669 if (interrupts)
2670 n_go_onintr_for_imap();
2671 return rv;
2674 static const char *
2675 tag(int new)
2677 static char ts[20];
2678 static long n;
2679 NYD2_ENTER;
2681 if (new)
2682 ++n;
2683 snprintf(ts, sizeof ts, "T%lu", n);
2684 NYD2_LEAVE;
2685 return ts;
2688 FL int
2689 c_imapcodec(void *vp){
2690 bool_t err;
2691 size_t alen;
2692 char const **argv, *varname, *varres, *act, *cp;
2693 NYD_ENTER;
2695 argv = vp;
2696 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2698 act = *argv;
2699 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2701 if(act == cp)
2702 goto jesynopsis;
2703 alen = PTR2SIZE(cp - act);
2704 if(*cp != '\0')
2705 ++cp;
2707 n_pstate_err_no = n_ERR_NONE;
2708 varres = imap_path_normalize(NULL, cp);
2710 if(is_ascncaseprefix(act, "encode", alen))
2711 varres = imap_path_encode(varres, &err);
2712 else if(is_ascncaseprefix(act, "decode", alen))
2713 varres = imap_path_decode(varres, &err);
2714 else
2715 goto jesynopsis;
2717 if(err){
2718 n_pstate_err_no = n_ERR_CANCELED;
2719 varres = cp;
2720 vp = NULL;
2723 if(varname != NULL){
2724 if(!n_var_vset(varname, (uintptr_t)varres)){
2725 n_pstate_err_no = n_ERR_NOTSUP;
2726 vp = NULL;
2728 }else{
2729 struct str in, out;
2731 in.l = strlen(in.s = n_UNCONST(varres));
2732 makeprint(&in, &out);
2733 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2734 n_pstate_err_no = n_err_no;
2735 vp = NULL;
2737 free(out.s);
2740 jleave:
2741 NYD_LEAVE;
2742 return (vp != NULL ? 0 : 1);
2743 jesynopsis:
2744 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2745 n_pstate_err_no = n_ERR_INVAL;
2746 vp = NULL;
2747 goto jleave;
2750 FL int
2751 c_imap_imap(void *vp)
2753 char o[LINESIZE];
2754 sighandler_type saveint, savepipe;
2755 struct mailbox *mp = &mb;
2756 FILE *queuefp = NULL;
2757 enum okay volatile ok = STOP;
2758 NYD_X;
2760 if (mp->mb_type != MB_IMAP) {
2761 printf("Not operating on an IMAP mailbox.\n");
2762 return 1;
2764 imaplock = 1;
2765 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2766 safe_signal(SIGINT, &_imap_maincatch);
2767 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2768 if (sigsetjmp(imapjmp, 1) == 0) {
2769 if (savepipe != SIG_IGN)
2770 safe_signal(SIGPIPE, imapcatch);
2772 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2773 IMAP_OUT(o, MB_COMD, goto out)
2774 while (mp->mb_active & MB_COMD) {
2775 ok = imap_answer(mp, 0);
2776 fputs(responded_text, stdout);
2779 out:
2780 safe_signal(SIGINT, saveint);
2781 safe_signal(SIGPIPE, savepipe);
2782 imaplock = 0;
2784 if (interrupts)
2785 n_go_onintr_for_imap();
2786 return ok != OKAY;
2789 FL int
2790 imap_newmail(int nmail)
2792 NYD_ENTER;
2794 if (nmail && had_exists < 0 && had_expunge < 0) {
2795 imaplock = 1;
2796 imap_noop();
2797 imaplock = 0;
2800 if (had_exists == msgCount && had_expunge < 0)
2801 /* Some servers always respond with EXISTS to NOOP. If
2802 * the mailbox has been changed but the number of messages
2803 * has not, an EXPUNGE must also had been sent; otherwise,
2804 * nothing has changed */
2805 had_exists = -1;
2806 NYD_LEAVE;
2807 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2810 static char *
2811 imap_putflags(int f)
2813 const char *cp;
2814 char *buf, *bp;
2815 NYD2_ENTER;
2817 bp = buf = salloc(100);
2818 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2819 *bp++ = '(';
2820 if (f & MREAD) {
2821 if (bp[-1] != '(')
2822 *bp++ = ' ';
2823 for (cp = "\\Seen"; *cp; cp++)
2824 *bp++ = *cp;
2826 if (f & MFLAGGED) {
2827 if (bp[-1] != '(')
2828 *bp++ = ' ';
2829 for (cp = "\\Flagged"; *cp; cp++)
2830 *bp++ = *cp;
2832 if (f & MANSWERED) {
2833 if (bp[-1] != '(')
2834 *bp++ = ' ';
2835 for (cp = "\\Answered"; *cp; cp++)
2836 *bp++ = *cp;
2838 if (f & MDRAFT) {
2839 if (bp[-1] != '(')
2840 *bp++ = ' ';
2841 for (cp = "\\Draft"; *cp; cp++)
2842 *bp++ = *cp;
2844 *bp++ = ')';
2845 *bp++ = ' ';
2847 *bp = '\0';
2848 NYD2_LEAVE;
2849 return buf;
2852 static void
2853 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2855 NYD2_ENTER;
2856 while (*cp != ')') {
2857 if (*cp == '\\') {
2858 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2859 *f |= MREAD;
2860 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2861 *f |= MNEW;
2862 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2863 *f |= MDELETED;
2864 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2865 *f |= MFLAGGED;
2866 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2867 *f |= MANSWERED;
2868 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2869 *f |= MDRAFTED;
2871 cp++;
2874 if (xp != NULL)
2875 *xp = cp;
2876 NYD2_LEAVE;
2879 static enum okay
2880 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2881 long xsize, enum mflag flag, time_t t)
2883 char o[LINESIZE], *buf;
2884 size_t bufsize, buflen, cnt;
2885 long size, lines, ysize;
2886 char const *qname;
2887 bool_t twice;
2888 FILE *queuefp;
2889 enum okay rv;
2890 NYD_ENTER;
2892 rv = STOP;
2893 queuefp = NULL;
2894 twice = FAL0;
2895 buf = NULL;
2897 if((qname = imap_path_quote(mp, name)) == NULL)
2898 goto jleave;
2900 if (mp->mb_type == MB_CACHE) {
2901 queuefp = cache_queue(mp);
2902 if (queuefp == NULL) {
2903 buf = NULL;
2904 goto jleave;
2906 rv = OKAY;
2909 buf = smalloc(bufsize = LINESIZE);
2910 buflen = 0;
2911 jagain:
2912 size = xsize;
2913 cnt = fsize(fp);
2914 if (fseek(fp, off1, SEEK_SET) < 0) {
2915 rv = STOP;
2916 goto jleave;
2919 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2920 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2921 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2922 while (mp->mb_active & MB_COMD) {
2923 rv = imap_answer(mp, twice);
2924 if (response_type == RESPONSE_CONT)
2925 break;
2928 if (mp->mb_type != MB_CACHE && rv == STOP) {
2929 if (!twice)
2930 goto jtrycreate;
2931 else
2932 goto jleave;
2935 lines = ysize = 0;
2936 while (size > 0) {
2937 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2938 lines++;
2939 ysize += buflen;
2940 buf[buflen - 1] = '\r';
2941 buf[buflen] = '\n';
2942 if (mp->mb_type != MB_CACHE)
2943 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2944 else if (queuefp)
2945 fwrite(buf, 1, buflen+1, queuefp);
2946 size -= buflen + 1;
2948 if (mp->mb_type != MB_CACHE)
2949 swrite(&mp->mb_sock, "\r\n");
2950 else if (queuefp)
2951 fputs("\r\n", queuefp);
2952 while (mp->mb_active & MB_COMD) {
2953 rv = imap_answer(mp, 0);
2954 if (response_status == RESPONSE_NO /*&&
2955 ascncasecmp(responded_text,
2956 "[TRYCREATE] ", 12) == 0*/) {
2957 jtrycreate:
2958 if (twice) {
2959 rv = STOP;
2960 goto jleave;
2962 twice = TRU1;
2963 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2964 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2965 while (mp->mb_active & MB_COMD)
2966 rv = imap_answer(mp, 1);
2967 if (rv == STOP)
2968 goto jleave;
2969 imap_created_mailbox++;
2970 goto jagain;
2971 } else if (rv != OKAY)
2972 n_err(_("IMAP error: %s"), responded_text);
2973 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2974 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2976 jleave:
2977 if (queuefp != NULL)
2978 Fclose(queuefp);
2979 if (buf != NULL)
2980 free(buf);
2981 NYD_LEAVE;
2982 return rv;
2985 static enum okay
2986 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
2988 char *buf, *bp, *lp;
2989 size_t bufsize, buflen, cnt;
2990 off_t off1 = -1, offs;
2991 int flag;
2992 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2993 time_t tim;
2994 long size;
2995 enum okay rv;
2996 NYD_ENTER;
2998 buf = smalloc(bufsize = LINESIZE);
2999 buflen = 0;
3000 cnt = fsize(fp);
3001 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3002 time(&tim);
3003 size = 0;
3005 for (flag = MNEW, state = _NLSEP;;) {
3006 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3008 if (bp == NULL ||
3009 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3010 is_head(buf, buflen, FAL0))) {
3011 if (off1 != (off_t)-1) {
3012 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3013 if (rv == STOP)
3014 goto jleave;
3015 fseek(fp, offs+buflen, SEEK_SET);
3017 off1 = offs + buflen;
3018 size = 0;
3019 flag = MNEW;
3020 state = _INHEAD;
3021 if (bp == NULL)
3022 break;
3023 tim = unixtime(buf);
3024 } else
3025 size += buflen+1;
3026 offs += buflen;
3028 state &= ~_NLSEP;
3029 if (buf[0] == '\n') {
3030 state &= ~_INHEAD;
3031 state |= _NLSEP;
3032 } else if (state & _INHEAD) {
3033 if (ascncasecmp(buf, "status", 6) == 0) {
3034 lp = &buf[6];
3035 while (whitechar(*lp))
3036 lp++;
3037 if (*lp == ':')
3038 while (*++lp != '\0')
3039 switch (*lp) {
3040 case 'R':
3041 flag |= MREAD;
3042 break;
3043 case 'O':
3044 flag &= ~MNEW;
3045 break;
3047 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3048 lp = &buf[8];
3049 while (whitechar(*lp))
3050 lp++;
3051 if (*lp == ':')
3052 while (*++lp != '\0')
3053 switch (*lp) {
3054 case 'F':
3055 flag |= MFLAGGED;
3056 break;
3057 case 'A':
3058 flag |= MANSWERED;
3059 break;
3060 case 'T':
3061 flag |= MDRAFTED;
3062 break;
3067 rv = OKAY;
3068 jleave:
3069 free(buf);
3070 NYD_LEAVE;
3071 return rv;
3074 FL enum okay
3075 imap_append(const char *xserver, FILE *fp, long offset)
3077 sighandler_type volatile saveint, savepipe;
3078 struct url url;
3079 struct ccred ccred;
3080 enum okay rv = STOP;
3081 NYD_ENTER;
3083 if (!url_parse(&url, CPROTO_IMAP, xserver))
3084 goto j_leave;
3085 if (!ok_blook(v15_compat) &&
3086 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3087 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3088 assert(url.url_path.s != NULL);
3090 imaplock = 1;
3091 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3092 safe_signal(SIGINT, &_imap_maincatch);
3093 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3094 if (sigsetjmp(imapjmp, 1))
3095 goto jleave;
3096 if (savepipe != SIG_IGN)
3097 safe_signal(SIGPIPE, imapcatch);
3099 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3100 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3101 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3102 } else {
3103 struct mailbox mx;
3105 memset(&mx, 0, sizeof mx);
3107 if (!_imap_getcred(&mx, &ccred, &url))
3108 goto jleave;
3110 imap_delim_init(&mx, &url);
3111 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3113 if (disconnected(url.url_p_eu_h_p) == 0) {
3114 if (!sopen(&mx.mb_sock, &url))
3115 goto jfail;
3116 mx.mb_sock.s_desc = "IMAP";
3117 mx.mb_type = MB_IMAP;
3118 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3119 /* TODO the code now did
3120 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3121 * TODO though imap_mailbox is sfree()d and mbx
3122 * TODO is possibly even a constant
3123 * TODO i changed this to sstrdup() sofar, as is used
3124 * TODO somewhere else in this file for this! */
3125 if (imap_preauth(&mx, &url) != OKAY ||
3126 imap_auth(&mx, &ccred) != OKAY) {
3127 sclose(&mx.mb_sock);
3128 goto jfail;
3130 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3131 imap_exit(&mx);
3132 } else {
3133 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3134 mx.mb_type = MB_CACHE;
3135 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3137 jfail:
3141 jleave:
3142 safe_signal(SIGINT, saveint);
3143 safe_signal(SIGPIPE, savepipe);
3144 imaplock = 0;
3145 j_leave:
3146 NYD_LEAVE;
3147 if (interrupts)
3148 n_go_onintr_for_imap();
3149 return rv;
3152 static enum okay
3153 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3154 struct list_item **lend, int level)
3156 char o[LINESIZE], *cp;
3157 struct list_item *lp;
3158 const char *qname, *bp;
3159 FILE *queuefp;
3160 enum okay ok;
3161 NYD_X;
3163 ok = STOP;
3164 queuefp = NULL;
3166 if((qname = imap_path_quote(mp, base)) == NULL)
3167 goto jleave;
3169 *list = *lend = NULL;
3170 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3171 IMAP_OUT(o, MB_COMD, goto jleave)
3172 while (mp->mb_active & MB_COMD) {
3173 ok = imap_answer(mp, 1);
3174 if (response_status == RESPONSE_OTHER &&
3175 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3176 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3177 lp = csalloc(1, sizeof *lp);
3178 lp->l_name = cp;
3179 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3180 ++cp;
3181 lp->l_base = *cp ? cp : savestr(base);
3182 lp->l_attr = list_attributes;
3183 lp->l_level = level+1;
3184 lp->l_delim = list_hierarchy_delimiter;
3185 if (*list && *lend) {
3186 (*lend)->l_next = lp;
3187 *lend = lp;
3188 } else
3189 *list = *lend = lp;
3192 jleave:
3193 return ok;
3196 static enum okay
3197 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3199 struct list_item *list, *lend, *lp, *lx, *ly;
3200 int n, depth;
3201 const char *bp;
3202 char *cp;
3203 enum okay rv;
3204 NYD_ENTER;
3206 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3207 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3208 goto jleave;
3209 rv = OKAY;
3210 if (list == NULL || lend == NULL)
3211 goto jleave;
3213 for (lp = list; lp; lp = lp->l_next)
3214 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3215 !(lp->l_attr & LIST_NOINFERIORS)) {
3216 cp = salloc((n = strlen(lp->l_name)) + 2);
3217 memcpy(cp, lp->l_name, n);
3218 cp[n] = lp->l_delim;
3219 cp[n+1] = '\0';
3220 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3221 lp->l_has_children = 1;
3222 if (strcmp(cp, lx->l_name) == 0)
3223 lx = lx->l_next;
3224 if (lx) {
3225 lend->l_next = lx;
3226 lend = ly;
3231 for (lp = list; lp; lp = lp->l_next) {
3232 if (strip) {
3233 cp = lp->l_name;
3234 for (bp = base; *bp && *bp == *cp; bp++)
3235 cp++;
3236 } else
3237 cp = lp->l_name;
3238 if (!(lp->l_attr & LIST_NOSELECT))
3239 fprintf(fp, "%s\n", *cp ? cp : base);
3240 else if (lp->l_has_children == 0)
3241 fprintf(fp, "%s%c\n", *cp ? cp : base,
3242 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3244 jleave:
3245 NYD_LEAVE;
3246 return rv;
3249 FL int
3250 imap_folders(const char * volatile name, int strip)
3252 sighandler_type saveint, savepipe;
3253 const char * volatile fold, *cp, *sp;
3254 FILE * volatile fp;
3255 int rv = 1;
3256 NYD_ENTER;
3258 cp = protbase(name);
3259 sp = mb.mb_imap_account;
3260 if (sp == NULL || strcmp(cp, sp)) {
3261 n_err(
3262 _("Cannot perform `folders' but when on the very IMAP "
3263 "account; the current one is\n `%s' -- "
3264 "try `folders @'\n"),
3265 (sp != NULL ? sp : _("[NONE]")));
3266 goto jleave;
3269 fold = imap_fileof(name);
3270 if (n_psonce & n_PSO_TTYOUT) {
3271 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3272 == NULL) {
3273 n_perr(_("tmpfile"), 0);
3274 goto jleave;
3276 } else
3277 fp = stdout;
3279 imaplock = 1;
3280 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3281 safe_signal(SIGINT, &_imap_maincatch);
3282 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3283 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3284 goto junroll;
3285 if (savepipe != SIG_IGN)
3286 safe_signal(SIGPIPE, imapcatch);
3288 if (mb.mb_type == MB_CACHE)
3289 cache_list(&mb, fold, strip, fp);
3290 else
3291 imap_list(&mb, fold, strip, fp);
3293 imaplock = 0;
3294 if (interrupts) {
3295 if (n_psonce & n_PSO_TTYOUT)
3296 Fclose(fp);
3297 rv = 0;
3298 goto jleave;
3300 fflush(fp);
3302 if (n_psonce & n_PSO_TTYOUT) {
3303 rewind(fp);
3304 if (fsize(fp) > 0){
3305 page_or_print(fp, 0);
3306 rv = 0;
3307 }else
3308 n_err(_("Folder not found\n"));
3309 }else
3310 rv = 0;
3311 junroll:
3312 safe_signal(SIGINT, saveint);
3313 safe_signal(SIGPIPE, savepipe);
3314 if (n_psonce & n_PSO_TTYOUT)
3315 Fclose(fp);
3316 jleave:
3317 NYD_LEAVE;
3318 if (interrupts)
3319 n_go_onintr_for_imap();
3320 return rv;
3323 static enum okay
3324 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3326 char o[LINESIZE];
3327 const char *qname;
3328 bool_t twice, stored;
3329 FILE *queuefp;
3330 enum okay ok;
3331 NYD_X;
3333 ok = STOP;
3334 queuefp = NULL;
3335 twice = stored = FAL0;
3337 /* C99 */{
3338 size_t i;
3340 i = strlen(name = imap_fileof(name));
3341 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3342 name = savecat(name, "INBOX");
3343 if((qname = imap_path_quote(mp, name)) == NULL)
3344 goto jleave;
3347 if (mp->mb_type == MB_CACHE) {
3348 if ((queuefp = cache_queue(mp)) == NULL)
3349 goto jleave;
3350 ok = OKAY;
3353 /* Since it is not possible to set flags on the copy, recently
3354 * set flags must be set on the original to include it in the copy */
3355 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3356 imap_store(mp, m, n, '+', "\\Seen", 0);
3357 if (m->m_flag&MFLAG)
3358 imap_store(mp, m, n, '+', "\\Flagged", 0);
3359 if (m->m_flag&MUNFLAG)
3360 imap_store(mp, m, n, '-', "\\Flagged", 0);
3361 if (m->m_flag&MANSWER)
3362 imap_store(mp, m, n, '+', "\\Answered", 0);
3363 if (m->m_flag&MUNANSWER)
3364 imap_store(mp, m, n, '-', "\\Flagged", 0);
3365 if (m->m_flag&MDRAFT)
3366 imap_store(mp, m, n, '+', "\\Draft", 0);
3367 if (m->m_flag&MUNDRAFT)
3368 imap_store(mp, m, n, '-', "\\Draft", 0);
3369 again:
3370 if (m->m_uid)
3371 snprintf(o, sizeof o, "%s UID COPY %" PRIu64 " %s\r\n",
3372 tag(1), m->m_uid, qname);
3373 else {
3374 if (check_expunged() == STOP)
3375 goto out;
3376 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3378 IMAP_OUT(o, MB_COMD, goto out)
3379 while (mp->mb_active & MB_COMD)
3380 ok = imap_answer(mp, twice);
3382 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3383 response_status == RESPONSE_OK)
3384 imap_copyuid(mp, m, name);
3386 if (response_status == RESPONSE_NO && !twice) {
3387 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3388 IMAP_OUT(o, MB_COMD, goto out)
3389 while (mp->mb_active & MB_COMD)
3390 ok = imap_answer(mp, 1);
3391 if (ok == OKAY) {
3392 imap_created_mailbox++;
3393 goto again;
3397 if (queuefp != NULL)
3398 Fclose(queuefp);
3400 /* ... and reset the flag to its initial value so that the 'exit'
3401 * command still leaves the message unread */
3402 out:
3403 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3404 imap_store(mp, m, n, '-', "\\Seen", 0);
3405 stored = TRU1;
3407 if (m->m_flag & MFLAG) {
3408 imap_store(mp, m, n, '-', "\\Flagged", 0);
3409 stored = TRU1;
3411 if (m->m_flag & MUNFLAG) {
3412 imap_store(mp, m, n, '+', "\\Flagged", 0);
3413 stored = TRU1;
3415 if (m->m_flag & MANSWER) {
3416 imap_store(mp, m, n, '-', "\\Answered", 0);
3417 stored = TRU1;
3419 if (m->m_flag & MUNANSWER) {
3420 imap_store(mp, m, n, '+', "\\Answered", 0);
3421 stored = TRU1;
3423 if (m->m_flag & MDRAFT) {
3424 imap_store(mp, m, n, '-', "\\Draft", 0);
3425 stored = TRU1;
3427 if (m->m_flag & MUNDRAFT) {
3428 imap_store(mp, m, n, '+', "\\Draft", 0);
3429 stored = TRU1;
3431 if (stored) {
3432 mp->mb_active |= MB_COMD;
3433 (void)imap_finish(mp);
3435 jleave:
3436 return ok;
3439 FL enum okay
3440 imap_copy(struct message *m, int n, const char *name)
3442 sighandler_type saveint, savepipe;
3443 enum okay volatile rv = STOP;
3444 NYD_ENTER;
3446 imaplock = 1;
3447 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3448 safe_signal(SIGINT, &_imap_maincatch);
3449 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3450 if (sigsetjmp(imapjmp, 1) == 0) {
3451 if (savepipe != SIG_IGN)
3452 safe_signal(SIGPIPE, imapcatch);
3454 rv = imap_copy1(&mb, m, n, name);
3456 safe_signal(SIGINT, saveint);
3457 safe_signal(SIGPIPE, savepipe);
3458 imaplock = 0;
3460 NYD_LEAVE;
3461 if (interrupts)
3462 n_go_onintr_for_imap();
3463 return rv;
3466 static enum okay
3467 imap_copyuid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *olduid,
3468 ui64_t *newuid)
3470 char const *xp, *yp, *zp;
3471 enum okay rv;
3472 NYD_ENTER;
3474 n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3475 n_idec_ui64_cp(olduid, xp, 10, &yp); /* TODO errors */
3476 n_idec_ui64_cp(newuid, yp, 10, &zp); /* TODO errors */
3477 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3478 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3479 NYD_LEAVE;
3480 return rv;
3483 static enum okay
3484 imap_appenduid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *uid)
3486 char const *xp, *yp;
3487 enum okay rv;
3488 NYD_ENTER;
3490 n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3491 n_idec_ui64_cp(uid, xp, 10, &yp); /* TODO errors */
3492 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3493 *yp == ']');
3494 NYD_LEAVE;
3495 return rv;
3498 static enum okay
3499 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3501 struct mailbox xmb;
3502 struct message xm;
3503 const char *cp;
3504 ui64_t uidvalidity, olduid, newuid;
3505 enum okay rv;
3506 NYD_ENTER;
3508 rv = STOP;
3510 memset(&xmb, 0, sizeof xmb);
3512 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3513 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3514 goto jleave;
3516 rv = OKAY;
3518 xmb = *mp;
3519 xmb.mb_cache_directory = NULL;
3520 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3521 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3522 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3523 sizeof(xmb.mb_imap_delim));
3524 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3525 if (mp->mb_cache_directory != NULL)
3526 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3527 xmb.mb_uidvalidity = uidvalidity;
3528 initcache(&xmb);
3530 if (m == NULL) {
3531 memset(&xm, 0, sizeof xm);
3532 xm.m_uid = olduid;
3533 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3534 goto jleave;
3535 getcache(mp, &xm, NEED_HEADER);
3536 getcache(mp, &xm, NEED_BODY);
3537 } else {
3538 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3539 getcache(mp, m, NEED_HEADER);
3540 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3541 getcache(mp, m, NEED_BODY);
3542 xm = *m;
3544 xm.m_uid = newuid;
3545 xm.m_flag &= ~MFULLYCACHED;
3546 putcache(&xmb, &xm);
3547 jleave:
3548 if (xmb.mb_cache_directory != NULL)
3549 free(xmb.mb_cache_directory);
3550 if (xmb.mb_imap_mailbox != NULL)
3551 free(xmb.mb_imap_mailbox);
3552 if (xmb.mb_imap_pass != NULL)
3553 free(xmb.mb_imap_pass);
3554 if (xmb.mb_imap_account != NULL)
3555 free(xmb.mb_imap_account);
3556 NYD_LEAVE;
3557 return rv;
3560 static enum okay
3561 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3562 long size, long lines, int flag, const char *name)
3564 struct mailbox xmb;
3565 struct message xm;
3566 const char *cp;
3567 ui64_t uidvalidity, uid;
3568 enum okay rv;
3569 NYD_ENTER;
3571 rv = STOP;
3573 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3574 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3575 goto jleave;
3577 rv = OKAY;
3579 xmb = *mp;
3580 xmb.mb_cache_directory = NULL;
3581 /* XXX mb_imap_delim reused */
3582 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3583 xmb.mb_uidvalidity = uidvalidity;
3584 xmb.mb_otf = xmb.mb_itf = fp;
3585 initcache(&xmb);
3586 memset(&xm, 0, sizeof xm);
3587 xm.m_flag = (flag & MREAD) | MNEW;
3588 xm.m_time = t;
3589 xm.m_block = mailx_blockof(off1);
3590 xm.m_offset = mailx_offsetof(off1);
3591 xm.m_size = size;
3592 xm.m_xsize = xsize;
3593 xm.m_lines = xm.m_xlines = lines;
3594 xm.m_uid = uid;
3595 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3596 putcache(&xmb, &xm);
3598 free(xmb.mb_imap_mailbox);
3599 jleave:
3600 NYD_LEAVE;
3601 return rv;
3604 static enum okay
3605 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3607 FILE *tp = NULL;
3608 time_t t;
3609 long size, xsize, ysize, lines;
3610 enum mflag flag = MNEW;
3611 char *name, *buf, *bp;
3612 char const *cp;
3613 size_t bufsize, buflen, cnt;
3614 enum okay rv = STOP;
3615 NYD_ENTER;
3617 buf = smalloc(bufsize = LINESIZE);
3618 buflen = 0;
3619 cnt = fsize(fp);
3620 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3621 goto jstop;
3623 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3625 while (*bp == ' ')
3626 ++bp;
3628 if ((cp = strrchr(bp, '{')) == NULL)
3629 goto jstop;
3631 xsize = atol(&cp[1]) + 2;
3632 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3633 goto jstop;
3634 while (*cp == ' ')
3635 cp++;
3637 if (*cp == '(') {
3638 imap_getflags(cp, &cp, &flag);
3639 while (*++cp == ' ')
3642 t = imap_read_date_time(cp);
3644 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3645 == NULL)
3646 goto jstop;
3648 size = xsize;
3649 ysize = lines = 0;
3650 while (size > 0) {
3651 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3652 goto jstop;
3653 size -= buflen;
3654 buf[--buflen] = '\0';
3655 buf[buflen-1] = '\n';
3656 fwrite(buf, 1, buflen, tp);
3657 ysize += buflen;
3658 ++lines;
3660 fflush(tp);
3661 rewind(tp);
3663 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3664 imap_unquotestr(name));
3665 rv = OKAY;
3666 jstop:
3667 free(buf);
3668 if (tp)
3669 Fclose(tp);
3670 NYD_LEAVE;
3671 return rv;
3674 #ifdef HAVE_IMAP_SEARCH
3675 static enum okay
3676 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3677 int f)
3679 char *o, *cs, c;
3680 size_t n;
3681 FILE *queuefp = NULL;
3682 int i;
3683 const char *cp, *xp;
3684 enum okay ok = STOP;
3685 NYD_X;
3687 c = 0;
3688 for (cp = spec; *cp; cp++)
3689 c |= *cp;
3690 if (c & 0200) {
3691 cp = ok_vlook(ttycharset);
3692 # ifdef HAVE_ICONV
3693 if(asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")){ /* XXX */
3694 char const *nspec;
3696 if((nspec = n_iconv_onetime_cp(n_ICONV_DEFAULT, "utf-8", cp, spec)
3697 ) != NULL){
3698 spec = nspec;
3699 cp = "utf-8";
3702 # endif
3703 cp = imap_quotestr(cp);
3704 cs = n_lofi_alloc(n = strlen(cp) + 10);
3705 snprintf(cs, n, "CHARSET %s ", cp);
3706 } else
3707 cs = n_UNCONST(n_empty);
3709 o = n_lofi_alloc(n = strlen(spec) + 60);
3710 snprintf(o, n, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3711 IMAP_OUT(o, MB_COMD, goto out)
3712 while (mp->mb_active & MB_COMD) {
3713 ok = imap_answer(mp, 0);
3714 if (response_status == RESPONSE_OTHER &&
3715 response_other == MAILBOX_DATA_SEARCH) {
3716 xp = responded_other_text;
3717 while (*xp && *xp != '\r') {
3718 ui64_t uid;
3720 n_idec_ui64_cp(&uid, xp, 10, &xp);/* TODO errors? */
3721 for (i = 0; i < cnt; i++)
3722 if (m[i].m_uid == uid && !(m[i].m_flag & MHIDDEN) &&
3723 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3724 mark(i+1, f);
3728 out:
3729 n_lofi_free(o);
3730 if(cs != n_empty)
3731 n_lofi_free(cs);
3732 return ok;
3735 FL enum okay
3736 imap_search1(const char * volatile spec, int f)
3738 sighandler_type saveint, savepipe;
3739 enum okay volatile rv = STOP;
3740 NYD_ENTER;
3742 if (mb.mb_type != MB_IMAP)
3743 goto jleave;
3745 imaplock = 1;
3746 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3747 safe_signal(SIGINT, &_imap_maincatch);
3748 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3749 if (sigsetjmp(imapjmp, 1) == 0) {
3750 if (savepipe != SIG_IGN)
3751 safe_signal(SIGPIPE, imapcatch);
3753 rv = imap_search2(&mb, message, msgCount, spec, f);
3755 safe_signal(SIGINT, saveint);
3756 safe_signal(SIGPIPE, savepipe);
3757 imaplock = 0;
3758 jleave:
3759 NYD_LEAVE;
3760 if (interrupts)
3761 n_go_onintr_for_imap();
3762 return rv;
3764 #endif /* HAVE_IMAP_SEARCH */
3766 FL int
3767 imap_thisaccount(const char *cp)
3769 int rv;
3770 NYD_ENTER;
3772 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3773 rv = 0;
3774 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3775 mb.mb_imap_account == NULL)
3776 rv = 0;
3777 else
3778 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3779 NYD_LEAVE;
3780 return rv;
3783 FL enum okay
3784 imap_remove(const char * volatile name)
3786 sighandler_type volatile saveint, savepipe;
3787 enum okay volatile rv = STOP;
3788 NYD_ENTER;
3790 if (mb.mb_type != MB_IMAP) {
3791 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3792 goto jleave;
3795 if (!imap_thisaccount(name)) {
3796 n_err(_("Can only remove mailboxes on current IMAP server: "
3797 "\"%s\" not removed\n"), name);
3798 goto jleave;
3801 imaplock = 1;
3802 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3803 safe_signal(SIGINT, &_imap_maincatch);
3804 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3805 if (sigsetjmp(imapjmp, 1) == 0) {
3806 if (savepipe != SIG_IGN)
3807 safe_signal(SIGPIPE, imapcatch);
3809 rv = imap_remove1(&mb, imap_fileof(name));
3811 safe_signal(SIGINT, saveint);
3812 safe_signal(SIGPIPE, savepipe);
3813 imaplock = 0;
3815 if (rv == OKAY)
3816 rv = cache_remove(name);
3817 jleave:
3818 NYD_LEAVE;
3819 if (interrupts)
3820 n_go_onintr_for_imap();
3821 return rv;
3824 static enum okay
3825 imap_remove1(struct mailbox *mp, const char *name)
3827 char *o;
3828 int os;
3829 char const *qname;
3830 FILE *queuefp;
3831 enum okay ok;
3832 NYD_X;
3834 ok = STOP;
3835 queuefp = NULL;
3837 if((qname = imap_path_quote(mp, name)) != NULL){
3838 o = ac_alloc(os = strlen(qname) + 100);
3839 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3840 IMAP_OUT(o, MB_COMD, goto out)
3841 while (mp->mb_active & MB_COMD)
3842 ok = imap_answer(mp, 1);
3843 out:
3844 ac_free(o);
3846 return ok;
3849 FL enum okay
3850 imap_rename(const char *old, const char *new)
3852 sighandler_type saveint, savepipe;
3853 enum okay volatile rv = STOP;
3854 NYD_ENTER;
3856 if (mb.mb_type != MB_IMAP) {
3857 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3858 goto jleave;
3861 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3862 n_err(_("Can only rename mailboxes on current IMAP "
3863 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3864 goto jleave;
3867 imaplock = 1;
3868 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3869 safe_signal(SIGINT, &_imap_maincatch);
3870 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3871 if (sigsetjmp(imapjmp, 1) == 0) {
3872 if (savepipe != SIG_IGN)
3873 safe_signal(SIGPIPE, imapcatch);
3875 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3877 safe_signal(SIGINT, saveint);
3878 safe_signal(SIGPIPE, savepipe);
3879 imaplock = 0;
3881 if (rv == OKAY)
3882 rv = cache_rename(old, new);
3883 jleave:
3884 NYD_LEAVE;
3885 if (interrupts)
3886 n_go_onintr_for_imap();
3887 return rv;
3890 static enum okay
3891 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3893 char *o;
3894 int os;
3895 char const *qoname, *qnname;
3896 FILE *queuefp;
3897 enum okay ok;
3898 NYD_X;
3900 ok = STOP;
3901 queuefp = NULL;
3903 if((qoname = imap_path_quote(mp, old)) != NULL &&
3904 (qnname = imap_path_quote(mp, new)) != NULL){
3905 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3906 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3907 IMAP_OUT(o, MB_COMD, goto out)
3908 while (mp->mb_active & MB_COMD)
3909 ok = imap_answer(mp, 1);
3910 out:
3911 ac_free(o);
3913 return ok;
3916 FL enum okay
3917 imap_dequeue(struct mailbox *mp, FILE *fp)
3919 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3920 size_t bufsize, buflen, cnt;
3921 long offs, offs1, offs2, octets;
3922 int twice, gotcha = 0;
3923 FILE *queuefp = NULL;
3924 enum okay ok = OKAY, rok = OKAY;
3925 NYD_X;
3927 buf = smalloc(bufsize = LINESIZE);
3928 buflen = 0;
3929 cnt = fsize(fp);
3930 while ((offs1 = ftell(fp)) >= 0 &&
3931 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3932 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3934 while (*bp == ' ')
3935 ++bp;
3936 twice = 0;
3937 if ((offs = ftell(fp)) < 0)
3938 goto fail;
3939 again:
3940 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3941 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3942 cp = &bp[9];
3943 while (digitchar(*cp))
3944 cp++;
3945 if (*cp != ' ')
3946 goto fail;
3947 while (*cp == ' ')
3948 cp++;
3949 if ((newname = imap_strex(cp, NULL)) == NULL)
3950 goto fail;
3951 IMAP_OUT(o, MB_COMD, continue)
3952 while (mp->mb_active & MB_COMD)
3953 ok = imap_answer(mp, twice);
3954 if (response_status == RESPONSE_NO && twice++ == 0)
3955 goto trycreate;
3956 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3957 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3959 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3960 IMAP_OUT(o, MB_COMD, continue)
3961 while (mp->mb_active & MB_COMD)
3962 ok = imap_answer(mp, 1);
3963 if (ok == OKAY)
3964 gotcha++;
3965 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3966 if ((cp = strrchr(bp, '{')) == NULL)
3967 goto fail;
3968 octets = atol(&cp[1]) + 2;
3969 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3970 goto fail;
3971 IMAP_OUT(o, MB_COMD, continue)
3972 while (mp->mb_active & MB_COMD) {
3973 ok = imap_answer(mp, twice);
3974 if (response_type == RESPONSE_CONT)
3975 break;
3977 if (ok == STOP) {
3978 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3979 goto trycreate;
3980 goto fail;
3982 while (octets > 0) {
3983 size_t n = (UICMP(z, octets, >, sizeof iob)
3984 ? sizeof iob : (size_t)octets);
3985 octets -= n;
3986 if (n != fread(iob, 1, n, fp))
3987 goto fail;
3988 swrite1(&mp->mb_sock, iob, n, 1);
3990 swrite(&mp->mb_sock, "");
3991 while (mp->mb_active & MB_COMD) {
3992 ok = imap_answer(mp, 0);
3993 if (response_status == RESPONSE_NO && twice++ == 0) {
3994 if (fseek(fp, offs, SEEK_SET) < 0)
3995 goto fail;
3996 goto trycreate;
3999 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4000 if ((offs2 = ftell(fp)) < 0)
4001 goto fail;
4002 fseek(fp, offs1, SEEK_SET);
4003 if (imap_appenduid_cached(mp, fp) == STOP) {
4004 (void)fseek(fp, offs2, SEEK_SET);
4005 goto fail;
4008 } else {
4009 fail:
4010 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4011 rok = STOP;
4013 continue;
4014 trycreate:
4015 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4016 IMAP_OUT(o, MB_COMD, continue)
4017 while (mp->mb_active & MB_COMD)
4018 ok = imap_answer(mp, 1);
4019 if (ok == OKAY)
4020 goto again;
4022 fflush(fp);
4023 rewind(fp);
4024 ftruncate(fileno(fp), 0);
4025 if (gotcha)
4026 imap_close(mp);
4027 free(buf);
4028 return rok;
4031 static char *
4032 imap_strex(char const *cp, char const **xp)
4034 char const *cq;
4035 char *n = NULL;
4036 NYD_ENTER;
4038 if (*cp != '"')
4039 goto jleave;
4041 for (cq = cp + 1; *cq != '\0'; ++cq) {
4042 if (*cq == '\\')
4043 cq++;
4044 else if (*cq == '"')
4045 break;
4047 if (*cq != '"')
4048 goto jleave;
4050 n = salloc(cq - cp + 2);
4051 memcpy(n, cp, cq - cp +1);
4052 n[cq - cp + 1] = '\0';
4053 if (xp != NULL)
4054 *xp = cq + 1;
4055 jleave:
4056 NYD_LEAVE;
4057 return n;
4060 static enum okay
4061 check_expunged(void)
4063 enum okay rv;
4064 NYD_ENTER;
4066 if (expunged_messages > 0) {
4067 n_err(_("Command not executed - messages have been expunged\n"));
4068 rv = STOP;
4069 } else
4070 rv = OKAY;
4071 NYD_LEAVE;
4072 return rv;
4075 FL int
4076 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4078 struct url url;
4079 int rv, omsgCount = msgCount;
4080 NYD_ENTER;
4081 n_UNUSED(vp);
4083 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4084 n_err(_("Already connected\n"));
4085 rv = 1;
4086 goto jleave;
4089 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4090 rv = 1;
4091 goto jleave;
4093 ok_bclear(disconnected);
4094 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4096 if (mb.mb_type == MB_CACHE) {
4097 enum fedit_mode fm = FEDIT_NONE;
4098 if (_imap_rdonly)
4099 fm |= FEDIT_RDONLY;
4100 if (!(n_pstate & n_PS_EDIT))
4101 fm |= FEDIT_SYSBOX;
4102 _imap_setfile1(&url, fm, 1);
4103 if (msgCount > omsgCount)
4104 newmailinfo(omsgCount);
4106 rv = 0;
4107 jleave:
4108 NYD_LEAVE;
4109 return rv;
4112 FL int
4113 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4115 struct url url;
4116 int rv = 1, *msgvec = vp;
4117 NYD_ENTER;
4119 if (mb.mb_type == MB_CACHE) {
4120 n_err(_("Not connected\n"));
4121 goto jleave;
4123 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4124 n_err(_("The current mailbox is not cached\n"));
4125 goto jleave;
4128 if (!url_parse(&url, CPROTO_IMAP, mailname))
4129 goto jleave;
4131 if (*msgvec)
4132 c_cache(vp);
4133 ok_bset(disconnected);
4134 if (mb.mb_type == MB_IMAP) {
4135 enum fedit_mode fm = FEDIT_NONE;
4136 if (_imap_rdonly)
4137 fm |= FEDIT_RDONLY;
4138 if (!(n_pstate & n_PS_EDIT))
4139 fm |= FEDIT_SYSBOX;
4140 sclose(&mb.mb_sock);
4141 _imap_setfile1(&url, fm, 1);
4143 rv = 0;
4144 jleave:
4145 NYD_LEAVE;
4146 return rv;
4149 FL int
4150 c_cache(void *vp)
4152 int rv = 1, *msgvec = vp, *ip;
4153 struct message *mp;
4154 NYD_ENTER;
4156 if (mb.mb_type != MB_IMAP) {
4157 n_err(_("Not connected to an IMAP server\n"));
4158 goto jleave;
4160 if (cached_uidvalidity(&mb) == 0) {
4161 n_err(_("The current mailbox is not cached\n"));
4162 goto jleave;
4165 srelax_hold();
4166 for (ip = msgvec; *ip; ++ip) {
4167 mp = &message[*ip - 1];
4168 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4169 get_body(mp);
4170 srelax();
4173 srelax_rele();
4174 rv = 0;
4175 jleave:
4176 NYD_LEAVE;
4177 return rv;
4180 FL int
4181 disconnected(const char *file)
4183 struct url url;
4184 int rv = 1;
4185 NYD_ENTER;
4187 if (ok_blook(disconnected)) {
4188 rv = 1;
4189 goto jleave;
4192 if (!url_parse(&url, CPROTO_IMAP, file)) {
4193 rv = 0;
4194 goto jleave;
4196 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4198 jleave:
4199 NYD_LEAVE;
4200 return rv;
4203 FL void
4204 transflags(struct message *omessage, long omsgCount, int transparent)
4206 struct message *omp, *nmp, *newdot, *newprevdot;
4207 int hf;
4208 NYD_ENTER;
4210 omp = omessage;
4211 nmp = message;
4212 newdot = message;
4213 newprevdot = NULL;
4214 while (PTRCMP(omp, <, omessage + omsgCount) &&
4215 PTRCMP(nmp, <, message + msgCount)) {
4216 if (dot && nmp->m_uid == dot->m_uid)
4217 newdot = nmp;
4218 if (prevdot && nmp->m_uid == prevdot->m_uid)
4219 newprevdot = nmp;
4220 if (omp->m_uid == nmp->m_uid) {
4221 hf = nmp->m_flag & MHIDDEN;
4222 if (transparent && mb.mb_type == MB_IMAP)
4223 omp->m_flag &= ~MHIDDEN;
4224 *nmp++ = *omp++;
4225 if (transparent && mb.mb_type == MB_CACHE)
4226 nmp[-1].m_flag |= hf;
4227 } else if (omp->m_uid < nmp->m_uid)
4228 ++omp;
4229 else
4230 ++nmp;
4232 dot = newdot;
4233 setdot(newdot);
4234 prevdot = newprevdot;
4235 free(omessage);
4236 NYD_LEAVE;
4239 FL time_t
4240 imap_read_date_time(const char *cp)
4242 char buf[3];
4243 time_t t;
4244 int i, year, month, day, hour, minute, second, sign = -1;
4245 NYD2_ENTER;
4247 /* "25-Jul-2004 15:33:44 +0200"
4248 * | | | | | |
4249 * 0 5 10 15 20 25 */
4250 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4251 goto jinvalid;
4252 day = strtol(&cp[1], NULL, 10);
4253 for (i = 0;;) {
4254 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4255 break;
4256 if (n_month_names[++i][0] == '\0')
4257 goto jinvalid;
4259 month = i + 1;
4260 year = strtol(&cp[8], NULL, 10);
4261 hour = strtol(&cp[13], NULL, 10);
4262 minute = strtol(&cp[16], NULL, 10);
4263 second = strtol(&cp[19], NULL, 10);
4264 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4265 goto jinvalid;
4266 switch (cp[22]) {
4267 case '-':
4268 sign = 1;
4269 break;
4270 case '+':
4271 break;
4272 default:
4273 goto jinvalid;
4275 buf[2] = '\0';
4276 buf[0] = cp[23];
4277 buf[1] = cp[24];
4278 t += strtol(buf, NULL, 10) * sign * 3600;
4279 buf[0] = cp[25];
4280 buf[1] = cp[26];
4281 t += strtol(buf, NULL, 10) * sign * 60;
4282 jleave:
4283 NYD2_LEAVE;
4284 return t;
4285 jinvalid:
4286 time(&t);
4287 goto jleave;
4290 FL const char *
4291 imap_make_date_time(time_t t)
4293 static char s[40];
4294 char const *mn;
4295 si32_t y, md, th, tm, ts;
4296 struct tm *tmp;
4297 int tzdiff, tzdiff_hour, tzdiff_min;
4298 time_t t2;
4299 NYD2_ENTER;
4301 jredo:
4302 if((t2 = mktime(gmtime(&t))) == (time_t)-1){
4303 t = 0;
4304 goto jredo;
4306 tzdiff = t - t2;
4307 if((tmp = localtime(&t)) == NULL){
4308 t = 0;
4309 goto jredo;
4312 tzdiff_hour = (int)(tzdiff / 60);
4313 tzdiff_min = tzdiff_hour % 60;
4314 tzdiff_hour /= 60;
4315 if (tmp->tm_isdst > 0)
4316 tzdiff_hour++;
4318 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
4319 y = 1970;
4320 mn = n_month_names[0];
4321 md = 1;
4322 th = tm = ts = 0;
4323 }else{
4324 y += 1900;
4325 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
4326 ? n_month_names[tmp->tm_mon] : n_qm;
4328 if((md = tmp->tm_mday) < 1 || md > 31)
4329 md = 1;
4331 if((th = tmp->tm_hour) < 0 || th > 23)
4332 th = 0;
4333 if((tm = tmp->tm_min) < 0 || tm > 59)
4334 tm = 0;
4335 if((ts = tmp->tm_sec) < 0 || ts > 60)
4336 ts = 0;
4339 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4340 md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
4341 NYD2_LEAVE;
4342 return s;
4345 FL char *
4346 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4348 char *n, *np;
4349 NYD2_ENTER;
4351 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4352 n_MEMORY_DEBUG_ARGSCALL);
4354 /* Just ignore the `is-system-mailbox' prefix XXX */
4355 if (cp[0] == '%' && cp[1] == ':')
4356 cp += 2;
4358 while (*cp != '\0') {
4359 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4360 *np++ = *cp++;
4361 *np++ = *cp++;
4362 *np++ = *cp++;
4363 } else if (cp[0] == '/')
4364 break;
4365 else
4366 *np++ = *cp++;
4368 *np = '\0';
4369 NYD2_LEAVE;
4370 return n;
4372 #endif /* HAVE_IMAP */
4374 /* s-it-mode */