Bump S-nail v14.8.13.ar, 2016-10-19
[s-mailx.git] / imap.c
blobea023d926307fb7ecea934608b95cc257a0ec47a
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ CRAM-MD5 as of RFC 2195.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 #ifndef HAVE_AMALGAMATION
42 # include "nail.h"
43 #endif
45 #ifdef HAVE_IMAP
46 # include <sys/socket.h>
48 # include <netdb.h>
50 # include <netinet/in.h>
52 # ifdef HAVE_ARPA_INET_H
53 # include <arpa/inet.h>
54 # endif
55 #endif
57 #ifdef HAVE_IMAP
58 #define IMAP_ANSWER() \
60 if (mp->mb_type != MB_CACHE) {\
61 enum okay ok = OKAY;\
62 while (mp->mb_active & MB_COMD)\
63 ok = imap_answer(mp, 1);\
64 if (ok == STOP)\
65 return STOP;\
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
77 do {\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
80 ACTIONBAIL;\
82 if (options & OPT_VERBVERB)\
83 n_err(">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static int volatile imaplock;
171 static int same_imap_account;
172 static bool_t _imap_rdonly;
174 static void imap_delim_init(struct mailbox *mp, struct url const *urlp);
175 static char const *imap_path_normalize(struct mailbox *mp, char const *cp);
176 /* Returns NULL on error */
177 static char *imap_path_quote(struct mailbox *mp, char const *cp);
178 static void imap_other_get(char *pp);
179 static void imap_response_get(const char **cp);
180 static void imap_response_parse(void);
181 static enum okay imap_answer(struct mailbox *mp, int errprnt);
182 static enum okay imap_parse_list(void);
183 static enum okay imap_finish(struct mailbox *mp);
184 static void imap_timer_off(void);
185 static void imapcatch(int s);
186 static void _imap_maincatch(int s);
187 static enum okay imap_noop1(struct mailbox *mp);
188 static void rec_queue(enum rec_type type, unsigned long cnt);
189 static enum okay rec_dequeue(void);
190 static void rec_rmqueue(void);
191 static void imapalarm(int s);
192 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
193 static enum okay imap_capability(struct mailbox *mp);
194 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
195 #ifdef HAVE_MD5
196 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
197 #endif
198 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
199 #ifdef HAVE_GSSAPI
200 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
201 #endif
202 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
203 static void imap_init(struct mailbox *mp, int n);
204 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
205 int *prevcount);
206 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
207 struct url *urlp);
208 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
209 int transparent);
210 static int imap_fetchdata(struct mailbox *mp, struct message *m,
211 size_t expected, int need, const char *head,
212 size_t headsize, long headlines);
213 static void imap_putstr(struct mailbox *mp, struct message *m,
214 const char *str, const char *head, size_t headsize,
215 long headlines);
216 static enum okay imap_get(struct mailbox *mp, struct message *m,
217 enum needspec need);
218 static void commitmsg(struct mailbox *mp, struct message *to,
219 struct message *from, enum havespec have);
220 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
221 int bot, int top);
222 static enum okay imap_exit(struct mailbox *mp);
223 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
224 int needstat);
225 static enum okay imap_close(struct mailbox *mp);
226 static enum okay imap_update(struct mailbox *mp);
227 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
228 int c, const char *sp, int needstat);
229 static enum okay imap_unstore(struct message *m, int n, const char *flag);
230 static const char *tag(int new);
231 static char * imap_putflags(int f);
232 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
233 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
234 off_t off1, long xsize, enum mflag flag, time_t t);
235 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
236 static enum okay imap_list1(struct mailbox *mp, const char *base,
237 struct list_item **list, struct list_item **lend,
238 int level);
239 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
240 FILE *fp);
241 static void dopr(FILE *fp);
242 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
243 const char *name);
244 static enum okay imap_copyuid_parse(const char *cp,
245 unsigned long *uidvalidity, unsigned long *olduid,
246 unsigned long *newuid);
247 static enum okay imap_appenduid_parse(const char *cp,
248 unsigned long *uidvalidity, unsigned long *uid);
249 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
250 const char *name);
251 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
252 long off1, long xsize, long size, long lines, int flag,
253 const char *name);
254 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
255 #ifdef HAVE_IMAP_SEARCH
256 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
257 const char *spec, int f);
258 #endif
259 static enum okay imap_remove1(struct mailbox *mp, const char *name);
260 static enum okay imap_rename1(struct mailbox *mp, const char *old,
261 const char *new);
262 static char * imap_strex(char const *cp, char const **xp);
263 static enum okay check_expunged(void);
265 static void
266 imap_delim_init(struct mailbox *mp, struct url const *urlp){
267 size_t i;
268 char const *cp;
269 NYD2_ENTER;
271 mp->mb_imap_delim[0] = '\0';
273 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
274 i = strlen(cp);
276 if(i == 0){
277 cp = n_IMAP_DELIM;
278 i = sizeof(n_IMAP_DELIM) -1;
279 goto jcopy;
282 if(i < NELEM(mp->mb_imap_delim))
283 jcopy:
284 memcpy(&mb.mb_imap_delim[0], cp, i +1);
285 else
286 n_err(_("*imap-delim* for %s is too long: %s\n"),
287 urlp->url_input, cp);
289 NYD2_LEAVE;
292 static char const *
293 imap_path_normalize(struct mailbox *mp, char const *cp){
294 char *rv_base, *rv, dc2, dc, c, lc;
295 char const *dcp;
296 NYD2_ENTER;
298 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
299 * exactly what i have specified" */
300 dcp = (mp == NULL) ? &n_IMAP_DELIM[0] : &mp->mb_imap_delim[0];
301 dc2 = ((dc = *dcp) != '\0') ? *++dcp : dc;
303 /* Plain names don't need path quoting */
304 /* C99 */{
305 size_t i, j;
306 char const *cpx;
308 for(cpx = cp;; ++cpx)
309 if((c = *cpx) == '\0')
310 goto jleave;
311 else if(dc == '\0'){
312 if(strchr(n_IMAP_DELIM, c)){
313 dc = c;
314 break;
316 }else if(c == dc)
317 break;
318 else if(dc2 && strchr(dcp, c) != NULL)
319 break;
321 /* And we don't need to reevaluate what we have seen yet */
322 i = PTR2SIZE(cpx - cp);
323 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
324 if(i > 0)
325 memcpy(rv, cp, i);
326 memcpy(&rv[i], cpx, j);
327 rv += i;
328 cp = cpx;
331 /* Squeeze adjacent delimiters, convert remain to dc */
332 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
333 if(c == dc || (dc2 && strchr(dcp, c) != NULL))
334 c = dc;
335 if(c != dc || lc != dc)
336 *rv++ = c;
338 *rv = '\0';
340 cp = rv_base;
341 jleave:
342 NYD2_LEAVE;
343 return cp;
346 FL char const *
347 imap_path_encode(char const *cp, bool_t *err_or_null){
348 /* To a large extend inspired by dovecot(1) */
349 struct str in, out;
350 bool_t err_def;
351 ui8_t *be16p_base, *be16p;
352 char const *emsg;
353 char c;
354 size_t l, l_plain;
355 NYD2_ENTER;
357 if(err_or_null == NULL)
358 err_or_null = &err_def;
359 *err_or_null = FAL0;
361 /* Is this a string that works out as "plain US-ASCII"? */
362 for(l = 0;; ++l)
363 if((c = cp[l]) == '\0')
364 goto jleave;
365 else if(c <= 0x1F || c >= 0x7F || c == '&')
366 break;
368 *err_or_null = TRU1;
370 /* We need to encode in mUTF-7! For that, we first have to convert the
371 * local charset to UTF-8, then convert all characters which need to be
372 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
373 * We can skip the UTF-8 conversion occasionally, however */
374 if(!(options & OPT_UNICODE)){
375 int ir;
376 iconv_t icd;
378 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
380 if((icd = iconv_open("utf-8", charset_get_lc())) == (iconv_t)-1)
381 goto jerr;
383 out.s = NULL, out.l = 0;
384 in.s = UNCONST(cp); /* logical */
385 l += strlen(&cp[l]);
386 in.l = l;
387 if((ir = n_iconv_str(icd, &out, &in, NULL, FAL0)) == 0)
388 cp = savestrbuf(out.s, out.l);
390 if(out.s != NULL)
391 free(out.s);
392 iconv_close(icd);
394 if(ir != 0)
395 goto jerr;
398 * So: Why not start all over again?
401 /* Is this a string that works out as "plain US-ASCII"? */
402 for(l = 0;; ++l)
403 if((c = cp[l]) == '\0')
404 goto jleave;
405 else if(c <= 0x1F || c >= 0x7F || c == '&')
406 break;
409 /* We need to encode, save what we have, encode the rest */
410 l_plain = l;
412 for(cp += l, l = 0; cp[l] != '\0'; ++l)
414 be16p_base = salloc((l << 1) +1); /* XXX use n_string, resize */
416 out.s = salloc(l_plain + (l << 2) +1); /* XXX use n_string, resize */
417 if(l_plain > 0)
418 memcpy(out.s, &cp[-l_plain], out.l = l_plain);
419 else
420 out.l = 0;
421 DBG( l_plain += (l << 2); )
423 while(l > 0){
424 c = *cp++;
425 --l;
427 if(c == '&'){
428 out.s[out.l + 0] = '&';
429 out.s[out.l + 1] = '-';
430 out.l += 2;
431 }else if(c > 0x1F && c < 0x7F)
432 out.s[out.l++] = c;
433 else{
434 static char const mb64ct[] =
435 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
436 ui32_t utf32;
438 /* Convert consecutive non-representables */
439 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
441 for(be16p = be16p_base, --cp, ++l;;){
442 if((utf32 = n_utf8_to_utf32(&cp, &l)) == UI32_MAX)
443 goto jerr;
445 /* TODO S-CText: magic utf16 conversions */
446 if(utf32 < 0x10000){
447 be16p[1] = utf32 & 0xFF;
448 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
449 be16p += 2;
450 }else{
451 ui16_t s7e;
453 utf32 -= 0x10000;
454 s7e = 0xD800u | (utf32 >> 10);
455 be16p[1] = s7e & 0xFF;
456 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
457 s7e = 0xDC00u | (utf32 &= 0x03FF);
458 be16p[3] = s7e & 0xFF;
459 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
460 be16p += 4;
463 if(l == 0)
464 break;
465 if((c = *cp) > 0x1F && c < 0x7F)
466 break;
469 /* And then warp that UTF-16BE to mUTF-7 */
470 out.s[out.l++] = '&';
471 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
472 be16p = be16p_base;
474 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
475 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
476 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
477 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
478 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
479 out.l += 4;
481 if(utf32 > 0){
482 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
483 if(--utf32 == 0){
484 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
485 out.l += 2;
486 }else{
487 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
488 (be16p[1] >> 4)];
489 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
490 out.l += 3;
493 out.s[out.l++] = '-';
496 out.s[out.l] = '\0';
497 assert(out.l <= l_plain);
498 *err_or_null = FAL0;
499 cp = out.s;
500 jleave:
501 NYD2_LEAVE;
502 return cp;
503 jerr:
504 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
505 goto jleave;
508 FL char *
509 imap_path_decode(char const *path, bool_t *err_or_null){
510 /* To a large extend inspired by dovecot(1) TODO use string */
511 struct str in, out;
512 bool_t err_def;
513 ui8_t *mb64p_base, *mb64p, *mb64xp;
514 char const *emsg, *cp;
515 char *rv_base, *rv, c;
516 size_t l_orig, l, i;
517 NYD2_ENTER;
519 if(err_or_null == NULL)
520 err_or_null = &err_def;
521 *err_or_null = FAL0;
523 l = l_orig = strlen(path);
524 rv = rv_base = salloc(l << 1);
525 memcpy(rv, path, l +1);
527 /* xxx Don't check for invalid characters from malicious servers */
528 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
529 goto jleave;
531 *err_or_null = TRU1;
533 emsg = N_("Invalid mUTF-7 encoding");
534 i = PTR2SIZE(cp - path);
535 rv += i;
536 l -= i;
537 mb64p_base = NULL;
539 while(l > 0){
540 if((c = *cp) != '&'){
541 if(c <= 0x1F || c >= 0x7F){
542 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
543 goto jerr;
545 *rv++ = c;
546 ++cp;
547 --l;
548 }else if(--l == 0)
549 goto jeincpl;
550 else if(*++cp == '-'){
551 *rv++ = '&';
552 ++cp;
553 --l;
554 }else if(l < 3){
555 jeincpl:
556 emsg = N_("Invalid mUTF-7: incomplete input");
557 goto jerr;
558 }else{
559 /* mUTF-7 -> UTF-16BE -> UTF-8 */
560 static ui8_t const mb64dt[256] = {
561 #undef XX
562 #define XX 0xFFu
563 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
564 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
565 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
566 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
567 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
568 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
569 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
570 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
571 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
572 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
573 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
574 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
575 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
576 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
577 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
578 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
581 if(mb64p_base == NULL)
582 mb64p_base = salloc(l);
584 /* Decode the mUTF-7 to what is indeed UTF-16BE */
585 for(mb64p = mb64p_base;;){
586 assert(l >= 3);
587 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
588 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
589 goto jerr;
590 mb64p += 2;
592 c = cp[2];
593 cp += 3;
594 l -= 3;
595 if(c == '-')
596 break;
597 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
598 goto jerr;
600 if(l == 0)
601 goto jerr;
602 --l;
603 if((c = *cp++) == '-')
604 break;
605 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
606 goto jerr;
608 if(l < 3){
609 if(l > 0 && *cp == '-'){
610 --l;
611 ++cp;
612 break;
614 goto jerr;
617 #undef XX
619 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
620 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
621 goto jerr;
624 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
625 i = PTR2SIZE(mb64p - mb64p_base);
626 mb64p = mb64xp = mb64p_base;
628 while(i > 0){
629 ui8_t ul, u0, u1, u2, u3;
631 ul = (i >= 4) ? 4 : i & 0x3;
632 i -= ul;
633 u0 = mb64xp[0];
634 u1 = mb64xp[1];
635 u2 = (ul < 3) ? 0 : mb64xp[2];
636 u3 = (ul < 4) ? 0 : mb64xp[3];
637 mb64xp += ul;
638 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
639 if(ul < 3)
640 break;
641 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
642 if(ul < 4)
643 break;
644 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
647 /* UTF-16BE we convert to UTF-8 */
648 i = PTR2SIZE(mb64p - mb64p_base);
649 if(i & 1){
650 emsg = N_("Odd bytecount for UTF-16BE input");
651 goto jerr;
654 /* TODO S-CText: magic utf16 conversions */
655 emsg = N_("Invalid UTF-16BE encoding");
657 for(mb64p = mb64p_base; i > 0;){
658 ui32_t utf32;
659 ui16_t uhi, ulo;
661 uhi = mb64p[0];
662 uhi <<= 8;
663 uhi |= mb64p[1];
665 /* Not a surrogate? */
666 if(uhi < 0xD800 || uhi > 0xDFFF){
667 utf32 = uhi;
668 mb64p += 2;
669 i -= 2;
670 }else if(uhi > 0xDBFF)
671 goto jerr;
672 else if(i < 4){
673 emsg = N_("Incomplete UTF-16BE surrogate pair");
674 goto jerr;
675 }else{
676 ulo = mb64p[2];
677 ulo <<= 8;
678 ulo |= mb64p[3];
679 if(ulo < 0xDC00 || ulo > 0xDFFF)
680 goto jerr;
682 utf32 = (uhi &= 0x03FF);
683 utf32 <<= 10;
684 utf32 += 0x10000;
685 utf32 |= (ulo &= 0x03FF);
686 mb64p += 4;
687 i -= 4;
690 utf32 = n_utf32_to_utf8(utf32, rv);
691 rv += utf32;
695 *rv = '\0';
697 /* We can skip the UTF-8 conversion occasionally */
698 if(!(options & OPT_UNICODE)){
699 int ir;
700 iconv_t icd;
702 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
704 if((icd = iconv_open(charset_get_lc(), "utf-8")) == (iconv_t)-1)
705 goto jerr;
707 out.s = NULL, out.l = 0;
708 in.l = strlen(in.s = rv_base);
709 if((ir = n_iconv_str(icd, &out, &in, NULL, FAL0)) == 0)
710 /* Because the length of this is unpredictable, copy*/
711 rv_base = savestrbuf(out.s, out.l);
713 if(out.s != NULL)
714 free(out.s);
715 iconv_close(icd);
717 if(ir != 0)
718 goto jerr;
721 *err_or_null = FAL0;
722 rv = rv_base;
723 jleave:
724 NYD2_LEAVE;
725 return rv;
726 jerr:
727 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
728 memcpy(rv = rv_base, path, ++l_orig);
729 goto jleave;
732 static char *
733 imap_path_quote(struct mailbox *mp, char const *cp){
734 bool_t err;
735 char *rv;
736 NYD2_ENTER;
738 cp = imap_path_normalize(mp, cp);
739 cp = imap_path_encode(cp, &err);
740 rv = err ? NULL : imap_quotestr(cp);
741 NYD2_LEAVE;
742 return rv;
745 static void
746 imap_other_get(char *pp)
748 char *xp;
749 NYD2_ENTER;
751 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
752 pp += 6;
753 response_other = MAILBOX_DATA_FLAGS;
754 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
755 pp += 5;
756 response_other = MAILBOX_DATA_LIST;
757 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
758 pp += 5;
759 response_other = MAILBOX_DATA_LSUB;
760 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
761 pp += 8;
762 response_other = MAILBOX_DATA_MAILBOX;
763 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
764 pp += 7;
765 response_other = MAILBOX_DATA_SEARCH;
766 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
767 pp += 7;
768 response_other = MAILBOX_DATA_STATUS;
769 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
770 pp += 11;
771 response_other = CAPABILITY_DATA;
772 } else {
773 responded_other_number = strtol(pp, &xp, 10);
774 while (*xp == ' ')
775 ++xp;
776 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
777 response_other = MAILBOX_DATA_EXISTS;
778 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
779 response_other = MAILBOX_DATA_RECENT;
780 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
781 response_other = MESSAGE_DATA_EXPUNGE;
782 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
783 pp = &xp[6];
784 response_other = MESSAGE_DATA_FETCH;
785 } else
786 response_other = RESPONSE_OTHER_UNKNOWN;
788 responded_other_text = pp;
789 NYD2_LEAVE;
792 static void
793 imap_response_get(const char **cp)
795 NYD2_ENTER;
796 if (ascncasecmp(*cp, "OK ", 3) == 0) {
797 *cp += 3;
798 response_status = RESPONSE_OK;
799 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
800 *cp += 3;
801 response_status = RESPONSE_NO;
802 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
803 *cp += 4;
804 response_status = RESPONSE_BAD;
805 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
806 *cp += 8;
807 response_status = RESPONSE_PREAUTH;
808 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
809 *cp += 4;
810 response_status = RESPONSE_BYE;
811 } else
812 response_status = RESPONSE_OTHER;
813 NYD2_LEAVE;
816 static void
817 imap_response_parse(void)
819 static char *parsebuf; /* TODO Use pool */
820 static size_t parsebufsize;
822 const char *ip = imapbuf;
823 char *pp;
824 NYD2_ENTER;
826 if (parsebufsize < imapbufsize)
827 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
828 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
829 pp = parsebuf;
830 switch (*ip) {
831 case '+':
832 response_type = RESPONSE_CONT;
833 ip++;
834 pp++;
835 while (*ip == ' ') {
836 ip++;
837 pp++;
839 break;
840 case '*':
841 ip++;
842 pp++;
843 while (*ip == ' ') {
844 ip++;
845 pp++;
847 imap_response_get(&ip);
848 pp = &parsebuf[ip - imapbuf];
849 switch (response_status) {
850 case RESPONSE_BYE:
851 response_type = RESPONSE_FATAL;
852 break;
853 default:
854 response_type = RESPONSE_DATA;
856 break;
857 default:
858 responded_tag = parsebuf;
859 while (*pp && *pp != ' ')
860 pp++;
861 if (*pp == '\0') {
862 response_type = RESPONSE_ILLEGAL;
863 break;
865 *pp++ = '\0';
866 while (*pp && *pp == ' ')
867 pp++;
868 if (*pp == '\0') {
869 response_type = RESPONSE_ILLEGAL;
870 break;
872 ip = &imapbuf[pp - parsebuf];
873 response_type = RESPONSE_TAGGED;
874 imap_response_get(&ip);
875 pp = &parsebuf[ip - imapbuf];
877 responded_text = pp;
878 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
879 response_status == RESPONSE_OTHER)
880 imap_other_get(pp);
881 NYD2_LEAVE;
884 static enum okay
885 imap_answer(struct mailbox *mp, int errprnt)
887 int i, complete;
888 enum okay rv;
889 NYD2_ENTER;
891 rv = OKAY;
892 if (mp->mb_type == MB_CACHE)
893 goto jleave;
894 rv = STOP;
895 jagain:
896 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
897 if (options & OPT_VERBVERB)
898 fputs(imapbuf, stderr);
899 imap_response_parse();
900 if (response_type == RESPONSE_ILLEGAL)
901 goto jagain;
902 if (response_type == RESPONSE_CONT) {
903 rv = OKAY;
904 goto jleave;
906 if (response_status == RESPONSE_OTHER) {
907 if (response_other == MAILBOX_DATA_EXISTS) {
908 had_exists = responded_other_number;
909 rec_queue(REC_EXISTS, responded_other_number);
910 if (had_expunge > 0)
911 had_expunge = 0;
912 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
913 rec_queue(REC_EXPUNGE, responded_other_number);
914 if (had_expunge < 0)
915 had_expunge = 0;
916 had_expunge++;
917 expunged_messages++;
920 complete = 0;
921 if (response_type == RESPONSE_TAGGED) {
922 if (asccasecmp(responded_tag, tag(0)) == 0)
923 complete |= 1;
924 else
925 goto jagain;
927 switch (response_status) {
928 case RESPONSE_PREAUTH:
929 mp->mb_active &= ~MB_PREAUTH;
930 /*FALLTHRU*/
931 case RESPONSE_OK:
932 jokay:
933 rv = OKAY;
934 complete |= 2;
935 break;
936 case RESPONSE_NO:
937 case RESPONSE_BAD:
938 jstop:
939 rv = STOP;
940 complete |= 2;
941 if (errprnt)
942 n_err(_("IMAP error: %s"), responded_text);
943 break;
944 case RESPONSE_UNKNOWN: /* does not happen */
945 case RESPONSE_BYE:
946 i = mp->mb_active;
947 mp->mb_active = MB_NONE;
948 if (i & MB_BYE)
949 goto jokay;
950 goto jstop;
951 case RESPONSE_OTHER:
952 rv = OKAY;
953 break;
955 if (response_status != RESPONSE_OTHER &&
956 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
957 n_err(_("IMAP alert: %s"), &responded_text[8]);
958 if (complete == 3)
959 mp->mb_active &= ~MB_COMD;
960 } else {
961 rv = STOP;
962 mp->mb_active = MB_NONE;
964 jleave:
965 NYD2_LEAVE;
966 return rv;
969 static enum okay
970 imap_parse_list(void)
972 char *cp;
973 enum okay rv;
974 NYD2_ENTER;
976 rv = STOP;
978 cp = responded_other_text;
979 list_attributes = LIST_NONE;
980 if (*cp == '(') {
981 while (*cp && *cp != ')') {
982 if (*cp == '\\') {
983 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
984 list_attributes |= LIST_NOINFERIORS;
985 cp += 12;
986 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
987 list_attributes |= LIST_NOSELECT;
988 cp += 9;
989 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
990 list_attributes |= LIST_MARKED;
991 cp += 7;
992 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
993 list_attributes |= LIST_UNMARKED;
994 cp += 9;
997 cp++;
999 if (*++cp != ' ')
1000 goto jleave;
1001 while (*cp == ' ')
1002 cp++;
1005 list_hierarchy_delimiter = EOF;
1006 if (*cp == '"') {
1007 if (*++cp == '\\')
1008 cp++;
1009 list_hierarchy_delimiter = *cp++ & 0377;
1010 if (cp[0] != '"' || cp[1] != ' ')
1011 goto jleave;
1012 cp++;
1013 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1014 list_hierarchy_delimiter = EOF;
1015 cp += 3;
1018 while (*cp == ' ')
1019 cp++;
1020 list_name = cp;
1021 while (*cp && *cp != '\r')
1022 cp++;
1023 *cp = '\0';
1024 rv = OKAY;
1025 jleave:
1026 NYD2_LEAVE;
1027 return rv;
1030 static enum okay
1031 imap_finish(struct mailbox *mp)
1033 NYD_ENTER;
1034 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1035 imap_answer(mp, 1);
1036 NYD_LEAVE;
1037 return OKAY;
1040 static void
1041 imap_timer_off(void)
1043 NYD_ENTER;
1044 if (imapkeepalive > 0) {
1045 alarm(0);
1046 safe_signal(SIGALRM, savealrm);
1048 NYD_LEAVE;
1051 static void
1052 imapcatch(int s)
1054 NYD_X; /* Signal handler */
1055 switch (s) {
1056 case SIGINT:
1057 n_err_sighdl(_("Interrupt\n"));
1058 siglongjmp(imapjmp, 1);
1059 /*NOTREACHED*/
1060 case SIGPIPE:
1061 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1062 break;
1066 static void
1067 _imap_maincatch(int s)
1069 NYD_X; /* Signal handler */
1070 UNUSED(s);
1071 if (interrupts++ == 0) {
1072 n_err_sighdl(_("Interrupt\n"));
1073 return;
1075 onintr(0);
1078 static enum okay
1079 imap_noop1(struct mailbox *mp)
1081 char o[LINESIZE];
1082 FILE *queuefp = NULL;
1083 NYD_X;
1085 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1086 IMAP_OUT(o, MB_COMD, return STOP)
1087 IMAP_ANSWER()
1088 return OKAY;
1091 FL char const *
1092 imap_fileof(char const *xcp)
1094 char const *cp = xcp;
1095 int state = 0;
1096 NYD_ENTER;
1098 while (*cp) {
1099 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1100 cp += 3;
1101 state = 1;
1103 if (cp[0] == '/' && state == 1) {
1104 ++cp;
1105 goto jleave;
1107 if (cp[0] == '/') {
1108 cp = xcp;
1109 goto jleave;
1111 ++cp;
1113 jleave:
1114 NYD_LEAVE;
1115 return cp;
1118 FL enum okay
1119 imap_noop(void)
1121 sighandler_type volatile oldint, oldpipe;
1122 enum okay rv = STOP;
1123 NYD_ENTER;
1125 if (mb.mb_type != MB_IMAP)
1126 goto jleave;
1128 imaplock = 1;
1129 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1130 safe_signal(SIGINT, &_imap_maincatch);
1131 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1132 if (sigsetjmp(imapjmp, 1) == 0) {
1133 if (oldpipe != SIG_IGN)
1134 safe_signal(SIGPIPE, imapcatch);
1136 rv = imap_noop1(&mb);
1138 safe_signal(SIGINT, oldint);
1139 safe_signal(SIGPIPE, oldpipe);
1140 imaplock = 0;
1141 jleave:
1142 NYD_LEAVE;
1143 if (interrupts)
1144 onintr(0);
1145 return rv;
1148 static void
1149 rec_queue(enum rec_type rt, unsigned long cnt)
1151 struct record *rp;
1152 NYD_ENTER;
1154 rp = scalloc(1, sizeof *rp);
1155 rp->rec_type = rt;
1156 rp->rec_count = cnt;
1157 if (record && recend) {
1158 recend->rec_next = rp;
1159 recend = rp;
1160 } else
1161 record = recend = rp;
1162 NYD_LEAVE;
1165 static enum okay
1166 rec_dequeue(void)
1168 struct message *omessage;
1169 struct record *rp, *rq;
1170 uiz_t exists = 0, i;
1171 enum okay rv = STOP;
1172 NYD_ENTER;
1174 if (record == NULL)
1175 goto jleave;
1177 omessage = message;
1178 message = smalloc((msgCount+1) * sizeof *message);
1179 if (msgCount)
1180 memcpy(message, omessage, msgCount * sizeof *message);
1181 memset(&message[msgCount], 0, sizeof *message);
1183 rp = record, rq = NULL;
1184 rv = OKAY;
1185 while (rp != NULL) {
1186 switch (rp->rec_type) {
1187 case REC_EXISTS:
1188 exists = rp->rec_count;
1189 break;
1190 case REC_EXPUNGE:
1191 if (rp->rec_count == 0) {
1192 rv = STOP;
1193 break;
1195 if (rp->rec_count > (unsigned long)msgCount) {
1196 if (exists == 0 || rp->rec_count > exists--)
1197 rv = STOP;
1198 break;
1200 if (exists > 0)
1201 exists--;
1202 delcache(&mb, &message[rp->rec_count-1]);
1203 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1204 ((msgCount - rp->rec_count + 1) * sizeof *message));
1205 --msgCount;
1206 /* If the message was part of a collapsed thread,
1207 * the m_collapsed field of one of its ancestors
1208 * should be incremented. It seems hardly possible
1209 * to do this with the current message structure,
1210 * though. The result is that a '+' may be shown
1211 * in the header summary even if no collapsed
1212 * children exists */
1213 break;
1215 if (rq != NULL)
1216 free(rq);
1217 rq = rp;
1218 rp = rp->rec_next;
1220 if (rq != NULL)
1221 free(rq);
1223 record = recend = NULL;
1224 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1225 message = srealloc(message, (exists + 1) * sizeof *message);
1226 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1227 for (i = msgCount; i < exists; ++i)
1228 imap_init(&mb, i);
1229 imap_flags(&mb, msgCount+1, exists);
1230 msgCount = exists;
1233 if (rv == STOP) {
1234 free(message);
1235 message = omessage;
1237 jleave:
1238 NYD_LEAVE;
1239 return rv;
1242 static void
1243 rec_rmqueue(void)
1245 struct record *rp;
1246 NYD_ENTER;
1248 for (rp = record; rp != NULL;) {
1249 struct record *tmp = rp;
1250 rp = rp->rec_next;
1251 free(tmp);
1253 record = recend = NULL;
1254 NYD_LEAVE;
1257 /*ARGSUSED*/
1258 static void
1259 imapalarm(int s)
1261 sighandler_type volatile saveint, savepipe;
1262 NYD_X; /* Signal handler */
1263 UNUSED(s);
1265 if (imaplock++ == 0) {
1266 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1267 safe_signal(SIGINT, &_imap_maincatch);
1268 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1269 if (sigsetjmp(imapjmp, 1)) {
1270 safe_signal(SIGINT, saveint);
1271 safe_signal(SIGPIPE, savepipe);
1272 goto jbrk;
1274 if (savepipe != SIG_IGN)
1275 safe_signal(SIGPIPE, imapcatch);
1276 if (imap_noop1(&mb) != OKAY) {
1277 safe_signal(SIGINT, saveint);
1278 safe_signal(SIGPIPE, savepipe);
1279 goto jleave;
1281 safe_signal(SIGINT, saveint);
1282 safe_signal(SIGPIPE, savepipe);
1284 jbrk:
1285 alarm(imapkeepalive);
1286 jleave:
1287 --imaplock;
1290 static enum okay
1291 imap_preauth(struct mailbox *mp, struct url const *urlp)
1293 NYD_X;
1295 mp->mb_active |= MB_PREAUTH;
1296 imap_answer(mp, 1);
1298 #ifdef HAVE_SSL
1299 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1300 FILE *queuefp = NULL;
1301 char o[LINESIZE];
1303 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1304 IMAP_OUT(o, MB_COMD, return STOP)
1305 IMAP_ANSWER()
1306 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1307 return STOP;
1309 #else
1310 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1311 n_err(_("No SSL support compiled in\n"));
1312 return STOP;
1314 #endif
1316 imap_capability(mp);
1317 return OKAY;
1320 static enum okay
1321 imap_capability(struct mailbox *mp)
1323 char o[LINESIZE];
1324 FILE *queuefp = NULL;
1325 enum okay ok = STOP;
1326 const char *cp;
1327 NYD_X;
1329 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1330 IMAP_OUT(o, MB_COMD, return STOP)
1331 while (mp->mb_active & MB_COMD) {
1332 ok = imap_answer(mp, 0);
1333 if (response_status == RESPONSE_OTHER &&
1334 response_other == CAPABILITY_DATA) {
1335 cp = responded_other_text;
1336 while (*cp) {
1337 while (spacechar(*cp))
1338 ++cp;
1339 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1340 /* RFC 2359 */
1341 mp->mb_flags |= MB_UIDPLUS;
1342 while (*cp && !spacechar(*cp))
1343 ++cp;
1347 return ok;
1350 static enum okay
1351 imap_auth(struct mailbox *mp, struct ccred *ccred)
1353 enum okay rv;
1354 NYD_ENTER;
1356 if (!(mp->mb_active & MB_PREAUTH)) {
1357 rv = OKAY;
1358 goto jleave;
1361 switch (ccred->cc_authtype) {
1362 case AUTHTYPE_LOGIN:
1363 rv = imap_login(mp, ccred);
1364 break;
1365 #ifdef HAVE_MD5
1366 case AUTHTYPE_CRAM_MD5:
1367 rv = imap_cram_md5(mp, ccred);
1368 break;
1369 #endif
1370 #ifdef HAVE_GSSAPI
1371 case AUTHTYPE_GSSAPI:
1372 rv = _imap_gssapi(mp, ccred);
1373 break;
1374 #endif
1375 default:
1376 rv = STOP;
1377 break;
1379 jleave:
1380 NYD_LEAVE;
1381 return rv;
1384 #ifdef HAVE_MD5
1385 static enum okay
1386 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1388 char o[LINESIZE], *cp;
1389 FILE *queuefp = NULL;
1390 enum okay rv = STOP;
1391 NYD_ENTER;
1393 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1394 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1395 imap_answer(mp, 1);
1396 if (response_type != RESPONSE_CONT)
1397 goto jleave;
1399 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1400 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1401 while (mp->mb_active & MB_COMD)
1402 rv = imap_answer(mp, 1);
1403 jleave:
1404 NYD_LEAVE;
1405 return rv;
1407 #endif /* HAVE_MD5 */
1409 static enum okay
1410 imap_login(struct mailbox *mp, struct ccred *ccred)
1412 char o[LINESIZE];
1413 FILE *queuefp = NULL;
1414 enum okay rv = STOP;
1415 NYD_ENTER;
1417 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1418 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1419 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1420 while (mp->mb_active & MB_COMD)
1421 rv = imap_answer(mp, 1);
1422 jleave:
1423 NYD_LEAVE;
1424 return rv;
1427 #ifdef HAVE_GSSAPI
1428 # include "imap_gssapi.h"
1429 #endif
1431 FL enum okay
1432 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1433 enum fedit_mode fm)
1435 char o[LINESIZE];
1436 char const *qname, *cp;
1437 FILE *queuefp;
1438 enum okay ok;
1439 NYD_X;
1440 UNUSED(size);
1442 ok = STOP;
1443 queuefp = NULL;
1445 if((qname = imap_path_quote(mp, mbx)) == NULL)
1446 goto jleave;
1448 ok = OKAY;
1450 mp->mb_uidvalidity = 0;
1451 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1452 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1453 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1454 while (mp->mb_active & MB_COMD) {
1455 ok = imap_answer(mp, 1);
1456 if (response_status != RESPONSE_OTHER &&
1457 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1458 mp->mb_uidvalidity = atol(&cp[13]);
1460 *cnt = (had_exists > 0) ? had_exists : 0;
1461 if (response_status != RESPONSE_OTHER &&
1462 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1463 mp->mb_perm = 0;
1464 jleave:
1465 return ok;
1468 static enum okay
1469 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1471 char o[LINESIZE];
1472 FILE *queuefp = NULL;
1473 char const *cp;
1474 struct message *m;
1475 unsigned x = X, y = Y, n;
1476 NYD_X;
1478 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1479 IMAP_OUT(o, MB_COMD, return STOP)
1480 while (mp->mb_active & MB_COMD) {
1481 imap_answer(mp, 1);
1482 if (response_status == RESPONSE_OTHER &&
1483 response_other == MESSAGE_DATA_FETCH) {
1484 n = responded_other_number;
1485 if (n < x || n > y)
1486 continue;
1487 m = &message[n-1];
1488 m->m_xsize = 0;
1489 } else
1490 continue;
1492 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1493 cp += 5;
1494 while (*cp == ' ')
1495 cp++;
1496 if (*cp == '(')
1497 imap_getflags(cp, &cp, &m->m_flag);
1500 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1501 m->m_uid = strtoul(&cp[4], NULL, 10);
1502 getcache1(mp, m, NEED_UNSPEC, 1);
1503 m->m_flag &= ~MHIDDEN;
1506 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1507 x++;
1508 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1509 y--;
1510 if (x <= y) {
1511 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1512 tag(1), x, y);
1513 IMAP_OUT(o, MB_COMD, return STOP)
1514 while (mp->mb_active & MB_COMD) {
1515 imap_answer(mp, 1);
1516 if (response_status == RESPONSE_OTHER &&
1517 response_other == MESSAGE_DATA_FETCH) {
1518 n = responded_other_number;
1519 if (n < x || n > y)
1520 continue;
1521 m = &message[n-1];
1522 } else
1523 continue;
1524 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1525 m->m_xsize = strtol(&cp[12], NULL, 10);
1526 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1527 m->m_time = imap_read_date_time(&cp[13]);
1531 srelax_hold();
1532 for (n = X; n <= Y; ++n) {
1533 putcache(mp, &message[n-1]);
1534 srelax();
1536 srelax_rele();
1537 return OKAY;
1540 static void
1541 imap_init(struct mailbox *mp, int n)
1543 struct message *m;
1544 NYD_ENTER;
1545 UNUSED(mp);
1547 m = message + n;
1548 m->m_flag = MUSED | MNOFROM;
1549 m->m_block = 0;
1550 m->m_offset = 0;
1551 NYD_LEAVE;
1554 static void
1555 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1557 struct message *omessage = 0;
1558 int i, omsgCount = 0;
1559 enum okay dequeued = STOP;
1560 NYD_ENTER;
1562 if (nmail || transparent) {
1563 omessage = message;
1564 omsgCount = msgCount;
1566 if (nmail)
1567 dequeued = rec_dequeue();
1569 if (had_exists >= 0) {
1570 if (dequeued != OKAY)
1571 msgCount = had_exists;
1572 had_exists = -1;
1574 if (had_expunge >= 0) {
1575 if (dequeued != OKAY)
1576 msgCount -= had_expunge;
1577 had_expunge = -1;
1580 if (nmail && expunged_messages)
1581 printf("Expunged %ld message%s.\n", expunged_messages,
1582 (expunged_messages != 1 ? "s" : ""));
1583 *prevcount = omsgCount - expunged_messages;
1584 expunged_messages = 0;
1585 if (msgCount < 0) {
1586 fputs("IMAP error: Negative message count\n", stderr);
1587 msgCount = 0;
1590 if (dequeued != OKAY) {
1591 message = scalloc(msgCount + 1, sizeof *message);
1592 for (i = 0; i < msgCount; i++)
1593 imap_init(mp, i);
1594 if (!nmail && mp->mb_type == MB_IMAP)
1595 initcache(mp);
1596 if (msgCount > 0)
1597 imap_flags(mp, 1, msgCount);
1598 message[msgCount].m_size = 0;
1599 message[msgCount].m_lines = 0;
1600 rec_rmqueue();
1602 if (nmail || transparent)
1603 transflags(omessage, omsgCount, transparent);
1604 else
1605 setdot(message);
1606 NYD_LEAVE;
1609 FL int
1610 imap_setfile(const char *xserver, enum fedit_mode fm)
1612 struct url url;
1613 int rv;
1614 NYD_ENTER;
1616 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1617 rv = 1;
1618 goto jleave;
1620 if (!ok_blook(v15_compat) &&
1621 (!url.url_had_user || url.url_pass.s != NULL))
1622 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1624 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1625 rv = _imap_setfile1(&url, fm, 0);
1626 jleave:
1627 NYD_LEAVE;
1628 return rv;
1631 static bool_t
1632 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1634 bool_t rv = FAL0;
1635 NYD_ENTER;
1637 if (ok_blook(v15_compat))
1638 rv = ccred_lookup(ccredp, urlp);
1639 else {
1640 char *var, *old,
1641 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1643 if ((var = mbp->mb_imap_pass) != NULL) {
1644 var = savecat("password-", xuhp);
1645 if ((old = vok_vlook(var)) != NULL)
1646 old = sstrdup(old);
1647 vok_vset(var, mbp->mb_imap_pass);
1649 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1650 if (var != NULL) {
1651 if (old != NULL) {
1652 vok_vset(var, old);
1653 free(old);
1654 } else
1655 vok_vclear(var);
1659 NYD_LEAVE;
1660 return rv;
1663 static int
1664 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1665 int volatile transparent)
1667 struct sock so;
1668 struct ccred ccred;
1669 sighandler_type volatile saveint, savepipe;
1670 char const *cp;
1671 int rv;
1672 int volatile prevcount = 0;
1673 enum mbflags same_flags;
1674 NYD_ENTER;
1676 if (fm & FEDIT_NEWMAIL) {
1677 saveint = safe_signal(SIGINT, SIG_IGN);
1678 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1679 if (saveint != SIG_IGN)
1680 safe_signal(SIGINT, imapcatch);
1681 if (savepipe != SIG_IGN)
1682 safe_signal(SIGPIPE, imapcatch);
1683 imaplock = 1;
1684 goto jnmail;
1687 same_flags = mb.mb_flags;
1688 same_imap_account = 0;
1689 if (mb.mb_imap_account != NULL &&
1690 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1691 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1692 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1693 disconnected(mb.mb_imap_account) == 0) {
1694 same_imap_account = 1;
1695 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1697 goto jduppass;
1698 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1699 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1700 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1701 jduppass:
1703 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1707 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1708 free(mb.mb_imap_pass);
1709 mb.mb_imap_pass = NULL;
1711 if (!_imap_getcred(&mb, &ccred, urlp)) {
1712 rv = -1;
1713 goto jleave;
1716 so.s_fd = -1;
1717 if (!same_imap_account) {
1718 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1719 rv = -1;
1720 goto jleave;
1722 } else
1723 so = mb.mb_sock;
1724 if (!transparent)
1725 quit();
1727 if (fm & FEDIT_SYSBOX)
1728 pstate &= ~PS_EDIT;
1729 else
1730 pstate |= PS_EDIT;
1731 if (mb.mb_imap_account != NULL)
1732 free(mb.mb_imap_account);
1733 if (mb.mb_imap_pass != NULL)
1734 free(mb.mb_imap_pass);
1735 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1736 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1737 * TODO object, and mailbox will naturally have an URL and credentials */
1738 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1740 if (!same_imap_account) {
1741 if (mb.mb_sock.s_fd >= 0)
1742 sclose(&mb.mb_sock);
1744 same_imap_account = 0;
1746 if (!transparent) {
1747 if (mb.mb_itf) {
1748 fclose(mb.mb_itf);
1749 mb.mb_itf = NULL;
1751 if (mb.mb_otf) {
1752 fclose(mb.mb_otf);
1753 mb.mb_otf = NULL;
1755 if (mb.mb_imap_mailbox != NULL)
1756 free(mb.mb_imap_mailbox);
1757 assert(urlp->url_path.s != NULL);
1758 imap_delim_init(&mb, urlp);
1759 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1760 initbox(savecatsep(urlp->url_p_eu_h_p,
1761 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1762 mb.mb_imap_mailbox));
1764 mb.mb_type = MB_VOID;
1765 mb.mb_active = MB_NONE;
1767 imaplock = 1;
1768 saveint = safe_signal(SIGINT, SIG_IGN);
1769 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1770 if (sigsetjmp(imapjmp, 1)) {
1771 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1772 sclose(&mb.mb_sock);
1773 safe_signal(SIGINT, saveint);
1774 safe_signal(SIGPIPE, savepipe);
1775 imaplock = 0;
1777 mb.mb_type = MB_VOID;
1778 mb.mb_active = MB_NONE;
1779 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1780 goto jleave;
1782 if (saveint != SIG_IGN)
1783 safe_signal(SIGINT, imapcatch);
1784 if (savepipe != SIG_IGN)
1785 safe_signal(SIGPIPE, imapcatch);
1787 if (mb.mb_sock.s_fd < 0) {
1788 if (disconnected(mb.mb_imap_account)) {
1789 if (cache_setptr(fm, transparent) == STOP)
1790 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1791 goto jdone;
1793 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1794 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1795 savealrm = safe_signal(SIGALRM, imapalarm);
1796 alarm(imapkeepalive);
1800 mb.mb_sock = so;
1801 mb.mb_sock.s_desc = "IMAP";
1802 mb.mb_sock.s_onclose = imap_timer_off;
1803 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1804 sclose(&mb.mb_sock);
1805 imap_timer_off();
1806 safe_signal(SIGINT, saveint);
1807 safe_signal(SIGPIPE, savepipe);
1808 imaplock = 0;
1809 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1810 goto jleave;
1812 } else /* same account */
1813 mb.mb_flags |= same_flags;
1815 if (options & OPT_R_FLAG)
1816 fm |= FEDIT_RDONLY;
1817 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1818 mb.mb_type = MB_IMAP;
1819 cache_dequeue(&mb);
1820 assert(urlp->url_path.s != NULL);
1821 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1822 /*sclose(&mb.mb_sock);
1823 imap_timer_off();*/
1824 safe_signal(SIGINT, saveint);
1825 safe_signal(SIGPIPE, savepipe);
1826 imaplock = 0;
1827 mb.mb_type = MB_VOID;
1828 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1829 goto jleave;
1832 jnmail:
1833 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1834 UNVOLATILE(&prevcount));
1835 jdone:
1836 setmsize(msgCount);
1837 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1838 pstate &= ~PS_SAW_COMMAND;
1839 safe_signal(SIGINT, saveint);
1840 safe_signal(SIGPIPE, savepipe);
1841 imaplock = 0;
1843 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1844 purgecache(&mb, message, msgCount);
1845 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1846 mb.mb_threaded = 0;
1847 c_sort((void*)-1);
1850 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1851 mb.mb_type == MB_CACHE)) {
1852 rv = (msgCount == 0);
1853 goto jleave;
1856 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1857 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1858 !ok_blook(emptystart))
1859 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1860 rv = 1;
1861 goto jleave;
1864 if (fm & FEDIT_NEWMAIL)
1865 newmailinfo(prevcount);
1866 rv = 0;
1867 jleave:
1868 NYD_LEAVE;
1869 return rv;
1872 static int
1873 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1874 int need, const char *head, size_t headsize, long headlines)
1876 char *line = NULL, *lp;
1877 size_t linesize = 0, linelen, size = 0;
1878 int emptyline = 0, lines = 0, excess = 0;
1879 off_t offset;
1880 NYD_ENTER;
1882 fseek(mp->mb_otf, 0L, SEEK_END);
1883 offset = ftell(mp->mb_otf);
1885 if (head)
1886 fwrite(head, 1, headsize, mp->mb_otf);
1888 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1889 lp = line;
1890 if (linelen > expected) {
1891 excess = linelen - expected;
1892 linelen = expected;
1894 /* TODO >>
1895 * Need to mask 'From ' lines. This cannot be done properly
1896 * since some servers pass them as 'From ' and others as
1897 * '>From '. Although one could identify the first kind of
1898 * server in principle, it is not possible to identify the
1899 * second as '>From ' may also come from a server of the
1900 * first type as actual data. So do what is absolutely
1901 * necessary only - mask 'From '.
1903 * If the line is the first line of the message header, it
1904 * is likely a real 'From ' line. In this case, it is just
1905 * ignored since it violates all standards.
1906 * TODO can the latter *really* happen??
1907 * TODO <<
1909 /* Since we simply copy over data without doing any transfer
1910 * encoding reclassification/adjustment we *have* to perform
1911 * RFC 4155 compliant From_ quoting here */
1912 if (emptyline && is_head(lp, linelen, FAL0)) {
1913 fputc('>', mp->mb_otf);
1914 ++size;
1916 emptyline = 0;
1917 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1918 if (linelen > 2) {
1919 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1920 size += linelen - 1;
1921 } else {
1922 emptyline = 1;
1923 ++size;
1925 fputc('\n', mp->mb_otf);
1926 } else {
1927 fwrite(lp, 1, linelen, mp->mb_otf);
1928 size += linelen;
1930 ++lines;
1931 if ((expected -= linelen) <= 0)
1932 break;
1934 if (!emptyline) {
1935 /* This is very ugly; but some IMAP daemons don't end a
1936 * message with \r\n\r\n, and we need \n\n for mbox format */
1937 fputc('\n', mp->mb_otf);
1938 ++lines;
1939 ++size;
1941 fflush(mp->mb_otf);
1943 if (m != NULL) {
1944 m->m_size = size + headsize;
1945 m->m_lines = lines + headlines;
1946 m->m_block = mailx_blockof(offset);
1947 m->m_offset = mailx_offsetof(offset);
1948 switch (need) {
1949 case NEED_HEADER:
1950 m->m_have |= HAVE_HEADER;
1951 break;
1952 case NEED_BODY:
1953 m->m_have |= HAVE_HEADER | HAVE_BODY;
1954 m->m_xlines = m->m_lines;
1955 m->m_xsize = m->m_size;
1956 break;
1959 free(line);
1960 NYD_LEAVE;
1961 return excess;
1964 static void
1965 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1966 const char *head, size_t headsize, long headlines)
1968 off_t offset;
1969 size_t len;
1970 NYD_ENTER;
1972 len = strlen(str);
1973 fseek(mp->mb_otf, 0L, SEEK_END);
1974 offset = ftell(mp->mb_otf);
1975 if (head)
1976 fwrite(head, 1, headsize, mp->mb_otf);
1977 if (len > 0) {
1978 fwrite(str, 1, len, mp->mb_otf);
1979 fputc('\n', mp->mb_otf);
1980 ++len;
1982 fflush(mp->mb_otf);
1984 if (m != NULL) {
1985 m->m_size = headsize + len;
1986 m->m_lines = headlines + 1;
1987 m->m_block = mailx_blockof(offset);
1988 m->m_offset = mailx_offsetof(offset);
1989 m->m_have |= HAVE_HEADER | HAVE_BODY;
1990 m->m_xlines = m->m_lines;
1991 m->m_xsize = m->m_size;
1993 NYD_LEAVE;
1996 static enum okay
1997 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1999 char o[LINESIZE];
2000 struct message mt;
2001 sighandler_type volatile saveint, savepipe;
2002 char * volatile head;
2003 char const *cp, *loc, * volatile item, * volatile resp;
2004 size_t expected;
2005 size_t volatile headsize;
2006 int number;
2007 FILE *queuefp;
2008 long volatile headlines;
2009 long n;
2010 ul_i volatile u;
2011 enum okay ok;
2012 NYD_X;
2014 saveint = savepipe = SIG_IGN;
2015 head = NULL;
2016 cp = loc = item = resp = NULL;
2017 headsize = 0;
2018 number = (int)PTR2SIZE(m - message + 1);
2019 queuefp = NULL;
2020 headlines = 0;
2021 n = -1;
2022 u = 0;
2023 ok = STOP;
2025 if (getcache(mp, m, need) == OKAY)
2026 return OKAY;
2027 if (mp->mb_type == MB_CACHE) {
2028 n_err(_("Message %lu not available\n"), (ul_i)number);
2029 return STOP;
2032 if (mp->mb_sock.s_fd < 0) {
2033 n_err(_("IMAP connection closed\n"));
2034 return STOP;
2037 switch (need) {
2038 case NEED_HEADER:
2039 resp = item = "RFC822.HEADER";
2040 break;
2041 case NEED_BODY:
2042 item = "BODY.PEEK[]";
2043 resp = "BODY[]";
2044 if (m->m_flag & HAVE_HEADER && m->m_size) {
2045 char *hdr = smalloc(m->m_size);
2046 fflush(mp->mb_otf);
2047 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2048 SEEK_SET) < 0 ||
2049 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2050 free(hdr);
2051 break;
2053 head = hdr;
2054 headsize = m->m_size;
2055 headlines = m->m_lines;
2056 item = "BODY.PEEK[TEXT]";
2057 resp = "BODY[TEXT]";
2059 break;
2060 case NEED_UNSPEC:
2061 return STOP;
2064 imaplock = 1;
2065 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2066 if (sigsetjmp(imapjmp, 1)) {
2067 safe_signal(SIGINT, saveint);
2068 safe_signal(SIGPIPE, savepipe);
2069 imaplock = 0;
2070 return STOP;
2072 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2073 safe_signal(SIGINT, &_imap_maincatch);
2074 if (savepipe != SIG_IGN)
2075 safe_signal(SIGPIPE, imapcatch);
2077 if (m->m_uid)
2078 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2079 tag(1), m->m_uid, item);
2080 else {
2081 if (check_expunged() == STOP)
2082 goto out;
2083 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2085 IMAP_OUT(o, MB_COMD, goto out)
2086 for (;;) {
2087 ok = imap_answer(mp, 1);
2088 if (ok == STOP)
2089 break;
2090 if (response_status != RESPONSE_OTHER ||
2091 response_other != MESSAGE_DATA_FETCH)
2092 continue;
2093 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2094 continue;
2095 if (m->m_uid) {
2096 if ((cp = asccasestr(responded_other_text, "UID "))) {
2097 u = atol(&cp[4]);
2098 n = 0;
2099 } else {
2100 n = -1;
2101 u = 0;
2103 } else
2104 n = responded_other_number;
2105 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2106 if (m->m_uid ? m->m_uid != u : n != number)
2107 continue;
2108 if ((cp = strchr(loc, '"')) != NULL) {
2109 cp = imap_unquotestr(cp);
2110 imap_putstr(mp, m, cp, head, headsize, headlines);
2111 } else {
2112 m->m_have |= HAVE_HEADER|HAVE_BODY;
2113 m->m_xlines = m->m_lines;
2114 m->m_xsize = m->m_size;
2116 goto out;
2118 expected = atol(&cp[1]);
2119 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2120 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2121 continue;
2123 mt = *m;
2124 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2125 if (n >= 0) {
2126 commitmsg(mp, m, &mt, mt.m_have);
2127 break;
2129 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2130 if (options & OPT_VERBVERB)
2131 fputs(imapbuf, stderr);
2132 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2133 u = atol(&cp[4]);
2134 if (u == m->m_uid) {
2135 commitmsg(mp, m, &mt, mt.m_have);
2136 break;
2141 out:
2142 while (mp->mb_active & MB_COMD)
2143 ok = imap_answer(mp, 1);
2145 if (saveint != SIG_IGN)
2146 safe_signal(SIGINT, saveint);
2147 if (savepipe != SIG_IGN)
2148 safe_signal(SIGPIPE, savepipe);
2149 imaplock--;
2151 if (ok == OKAY)
2152 putcache(mp, m);
2153 if (head != NULL)
2154 free(head);
2155 if (interrupts)
2156 onintr(0);
2157 return ok;
2160 FL enum okay
2161 imap_header(struct message *m)
2163 enum okay rv;
2164 NYD_ENTER;
2166 rv = imap_get(&mb, m, NEED_HEADER);
2167 NYD_LEAVE;
2168 return rv;
2172 FL enum okay
2173 imap_body(struct message *m)
2175 enum okay rv;
2176 NYD_ENTER;
2178 rv = imap_get(&mb, m, NEED_BODY);
2179 NYD_LEAVE;
2180 return rv;
2183 static void
2184 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2185 enum havespec have)
2187 NYD_ENTER;
2188 tomp->m_size = frommp->m_size;
2189 tomp->m_lines = frommp->m_lines;
2190 tomp->m_block = frommp->m_block;
2191 tomp->m_offset = frommp->m_offset;
2192 tomp->m_have = have;
2193 if (have & HAVE_BODY) {
2194 tomp->m_xlines = frommp->m_lines;
2195 tomp->m_xsize = frommp->m_size;
2197 putcache(mp, tomp);
2198 NYD_LEAVE;
2201 static enum okay
2202 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2204 /* bot > topp */
2205 char o[LINESIZE];
2206 char const *cp;
2207 struct message mt;
2208 size_t expected;
2209 int n = 0, u;
2210 FILE *queuefp = NULL;
2211 enum okay ok;
2212 NYD_X;
2214 if (m[bot].m_uid)
2215 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2216 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2217 else {
2218 if (check_expunged() == STOP)
2219 return STOP;
2220 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2221 tag(1), bot, topp);
2223 IMAP_OUT(o, MB_COMD, return STOP)
2225 srelax_hold();
2226 for (;;) {
2227 ok = imap_answer(mp, 1);
2228 if (response_status != RESPONSE_OTHER)
2229 break;
2230 if (response_other != MESSAGE_DATA_FETCH)
2231 continue;
2232 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2233 srelax_rele();
2234 return STOP;
2236 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2237 continue;
2238 expected = atol(&cp[1]);
2239 if (m[bot-1].m_uid) {
2240 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2241 u = atoi(&cp[4]);
2242 for (n = bot; n <= topp; n++)
2243 if ((unsigned long)u == m[n-1].m_uid)
2244 break;
2245 if (n > topp) {
2246 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2247 continue;
2249 } else
2250 n = -1;
2251 } else {
2252 n = responded_other_number;
2253 if (n <= 0 || n > msgCount) {
2254 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2255 continue;
2258 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2259 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
2260 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
2261 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2262 if (options & OPT_VERBVERB)
2263 fputs(imapbuf, stderr);
2264 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2265 u = atoi(&cp[4]);
2266 for (n = bot; n <= topp; n++)
2267 if ((unsigned long)u == m[n-1].m_uid)
2268 break;
2269 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
2270 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
2271 n = 0;
2274 srelax();
2276 srelax_rele();
2278 while (mp->mb_active & MB_COMD)
2279 ok = imap_answer(mp, 1);
2280 return ok;
2283 FL void
2284 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2286 sighandler_type saveint, savepipe;
2287 /*enum okay ok = STOP;*/
2288 int i, chunk = 256;
2289 NYD_X;
2291 if (mb.mb_type == MB_CACHE)
2292 return;
2293 if (bot < 1)
2294 bot = 1;
2295 if (topp > msgCount)
2296 topp = msgCount;
2297 for (i = bot; i < topp; i++) {
2298 if (message[i-1].m_have & HAVE_HEADER ||
2299 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2300 bot = i+1;
2301 else
2302 break;
2304 for (i = topp; i > bot; i--) {
2305 if (message[i-1].m_have & HAVE_HEADER ||
2306 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2307 topp = i-1;
2308 else
2309 break;
2311 if (bot >= topp)
2312 return;
2314 imaplock = 1;
2315 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2316 safe_signal(SIGINT, &_imap_maincatch);
2317 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2318 if (sigsetjmp(imapjmp, 1) == 0) {
2319 if (savepipe != SIG_IGN)
2320 safe_signal(SIGPIPE, imapcatch);
2322 for (i = bot; i <= topp; i += chunk) {
2323 int j = i + chunk - 1;
2324 j = MIN(j, topp);
2325 if (visible(message + j))
2326 /*ok = */imap_fetchheaders(&mb, message, i, j);
2327 if (interrupts)
2328 onintr(0); /* XXX imaplock? */
2331 safe_signal(SIGINT, saveint);
2332 safe_signal(SIGPIPE, savepipe);
2333 imaplock = 0;
2336 static enum okay
2337 __imap_exit(struct mailbox *mp)
2339 char o[LINESIZE];
2340 FILE *queuefp = NULL;
2341 NYD_X;
2343 mp->mb_active |= MB_BYE;
2344 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2345 IMAP_OUT(o, MB_COMD, return STOP)
2346 IMAP_ANSWER()
2347 return OKAY;
2350 static enum okay
2351 imap_exit(struct mailbox *mp)
2353 enum okay rv;
2354 NYD_ENTER;
2356 rv = __imap_exit(mp);
2357 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2358 free(mp->mb_imap_pass);
2359 free(mp->mb_imap_account);
2360 free(mp->mb_imap_mailbox);
2361 if (mp->mb_cache_directory != NULL)
2362 free(mp->mb_cache_directory);
2363 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2364 mp->mb_imap_account =
2365 mp->mb_imap_mailbox =
2366 mp->mb_cache_directory = "";
2367 #else
2368 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2369 mp->mb_imap_mailbox = NULL;
2370 mp->mb_cache_directory = NULL;
2371 #endif
2372 #endif
2373 sclose(&mp->mb_sock);
2374 NYD_LEAVE;
2375 return rv;
2378 static enum okay
2379 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2381 NYD_ENTER;
2382 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2383 if (mp->mb_type == MB_IMAP)
2384 delcache(mp, m);
2385 NYD_LEAVE;
2386 return OKAY;
2389 static enum okay
2390 imap_close(struct mailbox *mp)
2392 char o[LINESIZE];
2393 FILE *queuefp = NULL;
2394 NYD_X;
2396 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2397 IMAP_OUT(o, MB_COMD, return STOP)
2398 IMAP_ANSWER()
2399 return OKAY;
2402 static enum okay
2403 imap_update(struct mailbox *mp)
2405 struct message *m;
2406 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2407 NYD_ENTER;
2409 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
2410 holdbits();
2411 c = 0;
2412 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2413 if (m->m_flag & MBOX)
2414 ++c;
2415 if (c > 0)
2416 if (makembox() == STOP)
2417 goto jbypass;
2420 gotcha = held = 0;
2421 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2422 if (mp->mb_perm == 0)
2423 dodel = 0;
2424 else if (pstate & PS_EDIT)
2425 dodel = ((m->m_flag & MDELETED) != 0);
2426 else
2427 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2429 /* Fetch the result after around each 800 STORE commands
2430 * sent (approx. 32k data sent). Otherwise, servers will
2431 * try to flush the return queue at some point, leading
2432 * to a deadlock if we are still writing commands but not
2433 * reading their results */
2434 needstat = stored > 0 && stored % 800 == 0;
2435 /* Even if this message has been deleted, continue
2436 * to set further flags. This is necessary to support
2437 * Gmail semantics, where "delete" actually means
2438 * "archive", and the flags are applied to the copy
2439 * in "All Mail" */
2440 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2441 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2442 stored++;
2444 if (m->m_flag & MFLAG) {
2445 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2446 stored++;
2448 if (m->m_flag & MUNFLAG) {
2449 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2450 stored++;
2452 if (m->m_flag & MANSWER) {
2453 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2454 stored++;
2456 if (m->m_flag & MUNANSWER) {
2457 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2458 stored++;
2460 if (m->m_flag & MDRAFT) {
2461 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2462 stored++;
2464 if (m->m_flag & MUNDRAFT) {
2465 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2466 stored++;
2469 if (dodel) {
2470 imap_delete(mp, m-message+1, m, needstat);
2471 stored++;
2472 gotcha++;
2473 } else if (mp->mb_type != MB_CACHE ||
2474 (!(pstate & PS_EDIT) &&
2475 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2476 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2477 (MPRESERVE | MTOUCH) ||
2478 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
2479 held++;
2480 if (m->m_flag & MNEW) {
2481 m->m_flag &= ~MNEW;
2482 m->m_flag |= MSTATUS;
2485 jbypass:
2486 if (gotcha)
2487 imap_close(mp);
2489 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2490 if (!(m->m_flag & MUNLINKED) &&
2491 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2492 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2493 putcache(mp, m);
2494 modflags++;
2497 /* XXX should be readonly (but our IMAP code is weird...) */
2498 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST)) &&
2499 mb.mb_perm != 0) {
2500 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2501 printf(_("\"%s\" "), displayname);
2502 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2503 ? _("complete\n") : _("updated.\n"));
2504 } else if (held && !(pstate & PS_EDIT)) {
2505 if (held == 1)
2506 printf(_("Held 1 message in %s\n"), displayname);
2507 else
2508 printf(_("Held %d messages in %s\n"), held, displayname);
2510 fflush(stdout);
2512 NYD_LEAVE;
2513 return OKAY;
2516 FL void
2517 imap_quit(void)
2519 sighandler_type volatile saveint, savepipe;
2520 NYD_ENTER;
2522 if (mb.mb_type == MB_CACHE) {
2523 imap_update(&mb);
2524 goto jleave;
2527 if (mb.mb_sock.s_fd < 0) {
2528 n_err(_("IMAP connection closed\n"));
2529 goto jleave;
2532 imaplock = 1;
2533 saveint = safe_signal(SIGINT, SIG_IGN);
2534 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2535 if (sigsetjmp(imapjmp, 1)) {
2536 safe_signal(SIGINT, saveint);
2537 safe_signal(SIGPIPE, saveint);
2538 imaplock = 0;
2539 goto jleave;
2541 if (saveint != SIG_IGN)
2542 safe_signal(SIGINT, imapcatch);
2543 if (savepipe != SIG_IGN)
2544 safe_signal(SIGPIPE, imapcatch);
2546 imap_update(&mb);
2547 if (!same_imap_account)
2548 imap_exit(&mb);
2550 safe_signal(SIGINT, saveint);
2551 safe_signal(SIGPIPE, savepipe);
2552 imaplock = 0;
2553 jleave:
2554 NYD_LEAVE;
2557 static enum okay
2558 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2559 int needstat)
2561 char o[LINESIZE];
2562 FILE *queuefp = NULL;
2563 NYD_X;
2565 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2566 return STOP;
2567 if (m->m_uid)
2568 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2569 tag(1), m->m_uid, c, sp);
2570 else {
2571 if (check_expunged() == STOP)
2572 return STOP;
2573 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2575 IMAP_OUT(o, MB_COMD, return STOP)
2576 if (needstat)
2577 IMAP_ANSWER()
2578 else
2579 mb.mb_active &= ~MB_COMD;
2580 if (queuefp != NULL)
2581 Fclose(queuefp);
2582 return OKAY;
2585 FL enum okay
2586 imap_undelete(struct message *m, int n)
2588 enum okay rv;
2589 NYD_ENTER;
2591 rv = imap_unstore(m, n, "\\Deleted");
2592 NYD_LEAVE;
2593 return rv;
2596 FL enum okay
2597 imap_unread(struct message *m, int n)
2599 enum okay rv;
2600 NYD_ENTER;
2602 rv = imap_unstore(m, n, "\\Seen");
2603 NYD_LEAVE;
2604 return rv;
2607 static enum okay
2608 imap_unstore(struct message *m, int n, const char *flag)
2610 sighandler_type saveint, savepipe;
2611 enum okay rv = STOP;
2612 NYD_ENTER;
2614 imaplock = 1;
2615 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2616 safe_signal(SIGINT, &_imap_maincatch);
2617 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2618 if (sigsetjmp(imapjmp, 1) == 0) {
2619 if (savepipe != SIG_IGN)
2620 safe_signal(SIGPIPE, imapcatch);
2622 rv = imap_store(&mb, m, n, '-', flag, 1);
2624 safe_signal(SIGINT, saveint);
2625 safe_signal(SIGPIPE, savepipe);
2626 imaplock = 0;
2628 NYD_LEAVE;
2629 if (interrupts)
2630 onintr(0);
2631 return rv;
2634 static const char *
2635 tag(int new)
2637 static char ts[20];
2638 static long n;
2639 NYD2_ENTER;
2641 if (new)
2642 ++n;
2643 snprintf(ts, sizeof ts, "T%lu", n);
2644 NYD2_LEAVE;
2645 return ts;
2648 FL int
2649 c_imapcodec(void *v){
2650 bool_t err;
2651 char const **argv, *cp, *res;
2652 NYD_ENTER;
2654 if(is_prefix(cp = *(argv = v), "encode")){
2655 while((cp = *++argv) != NULL){
2656 res = imap_path_normalize(NULL, cp);
2657 res = imap_path_encode(res, &err);
2658 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2659 cp, strlen(cp), (err ? "ERROR " : ""), res, strlen(res));
2661 }else if(is_prefix(cp, "decode")){
2662 struct str in, out;
2664 while((cp = *++argv) != NULL){
2665 res = imap_path_normalize(NULL, cp);
2666 res = imap_path_decode(res, &err);
2667 in.l = strlen(in.s = UNCONST(res)); /* logical */
2668 makeprint(&in, &out);
2669 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2670 cp, strlen(cp), (err ? "ERROR " : ""), out.s, in.l);
2671 free(out.s);
2673 }else{
2674 n_err(_("`imapcodec': invalid subcommand: %s\n"), *argv);
2675 cp = NULL;
2677 NYD_LEAVE;
2678 return (cp != NULL ? OKAY : STOP);
2681 FL int
2682 c_imap_imap(void *vp)
2684 char o[LINESIZE];
2685 sighandler_type saveint, savepipe;
2686 struct mailbox *mp = &mb;
2687 FILE *queuefp = NULL;
2688 enum okay volatile ok = STOP;
2689 NYD_X;
2691 if (mp->mb_type != MB_IMAP) {
2692 printf("Not operating on an IMAP mailbox.\n");
2693 return 1;
2695 imaplock = 1;
2696 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2697 safe_signal(SIGINT, &_imap_maincatch);
2698 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2699 if (sigsetjmp(imapjmp, 1) == 0) {
2700 if (savepipe != SIG_IGN)
2701 safe_signal(SIGPIPE, imapcatch);
2703 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2704 IMAP_OUT(o, MB_COMD, goto out)
2705 while (mp->mb_active & MB_COMD) {
2706 ok = imap_answer(mp, 0);
2707 fputs(responded_text, stdout);
2710 out:
2711 safe_signal(SIGINT, saveint);
2712 safe_signal(SIGPIPE, savepipe);
2713 imaplock = 0;
2715 if (interrupts)
2716 onintr(0);
2717 return ok != OKAY;
2720 FL int
2721 imap_newmail(int nmail)
2723 NYD_ENTER;
2725 if (nmail && had_exists < 0 && had_expunge < 0) {
2726 imaplock = 1;
2727 imap_noop();
2728 imaplock = 0;
2731 if (had_exists == msgCount && had_expunge < 0)
2732 /* Some servers always respond with EXISTS to NOOP. If
2733 * the mailbox has been changed but the number of messages
2734 * has not, an EXPUNGE must also had been sent; otherwise,
2735 * nothing has changed */
2736 had_exists = -1;
2737 NYD_LEAVE;
2738 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2741 static char *
2742 imap_putflags(int f)
2744 const char *cp;
2745 char *buf, *bp;
2746 NYD2_ENTER;
2748 bp = buf = salloc(100);
2749 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2750 *bp++ = '(';
2751 if (f & MREAD) {
2752 if (bp[-1] != '(')
2753 *bp++ = ' ';
2754 for (cp = "\\Seen"; *cp; cp++)
2755 *bp++ = *cp;
2757 if (f & MFLAGGED) {
2758 if (bp[-1] != '(')
2759 *bp++ = ' ';
2760 for (cp = "\\Flagged"; *cp; cp++)
2761 *bp++ = *cp;
2763 if (f & MANSWERED) {
2764 if (bp[-1] != '(')
2765 *bp++ = ' ';
2766 for (cp = "\\Answered"; *cp; cp++)
2767 *bp++ = *cp;
2769 if (f & MDRAFT) {
2770 if (bp[-1] != '(')
2771 *bp++ = ' ';
2772 for (cp = "\\Draft"; *cp; cp++)
2773 *bp++ = *cp;
2775 *bp++ = ')';
2776 *bp++ = ' ';
2778 *bp = '\0';
2779 NYD2_LEAVE;
2780 return buf;
2783 static void
2784 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2786 NYD2_ENTER;
2787 while (*cp != ')') {
2788 if (*cp == '\\') {
2789 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2790 *f |= MREAD;
2791 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2792 *f |= MNEW;
2793 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2794 *f |= MDELETED;
2795 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2796 *f |= MFLAGGED;
2797 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2798 *f |= MANSWERED;
2799 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2800 *f |= MDRAFTED;
2802 cp++;
2805 if (xp != NULL)
2806 *xp = cp;
2807 NYD2_LEAVE;
2810 static enum okay
2811 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2812 long xsize, enum mflag flag, time_t t)
2814 char o[LINESIZE], *buf;
2815 size_t bufsize, buflen, cnt;
2816 long size, lines, ysize;
2817 char const *qname;
2818 bool_t twice;
2819 FILE *queuefp;
2820 enum okay rv;
2821 NYD_ENTER;
2823 rv = STOP;
2824 queuefp = NULL;
2825 twice = FAL0;
2826 buf = NULL;
2828 if((qname = imap_path_quote(mp, name)) == NULL)
2829 goto jleave;
2831 if (mp->mb_type == MB_CACHE) {
2832 queuefp = cache_queue(mp);
2833 if (queuefp == NULL) {
2834 buf = NULL;
2835 goto jleave;
2837 rv = OKAY;
2840 buf = smalloc(bufsize = LINESIZE);
2841 buflen = 0;
2842 jagain:
2843 size = xsize;
2844 cnt = fsize(fp);
2845 if (fseek(fp, off1, SEEK_SET) < 0) {
2846 rv = STOP;
2847 goto jleave;
2850 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2851 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2852 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2853 while (mp->mb_active & MB_COMD) {
2854 rv = imap_answer(mp, twice);
2855 if (response_type == RESPONSE_CONT)
2856 break;
2859 if (mp->mb_type != MB_CACHE && rv == STOP) {
2860 if (!twice)
2861 goto jtrycreate;
2862 else
2863 goto jleave;
2866 lines = ysize = 0;
2867 while (size > 0) {
2868 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2869 lines++;
2870 ysize += buflen;
2871 buf[buflen - 1] = '\r';
2872 buf[buflen] = '\n';
2873 if (mp->mb_type != MB_CACHE)
2874 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2875 else if (queuefp)
2876 fwrite(buf, 1, buflen+1, queuefp);
2877 size -= buflen + 1;
2879 if (mp->mb_type != MB_CACHE)
2880 swrite(&mp->mb_sock, "\r\n");
2881 else if (queuefp)
2882 fputs("\r\n", queuefp);
2883 while (mp->mb_active & MB_COMD) {
2884 rv = imap_answer(mp, 0);
2885 if (response_status == RESPONSE_NO /*&&
2886 ascncasecmp(responded_text,
2887 "[TRYCREATE] ", 12) == 0*/) {
2888 jtrycreate:
2889 if (twice) {
2890 rv = STOP;
2891 goto jleave;
2893 twice = TRU1;
2894 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2895 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2896 while (mp->mb_active & MB_COMD)
2897 rv = imap_answer(mp, 1);
2898 if (rv == STOP)
2899 goto jleave;
2900 imap_created_mailbox++;
2901 goto jagain;
2902 } else if (rv != OKAY)
2903 n_err(_("IMAP error: %s"), responded_text);
2904 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2905 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2907 jleave:
2908 if (queuefp != NULL)
2909 Fclose(queuefp);
2910 if (buf != NULL)
2911 free(buf);
2912 NYD_LEAVE;
2913 return rv;
2916 static enum okay
2917 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2919 char *buf, *bp, *lp;
2920 size_t bufsize, buflen, cnt;
2921 off_t off1 = -1, offs;
2922 int flag;
2923 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2924 time_t tim;
2925 long size;
2926 enum okay rv;
2927 NYD_ENTER;
2929 buf = smalloc(bufsize = LINESIZE);
2930 buflen = 0;
2931 cnt = fsize(fp);
2932 offs = ftell(fp);
2933 time(&tim);
2934 size = 0;
2936 for (flag = MNEW, state = _NLSEP;;) {
2937 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2939 if (bp == NULL ||
2940 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2941 is_head(buf, buflen, FAL0))) {
2942 if (off1 != (off_t)-1) {
2943 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2944 if (rv == STOP)
2945 goto jleave;
2946 fseek(fp, offs+buflen, SEEK_SET);
2948 off1 = offs + buflen;
2949 size = 0;
2950 flag = MNEW;
2951 state = _INHEAD;
2952 if (bp == NULL)
2953 break;
2954 tim = unixtime(buf);
2955 } else
2956 size += buflen+1;
2957 offs += buflen;
2959 state &= ~_NLSEP;
2960 if (buf[0] == '\n') {
2961 state &= ~_INHEAD;
2962 state |= _NLSEP;
2963 } else if (state & _INHEAD) {
2964 if (ascncasecmp(buf, "status", 6) == 0) {
2965 lp = &buf[6];
2966 while (whitechar(*lp))
2967 lp++;
2968 if (*lp == ':')
2969 while (*++lp != '\0')
2970 switch (*lp) {
2971 case 'R':
2972 flag |= MREAD;
2973 break;
2974 case 'O':
2975 flag &= ~MNEW;
2976 break;
2978 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2979 lp = &buf[8];
2980 while (whitechar(*lp))
2981 lp++;
2982 if (*lp == ':')
2983 while (*++lp != '\0')
2984 switch (*lp) {
2985 case 'F':
2986 flag |= MFLAGGED;
2987 break;
2988 case 'A':
2989 flag |= MANSWERED;
2990 break;
2991 case 'T':
2992 flag |= MDRAFTED;
2993 break;
2998 rv = OKAY;
2999 jleave:
3000 free(buf);
3001 NYD_LEAVE;
3002 return rv;
3005 FL enum okay
3006 imap_append(const char *xserver, FILE *fp)
3008 sighandler_type volatile saveint, savepipe;
3009 struct url url;
3010 struct ccred ccred;
3011 enum okay rv = STOP;
3012 NYD_ENTER;
3014 if (!url_parse(&url, CPROTO_IMAP, xserver))
3015 goto j_leave;
3016 if (!ok_blook(v15_compat) &&
3017 (!url.url_had_user || url.url_pass.s != NULL))
3018 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3019 assert(url.url_path.s != NULL);
3021 imaplock = 1;
3022 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3023 safe_signal(SIGINT, &_imap_maincatch);
3024 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3025 if (sigsetjmp(imapjmp, 1))
3026 goto jleave;
3027 if (savepipe != SIG_IGN)
3028 safe_signal(SIGPIPE, imapcatch);
3030 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3031 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3032 rv = imap_append0(&mb, url.url_path.s, fp);
3033 } else {
3034 struct mailbox mx;
3036 memset(&mx, 0, sizeof mx);
3038 if (!_imap_getcred(&mx, &ccred, &url))
3039 goto jleave;
3041 imap_delim_init(&mx, &url);
3042 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3044 if (disconnected(url.url_p_eu_h_p) == 0) {
3045 if (!sopen(&mx.mb_sock, &url))
3046 goto jfail;
3047 mx.mb_sock.s_desc = "IMAP";
3048 mx.mb_type = MB_IMAP;
3049 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3050 /* TODO the code now did
3051 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3052 * TODO though imap_mailbox is sfree()d and mbx
3053 * TODO is possibly even a constant
3054 * TODO i changed this to sstrdup() sofar, as is used
3055 * TODO somewhere else in this file for this! */
3056 if (imap_preauth(&mx, &url) != OKAY ||
3057 imap_auth(&mx, &ccred) != OKAY) {
3058 sclose(&mx.mb_sock);
3059 goto jfail;
3061 rv = imap_append0(&mx, url.url_path.s, fp);
3062 imap_exit(&mx);
3063 } else {
3064 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3065 mx.mb_type = MB_CACHE;
3066 rv = imap_append0(&mx, url.url_path.s, fp);
3068 jfail:
3072 jleave:
3073 safe_signal(SIGINT, saveint);
3074 safe_signal(SIGPIPE, savepipe);
3075 imaplock = 0;
3076 j_leave:
3077 NYD_LEAVE;
3078 if (interrupts)
3079 onintr(0);
3080 return rv;
3083 static enum okay
3084 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3085 struct list_item **lend, int level)
3087 char o[LINESIZE], *cp;
3088 struct list_item *lp;
3089 const char *qname, *bp;
3090 FILE *queuefp;
3091 enum okay ok;
3092 NYD_X;
3094 ok = STOP;
3095 queuefp = NULL;
3097 if((qname = imap_path_quote(mp, base)) == NULL)
3098 goto jleave;
3100 *list = *lend = NULL;
3101 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3102 IMAP_OUT(o, MB_COMD, goto jleave)
3103 while (mp->mb_active & MB_COMD) {
3104 ok = imap_answer(mp, 1);
3105 if (response_status == RESPONSE_OTHER &&
3106 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3107 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3108 lp = csalloc(1, sizeof *lp);
3109 lp->l_name = cp;
3110 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3111 ++cp;
3112 lp->l_base = *cp ? cp : savestr(base);
3113 lp->l_attr = list_attributes;
3114 lp->l_level = level+1;
3115 lp->l_delim = list_hierarchy_delimiter;
3116 if (*list && *lend) {
3117 (*lend)->l_next = lp;
3118 *lend = lp;
3119 } else
3120 *list = *lend = lp;
3123 jleave:
3124 return ok;
3127 static enum okay
3128 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3130 struct list_item *list, *lend, *lp, *lx, *ly;
3131 int n, depth;
3132 const char *bp;
3133 char *cp;
3134 enum okay rv;
3135 NYD_ENTER;
3137 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3138 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3139 goto jleave;
3140 rv = OKAY;
3141 if (list == NULL || lend == NULL)
3142 goto jleave;
3144 for (lp = list; lp; lp = lp->l_next)
3145 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3146 !(lp->l_attr & LIST_NOINFERIORS)) {
3147 cp = salloc((n = strlen(lp->l_name)) + 2);
3148 memcpy(cp, lp->l_name, n);
3149 cp[n] = lp->l_delim;
3150 cp[n+1] = '\0';
3151 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3152 lp->l_has_children = 1;
3153 if (strcmp(cp, lx->l_name) == 0)
3154 lx = lx->l_next;
3155 if (lx) {
3156 lend->l_next = lx;
3157 lend = ly;
3162 for (lp = list; lp; lp = lp->l_next) {
3163 if (strip) {
3164 cp = lp->l_name;
3165 for (bp = base; *bp && *bp == *cp; bp++)
3166 cp++;
3167 } else
3168 cp = lp->l_name;
3169 if (!(lp->l_attr & LIST_NOSELECT))
3170 fprintf(fp, "%s\n", *cp ? cp : base);
3171 else if (lp->l_has_children == 0)
3172 fprintf(fp, "%s%c\n", *cp ? cp : base,
3173 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3175 jleave:
3176 NYD_LEAVE;
3177 return rv;
3180 FL void
3181 imap_folders(const char * volatile name, int strip)
3183 sighandler_type saveint, savepipe;
3184 const char *fold, *cp, *sp;
3185 FILE * volatile fp;
3186 NYD_ENTER;
3188 cp = protbase(name);
3189 sp = mb.mb_imap_account;
3190 if (sp == NULL || strcmp(cp, sp)) {
3191 n_err(
3192 _("Cannot perform `folders' but when on the very IMAP "
3193 "account; the current one is\n `%s' -- "
3194 "try `folders @'\n"),
3195 (sp != NULL ? sp : _("[NONE]")));
3196 goto jleave;
3199 fold = imap_fileof(name);
3200 if (options & OPT_TTYOUT) {
3201 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
3202 0600)) == NULL) {
3203 n_perr(_("tmpfile"), 0);
3204 goto jleave;
3206 } else
3207 fp = stdout;
3209 imaplock = 1;
3210 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3211 safe_signal(SIGINT, &_imap_maincatch);
3212 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3213 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3214 goto junroll;
3215 if (savepipe != SIG_IGN)
3216 safe_signal(SIGPIPE, imapcatch);
3218 if (mb.mb_type == MB_CACHE)
3219 cache_list(&mb, fold, strip, fp);
3220 else
3221 imap_list(&mb, fold, strip, fp);
3223 imaplock = 0;
3224 if (interrupts) {
3225 if (options & OPT_TTYOUT)
3226 Fclose(fp);
3227 goto jleave;
3229 fflush(fp);
3231 if (options & OPT_TTYOUT) {
3232 rewind(fp);
3233 if (fsize(fp) > 0)
3234 dopr(fp);
3235 else
3236 n_err(_("Folder not found\n"));
3238 junroll:
3239 safe_signal(SIGINT, saveint);
3240 safe_signal(SIGPIPE, savepipe);
3241 if (options & OPT_TTYOUT)
3242 Fclose(fp);
3243 jleave:
3244 NYD_LEAVE;
3245 if (interrupts)
3246 onintr(0);
3249 static void
3250 dopr(FILE *fp)
3252 char o[LINESIZE];
3253 int c;
3254 long n = 0, mx = 0, columns, width;
3255 FILE *out;
3256 NYD_ENTER;
3258 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
3259 == NULL) {
3260 n_perr(_("tmpfile"), 0);
3261 goto jleave;
3264 while ((c = getc(fp)) != EOF) {
3265 if (c == '\n') {
3266 if (n > mx)
3267 mx = n;
3268 n = 0;
3269 } else
3270 ++n;
3272 rewind(fp);
3274 width = scrnwidth;
3275 if (mx < width / 2) {
3276 columns = width / (mx+2);
3277 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3278 } else
3279 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3280 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
3281 page_or_print(out, 0);
3282 Fclose(out);
3283 jleave:
3284 NYD_LEAVE;
3287 static enum okay
3288 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3290 char o[LINESIZE];
3291 const char *qname;
3292 bool_t twice, stored;
3293 FILE *queuefp;
3294 enum okay ok;
3295 NYD_X;
3297 ok = STOP;
3298 queuefp = NULL;
3299 twice = stored = FAL0;
3301 /* C99 */{
3302 size_t i;
3304 i = strlen(name = imap_fileof(name));
3305 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3306 name = savecat(name, "INBOX");
3307 if((qname = imap_path_quote(mp, name)) == NULL)
3308 goto jleave;
3311 if (mp->mb_type == MB_CACHE) {
3312 if ((queuefp = cache_queue(mp)) == NULL)
3313 goto jleave;
3314 ok = OKAY;
3317 /* Since it is not possible to set flags on the copy, recently
3318 * set flags must be set on the original to include it in the copy */
3319 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3320 imap_store(mp, m, n, '+', "\\Seen", 0);
3321 if (m->m_flag&MFLAG)
3322 imap_store(mp, m, n, '+', "\\Flagged", 0);
3323 if (m->m_flag&MUNFLAG)
3324 imap_store(mp, m, n, '-', "\\Flagged", 0);
3325 if (m->m_flag&MANSWER)
3326 imap_store(mp, m, n, '+', "\\Answered", 0);
3327 if (m->m_flag&MUNANSWER)
3328 imap_store(mp, m, n, '-', "\\Flagged", 0);
3329 if (m->m_flag&MDRAFT)
3330 imap_store(mp, m, n, '+', "\\Draft", 0);
3331 if (m->m_flag&MUNDRAFT)
3332 imap_store(mp, m, n, '-', "\\Draft", 0);
3333 again:
3334 if (m->m_uid)
3335 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3336 else {
3337 if (check_expunged() == STOP)
3338 goto out;
3339 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3341 IMAP_OUT(o, MB_COMD, goto out)
3342 while (mp->mb_active & MB_COMD)
3343 ok = imap_answer(mp, twice);
3345 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3346 response_status == RESPONSE_OK)
3347 imap_copyuid(mp, m, name);
3349 if (response_status == RESPONSE_NO && !twice) {
3350 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3351 IMAP_OUT(o, MB_COMD, goto out)
3352 while (mp->mb_active & MB_COMD)
3353 ok = imap_answer(mp, 1);
3354 if (ok == OKAY) {
3355 imap_created_mailbox++;
3356 goto again;
3359 twice = TRU1;
3361 if (queuefp != NULL)
3362 Fclose(queuefp);
3364 /* ... and reset the flag to its initial value so that the 'exit'
3365 * command still leaves the message unread */
3366 out:
3367 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3368 imap_store(mp, m, n, '-', "\\Seen", 0);
3369 stored = TRU1;
3371 if (m->m_flag & MFLAG) {
3372 imap_store(mp, m, n, '-', "\\Flagged", 0);
3373 stored = TRU1;
3375 if (m->m_flag & MUNFLAG) {
3376 imap_store(mp, m, n, '+', "\\Flagged", 0);
3377 stored = TRU1;
3379 if (m->m_flag & MANSWER) {
3380 imap_store(mp, m, n, '-', "\\Answered", 0);
3381 stored = TRU1;
3383 if (m->m_flag & MUNANSWER) {
3384 imap_store(mp, m, n, '+', "\\Answered", 0);
3385 stored = TRU1;
3387 if (m->m_flag & MDRAFT) {
3388 imap_store(mp, m, n, '-', "\\Draft", 0);
3389 stored = TRU1;
3391 if (m->m_flag & MUNDRAFT) {
3392 imap_store(mp, m, n, '+', "\\Draft", 0);
3393 stored = TRU1;
3395 if (stored) {
3396 mp->mb_active |= MB_COMD;
3397 (void)imap_finish(mp);
3399 jleave:
3400 return ok;
3403 FL enum okay
3404 imap_copy(struct message *m, int n, const char *name)
3406 sighandler_type saveint, savepipe;
3407 enum okay rv = STOP;
3408 NYD_ENTER;
3410 imaplock = 1;
3411 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3412 safe_signal(SIGINT, &_imap_maincatch);
3413 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3414 if (sigsetjmp(imapjmp, 1) == 0) {
3415 if (savepipe != SIG_IGN)
3416 safe_signal(SIGPIPE, imapcatch);
3418 rv = imap_copy1(&mb, m, n, name);
3420 safe_signal(SIGINT, saveint);
3421 safe_signal(SIGPIPE, savepipe);
3422 imaplock = 0;
3424 NYD_LEAVE;
3425 if (interrupts)
3426 onintr(0);
3427 return rv;
3430 static enum okay
3431 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3432 unsigned long *olduid, unsigned long *newuid)
3434 char *xp, *yp, *zp;
3435 enum okay rv;
3436 NYD_ENTER;
3438 *uidvalidity = strtoul(cp, &xp, 10);
3439 *olduid = strtoul(xp, &yp, 10);
3440 *newuid = strtoul(yp, &zp, 10);
3441 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3442 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3443 NYD_LEAVE;
3444 return rv;
3447 static enum okay
3448 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3449 unsigned long *uid)
3451 char *xp, *yp;
3452 enum okay rv;
3453 NYD_ENTER;
3455 *uidvalidity = strtoul(cp, &xp, 10);
3456 *uid = strtoul(xp, &yp, 10);
3457 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3458 *yp == ']');
3459 NYD_LEAVE;
3460 return rv;
3463 static enum okay
3464 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3466 struct mailbox xmb;
3467 struct message xm;
3468 const char *cp;
3469 unsigned long uidvalidity, olduid, newuid;
3470 enum okay rv;
3471 NYD_ENTER;
3473 rv = STOP;
3475 memset(&xmb, 0, sizeof xmb);
3477 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3478 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3479 goto jleave;
3481 rv = OKAY;
3483 xmb = *mp;
3484 xmb.mb_cache_directory = NULL;
3485 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3486 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3487 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3488 sizeof(xmb.mb_imap_delim));
3489 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3490 if (mp->mb_cache_directory != NULL)
3491 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3492 xmb.mb_uidvalidity = uidvalidity;
3493 initcache(&xmb);
3495 if (m == NULL) {
3496 memset(&xm, 0, sizeof xm);
3497 xm.m_uid = olduid;
3498 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3499 goto jleave;
3500 getcache(mp, &xm, NEED_HEADER);
3501 getcache(mp, &xm, NEED_BODY);
3502 } else {
3503 if ((m->m_flag & HAVE_HEADER) == 0)
3504 getcache(mp, m, NEED_HEADER);
3505 if ((m->m_flag & HAVE_BODY) == 0)
3506 getcache(mp, m, NEED_BODY);
3507 xm = *m;
3509 xm.m_uid = newuid;
3510 xm.m_flag &= ~MFULLYCACHED;
3511 putcache(&xmb, &xm);
3512 jleave:
3513 if (xmb.mb_cache_directory != NULL)
3514 free(xmb.mb_cache_directory);
3515 if (xmb.mb_imap_mailbox != NULL)
3516 free(xmb.mb_imap_mailbox);
3517 if (xmb.mb_imap_pass != NULL)
3518 free(xmb.mb_imap_pass);
3519 if (xmb.mb_imap_account != NULL)
3520 free(xmb.mb_imap_account);
3521 NYD_LEAVE;
3522 return rv;
3525 static enum okay
3526 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3527 long size, long lines, int flag, const char *name)
3529 struct mailbox xmb;
3530 struct message xm;
3531 const char *cp;
3532 unsigned long uidvalidity, uid;
3533 enum okay rv;
3534 NYD_ENTER;
3536 rv = STOP;
3538 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3539 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3540 goto jleave;
3542 rv = OKAY;
3544 xmb = *mp;
3545 xmb.mb_cache_directory = NULL;
3546 /* XXX mb_imap_delim reused */
3547 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3548 xmb.mb_uidvalidity = uidvalidity;
3549 xmb.mb_otf = xmb.mb_itf = fp;
3550 initcache(&xmb);
3551 memset(&xm, 0, sizeof xm);
3552 xm.m_flag = (flag & MREAD) | MNEW;
3553 xm.m_time = t;
3554 xm.m_block = mailx_blockof(off1);
3555 xm.m_offset = mailx_offsetof(off1);
3556 xm.m_size = size;
3557 xm.m_xsize = xsize;
3558 xm.m_lines = xm.m_xlines = lines;
3559 xm.m_uid = uid;
3560 xm.m_have = HAVE_HEADER | HAVE_BODY;
3561 putcache(&xmb, &xm);
3563 free(xmb.mb_imap_mailbox);
3564 jleave:
3565 NYD_LEAVE;
3566 return rv;
3569 static enum okay
3570 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3572 FILE *tp = NULL;
3573 time_t t;
3574 long size, xsize, ysize, lines;
3575 enum mflag flag = MNEW;
3576 char *name, *buf, *bp;
3577 char const *cp;
3578 size_t bufsize, buflen, cnt;
3579 enum okay rv = STOP;
3580 NYD_ENTER;
3582 buf = smalloc(bufsize = LINESIZE);
3583 buflen = 0;
3584 cnt = fsize(fp);
3585 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3586 goto jstop;
3588 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3590 while (*bp == ' ')
3591 ++bp;
3593 if ((cp = strrchr(bp, '{')) == NULL)
3594 goto jstop;
3596 xsize = atol(&cp[1]) + 2;
3597 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3598 goto jstop;
3599 while (*cp == ' ')
3600 cp++;
3602 if (*cp == '(') {
3603 imap_getflags(cp, &cp, &flag);
3604 while (*++cp == ' ')
3607 t = imap_read_date_time(cp);
3609 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3610 NULL)
3611 goto jstop;
3613 size = xsize;
3614 ysize = lines = 0;
3615 while (size > 0) {
3616 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3617 goto jstop;
3618 size -= buflen;
3619 buf[--buflen] = '\0';
3620 buf[buflen-1] = '\n';
3621 fwrite(buf, 1, buflen, tp);
3622 ysize += buflen;
3623 ++lines;
3625 fflush(tp);
3626 rewind(tp);
3628 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3629 imap_unquotestr(name));
3630 rv = OKAY;
3631 jstop:
3632 free(buf);
3633 if (tp)
3634 Fclose(tp);
3635 NYD_LEAVE;
3636 return rv;
3639 #ifdef HAVE_IMAP_SEARCH
3640 static enum okay
3641 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3642 int f)
3644 char *o, *xp, *cs, c;
3645 size_t osize;
3646 FILE *queuefp = NULL;
3647 int i;
3648 unsigned long n;
3649 const char *cp;
3650 enum okay ok = STOP;
3651 NYD_X;
3653 c = 0;
3654 for (cp = spec; *cp; cp++)
3655 c |= *cp;
3656 if (c & 0200) {
3657 cp = charset_get_lc();
3658 # ifdef HAVE_ICONV
3659 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3660 iconv_t it;
3661 char *nsp, *nspec;
3662 size_t sz, nsz;
3664 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3665 sz = strlen(spec) + 1;
3666 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3667 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3668 sz == 0) {
3669 spec = nspec;
3670 cp = "utf-8";
3672 n_iconv_close(it);
3675 # endif
3676 cp = imap_quotestr(cp);
3677 cs = salloc(n = strlen(cp) + 10);
3678 snprintf(cs, n, "CHARSET %s ", cp);
3679 } else
3680 cs = UNCONST("");
3682 o = ac_alloc(osize = strlen(spec) + 60);
3683 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3684 IMAP_OUT(o, MB_COMD, goto out)
3685 while (mp->mb_active & MB_COMD) {
3686 ok = imap_answer(mp, 0);
3687 if (response_status == RESPONSE_OTHER &&
3688 response_other == MAILBOX_DATA_SEARCH) {
3689 xp = responded_other_text;
3690 while (*xp && *xp != '\r') {
3691 n = strtoul(xp, &xp, 10);
3692 for (i = 0; i < cnt; i++)
3693 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3694 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3695 mark(i+1, f);
3699 out:
3700 ac_free(o);
3701 return ok;
3704 FL enum okay
3705 imap_search1(const char * volatile spec, int f)
3707 sighandler_type saveint, savepipe;
3708 enum okay volatile rv = STOP;
3709 NYD_ENTER;
3711 if (mb.mb_type != MB_IMAP)
3712 goto jleave;
3714 imaplock = 1;
3715 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3716 safe_signal(SIGINT, &_imap_maincatch);
3717 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3718 if (sigsetjmp(imapjmp, 1) == 0) {
3719 if (savepipe != SIG_IGN)
3720 safe_signal(SIGPIPE, imapcatch);
3722 rv = imap_search2(&mb, message, msgCount, spec, f);
3724 safe_signal(SIGINT, saveint);
3725 safe_signal(SIGPIPE, savepipe);
3726 imaplock = 0;
3727 jleave:
3728 NYD_LEAVE;
3729 if (interrupts)
3730 onintr(0);
3731 return rv;
3733 #endif /* HAVE_IMAP_SEARCH */
3735 FL int
3736 imap_thisaccount(const char *cp)
3738 int rv;
3739 NYD_ENTER;
3741 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3742 rv = 0;
3743 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3744 mb.mb_imap_account == NULL)
3745 rv = 0;
3746 else
3747 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3748 NYD_LEAVE;
3749 return rv;
3752 FL enum okay
3753 imap_remove(const char * volatile name)
3755 sighandler_type volatile saveint, savepipe;
3756 enum okay volatile rv = STOP;
3757 NYD_ENTER;
3759 if (mb.mb_type != MB_IMAP) {
3760 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3761 goto jleave;
3764 if (!imap_thisaccount(name)) {
3765 n_err(_("Can only remove mailboxes on current IMAP server: "
3766 "\"%s\" not removed\n"), name);
3767 goto jleave;
3770 imaplock = 1;
3771 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3772 safe_signal(SIGINT, &_imap_maincatch);
3773 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3774 if (sigsetjmp(imapjmp, 1) == 0) {
3775 if (savepipe != SIG_IGN)
3776 safe_signal(SIGPIPE, imapcatch);
3778 rv = imap_remove1(&mb, imap_fileof(name));
3780 safe_signal(SIGINT, saveint);
3781 safe_signal(SIGPIPE, savepipe);
3782 imaplock = 0;
3784 if (rv == OKAY)
3785 rv = cache_remove(name);
3786 jleave:
3787 NYD_LEAVE;
3788 if (interrupts)
3789 onintr(0);
3790 return rv;
3793 static enum okay
3794 imap_remove1(struct mailbox *mp, const char *name)
3796 char *o;
3797 int os;
3798 char const *qname;
3799 FILE *queuefp;
3800 enum okay ok;
3801 NYD_X;
3803 ok = STOP;
3804 queuefp = NULL;
3806 if((qname = imap_path_quote(mp, name)) != NULL){
3807 o = ac_alloc(os = strlen(qname) + 100);
3808 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3809 IMAP_OUT(o, MB_COMD, goto out)
3810 while (mp->mb_active & MB_COMD)
3811 ok = imap_answer(mp, 1);
3812 out:
3813 ac_free(o);
3815 return ok;
3818 FL enum okay
3819 imap_rename(const char *old, const char *new)
3821 sighandler_type saveint, savepipe;
3822 enum okay rv = STOP;
3823 NYD_ENTER;
3825 if (mb.mb_type != MB_IMAP) {
3826 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3827 goto jleave;
3830 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3831 n_err(_("Can only rename mailboxes on current IMAP "
3832 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3833 goto jleave;
3836 imaplock = 1;
3837 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3838 safe_signal(SIGINT, &_imap_maincatch);
3839 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3840 if (sigsetjmp(imapjmp, 1) == 0) {
3841 if (savepipe != SIG_IGN)
3842 safe_signal(SIGPIPE, imapcatch);
3844 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3846 safe_signal(SIGINT, saveint);
3847 safe_signal(SIGPIPE, savepipe);
3848 imaplock = 0;
3850 if (rv == OKAY)
3851 rv = cache_rename(old, new);
3852 jleave:
3853 NYD_LEAVE;
3854 if (interrupts)
3855 onintr(0);
3856 return rv;
3859 static enum okay
3860 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3862 char *o;
3863 int os;
3864 char const *qoname, *qnname;
3865 FILE *queuefp;
3866 enum okay ok;
3867 NYD_X;
3869 ok = STOP;
3870 queuefp = NULL;
3872 if((qoname = imap_path_quote(mp, old)) != NULL &&
3873 (qnname = imap_path_quote(mp, new)) != NULL){
3874 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3875 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3876 IMAP_OUT(o, MB_COMD, goto out)
3877 while (mp->mb_active & MB_COMD)
3878 ok = imap_answer(mp, 1);
3879 out:
3880 ac_free(o);
3882 return ok;
3885 FL enum okay
3886 imap_dequeue(struct mailbox *mp, FILE *fp)
3888 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3889 size_t bufsize, buflen, cnt;
3890 long offs, offs1, offs2, octets;
3891 int twice, gotcha = 0;
3892 FILE *queuefp = NULL;
3893 enum okay ok = OKAY, rok = OKAY;
3894 NYD_X;
3896 buf = smalloc(bufsize = LINESIZE);
3897 buflen = 0;
3898 cnt = fsize(fp);
3899 while ((offs1 = ftell(fp)) >= 0 &&
3900 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3901 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3903 while (*bp == ' ')
3904 ++bp;
3905 twice = 0;
3906 if ((offs = ftell(fp)) < 0)
3907 goto fail;
3908 again:
3909 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3910 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3911 cp = &bp[9];
3912 while (digitchar(*cp))
3913 cp++;
3914 if (*cp != ' ')
3915 goto fail;
3916 while (*cp == ' ')
3917 cp++;
3918 if ((newname = imap_strex(cp, NULL)) == NULL)
3919 goto fail;
3920 IMAP_OUT(o, MB_COMD, continue)
3921 while (mp->mb_active & MB_COMD)
3922 ok = imap_answer(mp, twice);
3923 if (response_status == RESPONSE_NO && twice++ == 0)
3924 goto trycreate;
3925 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3926 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3928 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3929 IMAP_OUT(o, MB_COMD, continue)
3930 while (mp->mb_active & MB_COMD)
3931 ok = imap_answer(mp, 1);
3932 if (ok == OKAY)
3933 gotcha++;
3934 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3935 if ((cp = strrchr(bp, '{')) == NULL)
3936 goto fail;
3937 octets = atol(&cp[1]) + 2;
3938 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3939 goto fail;
3940 IMAP_OUT(o, MB_COMD, continue)
3941 while (mp->mb_active & MB_COMD) {
3942 ok = imap_answer(mp, twice);
3943 if (response_type == RESPONSE_CONT)
3944 break;
3946 if (ok == STOP) {
3947 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3948 goto trycreate;
3949 goto fail;
3951 while (octets > 0) {
3952 size_t n = (UICMP(z, octets, >, sizeof iob)
3953 ? sizeof iob : (size_t)octets);
3954 octets -= n;
3955 if (n != fread(iob, 1, n, fp))
3956 goto fail;
3957 swrite1(&mp->mb_sock, iob, n, 1);
3959 swrite(&mp->mb_sock, "");
3960 while (mp->mb_active & MB_COMD) {
3961 ok = imap_answer(mp, 0);
3962 if (response_status == RESPONSE_NO && twice++ == 0) {
3963 if (fseek(fp, offs, SEEK_SET) < 0)
3964 goto fail;
3965 goto trycreate;
3968 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3969 if ((offs2 = ftell(fp)) < 0)
3970 goto fail;
3971 fseek(fp, offs1, SEEK_SET);
3972 if (imap_appenduid_cached(mp, fp) == STOP) {
3973 (void)fseek(fp, offs2, SEEK_SET);
3974 goto fail;
3977 } else {
3978 fail:
3979 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3980 rok = STOP;
3982 continue;
3983 trycreate:
3984 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3985 IMAP_OUT(o, MB_COMD, continue)
3986 while (mp->mb_active & MB_COMD)
3987 ok = imap_answer(mp, 1);
3988 if (ok == OKAY)
3989 goto again;
3991 fflush(fp);
3992 rewind(fp);
3993 ftruncate(fileno(fp), 0);
3994 if (gotcha)
3995 imap_close(mp);
3996 free(buf);
3997 return rok;
4000 static char *
4001 imap_strex(char const *cp, char const **xp)
4003 char const *cq;
4004 char *n = NULL;
4005 NYD_ENTER;
4007 if (*cp != '"')
4008 goto jleave;
4010 for (cq = cp + 1; *cq != '\0'; ++cq) {
4011 if (*cq == '\\')
4012 cq++;
4013 else if (*cq == '"')
4014 break;
4016 if (*cq != '"')
4017 goto jleave;
4019 n = salloc(cq - cp + 2);
4020 memcpy(n, cp, cq - cp +1);
4021 n[cq - cp + 1] = '\0';
4022 if (xp != NULL)
4023 *xp = cq + 1;
4024 jleave:
4025 NYD_LEAVE;
4026 return n;
4029 static enum okay
4030 check_expunged(void)
4032 enum okay rv;
4033 NYD_ENTER;
4035 if (expunged_messages > 0) {
4036 n_err(_("Command not executed - messages have been expunged\n"));
4037 rv = STOP;
4038 } else
4039 rv = OKAY;
4040 NYD_LEAVE;
4041 return rv;
4044 FL int
4045 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4047 struct url url;
4048 int rv, omsgCount = msgCount;
4049 NYD_ENTER;
4050 UNUSED(vp);
4052 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4053 n_err(_("Already connected\n"));
4054 rv = 1;
4055 goto jleave;
4058 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4059 rv = 1;
4060 goto jleave;
4062 ok_bclear(disconnected);
4063 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
4065 if (mb.mb_type == MB_CACHE) {
4066 enum fedit_mode fm = FEDIT_NONE;
4067 if (_imap_rdonly)
4068 fm |= FEDIT_RDONLY;
4069 if (!(pstate & PS_EDIT))
4070 fm |= FEDIT_SYSBOX;
4071 _imap_setfile1(&url, fm, 1);
4072 if (msgCount > omsgCount)
4073 newmailinfo(omsgCount);
4075 rv = 0;
4076 jleave:
4077 NYD_LEAVE;
4078 return rv;
4081 FL int
4082 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4084 struct url url;
4085 int rv = 1, *msgvec = vp;
4086 NYD_ENTER;
4088 if (mb.mb_type == MB_CACHE) {
4089 n_err(_("Not connected\n"));
4090 goto jleave;
4092 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4093 n_err(_("The current mailbox is not cached\n"));
4094 goto jleave;
4097 if (!url_parse(&url, CPROTO_IMAP, mailname))
4098 goto jleave;
4100 if (*msgvec)
4101 c_cache(vp);
4102 ok_bset(disconnected, TRU1);
4103 if (mb.mb_type == MB_IMAP) {
4104 enum fedit_mode fm = FEDIT_NONE;
4105 if (_imap_rdonly)
4106 fm |= FEDIT_RDONLY;
4107 if (!(pstate & PS_EDIT))
4108 fm |= FEDIT_SYSBOX;
4109 sclose(&mb.mb_sock);
4110 _imap_setfile1(&url, fm, 1);
4112 rv = 0;
4113 jleave:
4114 NYD_LEAVE;
4115 return rv;
4118 FL int
4119 c_cache(void *vp)
4121 int rv = 1, *msgvec = vp, *ip;
4122 struct message *mp;
4123 NYD_ENTER;
4125 if (mb.mb_type != MB_IMAP) {
4126 n_err(_("Not connected to an IMAP server\n"));
4127 goto jleave;
4129 if (cached_uidvalidity(&mb) == 0) {
4130 n_err(_("The current mailbox is not cached\n"));
4131 goto jleave;
4134 srelax_hold();
4135 for (ip = msgvec; *ip; ++ip) {
4136 mp = &message[*ip - 1];
4137 if (!(mp->m_have & HAVE_BODY)) {
4138 get_body(mp);
4139 srelax();
4142 srelax_rele();
4143 rv = 0;
4144 jleave:
4145 NYD_LEAVE;
4146 return rv;
4149 FL int
4150 disconnected(const char *file)
4152 struct url url;
4153 int rv = 1;
4154 NYD_ENTER;
4156 if (ok_blook(disconnected)) {
4157 rv = 1;
4158 goto jleave;
4161 if (!url_parse(&url, CPROTO_IMAP, file)) {
4162 rv = 0;
4163 goto jleave;
4165 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
4167 jleave:
4168 NYD_LEAVE;
4169 return rv;
4172 FL void
4173 transflags(struct message *omessage, long omsgCount, int transparent)
4175 struct message *omp, *nmp, *newdot, *newprevdot;
4176 int hf;
4177 NYD_ENTER;
4179 omp = omessage;
4180 nmp = message;
4181 newdot = message;
4182 newprevdot = NULL;
4183 while (PTRCMP(omp, <, omessage + omsgCount) &&
4184 PTRCMP(nmp, <, message + msgCount)) {
4185 if (dot && nmp->m_uid == dot->m_uid)
4186 newdot = nmp;
4187 if (prevdot && nmp->m_uid == prevdot->m_uid)
4188 newprevdot = nmp;
4189 if (omp->m_uid == nmp->m_uid) {
4190 hf = nmp->m_flag & MHIDDEN;
4191 if (transparent && mb.mb_type == MB_IMAP)
4192 omp->m_flag &= ~MHIDDEN;
4193 *nmp++ = *omp++;
4194 if (transparent && mb.mb_type == MB_CACHE)
4195 nmp[-1].m_flag |= hf;
4196 } else if (omp->m_uid < nmp->m_uid)
4197 ++omp;
4198 else
4199 ++nmp;
4201 dot = newdot;
4202 setdot(newdot);
4203 prevdot = newprevdot;
4204 free(omessage);
4205 NYD_LEAVE;
4208 FL time_t
4209 imap_read_date_time(const char *cp)
4211 char buf[3];
4212 time_t t;
4213 int i, year, month, day, hour, minute, second, sign = -1;
4214 NYD2_ENTER;
4216 /* "25-Jul-2004 15:33:44 +0200"
4217 * | | | | | |
4218 * 0 5 10 15 20 25 */
4219 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4220 goto jinvalid;
4221 day = strtol(&cp[1], NULL, 10);
4222 for (i = 0;;) {
4223 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
4224 break;
4225 if (month_names[++i][0] == '\0')
4226 goto jinvalid;
4228 month = i + 1;
4229 year = strtol(&cp[8], NULL, 10);
4230 hour = strtol(&cp[13], NULL, 10);
4231 minute = strtol(&cp[16], NULL, 10);
4232 second = strtol(&cp[19], NULL, 10);
4233 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4234 goto jinvalid;
4235 switch (cp[22]) {
4236 case '-':
4237 sign = 1;
4238 break;
4239 case '+':
4240 break;
4241 default:
4242 goto jinvalid;
4244 buf[2] = '\0';
4245 buf[0] = cp[23];
4246 buf[1] = cp[24];
4247 t += strtol(buf, NULL, 10) * sign * 3600;
4248 buf[0] = cp[25];
4249 buf[1] = cp[26];
4250 t += strtol(buf, NULL, 10) * sign * 60;
4251 jleave:
4252 NYD2_LEAVE;
4253 return t;
4254 jinvalid:
4255 time(&t);
4256 goto jleave;
4259 FL const char *
4260 imap_make_date_time(time_t t)
4262 static char s[30];
4263 struct tm *tmptr;
4264 int tzdiff, tzdiff_hour, tzdiff_min;
4265 NYD2_ENTER;
4267 tzdiff = t - mktime(gmtime(&t));
4268 tzdiff_hour = (int)(tzdiff / 60);
4269 tzdiff_min = tzdiff_hour % 60;
4270 tzdiff_hour /= 60;
4271 tmptr = localtime(&t);
4272 if (tmptr->tm_isdst > 0)
4273 tzdiff_hour++;
4274 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4275 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4276 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4277 NYD2_LEAVE;
4278 return s;
4280 #endif /* HAVE_IMAP */
4282 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
4283 FL char *
4284 imap_quotestr(char const *s)
4286 char *n, *np;
4287 NYD2_ENTER;
4289 np = n = salloc(2 * strlen(s) + 3);
4290 *np++ = '"';
4291 while (*s) {
4292 if (*s == '"' || *s == '\\')
4293 *np++ = '\\';
4294 *np++ = *s++;
4296 *np++ = '"';
4297 *np = '\0';
4298 NYD2_LEAVE;
4299 return n;
4302 FL char *
4303 imap_unquotestr(char const *s)
4305 char *n, *np;
4306 NYD2_ENTER;
4308 if (*s != '"') {
4309 n = savestr(s);
4310 goto jleave;
4313 np = n = salloc(strlen(s) + 1);
4314 while (*++s) {
4315 if (*s == '\\')
4316 s++;
4317 else if (*s == '"')
4318 break;
4319 *np++ = *s;
4321 *np = '\0';
4322 jleave:
4323 NYD2_LEAVE;
4324 return n;
4326 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
4328 /* s-it-mode */