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