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