make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / obs-imap.c
bloba2167d9a154a49b700a07920cbe925dc75b16f7a
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 * SPDX-License-Identifier: BSD-4-Clause
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE obs_imap
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 #ifdef HAVE_IMAP
48 # include <sys/socket.h>
50 # include <netdb.h>
52 # include <netinet/in.h>
54 # ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
56 # endif
57 #endif
59 EMPTY_FILE()
60 #ifdef HAVE_IMAP
61 #define IMAP_ANSWER() \
63 if (mp->mb_type != MB_CACHE) {\
64 enum okay ok = OKAY;\
65 while (mp->mb_active & MB_COMD)\
66 ok = imap_answer(mp, 1);\
67 if (ok == STOP)\
68 return STOP;\
72 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
73 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
74 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
75 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
76 * TODO I.e., that should be a function, not a macro ... or so.
77 * TODO This entire module needs MASSIVE work! */
78 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
79 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
81 if (mp->mb_type != MB_CACHE) {\
82 if (imap_finish(mp) == STOP) {\
83 ACTIONBAIL;\
85 if (n_poption & n_PO_D_VV)\
86 n_err(">>> %s", X);\
87 mp->mb_active |= Y;\
88 if (swrite(&mp->mb_sock, X) == STOP) {\
89 ACTIONERR;\
91 } else {\
92 if (queuefp != NULL)\
93 fputs(X, queuefp);\
97 static struct record {
98 struct record *rec_next;
99 unsigned long rec_count;
100 enum rec_type {
101 REC_EXISTS,
102 REC_EXPUNGE
103 } rec_type;
104 } *record, *recend;
106 static enum {
107 RESPONSE_TAGGED,
108 RESPONSE_DATA,
109 RESPONSE_FATAL,
110 RESPONSE_CONT,
111 RESPONSE_ILLEGAL
112 } response_type;
114 static enum {
115 RESPONSE_OK,
116 RESPONSE_NO,
117 RESPONSE_BAD,
118 RESPONSE_PREAUTH,
119 RESPONSE_BYE,
120 RESPONSE_OTHER,
121 RESPONSE_UNKNOWN
122 } response_status;
124 static char *responded_tag;
125 static char *responded_text;
126 static char *responded_other_text;
127 static long responded_other_number;
129 static enum {
130 MAILBOX_DATA_FLAGS,
131 MAILBOX_DATA_LIST,
132 MAILBOX_DATA_LSUB,
133 MAILBOX_DATA_MAILBOX,
134 MAILBOX_DATA_SEARCH,
135 MAILBOX_DATA_STATUS,
136 MAILBOX_DATA_EXISTS,
137 MAILBOX_DATA_RECENT,
138 MESSAGE_DATA_EXPUNGE,
139 MESSAGE_DATA_FETCH,
140 CAPABILITY_DATA,
141 RESPONSE_OTHER_UNKNOWN
142 } response_other;
144 static enum list_attributes {
145 LIST_NONE = 000,
146 LIST_NOINFERIORS = 001,
147 LIST_NOSELECT = 002,
148 LIST_MARKED = 004,
149 LIST_UNMARKED = 010
150 } list_attributes;
152 static int list_hierarchy_delimiter;
153 static char *list_name;
155 struct list_item {
156 struct list_item *l_next;
157 char *l_name;
158 char *l_base;
159 enum list_attributes l_attr;
160 int l_delim;
161 int l_level;
162 int l_has_children;
165 static char *imapbuf; /* TODO not static, use pool */
166 static size_t imapbufsize;
167 static sigjmp_buf imapjmp;
168 static sighandler_type savealrm;
169 static int imapkeepalive;
170 static long had_exists = -1;
171 static long had_expunge = -1;
172 static long expunged_messages;
173 static int volatile imaplock;
174 static int same_imap_account;
175 static bool_t _imap_rdonly;
177 static char *imap_quotestr(char const *s);
178 static char *imap_unquotestr(char const *s);
179 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
180 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
181 /* Returns NULL on error */
182 static char *imap_path_quote(struct mailbox *mp, char const *cp);
183 static void imap_other_get(char *pp);
184 static void imap_response_get(const char **cp);
185 static void imap_response_parse(void);
186 static enum okay imap_answer(struct mailbox *mp, int errprnt);
187 static enum okay imap_parse_list(void);
188 static enum okay imap_finish(struct mailbox *mp);
189 static void imap_timer_off(void);
190 static void imapcatch(int s);
191 static void _imap_maincatch(int s);
192 static enum okay imap_noop1(struct mailbox *mp);
193 static void rec_queue(enum rec_type type, unsigned long cnt);
194 static enum okay rec_dequeue(void);
195 static void rec_rmqueue(void);
196 static void imapalarm(int s);
197 static enum okay imap_preauth(struct mailbox *mp, struct url *urlp);
198 static enum okay imap_capability(struct mailbox *mp);
199 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
200 #ifdef HAVE_MD5
201 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
202 #endif
203 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
204 #ifdef HAVE_GSSAPI
205 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
206 #endif
207 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
208 static void imap_init(struct mailbox *mp, int n);
209 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
210 int *prevcount);
211 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
212 struct url *urlp);
213 static int _imap_setfile1(char const *who, struct url *urlp,
214 enum fedit_mode fm, int transparent);
215 static int imap_fetchdata(struct mailbox *mp, struct message *m,
216 size_t expected, int need, const char *head,
217 size_t headsize, long headlines);
218 static void imap_putstr(struct mailbox *mp, struct message *m,
219 const char *str, const char *head, size_t headsize,
220 long headlines);
221 static enum okay imap_get(struct mailbox *mp, struct message *m,
222 enum needspec need);
223 static void commitmsg(struct mailbox *mp, struct message *to,
224 struct message *from, enum content_info content_info);
225 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
226 int bot, int top);
227 static enum okay imap_exit(struct mailbox *mp);
228 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
229 int needstat);
230 static enum okay imap_close(struct mailbox *mp);
231 static enum okay imap_update(struct mailbox *mp);
232 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
233 int c, const char *sp, int needstat);
234 static enum okay imap_unstore(struct message *m, int n, const char *flag);
235 static const char *tag(int new);
236 static char * imap_putflags(int f);
237 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
238 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
239 off_t off1, long xsize, enum mflag flag, time_t t);
240 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
241 long offset);
242 static enum okay imap_list1(struct mailbox *mp, const char *base,
243 struct list_item **list, struct list_item **lend,
244 int level);
245 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
246 FILE *fp);
247 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
248 const char *name);
249 static enum okay imap_copyuid_parse(const char *cp,
250 ui64_t *uidvalidity, ui64_t *olduid, ui64_t *newuid);
251 static enum okay imap_appenduid_parse(const char *cp,
252 ui64_t *uidvalidity, ui64_t *uid);
253 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
254 const char *name);
255 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
256 long off1, long xsize, long size, long lines, int flag,
257 const char *name);
258 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
259 #ifdef HAVE_IMAP_SEARCH
260 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
261 const char *spec, int f);
262 #endif
263 static enum okay imap_remove1(struct mailbox *mp, const char *name);
264 static enum okay imap_rename1(struct mailbox *mp, const char *old,
265 const char *new);
266 static char * imap_strex(char const *cp, char const **xp);
267 static enum okay check_expunged(void);
269 static char *
270 imap_quotestr(char const *s)
272 char *n, *np;
273 NYD2_ENTER;
275 np = n = n_autorec_alloc(2 * strlen(s) + 3);
276 *np++ = '"';
277 while (*s) {
278 if (*s == '"' || *s == '\\')
279 *np++ = '\\';
280 *np++ = *s++;
282 *np++ = '"';
283 *np = '\0';
284 NYD2_LEAVE;
285 return n;
288 static char *
289 imap_unquotestr(char const *s)
291 char *n, *np;
292 NYD2_ENTER;
294 if (*s != '"') {
295 n = savestr(s);
296 goto jleave;
299 np = n = n_autorec_alloc(strlen(s) + 1);
300 while (*++s) {
301 if (*s == '\\')
302 s++;
303 else if (*s == '"')
304 break;
305 *np++ = *s;
307 *np = '\0';
308 jleave:
309 NYD2_LEAVE;
310 return n;
313 static void
314 imap_delim_init(struct mailbox *mp, struct url const *urlp){
315 size_t i;
316 char const *cp;
317 NYD2_ENTER;
319 mp->mb_imap_delim[0] = '\0';
321 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
322 i = strlen(cp);
324 if(i == 0){
325 cp = n_IMAP_DELIM;
326 i = sizeof(n_IMAP_DELIM) -1;
327 goto jcopy;
330 if(i < n_NELEM(mp->mb_imap_delim))
331 jcopy:
332 memcpy(&mb.mb_imap_delim[0], cp, i +1);
333 else
334 n_err(_("*imap-delim* for %s is too long: %s\n"),
335 urlp->url_input, cp);
337 NYD2_LEAVE;
340 static char const *
341 imap_path_normalize(struct mailbox *mp, char const *cp){
342 char *rv_base, *rv, dc2, dc, c, lc;
343 char const *dcp;
344 NYD2_ENTER;
346 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
347 * exactly what i have specified" */
348 if(mp == NULL || mp->mb_imap_delim[0] == '\0')
349 dcp = &n_IMAP_DELIM[0];
350 else
351 dcp = &mp->mb_imap_delim[0];
352 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
354 /* Plain names don't need path quoting */
355 /* C99 */{
356 size_t i, j;
357 char const *cpx;
359 for(cpx = cp;; ++cpx)
360 if((c = *cpx) == '\0')
361 goto jleave;
362 else if(dc == '\0'){
363 if(strchr(n_IMAP_DELIM, c)){
364 dc = c;
365 break;
367 }else if(c == dc)
368 break;
369 else if(dc2 && strchr(dcp, c) != NULL)
370 break;
372 /* And we don't need to reevaluate what we have seen yet */
373 i = PTR2SIZE(cpx - cp);
374 rv = rv_base = n_autorec_alloc(i + (j = strlen(cpx) +1));
375 if(i > 0)
376 memcpy(rv, cp, i);
377 memcpy(&rv[i], cpx, j);
378 rv += i;
379 cp = cpx;
382 /* Squeeze adjacent delimiters, convert remain to dc */
383 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
384 if(c == dc || (lc != '\0' && dc2 && strchr(dcp, c) != NULL))
385 c = dc;
386 if(c != dc || lc != dc)
387 *rv++ = c;
389 *rv = '\0';
391 cp = rv_base;
392 jleave:
393 NYD2_LEAVE;
394 return cp;
397 FL char const *
398 imap_path_encode(char const *cp, bool_t *err_or_null){
399 /* To a large extend inspired by dovecot(1) */
400 struct str out;
401 bool_t err_def;
402 ui8_t *be16p_base, *be16p;
403 char const *emsg;
404 char c;
405 size_t l, l_plain;
406 NYD2_ENTER;
408 if(err_or_null == NULL)
409 err_or_null = &err_def;
410 *err_or_null = FAL0;
412 /* Is this a string that works out as "plain US-ASCII"? */
413 for(l = 0;; ++l)
414 if((c = cp[l]) == '\0')
415 goto jleave;
416 else if(c <= 0x1F || c >= 0x7F || c == '&')
417 break;
419 *err_or_null = TRU1;
421 /* We need to encode in mUTF-7! For that, we first have to convert the
422 * local charset to UTF-8, then convert all characters which need to be
423 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
424 * We can skip the UTF-8 conversion occasionally, however */
425 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
426 !defined HAVE_ALWAYS_UNICODE_LOCALE
427 if(!(n_psonce & n_PSO_UNICODE)){
428 char const *x;
430 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
431 if((x = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
432 cp)) == NULL)
433 goto jerr;
434 cp = x;
436 /* So: Why not start all over again?
437 * Is this a string that works out as "plain US-ASCII"? */
438 for(l = 0;; ++l)
439 if((c = cp[l]) == '\0')
440 goto jleave;
441 else if(c <= 0x1F || c >= 0x7F || c == '&')
442 break;
444 #endif
446 /* We need to encode, save what we have, encode the rest */
447 l_plain = l;
449 for(cp += l, l = 0; cp[l] != '\0'; ++l)
451 be16p_base = n_autorec_alloc((l << 1) +1); /* XXX use n_string, resize */
453 out.s = n_autorec_alloc(l_plain + (l << 2) +1); /* XXX use n_string.. */
454 if(l_plain > 0)
455 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
456 else
457 out.l = 0;
458 DBG( l_plain += (l << 2); )
460 while(l > 0){
461 c = *cp++;
462 --l;
464 if(c == '&'){
465 out.s[out.l + 0] = '&';
466 out.s[out.l + 1] = '-';
467 out.l += 2;
468 }else if(c > 0x1F && c < 0x7F)
469 out.s[out.l++] = c;
470 else{
471 static char const mb64ct[] =
472 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
473 ui32_t utf32;
475 /* Convert consecutive non-representables */
476 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
478 for(be16p = be16p_base, --cp, ++l;;){
479 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
480 goto jerr;
482 /* TODO S-CText: magic utf16 conversions */
483 if(utf32 < 0x10000){
484 be16p[1] = utf32 & 0xFF;
485 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
486 be16p += 2;
487 }else{
488 ui16_t s7e;
490 utf32 -= 0x10000;
491 s7e = 0xD800u | (utf32 >> 10);
492 be16p[1] = s7e & 0xFF;
493 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
494 s7e = 0xDC00u | (utf32 &= 0x03FF);
495 be16p[3] = s7e & 0xFF;
496 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
497 be16p += 4;
500 if(l == 0)
501 break;
502 if((c = *cp) > 0x1F && c < 0x7F)
503 break;
506 /* And then warp that UTF-16BE to mUTF-7 */
507 out.s[out.l++] = '&';
508 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
509 be16p = be16p_base;
511 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
512 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
513 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
514 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
515 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
516 out.l += 4;
518 if(utf32 > 0){
519 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
520 if(--utf32 == 0){
521 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
522 out.l += 2;
523 }else{
524 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
525 (be16p[1] >> 4)];
526 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
527 out.l += 3;
530 out.s[out.l++] = '-';
533 out.s[out.l] = '\0';
534 assert(out.l <= l_plain);
535 *err_or_null = FAL0;
536 cp = out.s;
537 jleave:
538 NYD2_LEAVE;
539 return cp;
540 jerr:
541 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
542 goto jleave;
545 FL char *
546 imap_path_decode(char const *path, bool_t *err_or_null){
547 /* To a large extend inspired by dovecot(1) TODO use string */
548 bool_t err_def;
549 ui8_t *mb64p_base, *mb64p, *mb64xp;
550 char const *emsg, *cp;
551 char *rv_base, *rv, c;
552 size_t l_orig, l, i;
553 NYD2_ENTER;
555 if(err_or_null == NULL)
556 err_or_null = &err_def;
557 *err_or_null = FAL0;
559 l = l_orig = strlen(path);
560 rv = rv_base = n_autorec_alloc(l << 1);
561 memcpy(rv, path, l +1);
563 /* xxx Don't check for invalid characters from malicious servers */
564 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
565 goto jleave;
567 *err_or_null = TRU1;
569 emsg = N_("Invalid mUTF-7 encoding");
570 i = PTR2SIZE(cp - path);
571 rv += i;
572 l -= i;
573 mb64p_base = NULL;
575 while(l > 0){
576 if((c = *cp) != '&'){
577 if(c <= 0x1F || c >= 0x7F){
578 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
579 goto jerr;
581 *rv++ = c;
582 ++cp;
583 --l;
584 }else if(--l == 0)
585 goto jeincpl;
586 else if(*++cp == '-'){
587 *rv++ = '&';
588 ++cp;
589 --l;
590 }else if(l < 3){
591 jeincpl:
592 emsg = N_("Invalid mUTF-7: incomplete input");
593 goto jerr;
594 }else{
595 /* mUTF-7 -> UTF-16BE -> UTF-8 */
596 static ui8_t const mb64dt[256] = {
597 #undef XX
598 #define XX 0xFFu
599 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
600 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
601 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
602 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
603 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
604 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
605 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
606 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
607 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
608 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
609 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
610 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
611 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
612 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
613 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
614 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
617 if(mb64p_base == NULL)
618 mb64p_base = n_autorec_alloc(l);
620 /* Decode the mUTF-7 to what is indeed UTF-16BE */
621 for(mb64p = mb64p_base;;){
622 assert(l >= 3);
623 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
624 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
625 goto jerr;
626 mb64p += 2;
628 c = cp[2];
629 cp += 3;
630 l -= 3;
631 if(c == '-')
632 break;
633 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
634 goto jerr;
636 if(l == 0)
637 goto jerr;
638 --l;
639 if((c = *cp++) == '-')
640 break;
641 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
642 goto jerr;
644 if(l < 3){
645 if(l > 0 && *cp == '-'){
646 --l;
647 ++cp;
648 break;
650 goto jerr;
653 #undef XX
655 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
656 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
657 goto jerr;
660 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
661 i = PTR2SIZE(mb64p - mb64p_base);
662 mb64p = mb64xp = mb64p_base;
664 while(i > 0){
665 ui8_t ul, u0, u1, u2, u3;
667 ul = (i >= 4) ? 4 : i & 0x3;
668 i -= ul;
669 u0 = mb64xp[0];
670 u1 = mb64xp[1];
671 u2 = (ul < 3) ? 0 : mb64xp[2];
672 u3 = (ul < 4) ? 0 : mb64xp[3];
673 mb64xp += ul;
674 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
675 if(ul < 3)
676 break;
677 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
678 if(ul < 4)
679 break;
680 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
683 /* UTF-16BE we convert to UTF-8 */
684 i = PTR2SIZE(mb64p - mb64p_base);
685 if(i & 1){
686 emsg = N_("Odd bytecount for UTF-16BE input");
687 goto jerr;
690 /* TODO S-CText: magic utf16 conversions */
691 emsg = N_("Invalid UTF-16BE encoding");
693 for(mb64p = mb64p_base; i > 0;){
694 ui32_t utf32;
695 ui16_t uhi, ulo;
697 uhi = mb64p[0];
698 uhi <<= 8;
699 uhi |= mb64p[1];
701 /* Not a surrogate? */
702 if(uhi < 0xD800 || uhi > 0xDFFF){
703 utf32 = uhi;
704 mb64p += 2;
705 i -= 2;
706 }else if(uhi > 0xDBFF)
707 goto jerr;
708 else if(i < 4){
709 emsg = N_("Incomplete UTF-16BE surrogate pair");
710 goto jerr;
711 }else{
712 ulo = mb64p[2];
713 ulo <<= 8;
714 ulo |= mb64p[3];
715 if(ulo < 0xDC00 || ulo > 0xDFFF)
716 goto jerr;
718 utf32 = (uhi &= 0x03FF);
719 utf32 <<= 10;
720 utf32 += 0x10000;
721 utf32 |= (ulo &= 0x03FF);
722 mb64p += 4;
723 i -= 4;
726 utf32 = n_utf32_to_utf8(utf32, rv);
727 rv += utf32;
731 *rv = '\0';
733 /* We can skip the UTF-8 conversion occasionally */
734 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
735 !defined HAVE_ALWAYS_UNICODE_LOCALE
736 if(!(n_psonce & n_PSO_UNICODE)){
737 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
738 if((rv = n_iconv_onetime_cp(n_ICONV_NONE, NULL, NULL, rv_base)) == NULL)
739 goto jerr;
741 #endif
743 *err_or_null = FAL0;
744 rv = rv_base;
745 jleave:
746 NYD2_LEAVE;
747 return rv;
748 jerr:
749 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
750 memcpy(rv = rv_base, path, ++l_orig);
751 goto jleave;
754 static char *
755 imap_path_quote(struct mailbox *mp, char const *cp){
756 bool_t err;
757 char *rv;
758 NYD2_ENTER;
760 cp = imap_path_normalize(mp, cp);
761 cp = imap_path_encode(cp, &err);
762 rv = err ? NULL : imap_quotestr(cp);
763 NYD2_LEAVE;
764 return rv;
767 static void
768 imap_other_get(char *pp)
770 char *xp;
771 NYD2_ENTER;
773 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
774 pp += 6;
775 response_other = MAILBOX_DATA_FLAGS;
776 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
777 pp += 5;
778 response_other = MAILBOX_DATA_LIST;
779 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
780 pp += 5;
781 response_other = MAILBOX_DATA_LSUB;
782 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
783 pp += 8;
784 response_other = MAILBOX_DATA_MAILBOX;
785 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
786 pp += 7;
787 response_other = MAILBOX_DATA_SEARCH;
788 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
789 pp += 7;
790 response_other = MAILBOX_DATA_STATUS;
791 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
792 pp += 11;
793 response_other = CAPABILITY_DATA;
794 } else {
795 responded_other_number = strtol(pp, &xp, 10);
796 while (*xp == ' ')
797 ++xp;
798 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
799 response_other = MAILBOX_DATA_EXISTS;
800 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
801 response_other = MAILBOX_DATA_RECENT;
802 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
803 response_other = MESSAGE_DATA_EXPUNGE;
804 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
805 pp = &xp[6];
806 response_other = MESSAGE_DATA_FETCH;
807 } else
808 response_other = RESPONSE_OTHER_UNKNOWN;
810 responded_other_text = pp;
811 NYD2_LEAVE;
814 static void
815 imap_response_get(const char **cp)
817 NYD2_ENTER;
818 if (ascncasecmp(*cp, "OK ", 3) == 0) {
819 *cp += 3;
820 response_status = RESPONSE_OK;
821 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
822 *cp += 3;
823 response_status = RESPONSE_NO;
824 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
825 *cp += 4;
826 response_status = RESPONSE_BAD;
827 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
828 *cp += 8;
829 response_status = RESPONSE_PREAUTH;
830 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
831 *cp += 4;
832 response_status = RESPONSE_BYE;
833 } else
834 response_status = RESPONSE_OTHER;
835 NYD2_LEAVE;
838 static void
839 imap_response_parse(void)
841 static char *parsebuf; /* TODO Use pool */
842 static size_t parsebufsize;
844 const char *ip = imapbuf;
845 char *pp;
846 NYD2_ENTER;
848 if (parsebufsize < imapbufsize + 1)
849 parsebuf = n_realloc(parsebuf, parsebufsize = imapbufsize);
850 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
851 pp = parsebuf;
852 switch (*ip) {
853 case '+':
854 response_type = RESPONSE_CONT;
855 ip++;
856 pp++;
857 while (*ip == ' ') {
858 ip++;
859 pp++;
861 break;
862 case '*':
863 ip++;
864 pp++;
865 while (*ip == ' ') {
866 ip++;
867 pp++;
869 imap_response_get(&ip);
870 pp = &parsebuf[ip - imapbuf];
871 switch (response_status) {
872 case RESPONSE_BYE:
873 response_type = RESPONSE_FATAL;
874 break;
875 default:
876 response_type = RESPONSE_DATA;
878 break;
879 default:
880 responded_tag = parsebuf;
881 while (*pp && *pp != ' ')
882 pp++;
883 if (*pp == '\0') {
884 response_type = RESPONSE_ILLEGAL;
885 break;
887 *pp++ = '\0';
888 while (*pp && *pp == ' ')
889 pp++;
890 if (*pp == '\0') {
891 response_type = RESPONSE_ILLEGAL;
892 break;
894 ip = &imapbuf[pp - parsebuf];
895 response_type = RESPONSE_TAGGED;
896 imap_response_get(&ip);
897 pp = &parsebuf[ip - imapbuf];
899 responded_text = pp;
900 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
901 response_status == RESPONSE_OTHER)
902 imap_other_get(pp);
903 NYD2_LEAVE;
906 static enum okay
907 imap_answer(struct mailbox *mp, int errprnt)
909 int i, complete;
910 enum okay rv;
911 NYD2_ENTER;
913 rv = OKAY;
914 if (mp->mb_type == MB_CACHE)
915 goto jleave;
916 rv = STOP;
917 jagain:
918 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
919 if (n_poption & n_PO_D_VV)
920 n_err(">>> SERVER: %s", imapbuf);
921 imap_response_parse();
922 if (response_type == RESPONSE_ILLEGAL)
923 goto jagain;
924 if (response_type == RESPONSE_CONT) {
925 rv = OKAY;
926 goto jleave;
928 if (response_status == RESPONSE_OTHER) {
929 if (response_other == MAILBOX_DATA_EXISTS) {
930 had_exists = responded_other_number;
931 rec_queue(REC_EXISTS, responded_other_number);
932 if (had_expunge > 0)
933 had_expunge = 0;
934 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
935 rec_queue(REC_EXPUNGE, responded_other_number);
936 if (had_expunge < 0)
937 had_expunge = 0;
938 had_expunge++;
939 expunged_messages++;
942 complete = 0;
943 if (response_type == RESPONSE_TAGGED) {
944 if (asccasecmp(responded_tag, tag(0)) == 0)
945 complete |= 1;
946 else
947 goto jagain;
949 switch (response_status) {
950 case RESPONSE_PREAUTH:
951 mp->mb_active &= ~MB_PREAUTH;
952 /*FALLTHRU*/
953 case RESPONSE_OK:
954 jokay:
955 rv = OKAY;
956 complete |= 2;
957 break;
958 case RESPONSE_NO:
959 case RESPONSE_BAD:
960 jstop:
961 complete |= 2;
962 if (errprnt)
963 n_err(_("IMAP error: %s"), responded_text);
964 break;
965 case RESPONSE_UNKNOWN: /* does not happen */
966 case RESPONSE_BYE:
967 i = mp->mb_active;
968 mp->mb_active = MB_NONE;
969 if (i & MB_BYE)
970 goto jokay;
971 goto jstop;
972 case RESPONSE_OTHER:
973 rv = OKAY;
974 break;
976 if (response_status != RESPONSE_OTHER &&
977 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
978 n_err(_("IMAP alert: %s"), &responded_text[8]);
979 if (complete == 3)
980 mp->mb_active &= ~MB_COMD;
981 } else
982 mp->mb_active = MB_NONE;
983 jleave:
984 NYD2_LEAVE;
985 return rv;
988 static enum okay
989 imap_parse_list(void)
991 char *cp;
992 enum okay rv;
993 NYD2_ENTER;
995 rv = STOP;
997 cp = responded_other_text;
998 list_attributes = LIST_NONE;
999 if (*cp == '(') {
1000 while (*cp && *cp != ')') {
1001 if (*cp == '\\') {
1002 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
1003 list_attributes |= LIST_NOINFERIORS;
1004 cp += 12;
1005 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
1006 list_attributes |= LIST_NOSELECT;
1007 cp += 9;
1008 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
1009 list_attributes |= LIST_MARKED;
1010 cp += 7;
1011 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
1012 list_attributes |= LIST_UNMARKED;
1013 cp += 9;
1016 cp++;
1018 if (*++cp != ' ')
1019 goto jleave;
1020 while (*cp == ' ')
1021 cp++;
1024 list_hierarchy_delimiter = EOF;
1025 if (*cp == '"') {
1026 if (*++cp == '\\')
1027 cp++;
1028 list_hierarchy_delimiter = *cp++ & 0377;
1029 if (cp[0] != '"' || cp[1] != ' ')
1030 goto jleave;
1031 cp++;
1032 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1033 list_hierarchy_delimiter = EOF;
1034 cp += 3;
1037 while (*cp == ' ')
1038 cp++;
1039 list_name = cp;
1040 while (*cp && *cp != '\r')
1041 cp++;
1042 *cp = '\0';
1043 rv = OKAY;
1044 jleave:
1045 NYD2_LEAVE;
1046 return rv;
1049 static enum okay
1050 imap_finish(struct mailbox *mp)
1052 NYD_ENTER;
1053 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1054 imap_answer(mp, 1);
1055 NYD_LEAVE;
1056 return OKAY;
1059 static void
1060 imap_timer_off(void)
1062 NYD_ENTER;
1063 if (imapkeepalive > 0) {
1064 alarm(0);
1065 safe_signal(SIGALRM, savealrm);
1067 NYD_LEAVE;
1070 static void
1071 imapcatch(int s)
1073 NYD_X; /* Signal handler */
1074 switch (s) {
1075 case SIGINT:
1076 n_err_sighdl(_("Interrupt\n"));
1077 siglongjmp(imapjmp, 1);
1078 /*NOTREACHED*/
1079 case SIGPIPE:
1080 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1081 break;
1085 static void
1086 _imap_maincatch(int s)
1088 NYD_X; /* Signal handler */
1089 n_UNUSED(s);
1090 if (interrupts++ == 0) {
1091 n_err_sighdl(_("Interrupt\n"));
1092 return;
1094 n_go_onintr_for_imap();
1097 static enum okay
1098 imap_noop1(struct mailbox *mp)
1100 char o[LINESIZE];
1101 FILE *queuefp = NULL;
1102 NYD_X;
1104 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1105 IMAP_OUT(o, MB_COMD, return STOP)
1106 IMAP_ANSWER()
1107 return OKAY;
1110 FL char const *
1111 imap_fileof(char const *xcp)
1113 char const *cp = xcp;
1114 int state = 0;
1115 NYD_ENTER;
1117 while (*cp) {
1118 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1119 cp += 3;
1120 state = 1;
1122 if (cp[0] == '/' && state == 1) {
1123 ++cp;
1124 goto jleave;
1126 if (cp[0] == '/') {
1127 cp = xcp;
1128 goto jleave;
1130 ++cp;
1132 jleave:
1133 NYD_LEAVE;
1134 return cp;
1137 FL enum okay
1138 imap_noop(void)
1140 sighandler_type volatile oldint, oldpipe;
1141 enum okay volatile rv = STOP;
1142 NYD_ENTER;
1144 if (mb.mb_type != MB_IMAP)
1145 goto jleave;
1147 imaplock = 1;
1148 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1149 safe_signal(SIGINT, &_imap_maincatch);
1150 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1151 if (sigsetjmp(imapjmp, 1) == 0) {
1152 if (oldpipe != SIG_IGN)
1153 safe_signal(SIGPIPE, imapcatch);
1155 rv = imap_noop1(&mb);
1157 safe_signal(SIGINT, oldint);
1158 safe_signal(SIGPIPE, oldpipe);
1159 imaplock = 0;
1160 jleave:
1161 NYD_LEAVE;
1162 if (interrupts)
1163 n_go_onintr_for_imap();
1164 return rv;
1167 static void
1168 rec_queue(enum rec_type rt, unsigned long cnt)
1170 struct record *rp;
1171 NYD_ENTER;
1173 rp = n_calloc(1, sizeof *rp);
1174 rp->rec_type = rt;
1175 rp->rec_count = cnt;
1176 if (record && recend) {
1177 recend->rec_next = rp;
1178 recend = rp;
1179 } else
1180 record = recend = rp;
1181 NYD_LEAVE;
1184 static enum okay
1185 rec_dequeue(void)
1187 struct message *omessage;
1188 struct record *rp, *rq;
1189 uiz_t exists = 0, i;
1190 enum okay rv = STOP;
1191 NYD_ENTER;
1193 if (record == NULL)
1194 goto jleave;
1196 omessage = message;
1197 message = n_alloc((msgCount+1) * sizeof *message);
1198 if (msgCount)
1199 memcpy(message, omessage, msgCount * sizeof *message);
1200 memset(&message[msgCount], 0, sizeof *message);
1202 rp = record, rq = NULL;
1203 rv = OKAY;
1204 while (rp != NULL) {
1205 switch (rp->rec_type) {
1206 case REC_EXISTS:
1207 exists = rp->rec_count;
1208 break;
1209 case REC_EXPUNGE:
1210 if (rp->rec_count == 0) {
1211 rv = STOP;
1212 break;
1214 if (rp->rec_count > (unsigned long)msgCount) {
1215 if (exists == 0 || rp->rec_count > exists--)
1216 rv = STOP;
1217 break;
1219 if (exists > 0)
1220 exists--;
1221 delcache(&mb, &message[rp->rec_count-1]);
1222 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1223 ((msgCount - rp->rec_count + 1) * sizeof *message));
1224 --msgCount;
1225 /* If the message was part of a collapsed thread,
1226 * the m_collapsed field of one of its ancestors
1227 * should be incremented. It seems hardly possible
1228 * to do this with the current message structure,
1229 * though. The result is that a '+' may be shown
1230 * in the header summary even if no collapsed
1231 * children exists */
1232 break;
1234 if (rq != NULL)
1235 n_free(rq);
1236 rq = rp;
1237 rp = rp->rec_next;
1239 if (rq != NULL)
1240 n_free(rq);
1242 record = recend = NULL;
1243 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1244 message = n_realloc(message, (exists + 1) * sizeof *message);
1245 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1246 for (i = msgCount; i < exists; ++i)
1247 imap_init(&mb, i);
1248 imap_flags(&mb, msgCount+1, exists);
1249 msgCount = exists;
1252 if (rv == STOP) {
1253 n_free(message);
1254 message = omessage;
1256 jleave:
1257 NYD_LEAVE;
1258 return rv;
1261 static void
1262 rec_rmqueue(void)
1264 struct record *rp;
1265 NYD_ENTER;
1267 for (rp = record; rp != NULL;) {
1268 struct record *tmp = rp;
1269 rp = rp->rec_next;
1270 n_free(tmp);
1272 record = recend = NULL;
1273 NYD_LEAVE;
1276 /*ARGSUSED*/
1277 static void
1278 imapalarm(int s)
1280 sighandler_type volatile saveint, savepipe;
1281 NYD_X; /* Signal handler */
1282 n_UNUSED(s);
1284 if (imaplock++ == 0) {
1285 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1286 safe_signal(SIGINT, &_imap_maincatch);
1287 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1288 if (sigsetjmp(imapjmp, 1)) {
1289 safe_signal(SIGINT, saveint);
1290 safe_signal(SIGPIPE, savepipe);
1291 goto jbrk;
1293 if (savepipe != SIG_IGN)
1294 safe_signal(SIGPIPE, imapcatch);
1295 if (imap_noop1(&mb) != OKAY) {
1296 safe_signal(SIGINT, saveint);
1297 safe_signal(SIGPIPE, savepipe);
1298 goto jleave;
1300 safe_signal(SIGINT, saveint);
1301 safe_signal(SIGPIPE, savepipe);
1303 jbrk:
1304 alarm(imapkeepalive);
1305 jleave:
1306 --imaplock;
1309 static enum okay
1310 imap_preauth(struct mailbox *mp, struct url *urlp)
1312 NYD_X;
1314 mp->mb_active |= MB_PREAUTH;
1315 imap_answer(mp, 1);
1317 #ifdef HAVE_TLS
1318 if (!mp->mb_sock.s_use_tls && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1319 FILE *queuefp = NULL;
1320 char o[LINESIZE];
1322 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1323 IMAP_OUT(o, MB_COMD, return STOP)
1324 IMAP_ANSWER()
1325 if(!n_tls_open(urlp, &mp->mb_sock))
1326 return STOP;
1328 #else
1329 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1330 n_err(_("No TLS support compiled in\n"));
1331 return STOP;
1333 #endif
1335 imap_capability(mp);
1336 return OKAY;
1339 static enum okay
1340 imap_capability(struct mailbox *mp)
1342 char o[LINESIZE];
1343 FILE *queuefp = NULL;
1344 enum okay ok = STOP;
1345 const char *cp;
1346 NYD_X;
1348 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1349 IMAP_OUT(o, MB_COMD, return STOP)
1350 while (mp->mb_active & MB_COMD) {
1351 ok = imap_answer(mp, 0);
1352 if (response_status == RESPONSE_OTHER &&
1353 response_other == CAPABILITY_DATA) {
1354 cp = responded_other_text;
1355 while (*cp) {
1356 while (spacechar(*cp))
1357 ++cp;
1358 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1359 /* RFC 2359 */
1360 mp->mb_flags |= MB_UIDPLUS;
1361 while (*cp && !spacechar(*cp))
1362 ++cp;
1366 return ok;
1369 static enum okay
1370 imap_auth(struct mailbox *mp, struct ccred *ccred)
1372 enum okay rv;
1373 NYD_ENTER;
1375 if (!(mp->mb_active & MB_PREAUTH)) {
1376 rv = OKAY;
1377 goto jleave;
1380 switch (ccred->cc_authtype) {
1381 case AUTHTYPE_LOGIN:
1382 rv = imap_login(mp, ccred);
1383 break;
1384 #ifdef HAVE_MD5
1385 case AUTHTYPE_CRAM_MD5:
1386 rv = imap_cram_md5(mp, ccred);
1387 break;
1388 #endif
1389 #ifdef HAVE_GSSAPI
1390 case AUTHTYPE_GSSAPI:
1391 rv = _imap_gssapi(mp, ccred);
1392 break;
1393 #endif
1394 default:
1395 rv = STOP;
1396 break;
1398 jleave:
1399 NYD_LEAVE;
1400 return rv;
1403 #ifdef HAVE_MD5
1404 static enum okay
1405 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1407 char o[LINESIZE], *cp;
1408 FILE *queuefp = NULL;
1409 enum okay rv = STOP;
1410 NYD_ENTER;
1412 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1413 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1414 imap_answer(mp, 1);
1415 if (response_type != RESPONSE_CONT)
1416 goto jleave;
1418 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1419 if(cp == NULL)
1420 goto jleave;
1421 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1422 while (mp->mb_active & MB_COMD)
1423 rv = imap_answer(mp, 1);
1424 jleave:
1425 NYD_LEAVE;
1426 return rv;
1428 #endif /* HAVE_MD5 */
1430 static enum okay
1431 imap_login(struct mailbox *mp, struct ccred *ccred)
1433 char o[LINESIZE];
1434 FILE *queuefp = NULL;
1435 enum okay rv = STOP;
1436 NYD_ENTER;
1438 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1439 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1440 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1441 while (mp->mb_active & MB_COMD)
1442 rv = imap_answer(mp, 1);
1443 jleave:
1444 NYD_LEAVE;
1445 return rv;
1448 #ifdef HAVE_GSSAPI
1449 # include "obs-imap-gssapi.h"
1450 #endif
1452 FL enum okay
1453 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1454 enum fedit_mode fm)
1456 char o[LINESIZE];
1457 char const *qname, *cp;
1458 FILE *queuefp;
1459 enum okay ok;
1460 NYD_X;
1461 n_UNUSED(size);
1463 ok = STOP;
1464 queuefp = NULL;
1466 if((qname = imap_path_quote(mp, mbx)) == NULL)
1467 goto jleave;
1469 ok = OKAY;
1471 mp->mb_uidvalidity = 0;
1472 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1473 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1474 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1475 while (mp->mb_active & MB_COMD) {
1476 ok = imap_answer(mp, 1);
1477 if (response_status != RESPONSE_OTHER &&
1478 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1479 n_idec_ui64_cp(&mp->mb_uidvalidity, &cp[13], 10, NULL);/* TODO err? */
1481 *cnt = (had_exists > 0) ? had_exists : 0;
1482 if (response_status != RESPONSE_OTHER &&
1483 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1484 mp->mb_perm = 0;
1485 jleave:
1486 return ok;
1489 static enum okay
1490 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1492 char o[LINESIZE];
1493 FILE *queuefp = NULL;
1494 char const *cp;
1495 struct message *m;
1496 unsigned x = X, y = Y, n;
1497 NYD_X;
1499 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1500 IMAP_OUT(o, MB_COMD, return STOP)
1501 while (mp->mb_active & MB_COMD) {
1502 imap_answer(mp, 1);
1503 if (response_status == RESPONSE_OTHER &&
1504 response_other == MESSAGE_DATA_FETCH) {
1505 n = responded_other_number;
1506 if (n < x || n > y)
1507 continue;
1508 m = &message[n-1];
1509 m->m_xsize = 0;
1510 } else
1511 continue;
1513 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1514 cp += 5;
1515 while (*cp == ' ')
1516 cp++;
1517 if (*cp == '(')
1518 imap_getflags(cp, &cp, &m->m_flag);
1521 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1522 n_idec_ui64_cp(&m->m_uid, &cp[4], 10, NULL);/* TODO errors? */
1523 getcache1(mp, m, NEED_UNSPEC, 1);
1524 m->m_flag &= ~MHIDDEN;
1527 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1528 x++;
1529 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1530 y--;
1531 if (x <= y) {
1532 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1533 tag(1), x, y);
1534 IMAP_OUT(o, MB_COMD, return STOP)
1535 while (mp->mb_active & MB_COMD) {
1536 imap_answer(mp, 1);
1537 if (response_status == RESPONSE_OTHER &&
1538 response_other == MESSAGE_DATA_FETCH) {
1539 n = responded_other_number;
1540 if (n < x || n > y)
1541 continue;
1542 m = &message[n-1];
1543 } else
1544 continue;
1545 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1546 m->m_xsize = strtol(&cp[12], NULL, 10);
1547 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1548 m->m_time = imap_read_date_time(&cp[13]);
1552 srelax_hold();
1553 for (n = X; n <= Y; ++n) {
1554 putcache(mp, &message[n-1]);
1555 srelax();
1557 srelax_rele();
1558 return OKAY;
1561 static void
1562 imap_init(struct mailbox *mp, int n)
1564 struct message *m;
1565 NYD_ENTER;
1566 n_UNUSED(mp);
1568 m = message + n;
1569 m->m_flag = MUSED | MNOFROM;
1570 m->m_block = 0;
1571 m->m_offset = 0;
1572 NYD_LEAVE;
1575 static void
1576 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1578 struct message *omessage = 0;
1579 int i, omsgCount = 0;
1580 enum okay dequeued = STOP;
1581 NYD_ENTER;
1583 if (nmail || transparent) {
1584 omessage = message;
1585 omsgCount = msgCount;
1587 if (nmail)
1588 dequeued = rec_dequeue();
1590 if (had_exists >= 0) {
1591 if (dequeued != OKAY)
1592 msgCount = had_exists;
1593 had_exists = -1;
1595 if (had_expunge >= 0) {
1596 if (dequeued != OKAY)
1597 msgCount -= had_expunge;
1598 had_expunge = -1;
1601 if (nmail && expunged_messages)
1602 printf("Expunged %ld message%s.\n", expunged_messages,
1603 (expunged_messages != 1 ? "s" : ""));
1604 *prevcount = omsgCount - expunged_messages;
1605 expunged_messages = 0;
1606 if (msgCount < 0) {
1607 fputs("IMAP error: Negative message count\n", stderr);
1608 msgCount = 0;
1611 if (dequeued != OKAY) {
1612 message = n_calloc(msgCount + 1, sizeof *message);
1613 for (i = 0; i < msgCount; i++)
1614 imap_init(mp, i);
1615 if (!nmail && mp->mb_type == MB_IMAP)
1616 initcache(mp);
1617 if (msgCount > 0)
1618 imap_flags(mp, 1, msgCount);
1619 message[msgCount].m_size = 0;
1620 message[msgCount].m_lines = 0;
1621 rec_rmqueue();
1623 if (nmail || transparent)
1624 transflags(omessage, omsgCount, transparent);
1625 else
1626 setdot(message);
1627 NYD_LEAVE;
1630 FL int
1631 imap_setfile(char const * volatile who, const char *xserver,
1632 enum fedit_mode fm)
1634 struct url url;
1635 int rv;
1636 NYD_ENTER;
1638 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1639 rv = 1;
1640 goto jleave;
1642 if (!ok_blook(v15_compat) &&
1643 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
1644 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1646 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1647 rv = _imap_setfile1(who, &url, fm, 0);
1648 jleave:
1649 NYD_LEAVE;
1650 return rv;
1653 static bool_t
1654 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1656 bool_t rv = FAL0;
1657 NYD_ENTER;
1659 if (ok_blook(v15_compat))
1660 rv = ccred_lookup(ccredp, urlp);
1661 else {
1662 char *var, *old,
1663 *xuhp = ((urlp->url_flags & n_URL_HAD_USER) ? urlp->url_eu_h_p.s
1664 : urlp->url_u_h_p.s);
1666 if ((var = mbp->mb_imap_pass) != NULL) {
1667 var = savecat("password-", xuhp);
1668 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1669 old = sstrdup(old);
1670 n_var_vset(var, (uintptr_t)mbp->mb_imap_pass);
1672 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1673 if (var != NULL) {
1674 if (old != NULL) {
1675 n_var_vset(var, (uintptr_t)old);
1676 n_free(old);
1677 } else
1678 n_var_vclear(var);
1682 NYD_LEAVE;
1683 return rv;
1686 static int
1687 _imap_setfile1(char const * volatile who, struct url *urlp,
1688 enum fedit_mode volatile fm, int volatile transparent)
1690 struct sock so;
1691 struct ccred ccred;
1692 sighandler_type volatile saveint, savepipe;
1693 char const *cp;
1694 int rv;
1695 int volatile prevcount = 0;
1696 enum mbflags same_flags;
1697 NYD_ENTER;
1699 if (fm & FEDIT_NEWMAIL) {
1700 saveint = safe_signal(SIGINT, SIG_IGN);
1701 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1702 if (saveint != SIG_IGN)
1703 safe_signal(SIGINT, imapcatch);
1704 if (savepipe != SIG_IGN)
1705 safe_signal(SIGPIPE, imapcatch);
1706 imaplock = 1;
1707 goto jnmail;
1710 same_flags = mb.mb_flags;
1711 same_imap_account = 0;
1712 if (mb.mb_imap_account != NULL &&
1713 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1714 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1715 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1716 disconnected(mb.mb_imap_account) == 0) {
1717 same_imap_account = 1;
1718 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1720 goto jduppass;
1721 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1722 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1723 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1724 jduppass:
1726 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1730 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1731 n_free(mb.mb_imap_pass);
1732 mb.mb_imap_pass = NULL;
1734 if (!_imap_getcred(&mb, &ccred, urlp)) {
1735 rv = -1;
1736 goto jleave;
1739 memset(&so, 0, sizeof so);
1740 so.s_fd = -1;
1741 if (!same_imap_account) {
1742 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1743 rv = -1;
1744 goto jleave;
1746 } else
1747 so = mb.mb_sock;
1748 if (!transparent) {
1749 if(!quit(FAL0)){
1750 rv = -1;
1751 goto jleave;
1755 if (fm & FEDIT_SYSBOX)
1756 n_pstate &= ~n_PS_EDIT;
1757 else
1758 n_pstate |= n_PS_EDIT;
1759 if (mb.mb_imap_account != NULL)
1760 n_free(mb.mb_imap_account);
1761 if (mb.mb_imap_pass != NULL)
1762 n_free(mb.mb_imap_pass);
1763 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1764 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1765 * TODO object, and mailbox will naturally have an URL and credentials */
1766 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1768 if (!same_imap_account) {
1769 if (mb.mb_sock.s_fd >= 0)
1770 sclose(&mb.mb_sock);
1772 same_imap_account = 0;
1774 if (!transparent) {
1775 if (mb.mb_itf) {
1776 fclose(mb.mb_itf);
1777 mb.mb_itf = NULL;
1779 if (mb.mb_otf) {
1780 fclose(mb.mb_otf);
1781 mb.mb_otf = NULL;
1783 if (mb.mb_imap_mailbox != NULL)
1784 n_free(mb.mb_imap_mailbox);
1785 assert(urlp->url_path.s != NULL);
1786 imap_delim_init(&mb, urlp);
1787 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1788 initbox(savecatsep(urlp->url_p_eu_h_p,
1789 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1790 mb.mb_imap_mailbox));
1792 mb.mb_type = MB_VOID;
1793 mb.mb_active = MB_NONE;
1795 imaplock = 1;
1796 saveint = safe_signal(SIGINT, SIG_IGN);
1797 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1798 if (sigsetjmp(imapjmp, 1)) {
1799 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1800 sclose(&mb.mb_sock);
1801 safe_signal(SIGINT, saveint);
1802 safe_signal(SIGPIPE, savepipe);
1803 imaplock = 0;
1805 mb.mb_type = MB_VOID;
1806 mb.mb_active = MB_NONE;
1807 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1808 goto jleave;
1810 if (saveint != SIG_IGN)
1811 safe_signal(SIGINT, imapcatch);
1812 if (savepipe != SIG_IGN)
1813 safe_signal(SIGPIPE, imapcatch);
1815 if (mb.mb_sock.s_fd < 0) {
1816 if (disconnected(mb.mb_imap_account)) {
1817 if (cache_setptr(fm, transparent) == STOP)
1818 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1819 goto jdone;
1821 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1822 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1823 savealrm = safe_signal(SIGALRM, imapalarm);
1824 alarm(imapkeepalive);
1828 mb.mb_sock = so;
1829 mb.mb_sock.s_desc = "IMAP";
1830 mb.mb_sock.s_onclose = imap_timer_off;
1831 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1832 sclose(&mb.mb_sock);
1833 imap_timer_off();
1834 safe_signal(SIGINT, saveint);
1835 safe_signal(SIGPIPE, savepipe);
1836 imaplock = 0;
1837 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1838 goto jleave;
1840 } else /* same account */
1841 mb.mb_flags |= same_flags;
1843 if (n_poption & n_PO_R_FLAG)
1844 fm |= FEDIT_RDONLY;
1845 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1846 mb.mb_type = MB_IMAP;
1847 cache_dequeue(&mb);
1848 assert(urlp->url_path.s != NULL);
1849 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1850 /*sclose(&mb.mb_sock);
1851 imap_timer_off();*/
1852 safe_signal(SIGINT, saveint);
1853 safe_signal(SIGPIPE, savepipe);
1854 imaplock = 0;
1855 mb.mb_type = MB_VOID;
1856 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1857 goto jleave;
1860 jnmail:
1861 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1862 n_UNVOLATILE(&prevcount));
1863 jdone:
1864 setmsize(msgCount);
1865 safe_signal(SIGINT, saveint);
1866 safe_signal(SIGPIPE, savepipe);
1867 imaplock = 0;
1869 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1870 purgecache(&mb, message, msgCount);
1871 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1872 mb.mb_threaded = 0;
1873 c_sort((void*)-1);
1876 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
1877 n_pstate &= ~n_PS_SAW_COMMAND;
1878 n_pstate |= n_PS_SETFILE_OPENED;
1881 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1882 mb.mb_type == MB_CACHE)) {
1883 rv = (msgCount == 0);
1884 goto jleave;
1887 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
1888 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1889 !ok_blook(emptystart)){
1890 char const *intro;
1892 if(who == NULL)
1893 intro = who = n_empty;
1894 else
1895 intro = _(" for ");
1896 n_err(_("No mail%s%s at %s\n"), intro, who, urlp->url_p_eu_h_p_p);
1898 rv = 1;
1899 goto jleave;
1902 if (fm & FEDIT_NEWMAIL)
1903 newmailinfo(prevcount);
1904 rv = 0;
1905 jleave:
1906 NYD_LEAVE;
1907 return rv;
1910 static int
1911 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1912 int need, const char *head, size_t headsize, long headlines)
1914 char *line = NULL, *lp;
1915 size_t linesize = 0, linelen, size = 0;
1916 int emptyline = 0, lines = 0, excess = 0;
1917 off_t offset;
1918 NYD_ENTER;
1920 fseek(mp->mb_otf, 0L, SEEK_END);
1921 offset = ftell(mp->mb_otf);
1923 if (head)
1924 fwrite(head, 1, headsize, mp->mb_otf);
1926 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1927 lp = line;
1928 if (linelen > expected) {
1929 excess = linelen - expected;
1930 linelen = expected;
1932 /* TODO >>
1933 * Need to mask 'From ' lines. This cannot be done properly
1934 * since some servers pass them as 'From ' and others as
1935 * '>From '. Although one could identify the first kind of
1936 * server in principle, it is not possible to identify the
1937 * second as '>From ' may also come from a server of the
1938 * first type as actual data. So do what is absolutely
1939 * necessary only - mask 'From '.
1941 * If the line is the first line of the message header, it
1942 * is likely a real 'From ' line. In this case, it is just
1943 * ignored since it violates all standards.
1944 * TODO can the latter *really* happen??
1945 * TODO <<
1947 /* Since we simply copy over data without doing any transfer
1948 * encoding reclassification/adjustment we *have* to perform
1949 * RFC 4155 compliant From_ quoting here */
1950 if (emptyline && is_head(lp, linelen, FAL0)) {
1951 fputc('>', mp->mb_otf);
1952 ++size;
1954 emptyline = 0;
1955 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1956 if (linelen > 2) {
1957 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1958 size += linelen - 1;
1959 } else {
1960 emptyline = 1;
1961 ++size;
1963 fputc('\n', mp->mb_otf);
1964 } else {
1965 fwrite(lp, 1, linelen, mp->mb_otf);
1966 size += linelen;
1968 ++lines;
1969 if ((expected -= linelen) <= 0)
1970 break;
1972 if (!emptyline) {
1973 /* TODO This is very ugly; but some IMAP daemons don't end a
1974 * TODO message with \r\n\r\n, and we need \n\n for mbox format.
1975 * TODO That is to say we do it wrong here in order to get it right
1976 * TODO when send.c stuff or with MBOX handling, even though THIS
1977 * TODO line is solely a property of the MBOX database format! */
1978 fputc('\n', mp->mb_otf);
1979 ++lines;
1980 ++size;
1982 fflush(mp->mb_otf);
1984 if (m != NULL) {
1985 m->m_size = size + headsize;
1986 m->m_lines = lines + headlines;
1987 m->m_block = mailx_blockof(offset);
1988 m->m_offset = mailx_offsetof(offset);
1989 switch (need) {
1990 case NEED_HEADER:
1991 m->m_content_info = CI_HAVE_HEADER;
1992 break;
1993 case NEED_BODY:
1994 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
1995 m->m_xlines = m->m_lines;
1996 m->m_xsize = m->m_size;
1997 break;
2000 n_free(line);
2001 NYD_LEAVE;
2002 return excess;
2005 static void
2006 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2007 const char *head, size_t headsize, long headlines)
2009 off_t offset;
2010 size_t len;
2011 NYD_ENTER;
2013 len = strlen(str);
2014 fseek(mp->mb_otf, 0L, SEEK_END);
2015 offset = ftell(mp->mb_otf);
2016 if (head)
2017 fwrite(head, 1, headsize, mp->mb_otf);
2018 if (len > 0) {
2019 fwrite(str, 1, len, mp->mb_otf);
2020 fputc('\n', mp->mb_otf);
2021 ++len;
2023 fflush(mp->mb_otf);
2025 if (m != NULL) {
2026 m->m_size = headsize + len;
2027 m->m_lines = headlines + 1;
2028 m->m_block = mailx_blockof(offset);
2029 m->m_offset = mailx_offsetof(offset);
2030 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2031 m->m_xlines = m->m_lines;
2032 m->m_xsize = m->m_size;
2034 NYD_LEAVE;
2037 static enum okay
2038 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2040 char o[LINESIZE];
2041 struct message mt;
2042 sighandler_type volatile saveint, savepipe;
2043 char * volatile head;
2044 char const *cp, *loc, * volatile item, * volatile resp;
2045 size_t expected;
2046 size_t volatile headsize;
2047 int number;
2048 FILE *queuefp;
2049 long volatile headlines;
2050 long n;
2051 enum okay ok;
2052 NYD_X;
2054 saveint = savepipe = SIG_IGN;
2055 head = NULL;
2056 cp = loc = item = resp = NULL;
2057 headsize = 0;
2058 number = (int)PTR2SIZE(m - message + 1);
2059 queuefp = NULL;
2060 headlines = 0;
2061 ok = STOP;
2063 if (getcache(mp, m, need) == OKAY)
2064 return OKAY;
2065 if (mp->mb_type == MB_CACHE) {
2066 n_err(_("Message %lu not available\n"), (ul_i)number);
2067 return STOP;
2070 if (mp->mb_sock.s_fd < 0) {
2071 n_err(_("IMAP connection closed\n"));
2072 return STOP;
2075 switch (need) {
2076 case NEED_HEADER:
2077 resp = item = "RFC822.HEADER";
2078 break;
2079 case NEED_BODY:
2080 item = "BODY.PEEK[]";
2081 resp = "BODY[]";
2082 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2083 char *hdr = n_alloc(m->m_size);
2084 fflush(mp->mb_otf);
2085 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2086 SEEK_SET) < 0 ||
2087 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2088 n_free(hdr);
2089 break;
2091 head = hdr;
2092 headsize = m->m_size;
2093 headlines = m->m_lines;
2094 item = "BODY.PEEK[TEXT]";
2095 resp = "BODY[TEXT]";
2097 break;
2098 case NEED_UNSPEC:
2099 return STOP;
2102 imaplock = 1;
2103 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2104 if (sigsetjmp(imapjmp, 1)) {
2105 safe_signal(SIGINT, saveint);
2106 safe_signal(SIGPIPE, savepipe);
2107 imaplock = 0;
2108 return STOP;
2110 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2111 safe_signal(SIGINT, &_imap_maincatch);
2112 if (savepipe != SIG_IGN)
2113 safe_signal(SIGPIPE, imapcatch);
2115 if (m->m_uid)
2116 snprintf(o, sizeof o, "%s UID FETCH %" PRIu64 " (%s)\r\n",
2117 tag(1), m->m_uid, item);
2118 else {
2119 if (check_expunged() == STOP)
2120 goto out;
2121 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2123 IMAP_OUT(o, MB_COMD, goto out)
2124 for (;;) {
2125 ui64_t uid;
2127 ok = imap_answer(mp, 1);
2128 if (ok == STOP)
2129 break;
2130 if (response_status != RESPONSE_OTHER ||
2131 response_other != MESSAGE_DATA_FETCH)
2132 continue;
2133 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2134 continue;
2135 uid = 0;
2136 if (m->m_uid) {
2137 if ((cp = asccasestr(responded_other_text, "UID "))) {
2138 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2139 n = 0;
2140 } else
2141 n = -1;
2142 } else
2143 n = responded_other_number;
2144 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2145 if (m->m_uid ? m->m_uid != uid : n != number)
2146 continue;
2147 if ((cp = strchr(loc, '"')) != NULL) {
2148 cp = imap_unquotestr(cp);
2149 imap_putstr(mp, m, cp, head, headsize, headlines);
2150 } else {
2151 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2152 m->m_xlines = m->m_lines;
2153 m->m_xsize = m->m_size;
2155 goto out;
2157 expected = atol(&cp[1]);
2158 if (m->m_uid ? n == 0 && m->m_uid != uid : n != number) {
2159 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2160 continue;
2162 mt = *m;
2163 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2164 if (n >= 0) {
2165 commitmsg(mp, m, &mt, mt.m_content_info);
2166 break;
2168 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2169 if (n_poption & n_PO_VERBVERB)
2170 fputs(imapbuf, stderr);
2171 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2172 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2173 if (uid == m->m_uid) {
2174 commitmsg(mp, m, &mt, mt.m_content_info);
2175 break;
2180 out:
2181 while (mp->mb_active & MB_COMD)
2182 ok = imap_answer(mp, 1);
2184 if (saveint != SIG_IGN)
2185 safe_signal(SIGINT, saveint);
2186 if (savepipe != SIG_IGN)
2187 safe_signal(SIGPIPE, savepipe);
2188 imaplock--;
2190 if (ok == OKAY)
2191 putcache(mp, m);
2192 if (head != NULL)
2193 n_free(head);
2194 if (interrupts)
2195 n_go_onintr_for_imap();
2196 return ok;
2199 FL enum okay
2200 imap_header(struct message *m)
2202 enum okay rv;
2203 NYD_ENTER;
2205 rv = imap_get(&mb, m, NEED_HEADER);
2206 NYD_LEAVE;
2207 return rv;
2211 FL enum okay
2212 imap_body(struct message *m)
2214 enum okay rv;
2215 NYD_ENTER;
2217 rv = imap_get(&mb, m, NEED_BODY);
2218 NYD_LEAVE;
2219 return rv;
2222 static void
2223 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2224 enum content_info content_info)
2226 NYD_ENTER;
2227 tomp->m_size = frommp->m_size;
2228 tomp->m_lines = frommp->m_lines;
2229 tomp->m_block = frommp->m_block;
2230 tomp->m_offset = frommp->m_offset;
2231 tomp->m_content_info = content_info & CI_HAVE_MASK;
2232 if (content_info & CI_HAVE_BODY) {
2233 tomp->m_xlines = frommp->m_lines;
2234 tomp->m_xsize = frommp->m_size;
2236 putcache(mp, tomp);
2237 NYD_LEAVE;
2240 static enum okay
2241 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2243 /* bot > topp */
2244 char o[LINESIZE];
2245 char const *cp;
2246 struct message mt;
2247 size_t expected;
2248 int n = 0;
2249 FILE *queuefp = NULL;
2250 enum okay ok;
2251 NYD_X;
2253 if (m[bot].m_uid)
2254 snprintf(o, sizeof o,
2255 "%s UID FETCH %" PRIu64 ":%" PRIu64 " (RFC822.HEADER)\r\n",
2256 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2257 else {
2258 if (check_expunged() == STOP)
2259 return STOP;
2260 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2261 tag(1), bot, topp);
2263 IMAP_OUT(o, MB_COMD, return STOP)
2265 srelax_hold();
2266 for (;;) {
2267 ok = imap_answer(mp, 1);
2268 if (response_status != RESPONSE_OTHER)
2269 break;
2270 if (response_other != MESSAGE_DATA_FETCH)
2271 continue;
2272 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2273 srelax_rele();
2274 return STOP;
2276 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2277 continue;
2278 expected = atol(&cp[1]);
2279 if (m[bot-1].m_uid) {
2280 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2281 ui64_t uid;
2283 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2284 for (n = bot; n <= topp; n++)
2285 if (uid == m[n-1].m_uid)
2286 break;
2287 if (n > topp) {
2288 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2289 continue;
2291 } else
2292 n = -1;
2293 } else {
2294 n = responded_other_number;
2295 if (n <= 0 || n > msgCount) {
2296 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2297 continue;
2300 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2301 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2302 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2303 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2304 if (n_poption & n_PO_VERBVERB)
2305 fputs(imapbuf, stderr);
2306 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2307 ui64_t uid;
2309 n_idec_ui64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2310 for (n = bot; n <= topp; n++)
2311 if (uid == m[n-1].m_uid)
2312 break;
2313 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2314 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2317 srelax();
2319 srelax_rele();
2321 while (mp->mb_active & MB_COMD)
2322 ok = imap_answer(mp, 1);
2323 return ok;
2326 FL void
2327 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2329 sighandler_type saveint, savepipe;
2330 /*enum okay ok = STOP;*/
2331 int i, chunk = 256;
2332 NYD_X;
2334 if (mb.mb_type == MB_CACHE)
2335 return;
2336 if (bot < 1)
2337 bot = 1;
2338 if (topp > msgCount)
2339 topp = msgCount;
2340 for (i = bot; i < topp; i++) {
2341 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2342 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2343 bot = i+1;
2344 else
2345 break;
2347 for (i = topp; i > bot; i--) {
2348 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2349 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2350 topp = i-1;
2351 else
2352 break;
2354 if (bot >= topp)
2355 return;
2357 imaplock = 1;
2358 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2359 safe_signal(SIGINT, &_imap_maincatch);
2360 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2361 if (sigsetjmp(imapjmp, 1) == 0) {
2362 if (savepipe != SIG_IGN)
2363 safe_signal(SIGPIPE, imapcatch);
2365 for (i = bot; i <= topp; i += chunk) {
2366 int j = i + chunk - 1;
2367 j = n_MIN(j, topp);
2368 if (visible(message + j))
2369 /*ok = */imap_fetchheaders(&mb, message, i, j);
2370 if (interrupts)
2371 n_go_onintr_for_imap(); /* XXX imaplock? */
2374 safe_signal(SIGINT, saveint);
2375 safe_signal(SIGPIPE, savepipe);
2376 imaplock = 0;
2379 static enum okay
2380 __imap_exit(struct mailbox *mp)
2382 char o[LINESIZE];
2383 FILE *queuefp = NULL;
2384 NYD_X;
2386 mp->mb_active |= MB_BYE;
2387 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2388 IMAP_OUT(o, MB_COMD, return STOP)
2389 IMAP_ANSWER()
2390 return OKAY;
2393 static enum okay
2394 imap_exit(struct mailbox *mp)
2396 enum okay rv;
2397 NYD_ENTER;
2399 rv = __imap_exit(mp);
2400 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2401 n_free(mp->mb_imap_pass);
2402 n_free(mp->mb_imap_account);
2403 n_free(mp->mb_imap_mailbox);
2404 if (mp->mb_cache_directory != NULL)
2405 n_free(mp->mb_cache_directory);
2406 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2407 mp->mb_imap_account =
2408 mp->mb_imap_mailbox =
2409 mp->mb_cache_directory = "";
2410 #else
2411 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2412 mp->mb_imap_mailbox = NULL;
2413 mp->mb_cache_directory = NULL;
2414 #endif
2415 #endif
2416 sclose(&mp->mb_sock);
2417 NYD_LEAVE;
2418 return rv;
2421 static enum okay
2422 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2424 NYD_ENTER;
2425 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2426 if (mp->mb_type == MB_IMAP)
2427 delcache(mp, m);
2428 NYD_LEAVE;
2429 return OKAY;
2432 static enum okay
2433 imap_close(struct mailbox *mp)
2435 char o[LINESIZE];
2436 FILE *queuefp = NULL;
2437 NYD_X;
2439 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2440 IMAP_OUT(o, MB_COMD, return STOP)
2441 IMAP_ANSWER()
2442 return OKAY;
2445 static enum okay
2446 imap_update(struct mailbox *mp)
2448 struct message *m;
2449 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2450 NYD_ENTER;
2452 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2453 holdbits();
2454 c = 0;
2455 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2456 if (m->m_flag & MBOX)
2457 ++c;
2458 if (c > 0)
2459 if (makembox() == STOP)
2460 goto jbypass;
2463 gotcha = held = 0;
2464 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2465 if (mp->mb_perm == 0)
2466 dodel = 0;
2467 else if (n_pstate & n_PS_EDIT)
2468 dodel = ((m->m_flag & MDELETED) != 0);
2469 else
2470 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2472 /* Fetch the result after around each 800 STORE commands
2473 * sent (approx. 32k data sent). Otherwise, servers will
2474 * try to flush the return queue at some point, leading
2475 * to a deadlock if we are still writing commands but not
2476 * reading their results */
2477 needstat = stored > 0 && stored % 800 == 0;
2478 /* Even if this message has been deleted, continue
2479 * to set further flags. This is necessary to support
2480 * Gmail semantics, where "delete" actually means
2481 * "archive", and the flags are applied to the copy
2482 * in "All Mail" */
2483 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2484 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2485 stored++;
2487 if (m->m_flag & MFLAG) {
2488 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2489 stored++;
2491 if (m->m_flag & MUNFLAG) {
2492 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2493 stored++;
2495 if (m->m_flag & MANSWER) {
2496 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2497 stored++;
2499 if (m->m_flag & MUNANSWER) {
2500 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2501 stored++;
2503 if (m->m_flag & MDRAFT) {
2504 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2505 stored++;
2507 if (m->m_flag & MUNDRAFT) {
2508 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2509 stored++;
2512 if (dodel) {
2513 imap_delete(mp, m-message+1, m, needstat);
2514 stored++;
2515 gotcha++;
2516 } else if (mp->mb_type != MB_CACHE ||
2517 (!(n_pstate & n_PS_EDIT) &&
2518 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2519 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2520 (MPRESERVE | MTOUCH) ||
2521 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2522 held++;
2523 if (m->m_flag & MNEW) {
2524 m->m_flag &= ~MNEW;
2525 m->m_flag |= MSTATUS;
2528 jbypass:
2529 if (gotcha)
2530 imap_close(mp);
2532 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2533 if (!(m->m_flag & MUNLINKED) &&
2534 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2535 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2536 putcache(mp, m);
2537 modflags++;
2540 /* XXX should be readonly (but our IMAP code is weird...) */
2541 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2542 mb.mb_perm != 0) {
2543 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2544 printf(_("\"%s\" "), displayname);
2545 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2546 ? _("complete\n") : _("updated.\n"));
2547 } else if (held && !(n_pstate & n_PS_EDIT)) {
2548 if (held == 1)
2549 printf(_("Held 1 message in %s\n"), displayname);
2550 else
2551 printf(_("Held %d messages in %s\n"), held, displayname);
2553 fflush(stdout);
2555 NYD_LEAVE;
2556 return OKAY;
2559 FL bool_t
2560 imap_quit(bool_t hold_sigs_on)
2562 sighandler_type volatile saveint, savepipe;
2563 bool_t rv;
2564 NYD_ENTER;
2566 if(hold_sigs_on)
2567 rele_sigs();
2569 if (mb.mb_type == MB_CACHE) {
2570 rv = (imap_update(&mb) == OKAY);
2571 goto jleave;
2574 rv = FAL0;
2576 if (mb.mb_sock.s_fd < 0) {
2577 n_err(_("IMAP connection closed\n"));
2578 goto jleave;
2581 imaplock = 1;
2582 saveint = safe_signal(SIGINT, SIG_IGN);
2583 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2584 if (sigsetjmp(imapjmp, 1)) {
2585 safe_signal(SIGINT, saveint);
2586 safe_signal(SIGPIPE, saveint);
2587 imaplock = 0;
2588 goto jleave;
2590 if (saveint != SIG_IGN)
2591 safe_signal(SIGINT, imapcatch);
2592 if (savepipe != SIG_IGN)
2593 safe_signal(SIGPIPE, imapcatch);
2595 rv = (imap_update(&mb) == OKAY);
2596 if(!same_imap_account && imap_exit(&mb) != OKAY)
2597 rv = FAL0;
2599 safe_signal(SIGINT, saveint);
2600 safe_signal(SIGPIPE, savepipe);
2601 imaplock = 0;
2602 jleave:
2603 if(hold_sigs_on)
2604 hold_sigs();
2605 NYD_LEAVE;
2606 return rv;
2609 static enum okay
2610 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2611 int needstat)
2613 char o[LINESIZE];
2614 FILE *queuefp = NULL;
2615 NYD_X;
2617 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2618 return STOP;
2619 if (m->m_uid)
2620 snprintf(o, sizeof o, "%s UID STORE %" PRIu64 " %cFLAGS (%s)\r\n",
2621 tag(1), m->m_uid, c, sp);
2622 else {
2623 if (check_expunged() == STOP)
2624 return STOP;
2625 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2627 IMAP_OUT(o, MB_COMD, return STOP)
2628 if (needstat)
2629 IMAP_ANSWER()
2630 else
2631 mb.mb_active &= ~MB_COMD;
2632 if (queuefp != NULL)
2633 Fclose(queuefp);
2634 return OKAY;
2637 FL enum okay
2638 imap_undelete(struct message *m, int n)
2640 enum okay rv;
2641 NYD_ENTER;
2643 rv = imap_unstore(m, n, "\\Deleted");
2644 NYD_LEAVE;
2645 return rv;
2648 FL enum okay
2649 imap_unread(struct message *m, int n)
2651 enum okay rv;
2652 NYD_ENTER;
2654 rv = imap_unstore(m, n, "\\Seen");
2655 NYD_LEAVE;
2656 return rv;
2659 static enum okay
2660 imap_unstore(struct message *m, int n, const char *flag)
2662 sighandler_type saveint, savepipe;
2663 enum okay volatile rv = STOP;
2664 NYD_ENTER;
2666 imaplock = 1;
2667 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2668 safe_signal(SIGINT, &_imap_maincatch);
2669 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2670 if (sigsetjmp(imapjmp, 1) == 0) {
2671 if (savepipe != SIG_IGN)
2672 safe_signal(SIGPIPE, imapcatch);
2674 rv = imap_store(&mb, m, n, '-', flag, 1);
2676 safe_signal(SIGINT, saveint);
2677 safe_signal(SIGPIPE, savepipe);
2678 imaplock = 0;
2680 NYD_LEAVE;
2681 if (interrupts)
2682 n_go_onintr_for_imap();
2683 return rv;
2686 static const char *
2687 tag(int new)
2689 static char ts[20];
2690 static long n;
2691 NYD2_ENTER;
2693 if (new)
2694 ++n;
2695 snprintf(ts, sizeof ts, "T%lu", n);
2696 NYD2_LEAVE;
2697 return ts;
2700 FL int
2701 c_imapcodec(void *vp){
2702 bool_t err;
2703 size_t alen;
2704 char const **argv, *varname, *varres, *act, *cp;
2705 NYD_ENTER;
2707 argv = vp;
2708 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2710 act = *argv;
2711 for(cp = act; *cp != '\0' && !blankspacechar(*cp); ++cp)
2713 if(act == cp)
2714 goto jesynopsis;
2715 alen = PTR2SIZE(cp - act);
2716 if(*cp != '\0')
2717 ++cp;
2719 n_pstate_err_no = n_ERR_NONE;
2720 varres = imap_path_normalize(NULL, cp);
2722 if(is_ascncaseprefix(act, "encode", alen))
2723 varres = imap_path_encode(varres, &err);
2724 else if(is_ascncaseprefix(act, "decode", alen))
2725 varres = imap_path_decode(varres, &err);
2726 else
2727 goto jesynopsis;
2729 if(err){
2730 n_pstate_err_no = n_ERR_CANCELED;
2731 varres = cp;
2732 vp = NULL;
2735 if(varname != NULL){
2736 if(!n_var_vset(varname, (uintptr_t)varres)){
2737 n_pstate_err_no = n_ERR_NOTSUP;
2738 vp = NULL;
2740 }else{
2741 struct str in, out;
2743 in.l = strlen(in.s = n_UNCONST(varres));
2744 makeprint(&in, &out);
2745 if(fprintf(n_stdout, "%s\n", out.s) < 0){
2746 n_pstate_err_no = n_err_no;
2747 vp = NULL;
2749 n_free(out.s);
2752 jleave:
2753 NYD_LEAVE;
2754 return (vp != NULL ? 0 : 1);
2755 jesynopsis:
2756 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2757 n_pstate_err_no = n_ERR_INVAL;
2758 vp = NULL;
2759 goto jleave;
2762 FL int
2763 c_imap_imap(void *vp)
2765 char o[LINESIZE];
2766 sighandler_type saveint, savepipe;
2767 struct mailbox *mp = &mb;
2768 FILE *queuefp = NULL;
2769 enum okay volatile ok = STOP;
2770 NYD_X;
2772 if (mp->mb_type != MB_IMAP) {
2773 printf("Not operating on an IMAP mailbox.\n");
2774 return 1;
2776 imaplock = 1;
2777 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2778 safe_signal(SIGINT, &_imap_maincatch);
2779 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2780 if (sigsetjmp(imapjmp, 1) == 0) {
2781 if (savepipe != SIG_IGN)
2782 safe_signal(SIGPIPE, imapcatch);
2784 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2785 IMAP_OUT(o, MB_COMD, goto out)
2786 while (mp->mb_active & MB_COMD) {
2787 ok = imap_answer(mp, 0);
2788 fputs(responded_text, stdout);
2791 out:
2792 safe_signal(SIGINT, saveint);
2793 safe_signal(SIGPIPE, savepipe);
2794 imaplock = 0;
2796 if (interrupts)
2797 n_go_onintr_for_imap();
2798 return ok != OKAY;
2801 FL int
2802 imap_newmail(int nmail)
2804 NYD_ENTER;
2806 if (nmail && had_exists < 0 && had_expunge < 0) {
2807 imaplock = 1;
2808 imap_noop();
2809 imaplock = 0;
2812 if (had_exists == msgCount && had_expunge < 0)
2813 /* Some servers always respond with EXISTS to NOOP. If
2814 * the mailbox has been changed but the number of messages
2815 * has not, an EXPUNGE must also had been sent; otherwise,
2816 * nothing has changed */
2817 had_exists = -1;
2818 NYD_LEAVE;
2819 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2822 static char *
2823 imap_putflags(int f)
2825 const char *cp;
2826 char *buf, *bp;
2827 NYD2_ENTER;
2829 bp = buf = n_autorec_alloc(100);
2830 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2831 *bp++ = '(';
2832 if (f & MREAD) {
2833 if (bp[-1] != '(')
2834 *bp++ = ' ';
2835 for (cp = "\\Seen"; *cp; cp++)
2836 *bp++ = *cp;
2838 if (f & MFLAGGED) {
2839 if (bp[-1] != '(')
2840 *bp++ = ' ';
2841 for (cp = "\\Flagged"; *cp; cp++)
2842 *bp++ = *cp;
2844 if (f & MANSWERED) {
2845 if (bp[-1] != '(')
2846 *bp++ = ' ';
2847 for (cp = "\\Answered"; *cp; cp++)
2848 *bp++ = *cp;
2850 if (f & MDRAFT) {
2851 if (bp[-1] != '(')
2852 *bp++ = ' ';
2853 for (cp = "\\Draft"; *cp; cp++)
2854 *bp++ = *cp;
2856 *bp++ = ')';
2857 *bp++ = ' ';
2859 *bp = '\0';
2860 NYD2_LEAVE;
2861 return buf;
2864 static void
2865 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2867 NYD2_ENTER;
2868 while (*cp != ')') {
2869 if (*cp == '\\') {
2870 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2871 *f |= MREAD;
2872 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2873 *f |= MNEW;
2874 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2875 *f |= MDELETED;
2876 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2877 *f |= MFLAGGED;
2878 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2879 *f |= MANSWERED;
2880 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2881 *f |= MDRAFTED;
2883 cp++;
2886 if (xp != NULL)
2887 *xp = cp;
2888 NYD2_LEAVE;
2891 static enum okay
2892 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2893 long xsize, enum mflag flag, time_t t)
2895 char o[LINESIZE], *buf;
2896 size_t bufsize, buflen, cnt;
2897 long size, lines, ysize;
2898 char const *qname;
2899 bool_t twice;
2900 FILE *queuefp;
2901 enum okay rv;
2902 NYD_ENTER;
2904 rv = STOP;
2905 queuefp = NULL;
2906 twice = FAL0;
2907 buf = NULL;
2909 if((qname = imap_path_quote(mp, name)) == NULL)
2910 goto jleave;
2912 if (mp->mb_type == MB_CACHE) {
2913 queuefp = cache_queue(mp);
2914 if (queuefp == NULL) {
2915 buf = NULL;
2916 goto jleave;
2918 rv = OKAY;
2921 buf = n_alloc(bufsize = LINESIZE);
2922 buflen = 0;
2923 jagain:
2924 size = xsize;
2925 cnt = fsize(fp);
2926 if (fseek(fp, off1, SEEK_SET) < 0) {
2927 rv = STOP;
2928 goto jleave;
2931 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2932 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2933 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2934 while (mp->mb_active & MB_COMD) {
2935 rv = imap_answer(mp, twice);
2936 if (response_type == RESPONSE_CONT)
2937 break;
2940 if (mp->mb_type != MB_CACHE && rv == STOP) {
2941 if (!twice)
2942 goto jtrycreate;
2943 else
2944 goto jleave;
2947 lines = ysize = 0;
2948 while (size > 0) {
2949 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2950 lines++;
2951 ysize += buflen;
2952 buf[buflen - 1] = '\r';
2953 buf[buflen] = '\n';
2954 if (mp->mb_type != MB_CACHE)
2955 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2956 else if (queuefp)
2957 fwrite(buf, 1, buflen+1, queuefp);
2958 size -= buflen + 1;
2960 if (mp->mb_type != MB_CACHE)
2961 swrite(&mp->mb_sock, "\r\n");
2962 else if (queuefp)
2963 fputs("\r\n", queuefp);
2964 while (mp->mb_active & MB_COMD) {
2965 rv = imap_answer(mp, 0);
2966 if (response_status == RESPONSE_NO /*&&
2967 ascncasecmp(responded_text,
2968 "[TRYCREATE] ", 12) == 0*/) {
2969 jtrycreate:
2970 if (twice) {
2971 rv = STOP;
2972 goto jleave;
2974 twice = TRU1;
2975 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2976 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2977 while (mp->mb_active & MB_COMD)
2978 rv = imap_answer(mp, 1);
2979 if (rv == STOP)
2980 goto jleave;
2981 imap_created_mailbox++;
2982 goto jagain;
2983 } else if (rv != OKAY)
2984 n_err(_("IMAP error: %s"), responded_text);
2985 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2986 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2988 jleave:
2989 if (queuefp != NULL)
2990 Fclose(queuefp);
2991 if (buf != NULL)
2992 n_free(buf);
2993 NYD_LEAVE;
2994 return rv;
2997 static enum okay
2998 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3000 char *buf, *bp, *lp;
3001 size_t bufsize, buflen, cnt;
3002 off_t off1 = -1, offs;
3003 int flag;
3004 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3005 time_t tim;
3006 long size;
3007 enum okay rv;
3008 NYD_ENTER;
3010 buf = n_alloc(bufsize = LINESIZE);
3011 buflen = 0;
3012 cnt = fsize(fp);
3013 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3014 time(&tim);
3015 size = 0;
3017 for (flag = MNEW, state = _NLSEP;;) {
3018 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
3020 if (bp == NULL ||
3021 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3022 is_head(buf, buflen, FAL0))) {
3023 if (off1 != (off_t)-1) {
3024 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3025 if (rv == STOP)
3026 goto jleave;
3027 fseek(fp, offs+buflen, SEEK_SET);
3029 off1 = offs + buflen;
3030 size = 0;
3031 flag = MNEW;
3032 state = _INHEAD;
3033 if (bp == NULL)
3034 break;
3035 tim = unixtime(buf);
3036 } else
3037 size += buflen+1;
3038 offs += buflen;
3040 state &= ~_NLSEP;
3041 if (buf[0] == '\n') {
3042 state &= ~_INHEAD;
3043 state |= _NLSEP;
3044 } else if (state & _INHEAD) {
3045 if (ascncasecmp(buf, "status", 6) == 0) {
3046 lp = &buf[6];
3047 while (whitechar(*lp))
3048 lp++;
3049 if (*lp == ':')
3050 while (*++lp != '\0')
3051 switch (*lp) {
3052 case 'R':
3053 flag |= MREAD;
3054 break;
3055 case 'O':
3056 flag &= ~MNEW;
3057 break;
3059 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
3060 lp = &buf[8];
3061 while (whitechar(*lp))
3062 lp++;
3063 if (*lp == ':')
3064 while (*++lp != '\0')
3065 switch (*lp) {
3066 case 'F':
3067 flag |= MFLAGGED;
3068 break;
3069 case 'A':
3070 flag |= MANSWERED;
3071 break;
3072 case 'T':
3073 flag |= MDRAFTED;
3074 break;
3079 rv = OKAY;
3080 jleave:
3081 n_free(buf);
3082 NYD_LEAVE;
3083 return rv;
3086 FL enum okay
3087 imap_append(const char *xserver, FILE *fp, long offset)
3089 sighandler_type volatile saveint, savepipe;
3090 struct url url;
3091 struct ccred ccred;
3092 enum okay rv = STOP;
3093 NYD_ENTER;
3095 if (!url_parse(&url, CPROTO_IMAP, xserver))
3096 goto j_leave;
3097 if (!ok_blook(v15_compat) &&
3098 (!(url.url_flags & n_URL_HAD_USER) || url.url_pass.s != NULL))
3099 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3100 assert(url.url_path.s != NULL);
3102 imaplock = 1;
3103 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3104 safe_signal(SIGINT, &_imap_maincatch);
3105 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3106 if (sigsetjmp(imapjmp, 1))
3107 goto jleave;
3108 if (savepipe != SIG_IGN)
3109 safe_signal(SIGPIPE, imapcatch);
3111 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3112 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3113 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3114 } else {
3115 struct mailbox mx;
3117 memset(&mx, 0, sizeof mx);
3119 if (!_imap_getcred(&mx, &ccred, &url))
3120 goto jleave;
3122 imap_delim_init(&mx, &url);
3123 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3125 if (disconnected(url.url_p_eu_h_p) == 0) {
3126 if (!sopen(&mx.mb_sock, &url))
3127 goto jfail;
3128 mx.mb_sock.s_desc = "IMAP";
3129 mx.mb_type = MB_IMAP;
3130 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3131 /* TODO the code now did
3132 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3133 * TODO though imap_mailbox is sfree()d and mbx
3134 * TODO is possibly even a constant
3135 * TODO i changed this to sstrdup() sofar, as is used
3136 * TODO somewhere else in this file for this! */
3137 if (imap_preauth(&mx, &url) != OKAY ||
3138 imap_auth(&mx, &ccred) != OKAY) {
3139 sclose(&mx.mb_sock);
3140 goto jfail;
3142 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3143 imap_exit(&mx);
3144 } else {
3145 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3146 mx.mb_type = MB_CACHE;
3147 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3149 jfail:
3153 jleave:
3154 safe_signal(SIGINT, saveint);
3155 safe_signal(SIGPIPE, savepipe);
3156 imaplock = 0;
3157 j_leave:
3158 NYD_LEAVE;
3159 if (interrupts)
3160 n_go_onintr_for_imap();
3161 return rv;
3164 static enum okay
3165 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3166 struct list_item **lend, int level)
3168 char o[LINESIZE], *cp;
3169 struct list_item *lp;
3170 const char *qname, *bp;
3171 FILE *queuefp;
3172 enum okay ok;
3173 NYD_X;
3175 ok = STOP;
3176 queuefp = NULL;
3178 if((qname = imap_path_quote(mp, base)) == NULL)
3179 goto jleave;
3181 *list = *lend = NULL;
3182 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3183 IMAP_OUT(o, MB_COMD, goto jleave)
3184 while (mp->mb_active & MB_COMD) {
3185 ok = imap_answer(mp, 1);
3186 if (response_status == RESPONSE_OTHER &&
3187 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3188 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3189 lp = n_autorec_calloc(1, sizeof *lp);
3190 lp->l_name = cp;
3191 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3192 ++cp;
3193 lp->l_base = *cp ? cp : savestr(base);
3194 lp->l_attr = list_attributes;
3195 lp->l_level = level+1;
3196 lp->l_delim = list_hierarchy_delimiter;
3197 if (*list && *lend) {
3198 (*lend)->l_next = lp;
3199 *lend = lp;
3200 } else
3201 *list = *lend = lp;
3204 jleave:
3205 return ok;
3208 static enum okay
3209 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3211 struct list_item *list, *lend, *lp, *lx, *ly;
3212 int n, depth;
3213 const char *bp;
3214 char *cp;
3215 enum okay rv;
3216 NYD_ENTER;
3218 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3219 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3220 goto jleave;
3221 rv = OKAY;
3222 if (list == NULL || lend == NULL)
3223 goto jleave;
3225 for (lp = list; lp; lp = lp->l_next)
3226 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3227 !(lp->l_attr & LIST_NOINFERIORS)) {
3228 cp = n_autorec_alloc((n = strlen(lp->l_name)) + 2);
3229 memcpy(cp, lp->l_name, n);
3230 cp[n] = lp->l_delim;
3231 cp[n+1] = '\0';
3232 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3233 lp->l_has_children = 1;
3234 if (strcmp(cp, lx->l_name) == 0)
3235 lx = lx->l_next;
3236 if (lx) {
3237 lend->l_next = lx;
3238 lend = ly;
3243 for (lp = list; lp; lp = lp->l_next) {
3244 if (strip) {
3245 cp = lp->l_name;
3246 for (bp = base; *bp && *bp == *cp; bp++)
3247 cp++;
3248 } else
3249 cp = lp->l_name;
3250 if (!(lp->l_attr & LIST_NOSELECT))
3251 fprintf(fp, "%s\n", *cp ? cp : base);
3252 else if (lp->l_has_children == 0)
3253 fprintf(fp, "%s%c\n", *cp ? cp : base,
3254 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3256 jleave:
3257 NYD_LEAVE;
3258 return rv;
3261 FL int
3262 imap_folders(const char * volatile name, int strip)
3264 sighandler_type saveint, savepipe;
3265 const char * volatile fold, *cp, *sp;
3266 FILE * volatile fp;
3267 int rv = 1;
3268 NYD_ENTER;
3270 cp = protbase(name);
3271 sp = mb.mb_imap_account;
3272 if (sp == NULL || strcmp(cp, sp)) {
3273 n_err(
3274 _("Cannot perform `folders' but when on the very IMAP "
3275 "account; the current one is\n `%s' -- "
3276 "try `folders @'\n"),
3277 (sp != NULL ? sp : _("[NONE]")));
3278 goto jleave;
3281 fold = imap_fileof(name);
3282 if (n_psonce & n_PSO_TTYOUT) {
3283 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER))
3284 == NULL) {
3285 n_perr(_("tmpfile"), 0);
3286 goto jleave;
3288 } else
3289 fp = stdout;
3291 imaplock = 1;
3292 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3293 safe_signal(SIGINT, &_imap_maincatch);
3294 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3295 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3296 goto junroll;
3297 if (savepipe != SIG_IGN)
3298 safe_signal(SIGPIPE, imapcatch);
3300 if (mb.mb_type == MB_CACHE)
3301 cache_list(&mb, fold, strip, fp);
3302 else
3303 imap_list(&mb, fold, strip, fp);
3305 imaplock = 0;
3306 if (interrupts) {
3307 if (n_psonce & n_PSO_TTYOUT)
3308 Fclose(fp);
3309 rv = 0;
3310 goto jleave;
3312 fflush(fp);
3314 if (n_psonce & n_PSO_TTYOUT) {
3315 rewind(fp);
3316 if (fsize(fp) > 0){
3317 page_or_print(fp, 0);
3318 rv = 0;
3319 }else
3320 n_err(_("Folder not found\n"));
3321 }else
3322 rv = 0;
3323 junroll:
3324 safe_signal(SIGINT, saveint);
3325 safe_signal(SIGPIPE, savepipe);
3326 if (n_psonce & n_PSO_TTYOUT)
3327 Fclose(fp);
3328 jleave:
3329 NYD_LEAVE;
3330 if (interrupts)
3331 n_go_onintr_for_imap();
3332 return rv;
3335 static enum okay
3336 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3338 char o[LINESIZE];
3339 const char *qname;
3340 bool_t twice, stored;
3341 FILE *queuefp;
3342 enum okay ok;
3343 NYD_X;
3345 ok = STOP;
3346 queuefp = NULL;
3347 twice = stored = FAL0;
3349 /* C99 */{
3350 size_t i;
3352 i = strlen(name = imap_fileof(name));
3353 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3354 name = savecat(name, "INBOX");
3355 if((qname = imap_path_quote(mp, name)) == NULL)
3356 goto jleave;
3359 if (mp->mb_type == MB_CACHE) {
3360 if ((queuefp = cache_queue(mp)) == NULL)
3361 goto jleave;
3362 ok = OKAY;
3365 /* Since it is not possible to set flags on the copy, recently
3366 * set flags must be set on the original to include it in the copy */
3367 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3368 imap_store(mp, m, n, '+', "\\Seen", 0);
3369 if (m->m_flag&MFLAG)
3370 imap_store(mp, m, n, '+', "\\Flagged", 0);
3371 if (m->m_flag&MUNFLAG)
3372 imap_store(mp, m, n, '-', "\\Flagged", 0);
3373 if (m->m_flag&MANSWER)
3374 imap_store(mp, m, n, '+', "\\Answered", 0);
3375 if (m->m_flag&MUNANSWER)
3376 imap_store(mp, m, n, '-', "\\Flagged", 0);
3377 if (m->m_flag&MDRAFT)
3378 imap_store(mp, m, n, '+', "\\Draft", 0);
3379 if (m->m_flag&MUNDRAFT)
3380 imap_store(mp, m, n, '-', "\\Draft", 0);
3381 again:
3382 if (m->m_uid)
3383 snprintf(o, sizeof o, "%s UID COPY %" PRIu64 " %s\r\n",
3384 tag(1), m->m_uid, qname);
3385 else {
3386 if (check_expunged() == STOP)
3387 goto out;
3388 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3390 IMAP_OUT(o, MB_COMD, goto out)
3391 while (mp->mb_active & MB_COMD)
3392 ok = imap_answer(mp, twice);
3394 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3395 response_status == RESPONSE_OK)
3396 imap_copyuid(mp, m, name);
3398 if (response_status == RESPONSE_NO && !twice) {
3399 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3400 IMAP_OUT(o, MB_COMD, goto out)
3401 while (mp->mb_active & MB_COMD)
3402 ok = imap_answer(mp, 1);
3403 if (ok == OKAY) {
3404 imap_created_mailbox++;
3405 goto again;
3409 if (queuefp != NULL)
3410 Fclose(queuefp);
3412 /* ... and reset the flag to its initial value so that the 'exit'
3413 * command still leaves the message unread */
3414 out:
3415 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3416 imap_store(mp, m, n, '-', "\\Seen", 0);
3417 stored = TRU1;
3419 if (m->m_flag & MFLAG) {
3420 imap_store(mp, m, n, '-', "\\Flagged", 0);
3421 stored = TRU1;
3423 if (m->m_flag & MUNFLAG) {
3424 imap_store(mp, m, n, '+', "\\Flagged", 0);
3425 stored = TRU1;
3427 if (m->m_flag & MANSWER) {
3428 imap_store(mp, m, n, '-', "\\Answered", 0);
3429 stored = TRU1;
3431 if (m->m_flag & MUNANSWER) {
3432 imap_store(mp, m, n, '+', "\\Answered", 0);
3433 stored = TRU1;
3435 if (m->m_flag & MDRAFT) {
3436 imap_store(mp, m, n, '-', "\\Draft", 0);
3437 stored = TRU1;
3439 if (m->m_flag & MUNDRAFT) {
3440 imap_store(mp, m, n, '+', "\\Draft", 0);
3441 stored = TRU1;
3443 if (stored) {
3444 mp->mb_active |= MB_COMD;
3445 (void)imap_finish(mp);
3447 jleave:
3448 return ok;
3451 FL enum okay
3452 imap_copy(struct message *m, int n, const char *name)
3454 sighandler_type saveint, savepipe;
3455 enum okay volatile rv = STOP;
3456 NYD_ENTER;
3458 imaplock = 1;
3459 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3460 safe_signal(SIGINT, &_imap_maincatch);
3461 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3462 if (sigsetjmp(imapjmp, 1) == 0) {
3463 if (savepipe != SIG_IGN)
3464 safe_signal(SIGPIPE, imapcatch);
3466 rv = imap_copy1(&mb, m, n, name);
3468 safe_signal(SIGINT, saveint);
3469 safe_signal(SIGPIPE, savepipe);
3470 imaplock = 0;
3472 NYD_LEAVE;
3473 if (interrupts)
3474 n_go_onintr_for_imap();
3475 return rv;
3478 static enum okay
3479 imap_copyuid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *olduid,
3480 ui64_t *newuid)
3482 char const *xp, *yp, *zp;
3483 enum okay rv;
3484 NYD_ENTER;
3486 n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3487 n_idec_ui64_cp(olduid, xp, 10, &yp); /* TODO errors */
3488 n_idec_ui64_cp(newuid, yp, 10, &zp); /* TODO errors */
3489 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3490 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3491 NYD_LEAVE;
3492 return rv;
3495 static enum okay
3496 imap_appenduid_parse(const char *cp, ui64_t *uidvalidity, ui64_t *uid)
3498 char const *xp, *yp;
3499 enum okay rv;
3500 NYD_ENTER;
3502 n_idec_ui64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3503 n_idec_ui64_cp(uid, xp, 10, &yp); /* TODO errors */
3504 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3505 *yp == ']');
3506 NYD_LEAVE;
3507 return rv;
3510 static enum okay
3511 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3513 struct mailbox xmb;
3514 struct message xm;
3515 const char *cp;
3516 ui64_t uidvalidity, olduid, newuid;
3517 enum okay rv;
3518 NYD_ENTER;
3520 rv = STOP;
3522 memset(&xmb, 0, sizeof xmb);
3524 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3525 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3526 goto jleave;
3528 rv = OKAY;
3530 xmb = *mp;
3531 xmb.mb_cache_directory = NULL;
3532 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3533 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3534 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3535 sizeof(xmb.mb_imap_delim));
3536 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3537 if (mp->mb_cache_directory != NULL)
3538 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3539 xmb.mb_uidvalidity = uidvalidity;
3540 initcache(&xmb);
3542 if (m == NULL) {
3543 memset(&xm, 0, sizeof xm);
3544 xm.m_uid = olduid;
3545 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3546 goto jleave;
3547 getcache(mp, &xm, NEED_HEADER);
3548 getcache(mp, &xm, NEED_BODY);
3549 } else {
3550 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3551 getcache(mp, m, NEED_HEADER);
3552 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3553 getcache(mp, m, NEED_BODY);
3554 xm = *m;
3556 xm.m_uid = newuid;
3557 xm.m_flag &= ~MFULLYCACHED;
3558 putcache(&xmb, &xm);
3559 jleave:
3560 if (xmb.mb_cache_directory != NULL)
3561 n_free(xmb.mb_cache_directory);
3562 if (xmb.mb_imap_mailbox != NULL)
3563 n_free(xmb.mb_imap_mailbox);
3564 if (xmb.mb_imap_pass != NULL)
3565 n_free(xmb.mb_imap_pass);
3566 if (xmb.mb_imap_account != NULL)
3567 n_free(xmb.mb_imap_account);
3568 NYD_LEAVE;
3569 return rv;
3572 static enum okay
3573 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3574 long size, long lines, int flag, const char *name)
3576 struct mailbox xmb;
3577 struct message xm;
3578 const char *cp;
3579 ui64_t uidvalidity, uid;
3580 enum okay rv;
3581 NYD_ENTER;
3583 rv = STOP;
3585 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3586 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3587 goto jleave;
3589 rv = OKAY;
3591 xmb = *mp;
3592 xmb.mb_cache_directory = NULL;
3593 /* XXX mb_imap_delim reused */
3594 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3595 xmb.mb_uidvalidity = uidvalidity;
3596 xmb.mb_otf = xmb.mb_itf = fp;
3597 initcache(&xmb);
3598 memset(&xm, 0, sizeof xm);
3599 xm.m_flag = (flag & MREAD) | MNEW;
3600 xm.m_time = t;
3601 xm.m_block = mailx_blockof(off1);
3602 xm.m_offset = mailx_offsetof(off1);
3603 xm.m_size = size;
3604 xm.m_xsize = xsize;
3605 xm.m_lines = xm.m_xlines = lines;
3606 xm.m_uid = uid;
3607 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3608 putcache(&xmb, &xm);
3610 n_free(xmb.mb_imap_mailbox);
3611 jleave:
3612 NYD_LEAVE;
3613 return rv;
3616 static enum okay
3617 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3619 FILE *tp = NULL;
3620 time_t t;
3621 long size, xsize, ysize, lines;
3622 enum mflag flag = MNEW;
3623 char *name, *buf, *bp;
3624 char const *cp;
3625 size_t bufsize, buflen, cnt;
3626 enum okay rv = STOP;
3627 NYD_ENTER;
3629 buf = n_alloc(bufsize = LINESIZE);
3630 buflen = 0;
3631 cnt = fsize(fp);
3632 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3633 goto jstop;
3635 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3637 while (*bp == ' ')
3638 ++bp;
3640 if ((cp = strrchr(bp, '{')) == NULL)
3641 goto jstop;
3643 xsize = atol(&cp[1]) + 2;
3644 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3645 goto jstop;
3646 while (*cp == ' ')
3647 cp++;
3649 if (*cp == '(') {
3650 imap_getflags(cp, &cp, &flag);
3651 while (*++cp == ' ')
3654 t = imap_read_date_time(cp);
3656 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER))
3657 == NULL)
3658 goto jstop;
3660 size = xsize;
3661 ysize = lines = 0;
3662 while (size > 0) {
3663 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3664 goto jstop;
3665 size -= buflen;
3666 buf[--buflen] = '\0';
3667 buf[buflen-1] = '\n';
3668 fwrite(buf, 1, buflen, tp);
3669 ysize += buflen;
3670 ++lines;
3672 fflush(tp);
3673 rewind(tp);
3675 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3676 imap_unquotestr(name));
3677 rv = OKAY;
3678 jstop:
3679 n_free(buf);
3680 if (tp)
3681 Fclose(tp);
3682 NYD_LEAVE;
3683 return rv;
3686 #ifdef HAVE_IMAP_SEARCH
3687 static enum okay
3688 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3689 int f)
3691 char *o, *cs, c;
3692 size_t n;
3693 FILE *queuefp = NULL;
3694 int i;
3695 const char *cp, *xp;
3696 enum okay ok = STOP;
3697 NYD_X;
3699 c = 0;
3700 for (cp = spec; *cp; cp++)
3701 c |= *cp;
3702 if (c & 0200) {
3703 cp = ok_vlook(ttycharset);
3704 # ifdef HAVE_ICONV
3705 if(asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")){ /* XXX */
3706 char const *nspec;
3708 if((nspec = n_iconv_onetime_cp(n_ICONV_DEFAULT, "utf-8", cp, spec)
3709 ) != NULL){
3710 spec = nspec;
3711 cp = "utf-8";
3714 # endif
3715 cp = imap_quotestr(cp);
3716 cs = n_lofi_alloc(n = strlen(cp) + 10);
3717 snprintf(cs, n, "CHARSET %s ", cp);
3718 } else
3719 cs = n_UNCONST(n_empty);
3721 o = n_lofi_alloc(n = strlen(spec) + 60);
3722 snprintf(o, n, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3723 IMAP_OUT(o, MB_COMD, goto out)
3724 while (mp->mb_active & MB_COMD) {
3725 ok = imap_answer(mp, 0);
3726 if (response_status == RESPONSE_OTHER &&
3727 response_other == MAILBOX_DATA_SEARCH) {
3728 xp = responded_other_text;
3729 while (*xp && *xp != '\r') {
3730 ui64_t uid;
3732 n_idec_ui64_cp(&uid, xp, 10, &xp);/* TODO errors? */
3733 for (i = 0; i < cnt; i++)
3734 if (m[i].m_uid == uid && !(m[i].m_flag & MHIDDEN) &&
3735 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3736 mark(i+1, f);
3740 out:
3741 n_lofi_free(o);
3742 if(cs != n_empty)
3743 n_lofi_free(cs);
3744 return ok;
3747 FL enum okay
3748 imap_search1(const char * volatile spec, int f)
3750 sighandler_type saveint, savepipe;
3751 enum okay volatile rv = STOP;
3752 NYD_ENTER;
3754 if (mb.mb_type != MB_IMAP)
3755 goto jleave;
3757 imaplock = 1;
3758 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3759 safe_signal(SIGINT, &_imap_maincatch);
3760 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3761 if (sigsetjmp(imapjmp, 1) == 0) {
3762 if (savepipe != SIG_IGN)
3763 safe_signal(SIGPIPE, imapcatch);
3765 rv = imap_search2(&mb, message, msgCount, spec, f);
3767 safe_signal(SIGINT, saveint);
3768 safe_signal(SIGPIPE, savepipe);
3769 imaplock = 0;
3770 jleave:
3771 NYD_LEAVE;
3772 if (interrupts)
3773 n_go_onintr_for_imap();
3774 return rv;
3776 #endif /* HAVE_IMAP_SEARCH */
3778 FL int
3779 imap_thisaccount(const char *cp)
3781 int rv;
3782 NYD_ENTER;
3784 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3785 rv = 0;
3786 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3787 mb.mb_imap_account == NULL)
3788 rv = 0;
3789 else
3790 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3791 NYD_LEAVE;
3792 return rv;
3795 FL enum okay
3796 imap_remove(const char * volatile name)
3798 sighandler_type volatile saveint, savepipe;
3799 enum okay volatile rv = STOP;
3800 NYD_ENTER;
3802 if (mb.mb_type != MB_IMAP) {
3803 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3804 goto jleave;
3807 if (!imap_thisaccount(name)) {
3808 n_err(_("Can only remove mailboxes on current IMAP server: "
3809 "\"%s\" not removed\n"), name);
3810 goto jleave;
3813 imaplock = 1;
3814 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3815 safe_signal(SIGINT, &_imap_maincatch);
3816 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3817 if (sigsetjmp(imapjmp, 1) == 0) {
3818 if (savepipe != SIG_IGN)
3819 safe_signal(SIGPIPE, imapcatch);
3821 rv = imap_remove1(&mb, imap_fileof(name));
3823 safe_signal(SIGINT, saveint);
3824 safe_signal(SIGPIPE, savepipe);
3825 imaplock = 0;
3827 if (rv == OKAY)
3828 rv = cache_remove(name);
3829 jleave:
3830 NYD_LEAVE;
3831 if (interrupts)
3832 n_go_onintr_for_imap();
3833 return rv;
3836 static enum okay
3837 imap_remove1(struct mailbox *mp, const char *name)
3839 char *o;
3840 int os;
3841 char const *qname;
3842 FILE *queuefp;
3843 enum okay ok;
3844 NYD_X;
3846 ok = STOP;
3847 queuefp = NULL;
3849 if((qname = imap_path_quote(mp, name)) != NULL){
3850 o = n_lofi_alloc(os = strlen(qname) + 100);
3851 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3852 IMAP_OUT(o, MB_COMD, goto out)
3853 while (mp->mb_active & MB_COMD)
3854 ok = imap_answer(mp, 1);
3855 out:
3856 n_lofi_free(o);
3858 return ok;
3861 FL enum okay
3862 imap_rename(const char *old, const char *new)
3864 sighandler_type saveint, savepipe;
3865 enum okay volatile rv = STOP;
3866 NYD_ENTER;
3868 if (mb.mb_type != MB_IMAP) {
3869 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3870 goto jleave;
3873 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3874 n_err(_("Can only rename mailboxes on current IMAP "
3875 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3876 goto jleave;
3879 imaplock = 1;
3880 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3881 safe_signal(SIGINT, &_imap_maincatch);
3882 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3883 if (sigsetjmp(imapjmp, 1) == 0) {
3884 if (savepipe != SIG_IGN)
3885 safe_signal(SIGPIPE, imapcatch);
3887 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3889 safe_signal(SIGINT, saveint);
3890 safe_signal(SIGPIPE, savepipe);
3891 imaplock = 0;
3893 if (rv == OKAY)
3894 rv = cache_rename(old, new);
3895 jleave:
3896 NYD_LEAVE;
3897 if (interrupts)
3898 n_go_onintr_for_imap();
3899 return rv;
3902 static enum okay
3903 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3905 char *o;
3906 int os;
3907 char const *qoname, *qnname;
3908 FILE *queuefp;
3909 enum okay ok;
3910 NYD_X;
3912 ok = STOP;
3913 queuefp = NULL;
3915 if((qoname = imap_path_quote(mp, old)) != NULL &&
3916 (qnname = imap_path_quote(mp, new)) != NULL){
3917 o = n_lofi_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3918 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3919 IMAP_OUT(o, MB_COMD, goto out)
3920 while (mp->mb_active & MB_COMD)
3921 ok = imap_answer(mp, 1);
3922 out:
3923 n_lofi_free(o);
3925 return ok;
3928 FL enum okay
3929 imap_dequeue(struct mailbox *mp, FILE *fp)
3931 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3932 size_t bufsize, buflen, cnt;
3933 long offs, offs1, offs2, octets;
3934 int twice, gotcha = 0;
3935 FILE *queuefp = NULL;
3936 enum okay ok = OKAY, rok = OKAY;
3937 NYD_X;
3939 buf = n_alloc(bufsize = LINESIZE);
3940 buflen = 0;
3941 cnt = fsize(fp);
3942 while ((offs1 = ftell(fp)) >= 0 &&
3943 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3944 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3946 while (*bp == ' ')
3947 ++bp;
3948 twice = 0;
3949 if ((offs = ftell(fp)) < 0)
3950 goto fail;
3951 again:
3952 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3953 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3954 cp = &bp[9];
3955 while (digitchar(*cp))
3956 cp++;
3957 if (*cp != ' ')
3958 goto fail;
3959 while (*cp == ' ')
3960 cp++;
3961 if ((newname = imap_strex(cp, NULL)) == NULL)
3962 goto fail;
3963 IMAP_OUT(o, MB_COMD, continue)
3964 while (mp->mb_active & MB_COMD)
3965 ok = imap_answer(mp, twice);
3966 if (response_status == RESPONSE_NO && twice++ == 0)
3967 goto trycreate;
3968 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3969 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3971 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3972 IMAP_OUT(o, MB_COMD, continue)
3973 while (mp->mb_active & MB_COMD)
3974 ok = imap_answer(mp, 1);
3975 if (ok == OKAY)
3976 gotcha++;
3977 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3978 if ((cp = strrchr(bp, '{')) == NULL)
3979 goto fail;
3980 octets = atol(&cp[1]) + 2;
3981 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3982 goto fail;
3983 IMAP_OUT(o, MB_COMD, continue)
3984 while (mp->mb_active & MB_COMD) {
3985 ok = imap_answer(mp, twice);
3986 if (response_type == RESPONSE_CONT)
3987 break;
3989 if (ok == STOP) {
3990 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3991 goto trycreate;
3992 goto fail;
3994 while (octets > 0) {
3995 size_t n = (UICMP(z, octets, >, sizeof iob)
3996 ? sizeof iob : (size_t)octets);
3997 octets -= n;
3998 if (n != fread(iob, 1, n, fp))
3999 goto fail;
4000 swrite1(&mp->mb_sock, iob, n, 1);
4002 swrite(&mp->mb_sock, "");
4003 while (mp->mb_active & MB_COMD) {
4004 ok = imap_answer(mp, 0);
4005 if (response_status == RESPONSE_NO && twice++ == 0) {
4006 if (fseek(fp, offs, SEEK_SET) < 0)
4007 goto fail;
4008 goto trycreate;
4011 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4012 if ((offs2 = ftell(fp)) < 0)
4013 goto fail;
4014 fseek(fp, offs1, SEEK_SET);
4015 if (imap_appenduid_cached(mp, fp) == STOP) {
4016 (void)fseek(fp, offs2, SEEK_SET);
4017 goto fail;
4020 } else {
4021 fail:
4022 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4023 rok = STOP;
4025 continue;
4026 trycreate:
4027 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4028 IMAP_OUT(o, MB_COMD, continue)
4029 while (mp->mb_active & MB_COMD)
4030 ok = imap_answer(mp, 1);
4031 if (ok == OKAY)
4032 goto again;
4034 fflush(fp);
4035 rewind(fp);
4036 ftruncate(fileno(fp), 0);
4037 if (gotcha)
4038 imap_close(mp);
4039 n_free(buf);
4040 return rok;
4043 static char *
4044 imap_strex(char const *cp, char const **xp)
4046 char const *cq;
4047 char *n = NULL;
4048 NYD_ENTER;
4050 if (*cp != '"')
4051 goto jleave;
4053 for (cq = cp + 1; *cq != '\0'; ++cq) {
4054 if (*cq == '\\')
4055 cq++;
4056 else if (*cq == '"')
4057 break;
4059 if (*cq != '"')
4060 goto jleave;
4062 n = n_autorec_alloc(cq - cp + 2);
4063 memcpy(n, cp, cq - cp +1);
4064 n[cq - cp + 1] = '\0';
4065 if (xp != NULL)
4066 *xp = cq + 1;
4067 jleave:
4068 NYD_LEAVE;
4069 return n;
4072 static enum okay
4073 check_expunged(void)
4075 enum okay rv;
4076 NYD_ENTER;
4078 if (expunged_messages > 0) {
4079 n_err(_("Command not executed - messages have been expunged\n"));
4080 rv = STOP;
4081 } else
4082 rv = OKAY;
4083 NYD_LEAVE;
4084 return rv;
4087 FL int
4088 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4090 struct url url;
4091 int rv, omsgCount = msgCount;
4092 NYD_ENTER;
4093 n_UNUSED(vp);
4095 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4096 n_err(_("Already connected\n"));
4097 rv = 1;
4098 goto jleave;
4101 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4102 rv = 1;
4103 goto jleave;
4105 ok_bclear(disconnected);
4106 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4108 if (mb.mb_type == MB_CACHE) {
4109 enum fedit_mode fm = FEDIT_NONE;
4110 if (_imap_rdonly)
4111 fm |= FEDIT_RDONLY;
4112 if (!(n_pstate & n_PS_EDIT))
4113 fm |= FEDIT_SYSBOX;
4114 _imap_setfile1(NULL, &url, fm, 1);
4115 if (msgCount > omsgCount)
4116 newmailinfo(omsgCount);
4118 rv = 0;
4119 jleave:
4120 NYD_LEAVE;
4121 return rv;
4124 FL int
4125 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4127 struct url url;
4128 int rv = 1, *msgvec = vp;
4129 NYD_ENTER;
4131 if (mb.mb_type == MB_CACHE) {
4132 n_err(_("Not connected\n"));
4133 goto jleave;
4135 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4136 n_err(_("The current mailbox is not cached\n"));
4137 goto jleave;
4140 if (!url_parse(&url, CPROTO_IMAP, mailname))
4141 goto jleave;
4143 if (*msgvec)
4144 c_cache(vp);
4145 ok_bset(disconnected);
4146 if (mb.mb_type == MB_IMAP) {
4147 enum fedit_mode fm = FEDIT_NONE;
4148 if (_imap_rdonly)
4149 fm |= FEDIT_RDONLY;
4150 if (!(n_pstate & n_PS_EDIT))
4151 fm |= FEDIT_SYSBOX;
4152 sclose(&mb.mb_sock);
4153 _imap_setfile1(NULL, &url, fm, 1);
4155 rv = 0;
4156 jleave:
4157 NYD_LEAVE;
4158 return rv;
4161 FL int
4162 c_cache(void *vp)
4164 int rv = 1, *msgvec = vp, *ip;
4165 struct message *mp;
4166 NYD_ENTER;
4168 if (mb.mb_type != MB_IMAP) {
4169 n_err(_("Not connected to an IMAP server\n"));
4170 goto jleave;
4172 if (cached_uidvalidity(&mb) == 0) {
4173 n_err(_("The current mailbox is not cached\n"));
4174 goto jleave;
4177 srelax_hold();
4178 for (ip = msgvec; *ip; ++ip) {
4179 mp = &message[*ip - 1];
4180 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4181 get_body(mp);
4182 srelax();
4185 srelax_rele();
4186 rv = 0;
4187 jleave:
4188 NYD_LEAVE;
4189 return rv;
4192 FL int
4193 disconnected(const char *file)
4195 struct url url;
4196 int rv = 1;
4197 NYD_ENTER;
4199 if (ok_blook(disconnected)) {
4200 rv = 1;
4201 goto jleave;
4204 if (!url_parse(&url, CPROTO_IMAP, file)) {
4205 rv = 0;
4206 goto jleave;
4208 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4210 jleave:
4211 NYD_LEAVE;
4212 return rv;
4215 FL void
4216 transflags(struct message *omessage, long omsgCount, int transparent)
4218 struct message *omp, *nmp, *newdot, *newprevdot;
4219 int hf;
4220 NYD_ENTER;
4222 omp = omessage;
4223 nmp = message;
4224 newdot = message;
4225 newprevdot = NULL;
4226 while (PTRCMP(omp, <, omessage + omsgCount) &&
4227 PTRCMP(nmp, <, message + msgCount)) {
4228 if (dot && nmp->m_uid == dot->m_uid)
4229 newdot = nmp;
4230 if (prevdot && nmp->m_uid == prevdot->m_uid)
4231 newprevdot = nmp;
4232 if (omp->m_uid == nmp->m_uid) {
4233 hf = nmp->m_flag & MHIDDEN;
4234 if (transparent && mb.mb_type == MB_IMAP)
4235 omp->m_flag &= ~MHIDDEN;
4236 *nmp++ = *omp++;
4237 if (transparent && mb.mb_type == MB_CACHE)
4238 nmp[-1].m_flag |= hf;
4239 } else if (omp->m_uid < nmp->m_uid)
4240 ++omp;
4241 else
4242 ++nmp;
4244 dot = newdot;
4245 setdot(newdot);
4246 prevdot = newprevdot;
4247 n_free(omessage);
4248 NYD_LEAVE;
4251 FL time_t
4252 imap_read_date_time(const char *cp)
4254 char buf[3];
4255 time_t t;
4256 int i, year, month, day, hour, minute, second, sign = -1;
4257 NYD2_ENTER;
4259 /* "25-Jul-2004 15:33:44 +0200"
4260 * | | | | | |
4261 * 0 5 10 15 20 25 */
4262 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4263 goto jinvalid;
4264 day = strtol(&cp[1], NULL, 10);
4265 for (i = 0;;) {
4266 if (ascncasecmp(&cp[4], n_month_names[i], 3) == 0)
4267 break;
4268 if (n_month_names[++i][0] == '\0')
4269 goto jinvalid;
4271 month = i + 1;
4272 year = strtol(&cp[8], NULL, 10);
4273 hour = strtol(&cp[13], NULL, 10);
4274 minute = strtol(&cp[16], NULL, 10);
4275 second = strtol(&cp[19], NULL, 10);
4276 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4277 goto jinvalid;
4278 switch (cp[22]) {
4279 case '-':
4280 sign = 1;
4281 break;
4282 case '+':
4283 break;
4284 default:
4285 goto jinvalid;
4287 buf[2] = '\0';
4288 buf[0] = cp[23];
4289 buf[1] = cp[24];
4290 t += strtol(buf, NULL, 10) * sign * 3600;
4291 buf[0] = cp[25];
4292 buf[1] = cp[26];
4293 t += strtol(buf, NULL, 10) * sign * 60;
4294 jleave:
4295 NYD2_LEAVE;
4296 return t;
4297 jinvalid:
4298 time(&t);
4299 goto jleave;
4302 FL const char *
4303 imap_make_date_time(time_t t)
4305 static char s[40];
4306 char const *mn;
4307 si32_t y, md, th, tm, ts;
4308 struct tm *tmp;
4309 int tzdiff, tzdiff_hour, tzdiff_min;
4310 time_t t2;
4311 NYD2_ENTER;
4313 jredo:
4314 if((t2 = mktime(gmtime(&t))) == (time_t)-1){
4315 t = 0;
4316 goto jredo;
4318 tzdiff = t - t2;
4319 if((tmp = localtime(&t)) == NULL){
4320 t = 0;
4321 goto jredo;
4324 tzdiff_hour = (int)(tzdiff / 60);
4325 tzdiff_min = tzdiff_hour % 60;
4326 tzdiff_hour /= 60;
4327 if (tmp->tm_isdst > 0)
4328 tzdiff_hour++;
4330 if(n_UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*SI32_MAX*/ - 1900)){
4331 y = 1970;
4332 mn = n_month_names[0];
4333 md = 1;
4334 th = tm = ts = 0;
4335 }else{
4336 y += 1900;
4337 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
4338 ? n_month_names[tmp->tm_mon] : n_qm;
4340 if((md = tmp->tm_mday) < 1 || md > 31)
4341 md = 1;
4343 if((th = tmp->tm_hour) < 0 || th > 23)
4344 th = 0;
4345 if((tm = tmp->tm_min) < 0 || tm > 59)
4346 tm = 0;
4347 if((ts = tmp->tm_sec) < 0 || ts > 60)
4348 ts = 0;
4351 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4352 md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
4353 NYD2_LEAVE;
4354 return s;
4357 FL char *
4358 (protbase)(char const *cp n_MEMORY_DEBUG_ARGS)
4360 char *n, *np;
4361 NYD2_ENTER;
4363 np = n = (n_autorec_alloc_from_pool)(NULL, strlen(cp) +1
4364 n_MEMORY_DEBUG_ARGSCALL);
4366 /* Just ignore the `is-system-mailbox' prefix XXX */
4367 if (cp[0] == '%' && cp[1] == ':')
4368 cp += 2;
4370 while (*cp != '\0') {
4371 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4372 *np++ = *cp++;
4373 *np++ = *cp++;
4374 *np++ = *cp++;
4375 } else if (cp[0] == '/')
4376 break;
4377 else
4378 *np++ = *cp++;
4380 *np = '\0';
4381 NYD2_LEAVE;
4382 return n;
4384 #endif /* HAVE_IMAP */
4386 /* s-it-mode */