imap_path_decode(): err: do not "savestrbuf(,buflen * 2)"
[s-mailx.git] / imap.c
blobe8e88c9247b2c9c9113749b2bf03cbbb1b3cf325
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 s7e = 0xD800u | ((utf32 - 0x10000) >> 10);
454 be16p[1] = s7e & 0xFF;
455 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
456 s7e = 0xDC00u | ((utf32 - 0x10000) & 0x03FF);
457 be16p[3] = s7e & 0xFF;
458 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
459 be16p += 4;
462 if(l == 0)
463 break;
464 if((c = *cp) > 0x1F && c < 0x7F)
465 break;
468 /* And then warp that UTF-16BE to mUTF-7 */
469 out.s[out.l++] = '&';
470 utf32 = (ui32_t)PTR2SIZE(be16p - be16p_base);
471 be16p = be16p_base;
473 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
474 out.s[out.l+0] = mb64ct[ be16p[0] >> 2 ];
475 out.s[out.l+1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
476 out.s[out.l+2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
477 out.s[out.l+3] = mb64ct[ be16p[2] & 0x3F];
478 out.l += 4;
480 if(utf32 > 0){
481 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
482 if(--utf32 == 0){
483 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
484 out.l += 2;
485 }else{
486 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
487 (be16p[1] >> 4)];
488 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
489 out.l += 3;
492 out.s[out.l++] = '-';
495 out.s[out.l] = '\0';
496 assert(out.l <= l_plain);
497 *err_or_null = FAL0;
498 cp = out.s;
499 jleave:
500 NYD2_LEAVE;
501 return cp;
502 jerr:
503 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
504 goto jleave;
507 FL char *
508 imap_path_decode(char const *path, bool_t *err_or_null){
509 /* To a large extend inspired by dovecot(1) TODO use string */
510 struct str in, out;
511 bool_t err_def;
512 ui8_t *mb64p_base, *mb64p, *mb64xp;
513 char const *emsg, *cp;
514 char *rv_base, *rv, c;
515 size_t l_orig, l, i;
516 NYD2_ENTER;
518 if(err_or_null == NULL)
519 err_or_null = &err_def;
520 *err_or_null = FAL0;
522 l = l_orig = strlen(path);
523 rv = rv_base = salloc(l << 1);
524 memcpy(rv, path, l +1);
526 /* xxx Don't check for invalid characters from malicious servers */
527 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
528 goto jleave;
530 *err_or_null = TRU1;
532 emsg = N_("Invalid mUTF-7 encoding");
533 i = PTR2SIZE(cp - path);
534 rv += i;
535 l -= i;
536 mb64p_base = NULL;
538 while(l > 0){
539 if((c = *cp) != '&'){
540 if(c <= 0x1F || c >= 0x7F){
541 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
542 goto jerr;
544 *rv++ = c;
545 ++cp;
546 --l;
547 }else if(--l == 0)
548 goto jeincpl;
549 else if(*++cp == '-'){
550 *rv++ = '&';
551 ++cp;
552 --l;
553 }else if(l < 3){
554 jeincpl:
555 emsg = N_("Invalid mUTF-7: incomplete input");
556 goto jerr;
557 }else{
558 /* mUTF-7 -> UTF-16BE -> UTF-8 */
559 static ui8_t const mb64dt[256] = {
560 #undef XX
561 #define XX 0xFFu
562 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
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,62, 63,XX,XX,XX,
565 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
566 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
567 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
568 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
569 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
570 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,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
580 if(mb64p_base == NULL)
581 mb64p_base = salloc(l);
583 /* Decode the mUTF-7 to what is indeed UTF-16BE */
584 for(mb64p = mb64p_base;;){
585 assert(l >= 3);
586 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
587 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
588 goto jerr;
589 mb64p += 2;
591 c = cp[2];
592 cp += 3;
593 l -= 3;
594 if(c == '-')
595 break;
596 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
597 goto jerr;
599 if(l == 0)
600 goto jerr;
601 --l;
602 if((c = *cp++) == '-')
603 break;
604 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
605 goto jerr;
607 if(l < 3){
608 if(l > 0 && *cp == '-'){
609 --l;
610 ++cp;
611 break;
613 goto jerr;
616 #undef XX
618 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
619 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
620 goto jerr;
623 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
624 i = PTR2SIZE(mb64p - mb64p_base);
625 mb64p = mb64xp = mb64p_base;
627 while(i > 0){
628 ui8_t ul, u0, u1, u2, u3;
630 ul = (i >= 4) ? 4 : i & 0x3;
631 i -= ul;
632 u0 = mb64xp[0];
633 u1 = mb64xp[1];
634 u2 = (ul < 3) ? 0 : mb64xp[2];
635 u3 = (ul < 4) ? 0 : mb64xp[3];
636 mb64xp += ul;
637 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
638 if(ul < 3)
639 break;
640 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
641 if(ul < 4)
642 break;
643 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
646 /* UTF-16BE we convert to UTF-8 */
647 i = PTR2SIZE(mb64p - mb64p_base);
648 if(i & 1){
649 emsg = N_("Odd bytecount for UTF-16BE input");
650 goto jerr;
653 /* TODO S-CText: magic utf16 conversions */
654 emsg = N_("Invalid UTF-16BE encoding");
656 for(mb64p = mb64p_base; i > 0;){
657 ui32_t utf32;
658 ui16_t uhi, ulo;
660 uhi = mb64p[0];
661 uhi <<= 8;
662 uhi |= mb64p[1];
664 /* Not a surrogate? */
665 if(uhi < 0xD800 || uhi > 0xDFFF){
666 utf32 = uhi;
667 mb64p += 2;
668 i -= 2;
669 }else if(uhi > 0xDBFF)
670 goto jerr;
671 else if(i < 4){
672 emsg = N_("Incomplete UTF-16BE surrogate pair");
673 goto jerr;
674 }else{
675 ulo = mb64p[2];
676 ulo <<= 8;
677 ulo |= mb64p[3];
678 if(ulo < 0xDC00 || ulo > 0xDFFF){
679 goto jerr;
682 uhi &= 0x03FF;
683 uhi <<= 10;
684 ulo &= 0x03FF;
685 uhi |= ulo;
687 utf32 = 0x10000u + uhi;
688 mb64p += 4;
689 i -= 4;
692 utf32 = n_utf32_to_utf8(utf32, rv);
693 rv += utf32;
697 *rv = '\0';
699 /* We can skip the UTF-8 conversion occasionally */
700 if(!(options & OPT_UNICODE)){
701 int ir;
702 iconv_t icd;
704 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
706 if((icd = iconv_open(charset_get_lc(), "utf-8")) == (iconv_t)-1)
707 goto jerr;
709 out.s = NULL, out.l = 0;
710 in.l = strlen(in.s = rv_base);
711 if((ir = n_iconv_str(icd, &out, &in, NULL, FAL0)) == 0)
712 /* Because the length of this is unpredictable, copy*/
713 rv_base = savestrbuf(out.s, out.l);
715 if(out.s != NULL)
716 free(out.s);
717 iconv_close(icd);
719 if(ir != 0)
720 goto jerr;
723 *err_or_null = FAL0;
724 rv = rv_base;
725 jleave:
726 NYD2_LEAVE;
727 return rv;
728 jerr:
729 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
730 memcpy(rv = rv_base, path, ++l_orig);
731 goto jleave;
734 static char *
735 imap_path_quote(struct mailbox *mp, char const *cp){
736 bool_t err;
737 char *rv;
738 NYD2_ENTER;
740 cp = imap_path_normalize(mp, cp);
741 cp = imap_path_encode(cp, &err);
742 rv = err ? NULL : imap_quotestr(cp);
743 NYD2_LEAVE;
744 return rv;
747 static void
748 imap_other_get(char *pp)
750 char *xp;
751 NYD2_ENTER;
753 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
754 pp += 6;
755 response_other = MAILBOX_DATA_FLAGS;
756 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
757 pp += 5;
758 response_other = MAILBOX_DATA_LIST;
759 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
760 pp += 5;
761 response_other = MAILBOX_DATA_LSUB;
762 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
763 pp += 8;
764 response_other = MAILBOX_DATA_MAILBOX;
765 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
766 pp += 7;
767 response_other = MAILBOX_DATA_SEARCH;
768 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
769 pp += 7;
770 response_other = MAILBOX_DATA_STATUS;
771 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
772 pp += 11;
773 response_other = CAPABILITY_DATA;
774 } else {
775 responded_other_number = strtol(pp, &xp, 10);
776 while (*xp == ' ')
777 ++xp;
778 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
779 response_other = MAILBOX_DATA_EXISTS;
780 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
781 response_other = MAILBOX_DATA_RECENT;
782 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
783 response_other = MESSAGE_DATA_EXPUNGE;
784 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
785 pp = &xp[6];
786 response_other = MESSAGE_DATA_FETCH;
787 } else
788 response_other = RESPONSE_OTHER_UNKNOWN;
790 responded_other_text = pp;
791 NYD2_LEAVE;
794 static void
795 imap_response_get(const char **cp)
797 NYD2_ENTER;
798 if (ascncasecmp(*cp, "OK ", 3) == 0) {
799 *cp += 3;
800 response_status = RESPONSE_OK;
801 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
802 *cp += 3;
803 response_status = RESPONSE_NO;
804 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
805 *cp += 4;
806 response_status = RESPONSE_BAD;
807 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
808 *cp += 8;
809 response_status = RESPONSE_PREAUTH;
810 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
811 *cp += 4;
812 response_status = RESPONSE_BYE;
813 } else
814 response_status = RESPONSE_OTHER;
815 NYD2_LEAVE;
818 static void
819 imap_response_parse(void)
821 static char *parsebuf; /* TODO Use pool */
822 static size_t parsebufsize;
824 const char *ip = imapbuf;
825 char *pp;
826 NYD2_ENTER;
828 if (parsebufsize < imapbufsize)
829 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
830 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
831 pp = parsebuf;
832 switch (*ip) {
833 case '+':
834 response_type = RESPONSE_CONT;
835 ip++;
836 pp++;
837 while (*ip == ' ') {
838 ip++;
839 pp++;
841 break;
842 case '*':
843 ip++;
844 pp++;
845 while (*ip == ' ') {
846 ip++;
847 pp++;
849 imap_response_get(&ip);
850 pp = &parsebuf[ip - imapbuf];
851 switch (response_status) {
852 case RESPONSE_BYE:
853 response_type = RESPONSE_FATAL;
854 break;
855 default:
856 response_type = RESPONSE_DATA;
858 break;
859 default:
860 responded_tag = parsebuf;
861 while (*pp && *pp != ' ')
862 pp++;
863 if (*pp == '\0') {
864 response_type = RESPONSE_ILLEGAL;
865 break;
867 *pp++ = '\0';
868 while (*pp && *pp == ' ')
869 pp++;
870 if (*pp == '\0') {
871 response_type = RESPONSE_ILLEGAL;
872 break;
874 ip = &imapbuf[pp - parsebuf];
875 response_type = RESPONSE_TAGGED;
876 imap_response_get(&ip);
877 pp = &parsebuf[ip - imapbuf];
879 responded_text = pp;
880 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
881 response_status == RESPONSE_OTHER)
882 imap_other_get(pp);
883 NYD2_LEAVE;
886 static enum okay
887 imap_answer(struct mailbox *mp, int errprnt)
889 int i, complete;
890 enum okay rv;
891 NYD2_ENTER;
893 rv = OKAY;
894 if (mp->mb_type == MB_CACHE)
895 goto jleave;
896 rv = STOP;
897 jagain:
898 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
899 if (options & OPT_VERBVERB)
900 fputs(imapbuf, stderr);
901 imap_response_parse();
902 if (response_type == RESPONSE_ILLEGAL)
903 goto jagain;
904 if (response_type == RESPONSE_CONT) {
905 rv = OKAY;
906 goto jleave;
908 if (response_status == RESPONSE_OTHER) {
909 if (response_other == MAILBOX_DATA_EXISTS) {
910 had_exists = responded_other_number;
911 rec_queue(REC_EXISTS, responded_other_number);
912 if (had_expunge > 0)
913 had_expunge = 0;
914 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
915 rec_queue(REC_EXPUNGE, responded_other_number);
916 if (had_expunge < 0)
917 had_expunge = 0;
918 had_expunge++;
919 expunged_messages++;
922 complete = 0;
923 if (response_type == RESPONSE_TAGGED) {
924 if (asccasecmp(responded_tag, tag(0)) == 0)
925 complete |= 1;
926 else
927 goto jagain;
929 switch (response_status) {
930 case RESPONSE_PREAUTH:
931 mp->mb_active &= ~MB_PREAUTH;
932 /*FALLTHRU*/
933 case RESPONSE_OK:
934 jokay:
935 rv = OKAY;
936 complete |= 2;
937 break;
938 case RESPONSE_NO:
939 case RESPONSE_BAD:
940 jstop:
941 rv = STOP;
942 complete |= 2;
943 if (errprnt)
944 n_err(_("IMAP error: %s"), responded_text);
945 break;
946 case RESPONSE_UNKNOWN: /* does not happen */
947 case RESPONSE_BYE:
948 i = mp->mb_active;
949 mp->mb_active = MB_NONE;
950 if (i & MB_BYE)
951 goto jokay;
952 goto jstop;
953 case RESPONSE_OTHER:
954 rv = OKAY;
955 break;
957 if (response_status != RESPONSE_OTHER &&
958 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
959 n_err(_("IMAP alert: %s"), &responded_text[8]);
960 if (complete == 3)
961 mp->mb_active &= ~MB_COMD;
962 } else {
963 rv = STOP;
964 mp->mb_active = MB_NONE;
966 jleave:
967 NYD2_LEAVE;
968 return rv;
971 static enum okay
972 imap_parse_list(void)
974 char *cp;
975 enum okay rv;
976 NYD2_ENTER;
978 rv = STOP;
980 cp = responded_other_text;
981 list_attributes = LIST_NONE;
982 if (*cp == '(') {
983 while (*cp && *cp != ')') {
984 if (*cp == '\\') {
985 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
986 list_attributes |= LIST_NOINFERIORS;
987 cp += 12;
988 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
989 list_attributes |= LIST_NOSELECT;
990 cp += 9;
991 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
992 list_attributes |= LIST_MARKED;
993 cp += 7;
994 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
995 list_attributes |= LIST_UNMARKED;
996 cp += 9;
999 cp++;
1001 if (*++cp != ' ')
1002 goto jleave;
1003 while (*cp == ' ')
1004 cp++;
1007 list_hierarchy_delimiter = EOF;
1008 if (*cp == '"') {
1009 if (*++cp == '\\')
1010 cp++;
1011 list_hierarchy_delimiter = *cp++ & 0377;
1012 if (cp[0] != '"' || cp[1] != ' ')
1013 goto jleave;
1014 cp++;
1015 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1016 list_hierarchy_delimiter = EOF;
1017 cp += 3;
1020 while (*cp == ' ')
1021 cp++;
1022 list_name = cp;
1023 while (*cp && *cp != '\r')
1024 cp++;
1025 *cp = '\0';
1026 rv = OKAY;
1027 jleave:
1028 NYD2_LEAVE;
1029 return rv;
1032 static enum okay
1033 imap_finish(struct mailbox *mp)
1035 NYD_ENTER;
1036 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1037 imap_answer(mp, 1);
1038 NYD_LEAVE;
1039 return OKAY;
1042 static void
1043 imap_timer_off(void)
1045 NYD_ENTER;
1046 if (imapkeepalive > 0) {
1047 alarm(0);
1048 safe_signal(SIGALRM, savealrm);
1050 NYD_LEAVE;
1053 static void
1054 imapcatch(int s)
1056 NYD_X; /* Signal handler */
1057 switch (s) {
1058 case SIGINT:
1059 n_err_sighdl(_("Interrupt\n"));
1060 siglongjmp(imapjmp, 1);
1061 /*NOTREACHED*/
1062 case SIGPIPE:
1063 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1064 break;
1068 static void
1069 _imap_maincatch(int s)
1071 NYD_X; /* Signal handler */
1072 UNUSED(s);
1073 if (interrupts++ == 0) {
1074 n_err_sighdl(_("Interrupt\n"));
1075 return;
1077 onintr(0);
1080 static enum okay
1081 imap_noop1(struct mailbox *mp)
1083 char o[LINESIZE];
1084 FILE *queuefp = NULL;
1085 NYD_X;
1087 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1088 IMAP_OUT(o, MB_COMD, return STOP)
1089 IMAP_ANSWER()
1090 return OKAY;
1093 FL char const *
1094 imap_fileof(char const *xcp)
1096 char const *cp = xcp;
1097 int state = 0;
1098 NYD_ENTER;
1100 while (*cp) {
1101 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1102 cp += 3;
1103 state = 1;
1105 if (cp[0] == '/' && state == 1) {
1106 ++cp;
1107 goto jleave;
1109 if (cp[0] == '/') {
1110 cp = xcp;
1111 goto jleave;
1113 ++cp;
1115 jleave:
1116 NYD_LEAVE;
1117 return cp;
1120 FL enum okay
1121 imap_noop(void)
1123 sighandler_type volatile oldint, oldpipe;
1124 enum okay rv = STOP;
1125 NYD_ENTER;
1127 if (mb.mb_type != MB_IMAP)
1128 goto jleave;
1130 imaplock = 1;
1131 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1132 safe_signal(SIGINT, &_imap_maincatch);
1133 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1134 if (sigsetjmp(imapjmp, 1) == 0) {
1135 if (oldpipe != SIG_IGN)
1136 safe_signal(SIGPIPE, imapcatch);
1138 rv = imap_noop1(&mb);
1140 safe_signal(SIGINT, oldint);
1141 safe_signal(SIGPIPE, oldpipe);
1142 imaplock = 0;
1143 jleave:
1144 NYD_LEAVE;
1145 if (interrupts)
1146 onintr(0);
1147 return rv;
1150 static void
1151 rec_queue(enum rec_type rt, unsigned long cnt)
1153 struct record *rp;
1154 NYD_ENTER;
1156 rp = scalloc(1, sizeof *rp);
1157 rp->rec_type = rt;
1158 rp->rec_count = cnt;
1159 if (record && recend) {
1160 recend->rec_next = rp;
1161 recend = rp;
1162 } else
1163 record = recend = rp;
1164 NYD_LEAVE;
1167 static enum okay
1168 rec_dequeue(void)
1170 struct message *omessage;
1171 struct record *rp, *rq;
1172 uiz_t exists = 0, i;
1173 enum okay rv = STOP;
1174 NYD_ENTER;
1176 if (record == NULL)
1177 goto jleave;
1179 omessage = message;
1180 message = smalloc((msgCount+1) * sizeof *message);
1181 if (msgCount)
1182 memcpy(message, omessage, msgCount * sizeof *message);
1183 memset(&message[msgCount], 0, sizeof *message);
1185 rp = record, rq = NULL;
1186 rv = OKAY;
1187 while (rp != NULL) {
1188 switch (rp->rec_type) {
1189 case REC_EXISTS:
1190 exists = rp->rec_count;
1191 break;
1192 case REC_EXPUNGE:
1193 if (rp->rec_count == 0) {
1194 rv = STOP;
1195 break;
1197 if (rp->rec_count > (unsigned long)msgCount) {
1198 if (exists == 0 || rp->rec_count > exists--)
1199 rv = STOP;
1200 break;
1202 if (exists > 0)
1203 exists--;
1204 delcache(&mb, &message[rp->rec_count-1]);
1205 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1206 ((msgCount - rp->rec_count + 1) * sizeof *message));
1207 --msgCount;
1208 /* If the message was part of a collapsed thread,
1209 * the m_collapsed field of one of its ancestors
1210 * should be incremented. It seems hardly possible
1211 * to do this with the current message structure,
1212 * though. The result is that a '+' may be shown
1213 * in the header summary even if no collapsed
1214 * children exists */
1215 break;
1217 if (rq != NULL)
1218 free(rq);
1219 rq = rp;
1220 rp = rp->rec_next;
1222 if (rq != NULL)
1223 free(rq);
1225 record = recend = NULL;
1226 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1227 message = srealloc(message, (exists + 1) * sizeof *message);
1228 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1229 for (i = msgCount; i < exists; ++i)
1230 imap_init(&mb, i);
1231 imap_flags(&mb, msgCount+1, exists);
1232 msgCount = exists;
1235 if (rv == STOP) {
1236 free(message);
1237 message = omessage;
1239 jleave:
1240 NYD_LEAVE;
1241 return rv;
1244 static void
1245 rec_rmqueue(void)
1247 struct record *rp;
1248 NYD_ENTER;
1250 for (rp = record; rp != NULL;) {
1251 struct record *tmp = rp;
1252 rp = rp->rec_next;
1253 free(tmp);
1255 record = recend = NULL;
1256 NYD_LEAVE;
1259 /*ARGSUSED*/
1260 static void
1261 imapalarm(int s)
1263 sighandler_type volatile saveint, savepipe;
1264 NYD_X; /* Signal handler */
1265 UNUSED(s);
1267 if (imaplock++ == 0) {
1268 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1269 safe_signal(SIGINT, &_imap_maincatch);
1270 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1271 if (sigsetjmp(imapjmp, 1)) {
1272 safe_signal(SIGINT, saveint);
1273 safe_signal(SIGPIPE, savepipe);
1274 goto jbrk;
1276 if (savepipe != SIG_IGN)
1277 safe_signal(SIGPIPE, imapcatch);
1278 if (imap_noop1(&mb) != OKAY) {
1279 safe_signal(SIGINT, saveint);
1280 safe_signal(SIGPIPE, savepipe);
1281 goto jleave;
1283 safe_signal(SIGINT, saveint);
1284 safe_signal(SIGPIPE, savepipe);
1286 jbrk:
1287 alarm(imapkeepalive);
1288 jleave:
1289 --imaplock;
1292 static enum okay
1293 imap_preauth(struct mailbox *mp, struct url const *urlp)
1295 NYD_X;
1297 mp->mb_active |= MB_PREAUTH;
1298 imap_answer(mp, 1);
1300 #ifdef HAVE_SSL
1301 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1302 FILE *queuefp = NULL;
1303 char o[LINESIZE];
1305 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1306 IMAP_OUT(o, MB_COMD, return STOP)
1307 IMAP_ANSWER()
1308 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1309 return STOP;
1311 #else
1312 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1313 n_err(_("No SSL support compiled in\n"));
1314 return STOP;
1316 #endif
1318 imap_capability(mp);
1319 return OKAY;
1322 static enum okay
1323 imap_capability(struct mailbox *mp)
1325 char o[LINESIZE];
1326 FILE *queuefp = NULL;
1327 enum okay ok = STOP;
1328 const char *cp;
1329 NYD_X;
1331 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1332 IMAP_OUT(o, MB_COMD, return STOP)
1333 while (mp->mb_active & MB_COMD) {
1334 ok = imap_answer(mp, 0);
1335 if (response_status == RESPONSE_OTHER &&
1336 response_other == CAPABILITY_DATA) {
1337 cp = responded_other_text;
1338 while (*cp) {
1339 while (spacechar(*cp))
1340 ++cp;
1341 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1342 /* RFC 2359 */
1343 mp->mb_flags |= MB_UIDPLUS;
1344 while (*cp && !spacechar(*cp))
1345 ++cp;
1349 return ok;
1352 static enum okay
1353 imap_auth(struct mailbox *mp, struct ccred *ccred)
1355 enum okay rv;
1356 NYD_ENTER;
1358 if (!(mp->mb_active & MB_PREAUTH)) {
1359 rv = OKAY;
1360 goto jleave;
1363 switch (ccred->cc_authtype) {
1364 case AUTHTYPE_LOGIN:
1365 rv = imap_login(mp, ccred);
1366 break;
1367 #ifdef HAVE_MD5
1368 case AUTHTYPE_CRAM_MD5:
1369 rv = imap_cram_md5(mp, ccred);
1370 break;
1371 #endif
1372 #ifdef HAVE_GSSAPI
1373 case AUTHTYPE_GSSAPI:
1374 rv = _imap_gssapi(mp, ccred);
1375 break;
1376 #endif
1377 default:
1378 rv = STOP;
1379 break;
1381 jleave:
1382 NYD_LEAVE;
1383 return rv;
1386 #ifdef HAVE_MD5
1387 static enum okay
1388 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1390 char o[LINESIZE], *cp;
1391 FILE *queuefp = NULL;
1392 enum okay rv = STOP;
1393 NYD_ENTER;
1395 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1396 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1397 imap_answer(mp, 1);
1398 if (response_type != RESPONSE_CONT)
1399 goto jleave;
1401 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1402 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1403 while (mp->mb_active & MB_COMD)
1404 rv = imap_answer(mp, 1);
1405 jleave:
1406 NYD_LEAVE;
1407 return rv;
1409 #endif /* HAVE_MD5 */
1411 static enum okay
1412 imap_login(struct mailbox *mp, struct ccred *ccred)
1414 char o[LINESIZE];
1415 FILE *queuefp = NULL;
1416 enum okay rv = STOP;
1417 NYD_ENTER;
1419 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1420 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1421 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1422 while (mp->mb_active & MB_COMD)
1423 rv = imap_answer(mp, 1);
1424 jleave:
1425 NYD_LEAVE;
1426 return rv;
1429 #ifdef HAVE_GSSAPI
1430 # include "imap_gssapi.h"
1431 #endif
1433 FL enum okay
1434 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1435 enum fedit_mode fm)
1437 char o[LINESIZE];
1438 char const *qname, *cp;
1439 FILE *queuefp;
1440 enum okay ok;
1441 NYD_X;
1442 UNUSED(size);
1444 ok = STOP;
1445 queuefp = NULL;
1447 if((qname = imap_path_quote(mp, mbx)) == NULL)
1448 goto jleave;
1450 ok = OKAY;
1452 mp->mb_uidvalidity = 0;
1453 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1454 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1455 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1456 while (mp->mb_active & MB_COMD) {
1457 ok = imap_answer(mp, 1);
1458 if (response_status != RESPONSE_OTHER &&
1459 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1460 mp->mb_uidvalidity = atol(&cp[13]);
1462 *cnt = (had_exists > 0) ? had_exists : 0;
1463 if (response_status != RESPONSE_OTHER &&
1464 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1465 mp->mb_perm = 0;
1466 jleave:
1467 return ok;
1470 static enum okay
1471 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1473 char o[LINESIZE];
1474 FILE *queuefp = NULL;
1475 char const *cp;
1476 struct message *m;
1477 unsigned x = X, y = Y, n;
1478 NYD_X;
1480 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1481 IMAP_OUT(o, MB_COMD, return STOP)
1482 while (mp->mb_active & MB_COMD) {
1483 imap_answer(mp, 1);
1484 if (response_status == RESPONSE_OTHER &&
1485 response_other == MESSAGE_DATA_FETCH) {
1486 n = responded_other_number;
1487 if (n < x || n > y)
1488 continue;
1489 m = &message[n-1];
1490 m->m_xsize = 0;
1491 } else
1492 continue;
1494 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1495 cp += 5;
1496 while (*cp == ' ')
1497 cp++;
1498 if (*cp == '(')
1499 imap_getflags(cp, &cp, &m->m_flag);
1502 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1503 m->m_uid = strtoul(&cp[4], NULL, 10);
1504 getcache1(mp, m, NEED_UNSPEC, 1);
1505 m->m_flag &= ~MHIDDEN;
1508 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1509 x++;
1510 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1511 y--;
1512 if (x <= y) {
1513 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1514 tag(1), x, y);
1515 IMAP_OUT(o, MB_COMD, return STOP)
1516 while (mp->mb_active & MB_COMD) {
1517 imap_answer(mp, 1);
1518 if (response_status == RESPONSE_OTHER &&
1519 response_other == MESSAGE_DATA_FETCH) {
1520 n = responded_other_number;
1521 if (n < x || n > y)
1522 continue;
1523 m = &message[n-1];
1524 } else
1525 continue;
1526 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1527 m->m_xsize = strtol(&cp[12], NULL, 10);
1528 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1529 m->m_time = imap_read_date_time(&cp[13]);
1533 srelax_hold();
1534 for (n = X; n <= Y; ++n) {
1535 putcache(mp, &message[n-1]);
1536 srelax();
1538 srelax_rele();
1539 return OKAY;
1542 static void
1543 imap_init(struct mailbox *mp, int n)
1545 struct message *m;
1546 NYD_ENTER;
1547 UNUSED(mp);
1549 m = message + n;
1550 m->m_flag = MUSED | MNOFROM;
1551 m->m_block = 0;
1552 m->m_offset = 0;
1553 NYD_LEAVE;
1556 static void
1557 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1559 struct message *omessage = 0;
1560 int i, omsgCount = 0;
1561 enum okay dequeued = STOP;
1562 NYD_ENTER;
1564 if (nmail || transparent) {
1565 omessage = message;
1566 omsgCount = msgCount;
1568 if (nmail)
1569 dequeued = rec_dequeue();
1571 if (had_exists >= 0) {
1572 if (dequeued != OKAY)
1573 msgCount = had_exists;
1574 had_exists = -1;
1576 if (had_expunge >= 0) {
1577 if (dequeued != OKAY)
1578 msgCount -= had_expunge;
1579 had_expunge = -1;
1582 if (nmail && expunged_messages)
1583 printf("Expunged %ld message%s.\n", expunged_messages,
1584 (expunged_messages != 1 ? "s" : ""));
1585 *prevcount = omsgCount - expunged_messages;
1586 expunged_messages = 0;
1587 if (msgCount < 0) {
1588 fputs("IMAP error: Negative message count\n", stderr);
1589 msgCount = 0;
1592 if (dequeued != OKAY) {
1593 message = scalloc(msgCount + 1, sizeof *message);
1594 for (i = 0; i < msgCount; i++)
1595 imap_init(mp, i);
1596 if (!nmail && mp->mb_type == MB_IMAP)
1597 initcache(mp);
1598 if (msgCount > 0)
1599 imap_flags(mp, 1, msgCount);
1600 message[msgCount].m_size = 0;
1601 message[msgCount].m_lines = 0;
1602 rec_rmqueue();
1604 if (nmail || transparent)
1605 transflags(omessage, omsgCount, transparent);
1606 else
1607 setdot(message);
1608 NYD_LEAVE;
1611 FL int
1612 imap_setfile(const char *xserver, enum fedit_mode fm)
1614 struct url url;
1615 int rv;
1616 NYD_ENTER;
1618 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1619 rv = 1;
1620 goto jleave;
1622 if (!ok_blook(v15_compat) &&
1623 (!url.url_had_user || url.url_pass.s != NULL))
1624 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1626 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1627 rv = _imap_setfile1(&url, fm, 0);
1628 jleave:
1629 NYD_LEAVE;
1630 return rv;
1633 static bool_t
1634 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1636 bool_t rv = FAL0;
1637 NYD_ENTER;
1639 if (ok_blook(v15_compat))
1640 rv = ccred_lookup(ccredp, urlp);
1641 else {
1642 char *var, *old,
1643 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1645 if ((var = mbp->mb_imap_pass) != NULL) {
1646 var = savecat("password-", xuhp);
1647 if ((old = vok_vlook(var)) != NULL)
1648 old = sstrdup(old);
1649 vok_vset(var, mbp->mb_imap_pass);
1651 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1652 if (var != NULL) {
1653 if (old != NULL) {
1654 vok_vset(var, old);
1655 free(old);
1656 } else
1657 vok_vclear(var);
1661 NYD_LEAVE;
1662 return rv;
1665 static int
1666 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1667 int volatile transparent)
1669 struct sock so;
1670 struct ccred ccred;
1671 sighandler_type volatile saveint, savepipe;
1672 char const *cp;
1673 int rv;
1674 int volatile prevcount = 0;
1675 enum mbflags same_flags;
1676 NYD_ENTER;
1678 if (fm & FEDIT_NEWMAIL) {
1679 saveint = safe_signal(SIGINT, SIG_IGN);
1680 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1681 if (saveint != SIG_IGN)
1682 safe_signal(SIGINT, imapcatch);
1683 if (savepipe != SIG_IGN)
1684 safe_signal(SIGPIPE, imapcatch);
1685 imaplock = 1;
1686 goto jnmail;
1689 same_flags = mb.mb_flags;
1690 same_imap_account = 0;
1691 if (mb.mb_imap_account != NULL &&
1692 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1693 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1694 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1695 disconnected(mb.mb_imap_account) == 0) {
1696 same_imap_account = 1;
1697 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1699 goto jduppass;
1700 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1701 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1702 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1703 jduppass:
1705 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1709 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1710 free(mb.mb_imap_pass);
1711 mb.mb_imap_pass = NULL;
1713 if (!_imap_getcred(&mb, &ccred, urlp)) {
1714 rv = -1;
1715 goto jleave;
1718 so.s_fd = -1;
1719 if (!same_imap_account) {
1720 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1721 rv = -1;
1722 goto jleave;
1724 } else
1725 so = mb.mb_sock;
1726 if (!transparent)
1727 quit();
1729 if (fm & FEDIT_SYSBOX)
1730 pstate &= ~PS_EDIT;
1731 else
1732 pstate |= PS_EDIT;
1733 if (mb.mb_imap_account != NULL)
1734 free(mb.mb_imap_account);
1735 if (mb.mb_imap_pass != NULL)
1736 free(mb.mb_imap_pass);
1737 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1738 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1739 * TODO object, and mailbox will naturally have an URL and credentials */
1740 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1742 if (!same_imap_account) {
1743 if (mb.mb_sock.s_fd >= 0)
1744 sclose(&mb.mb_sock);
1746 same_imap_account = 0;
1748 if (!transparent) {
1749 if (mb.mb_itf) {
1750 fclose(mb.mb_itf);
1751 mb.mb_itf = NULL;
1753 if (mb.mb_otf) {
1754 fclose(mb.mb_otf);
1755 mb.mb_otf = NULL;
1757 if (mb.mb_imap_mailbox != NULL)
1758 free(mb.mb_imap_mailbox);
1759 assert(urlp->url_path.s != NULL);
1760 imap_delim_init(&mb, urlp);
1761 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1762 initbox(savecatsep(urlp->url_p_eu_h_p,
1763 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1764 mb.mb_imap_mailbox));
1766 mb.mb_type = MB_VOID;
1767 mb.mb_active = MB_NONE;
1769 imaplock = 1;
1770 saveint = safe_signal(SIGINT, SIG_IGN);
1771 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1772 if (sigsetjmp(imapjmp, 1)) {
1773 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1774 sclose(&mb.mb_sock);
1775 safe_signal(SIGINT, saveint);
1776 safe_signal(SIGPIPE, savepipe);
1777 imaplock = 0;
1779 mb.mb_type = MB_VOID;
1780 mb.mb_active = MB_NONE;
1781 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1782 goto jleave;
1784 if (saveint != SIG_IGN)
1785 safe_signal(SIGINT, imapcatch);
1786 if (savepipe != SIG_IGN)
1787 safe_signal(SIGPIPE, imapcatch);
1789 if (mb.mb_sock.s_fd < 0) {
1790 if (disconnected(mb.mb_imap_account)) {
1791 if (cache_setptr(fm, transparent) == STOP)
1792 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1793 goto jdone;
1795 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1796 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1797 savealrm = safe_signal(SIGALRM, imapalarm);
1798 alarm(imapkeepalive);
1802 mb.mb_sock = so;
1803 mb.mb_sock.s_desc = "IMAP";
1804 mb.mb_sock.s_onclose = imap_timer_off;
1805 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1806 sclose(&mb.mb_sock);
1807 imap_timer_off();
1808 safe_signal(SIGINT, saveint);
1809 safe_signal(SIGPIPE, savepipe);
1810 imaplock = 0;
1811 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1812 goto jleave;
1814 } else /* same account */
1815 mb.mb_flags |= same_flags;
1817 if (options & OPT_R_FLAG)
1818 fm |= FEDIT_RDONLY;
1819 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1820 mb.mb_type = MB_IMAP;
1821 cache_dequeue(&mb);
1822 assert(urlp->url_path.s != NULL);
1823 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1824 /*sclose(&mb.mb_sock);
1825 imap_timer_off();*/
1826 safe_signal(SIGINT, saveint);
1827 safe_signal(SIGPIPE, savepipe);
1828 imaplock = 0;
1829 mb.mb_type = MB_VOID;
1830 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1831 goto jleave;
1834 jnmail:
1835 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1836 UNVOLATILE(&prevcount));
1837 jdone:
1838 setmsize(msgCount);
1839 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1840 pstate &= ~PS_SAW_COMMAND;
1841 safe_signal(SIGINT, saveint);
1842 safe_signal(SIGPIPE, savepipe);
1843 imaplock = 0;
1845 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1846 purgecache(&mb, message, msgCount);
1847 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1848 mb.mb_threaded = 0;
1849 c_sort((void*)-1);
1852 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1853 mb.mb_type == MB_CACHE)) {
1854 rv = (msgCount == 0);
1855 goto jleave;
1858 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1859 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1860 !ok_blook(emptystart))
1861 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1862 rv = 1;
1863 goto jleave;
1866 if (fm & FEDIT_NEWMAIL)
1867 newmailinfo(prevcount);
1868 rv = 0;
1869 jleave:
1870 NYD_LEAVE;
1871 return rv;
1874 static int
1875 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1876 int need, const char *head, size_t headsize, long headlines)
1878 char *line = NULL, *lp;
1879 size_t linesize = 0, linelen, size = 0;
1880 int emptyline = 0, lines = 0, excess = 0;
1881 off_t offset;
1882 NYD_ENTER;
1884 fseek(mp->mb_otf, 0L, SEEK_END);
1885 offset = ftell(mp->mb_otf);
1887 if (head)
1888 fwrite(head, 1, headsize, mp->mb_otf);
1890 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1891 lp = line;
1892 if (linelen > expected) {
1893 excess = linelen - expected;
1894 linelen = expected;
1896 /* TODO >>
1897 * Need to mask 'From ' lines. This cannot be done properly
1898 * since some servers pass them as 'From ' and others as
1899 * '>From '. Although one could identify the first kind of
1900 * server in principle, it is not possible to identify the
1901 * second as '>From ' may also come from a server of the
1902 * first type as actual data. So do what is absolutely
1903 * necessary only - mask 'From '.
1905 * If the line is the first line of the message header, it
1906 * is likely a real 'From ' line. In this case, it is just
1907 * ignored since it violates all standards.
1908 * TODO can the latter *really* happen??
1909 * TODO <<
1911 /* Since we simply copy over data without doing any transfer
1912 * encoding reclassification/adjustment we *have* to perform
1913 * RFC 4155 compliant From_ quoting here */
1914 if (emptyline && is_head(lp, linelen, FAL0)) {
1915 fputc('>', mp->mb_otf);
1916 ++size;
1918 emptyline = 0;
1919 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1920 if (linelen > 2) {
1921 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1922 size += linelen - 1;
1923 } else {
1924 emptyline = 1;
1925 ++size;
1927 fputc('\n', mp->mb_otf);
1928 } else {
1929 fwrite(lp, 1, linelen, mp->mb_otf);
1930 size += linelen;
1932 ++lines;
1933 if ((expected -= linelen) <= 0)
1934 break;
1936 if (!emptyline) {
1937 /* This is very ugly; but some IMAP daemons don't end a
1938 * message with \r\n\r\n, and we need \n\n for mbox format */
1939 fputc('\n', mp->mb_otf);
1940 ++lines;
1941 ++size;
1943 fflush(mp->mb_otf);
1945 if (m != NULL) {
1946 m->m_size = size + headsize;
1947 m->m_lines = lines + headlines;
1948 m->m_block = mailx_blockof(offset);
1949 m->m_offset = mailx_offsetof(offset);
1950 switch (need) {
1951 case NEED_HEADER:
1952 m->m_have |= HAVE_HEADER;
1953 break;
1954 case NEED_BODY:
1955 m->m_have |= HAVE_HEADER | HAVE_BODY;
1956 m->m_xlines = m->m_lines;
1957 m->m_xsize = m->m_size;
1958 break;
1961 free(line);
1962 NYD_LEAVE;
1963 return excess;
1966 static void
1967 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1968 const char *head, size_t headsize, long headlines)
1970 off_t offset;
1971 size_t len;
1972 NYD_ENTER;
1974 len = strlen(str);
1975 fseek(mp->mb_otf, 0L, SEEK_END);
1976 offset = ftell(mp->mb_otf);
1977 if (head)
1978 fwrite(head, 1, headsize, mp->mb_otf);
1979 if (len > 0) {
1980 fwrite(str, 1, len, mp->mb_otf);
1981 fputc('\n', mp->mb_otf);
1982 ++len;
1984 fflush(mp->mb_otf);
1986 if (m != NULL) {
1987 m->m_size = headsize + len;
1988 m->m_lines = headlines + 1;
1989 m->m_block = mailx_blockof(offset);
1990 m->m_offset = mailx_offsetof(offset);
1991 m->m_have |= HAVE_HEADER | HAVE_BODY;
1992 m->m_xlines = m->m_lines;
1993 m->m_xsize = m->m_size;
1995 NYD_LEAVE;
1998 static enum okay
1999 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2001 char o[LINESIZE];
2002 struct message mt;
2003 sighandler_type volatile saveint, savepipe;
2004 char * volatile head;
2005 char const *cp, *loc, * volatile item, * volatile resp;
2006 size_t expected;
2007 size_t volatile headsize;
2008 int number;
2009 FILE *queuefp;
2010 long volatile headlines;
2011 long n;
2012 ul_i volatile u;
2013 enum okay ok;
2014 NYD_X;
2016 saveint = savepipe = SIG_IGN;
2017 head = NULL;
2018 cp = loc = item = resp = NULL;
2019 headsize = 0;
2020 number = (int)PTR2SIZE(m - message + 1);
2021 queuefp = NULL;
2022 headlines = 0;
2023 n = -1;
2024 u = 0;
2025 ok = STOP;
2027 if (getcache(mp, m, need) == OKAY)
2028 return OKAY;
2029 if (mp->mb_type == MB_CACHE) {
2030 n_err(_("Message %lu not available\n"), (ul_i)number);
2031 return STOP;
2034 if (mp->mb_sock.s_fd < 0) {
2035 n_err(_("IMAP connection closed\n"));
2036 return STOP;
2039 switch (need) {
2040 case NEED_HEADER:
2041 resp = item = "RFC822.HEADER";
2042 break;
2043 case NEED_BODY:
2044 item = "BODY.PEEK[]";
2045 resp = "BODY[]";
2046 if (m->m_flag & HAVE_HEADER && m->m_size) {
2047 char *hdr = smalloc(m->m_size);
2048 fflush(mp->mb_otf);
2049 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2050 SEEK_SET) < 0 ||
2051 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2052 free(hdr);
2053 break;
2055 head = hdr;
2056 headsize = m->m_size;
2057 headlines = m->m_lines;
2058 item = "BODY.PEEK[TEXT]";
2059 resp = "BODY[TEXT]";
2061 break;
2062 case NEED_UNSPEC:
2063 return STOP;
2066 imaplock = 1;
2067 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2068 if (sigsetjmp(imapjmp, 1)) {
2069 safe_signal(SIGINT, saveint);
2070 safe_signal(SIGPIPE, savepipe);
2071 imaplock = 0;
2072 return STOP;
2074 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2075 safe_signal(SIGINT, &_imap_maincatch);
2076 if (savepipe != SIG_IGN)
2077 safe_signal(SIGPIPE, imapcatch);
2079 if (m->m_uid)
2080 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2081 tag(1), m->m_uid, item);
2082 else {
2083 if (check_expunged() == STOP)
2084 goto out;
2085 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2087 IMAP_OUT(o, MB_COMD, goto out)
2088 for (;;) {
2089 ok = imap_answer(mp, 1);
2090 if (ok == STOP)
2091 break;
2092 if (response_status != RESPONSE_OTHER ||
2093 response_other != MESSAGE_DATA_FETCH)
2094 continue;
2095 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2096 continue;
2097 if (m->m_uid) {
2098 if ((cp = asccasestr(responded_other_text, "UID "))) {
2099 u = atol(&cp[4]);
2100 n = 0;
2101 } else {
2102 n = -1;
2103 u = 0;
2105 } else
2106 n = responded_other_number;
2107 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2108 if (m->m_uid ? m->m_uid != u : n != number)
2109 continue;
2110 if ((cp = strchr(loc, '"')) != NULL) {
2111 cp = imap_unquotestr(cp);
2112 imap_putstr(mp, m, cp, head, headsize, headlines);
2113 } else {
2114 m->m_have |= HAVE_HEADER|HAVE_BODY;
2115 m->m_xlines = m->m_lines;
2116 m->m_xsize = m->m_size;
2118 goto out;
2120 expected = atol(&cp[1]);
2121 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2122 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2123 continue;
2125 mt = *m;
2126 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2127 if (n >= 0) {
2128 commitmsg(mp, m, &mt, mt.m_have);
2129 break;
2131 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2132 if (options & OPT_VERBVERB)
2133 fputs(imapbuf, stderr);
2134 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2135 u = atol(&cp[4]);
2136 if (u == m->m_uid) {
2137 commitmsg(mp, m, &mt, mt.m_have);
2138 break;
2143 out:
2144 while (mp->mb_active & MB_COMD)
2145 ok = imap_answer(mp, 1);
2147 if (saveint != SIG_IGN)
2148 safe_signal(SIGINT, saveint);
2149 if (savepipe != SIG_IGN)
2150 safe_signal(SIGPIPE, savepipe);
2151 imaplock--;
2153 if (ok == OKAY)
2154 putcache(mp, m);
2155 if (head != NULL)
2156 free(head);
2157 if (interrupts)
2158 onintr(0);
2159 return ok;
2162 FL enum okay
2163 imap_header(struct message *m)
2165 enum okay rv;
2166 NYD_ENTER;
2168 rv = imap_get(&mb, m, NEED_HEADER);
2169 NYD_LEAVE;
2170 return rv;
2174 FL enum okay
2175 imap_body(struct message *m)
2177 enum okay rv;
2178 NYD_ENTER;
2180 rv = imap_get(&mb, m, NEED_BODY);
2181 NYD_LEAVE;
2182 return rv;
2185 static void
2186 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2187 enum havespec have)
2189 NYD_ENTER;
2190 tomp->m_size = frommp->m_size;
2191 tomp->m_lines = frommp->m_lines;
2192 tomp->m_block = frommp->m_block;
2193 tomp->m_offset = frommp->m_offset;
2194 tomp->m_have = have;
2195 if (have & HAVE_BODY) {
2196 tomp->m_xlines = frommp->m_lines;
2197 tomp->m_xsize = frommp->m_size;
2199 putcache(mp, tomp);
2200 NYD_LEAVE;
2203 static enum okay
2204 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2206 /* bot > topp */
2207 char o[LINESIZE];
2208 char const *cp;
2209 struct message mt;
2210 size_t expected;
2211 int n = 0, u;
2212 FILE *queuefp = NULL;
2213 enum okay ok;
2214 NYD_X;
2216 if (m[bot].m_uid)
2217 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2218 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2219 else {
2220 if (check_expunged() == STOP)
2221 return STOP;
2222 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2223 tag(1), bot, topp);
2225 IMAP_OUT(o, MB_COMD, return STOP)
2227 srelax_hold();
2228 for (;;) {
2229 ok = imap_answer(mp, 1);
2230 if (response_status != RESPONSE_OTHER)
2231 break;
2232 if (response_other != MESSAGE_DATA_FETCH)
2233 continue;
2234 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2235 srelax_rele();
2236 return STOP;
2238 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2239 continue;
2240 expected = atol(&cp[1]);
2241 if (m[bot-1].m_uid) {
2242 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2243 u = atoi(&cp[4]);
2244 for (n = bot; n <= topp; n++)
2245 if ((unsigned long)u == m[n-1].m_uid)
2246 break;
2247 if (n > topp) {
2248 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2249 continue;
2251 } else
2252 n = -1;
2253 } else {
2254 n = responded_other_number;
2255 if (n <= 0 || n > msgCount) {
2256 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2257 continue;
2260 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2261 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
2262 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
2263 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2264 if (options & OPT_VERBVERB)
2265 fputs(imapbuf, stderr);
2266 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2267 u = atoi(&cp[4]);
2268 for (n = bot; n <= topp; n++)
2269 if ((unsigned long)u == m[n-1].m_uid)
2270 break;
2271 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
2272 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
2273 n = 0;
2276 srelax();
2278 srelax_rele();
2280 while (mp->mb_active & MB_COMD)
2281 ok = imap_answer(mp, 1);
2282 return ok;
2285 FL void
2286 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2288 sighandler_type saveint, savepipe;
2289 /*enum okay ok = STOP;*/
2290 int i, chunk = 256;
2291 NYD_X;
2293 if (mb.mb_type == MB_CACHE)
2294 return;
2295 if (bot < 1)
2296 bot = 1;
2297 if (topp > msgCount)
2298 topp = msgCount;
2299 for (i = bot; i < topp; i++) {
2300 if (message[i-1].m_have & HAVE_HEADER ||
2301 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2302 bot = i+1;
2303 else
2304 break;
2306 for (i = topp; i > bot; i--) {
2307 if (message[i-1].m_have & HAVE_HEADER ||
2308 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2309 topp = i-1;
2310 else
2311 break;
2313 if (bot >= topp)
2314 return;
2316 imaplock = 1;
2317 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2318 safe_signal(SIGINT, &_imap_maincatch);
2319 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2320 if (sigsetjmp(imapjmp, 1) == 0) {
2321 if (savepipe != SIG_IGN)
2322 safe_signal(SIGPIPE, imapcatch);
2324 for (i = bot; i <= topp; i += chunk) {
2325 int j = i + chunk - 1;
2326 j = MIN(j, topp);
2327 if (visible(message + j))
2328 /*ok = */imap_fetchheaders(&mb, message, i, j);
2329 if (interrupts)
2330 onintr(0); /* XXX imaplock? */
2333 safe_signal(SIGINT, saveint);
2334 safe_signal(SIGPIPE, savepipe);
2335 imaplock = 0;
2338 static enum okay
2339 __imap_exit(struct mailbox *mp)
2341 char o[LINESIZE];
2342 FILE *queuefp = NULL;
2343 NYD_X;
2345 mp->mb_active |= MB_BYE;
2346 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2347 IMAP_OUT(o, MB_COMD, return STOP)
2348 IMAP_ANSWER()
2349 return OKAY;
2352 static enum okay
2353 imap_exit(struct mailbox *mp)
2355 enum okay rv;
2356 NYD_ENTER;
2358 rv = __imap_exit(mp);
2359 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2360 free(mp->mb_imap_pass);
2361 free(mp->mb_imap_account);
2362 free(mp->mb_imap_mailbox);
2363 if (mp->mb_cache_directory != NULL)
2364 free(mp->mb_cache_directory);
2365 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2366 mp->mb_imap_account =
2367 mp->mb_imap_mailbox =
2368 mp->mb_cache_directory = "";
2369 #else
2370 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2371 mp->mb_imap_mailbox = NULL;
2372 mp->mb_cache_directory = NULL;
2373 #endif
2374 #endif
2375 sclose(&mp->mb_sock);
2376 NYD_LEAVE;
2377 return rv;
2380 static enum okay
2381 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2383 NYD_ENTER;
2384 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2385 if (mp->mb_type == MB_IMAP)
2386 delcache(mp, m);
2387 NYD_LEAVE;
2388 return OKAY;
2391 static enum okay
2392 imap_close(struct mailbox *mp)
2394 char o[LINESIZE];
2395 FILE *queuefp = NULL;
2396 NYD_X;
2398 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2399 IMAP_OUT(o, MB_COMD, return STOP)
2400 IMAP_ANSWER()
2401 return OKAY;
2404 static enum okay
2405 imap_update(struct mailbox *mp)
2407 struct message *m;
2408 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2409 NYD_ENTER;
2411 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
2412 holdbits();
2413 c = 0;
2414 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2415 if (m->m_flag & MBOX)
2416 ++c;
2417 if (c > 0)
2418 if (makembox() == STOP)
2419 goto jbypass;
2422 gotcha = held = 0;
2423 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2424 if (mp->mb_perm == 0)
2425 dodel = 0;
2426 else if (pstate & PS_EDIT)
2427 dodel = ((m->m_flag & MDELETED) != 0);
2428 else
2429 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2431 /* Fetch the result after around each 800 STORE commands
2432 * sent (approx. 32k data sent). Otherwise, servers will
2433 * try to flush the return queue at some point, leading
2434 * to a deadlock if we are still writing commands but not
2435 * reading their results */
2436 needstat = stored > 0 && stored % 800 == 0;
2437 /* Even if this message has been deleted, continue
2438 * to set further flags. This is necessary to support
2439 * Gmail semantics, where "delete" actually means
2440 * "archive", and the flags are applied to the copy
2441 * in "All Mail" */
2442 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2443 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2444 stored++;
2446 if (m->m_flag & MFLAG) {
2447 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2448 stored++;
2450 if (m->m_flag & MUNFLAG) {
2451 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2452 stored++;
2454 if (m->m_flag & MANSWER) {
2455 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2456 stored++;
2458 if (m->m_flag & MUNANSWER) {
2459 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2460 stored++;
2462 if (m->m_flag & MDRAFT) {
2463 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2464 stored++;
2466 if (m->m_flag & MUNDRAFT) {
2467 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2468 stored++;
2471 if (dodel) {
2472 imap_delete(mp, m-message+1, m, needstat);
2473 stored++;
2474 gotcha++;
2475 } else if (mp->mb_type != MB_CACHE ||
2476 (!(pstate & PS_EDIT) &&
2477 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2478 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2479 (MPRESERVE | MTOUCH) ||
2480 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
2481 held++;
2482 if (m->m_flag & MNEW) {
2483 m->m_flag &= ~MNEW;
2484 m->m_flag |= MSTATUS;
2487 jbypass:
2488 if (gotcha)
2489 imap_close(mp);
2491 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2492 if (!(m->m_flag & MUNLINKED) &&
2493 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2494 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2495 putcache(mp, m);
2496 modflags++;
2499 /* XXX should be readonly (but our IMAP code is weird...) */
2500 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST)) &&
2501 mb.mb_perm != 0) {
2502 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2503 printf(_("\"%s\" "), displayname);
2504 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2505 ? _("complete\n") : _("updated.\n"));
2506 } else if (held && !(pstate & PS_EDIT)) {
2507 if (held == 1)
2508 printf(_("Held 1 message in %s\n"), displayname);
2509 else
2510 printf(_("Held %d messages in %s\n"), held, displayname);
2512 fflush(stdout);
2514 NYD_LEAVE;
2515 return OKAY;
2518 FL void
2519 imap_quit(void)
2521 sighandler_type volatile saveint, savepipe;
2522 NYD_ENTER;
2524 if (mb.mb_type == MB_CACHE) {
2525 imap_update(&mb);
2526 goto jleave;
2529 if (mb.mb_sock.s_fd < 0) {
2530 n_err(_("IMAP connection closed\n"));
2531 goto jleave;
2534 imaplock = 1;
2535 saveint = safe_signal(SIGINT, SIG_IGN);
2536 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2537 if (sigsetjmp(imapjmp, 1)) {
2538 safe_signal(SIGINT, saveint);
2539 safe_signal(SIGPIPE, saveint);
2540 imaplock = 0;
2541 goto jleave;
2543 if (saveint != SIG_IGN)
2544 safe_signal(SIGINT, imapcatch);
2545 if (savepipe != SIG_IGN)
2546 safe_signal(SIGPIPE, imapcatch);
2548 imap_update(&mb);
2549 if (!same_imap_account)
2550 imap_exit(&mb);
2552 safe_signal(SIGINT, saveint);
2553 safe_signal(SIGPIPE, savepipe);
2554 imaplock = 0;
2555 jleave:
2556 NYD_LEAVE;
2559 static enum okay
2560 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2561 int needstat)
2563 char o[LINESIZE];
2564 FILE *queuefp = NULL;
2565 NYD_X;
2567 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2568 return STOP;
2569 if (m->m_uid)
2570 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2571 tag(1), m->m_uid, c, sp);
2572 else {
2573 if (check_expunged() == STOP)
2574 return STOP;
2575 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2577 IMAP_OUT(o, MB_COMD, return STOP)
2578 if (needstat)
2579 IMAP_ANSWER()
2580 else
2581 mb.mb_active &= ~MB_COMD;
2582 if (queuefp != NULL)
2583 Fclose(queuefp);
2584 return OKAY;
2587 FL enum okay
2588 imap_undelete(struct message *m, int n)
2590 enum okay rv;
2591 NYD_ENTER;
2593 rv = imap_unstore(m, n, "\\Deleted");
2594 NYD_LEAVE;
2595 return rv;
2598 FL enum okay
2599 imap_unread(struct message *m, int n)
2601 enum okay rv;
2602 NYD_ENTER;
2604 rv = imap_unstore(m, n, "\\Seen");
2605 NYD_LEAVE;
2606 return rv;
2609 static enum okay
2610 imap_unstore(struct message *m, int n, const char *flag)
2612 sighandler_type saveint, savepipe;
2613 enum okay rv = STOP;
2614 NYD_ENTER;
2616 imaplock = 1;
2617 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2618 safe_signal(SIGINT, &_imap_maincatch);
2619 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2620 if (sigsetjmp(imapjmp, 1) == 0) {
2621 if (savepipe != SIG_IGN)
2622 safe_signal(SIGPIPE, imapcatch);
2624 rv = imap_store(&mb, m, n, '-', flag, 1);
2626 safe_signal(SIGINT, saveint);
2627 safe_signal(SIGPIPE, savepipe);
2628 imaplock = 0;
2630 NYD_LEAVE;
2631 if (interrupts)
2632 onintr(0);
2633 return rv;
2636 static const char *
2637 tag(int new)
2639 static char ts[20];
2640 static long n;
2641 NYD2_ENTER;
2643 if (new)
2644 ++n;
2645 snprintf(ts, sizeof ts, "T%lu", n);
2646 NYD2_LEAVE;
2647 return ts;
2650 FL int
2651 c_imapcodec(void *v){
2652 bool_t err;
2653 char const **argv, *cp, *res;
2654 NYD_ENTER;
2656 if(is_prefix(cp = *(argv = v), "encode")){
2657 while((cp = *++argv) != NULL){
2658 res = imap_path_normalize(NULL, cp);
2659 res = imap_path_encode(res, &err);
2660 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2661 cp, strlen(cp), (err ? "ERROR " : ""), res, strlen(res));
2663 }else if(is_prefix(cp, "decode")){
2664 struct str in, out;
2666 while((cp = *++argv) != NULL){
2667 res = imap_path_normalize(NULL, cp);
2668 res = imap_path_decode(res, &err);
2669 in.l = strlen(in.s = UNCONST(res)); /* logical */
2670 makeprint(&in, &out);
2671 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2672 cp, strlen(cp), (err ? "ERROR " : ""), out.s, in.l);
2673 free(out.s);
2675 }else{
2676 n_err(_("`imapcodec': invalid subcommand: %s\n"), *argv);
2677 cp = NULL;
2679 NYD_LEAVE;
2680 return (cp != NULL ? OKAY : STOP);
2683 FL int
2684 c_imap_imap(void *vp)
2686 char o[LINESIZE];
2687 sighandler_type saveint, savepipe;
2688 struct mailbox *mp = &mb;
2689 FILE *queuefp = NULL;
2690 enum okay volatile ok = STOP;
2691 NYD_X;
2693 if (mp->mb_type != MB_IMAP) {
2694 printf("Not operating on an IMAP mailbox.\n");
2695 return 1;
2697 imaplock = 1;
2698 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2699 safe_signal(SIGINT, &_imap_maincatch);
2700 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2701 if (sigsetjmp(imapjmp, 1) == 0) {
2702 if (savepipe != SIG_IGN)
2703 safe_signal(SIGPIPE, imapcatch);
2705 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2706 IMAP_OUT(o, MB_COMD, goto out)
2707 while (mp->mb_active & MB_COMD) {
2708 ok = imap_answer(mp, 0);
2709 fputs(responded_text, stdout);
2712 out:
2713 safe_signal(SIGINT, saveint);
2714 safe_signal(SIGPIPE, savepipe);
2715 imaplock = 0;
2717 if (interrupts)
2718 onintr(0);
2719 return ok != OKAY;
2722 FL int
2723 imap_newmail(int nmail)
2725 NYD_ENTER;
2727 if (nmail && had_exists < 0 && had_expunge < 0) {
2728 imaplock = 1;
2729 imap_noop();
2730 imaplock = 0;
2733 if (had_exists == msgCount && had_expunge < 0)
2734 /* Some servers always respond with EXISTS to NOOP. If
2735 * the mailbox has been changed but the number of messages
2736 * has not, an EXPUNGE must also had been sent; otherwise,
2737 * nothing has changed */
2738 had_exists = -1;
2739 NYD_LEAVE;
2740 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2743 static char *
2744 imap_putflags(int f)
2746 const char *cp;
2747 char *buf, *bp;
2748 NYD2_ENTER;
2750 bp = buf = salloc(100);
2751 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2752 *bp++ = '(';
2753 if (f & MREAD) {
2754 if (bp[-1] != '(')
2755 *bp++ = ' ';
2756 for (cp = "\\Seen"; *cp; cp++)
2757 *bp++ = *cp;
2759 if (f & MFLAGGED) {
2760 if (bp[-1] != '(')
2761 *bp++ = ' ';
2762 for (cp = "\\Flagged"; *cp; cp++)
2763 *bp++ = *cp;
2765 if (f & MANSWERED) {
2766 if (bp[-1] != '(')
2767 *bp++ = ' ';
2768 for (cp = "\\Answered"; *cp; cp++)
2769 *bp++ = *cp;
2771 if (f & MDRAFT) {
2772 if (bp[-1] != '(')
2773 *bp++ = ' ';
2774 for (cp = "\\Draft"; *cp; cp++)
2775 *bp++ = *cp;
2777 *bp++ = ')';
2778 *bp++ = ' ';
2780 *bp = '\0';
2781 NYD2_LEAVE;
2782 return buf;
2785 static void
2786 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2788 NYD2_ENTER;
2789 while (*cp != ')') {
2790 if (*cp == '\\') {
2791 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2792 *f |= MREAD;
2793 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2794 *f |= MNEW;
2795 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2796 *f |= MDELETED;
2797 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2798 *f |= MFLAGGED;
2799 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2800 *f |= MANSWERED;
2801 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2802 *f |= MDRAFTED;
2804 cp++;
2807 if (xp != NULL)
2808 *xp = cp;
2809 NYD2_LEAVE;
2812 static enum okay
2813 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2814 long xsize, enum mflag flag, time_t t)
2816 char o[LINESIZE], *buf;
2817 size_t bufsize, buflen, cnt;
2818 long size, lines, ysize;
2819 char const *qname;
2820 bool_t twice;
2821 FILE *queuefp;
2822 enum okay rv;
2823 NYD_ENTER;
2825 rv = STOP;
2826 queuefp = NULL;
2827 twice = FAL0;
2828 buf = NULL;
2830 if((qname = imap_path_quote(mp, name)) == NULL)
2831 goto jleave;
2833 if (mp->mb_type == MB_CACHE) {
2834 queuefp = cache_queue(mp);
2835 if (queuefp == NULL) {
2836 buf = NULL;
2837 goto jleave;
2839 rv = OKAY;
2842 buf = smalloc(bufsize = LINESIZE);
2843 buflen = 0;
2844 jagain:
2845 size = xsize;
2846 cnt = fsize(fp);
2847 if (fseek(fp, off1, SEEK_SET) < 0) {
2848 rv = STOP;
2849 goto jleave;
2852 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2853 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2854 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2855 while (mp->mb_active & MB_COMD) {
2856 rv = imap_answer(mp, twice);
2857 if (response_type == RESPONSE_CONT)
2858 break;
2861 if (mp->mb_type != MB_CACHE && rv == STOP) {
2862 if (!twice)
2863 goto jtrycreate;
2864 else
2865 goto jleave;
2868 lines = ysize = 0;
2869 while (size > 0) {
2870 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2871 lines++;
2872 ysize += buflen;
2873 buf[buflen - 1] = '\r';
2874 buf[buflen] = '\n';
2875 if (mp->mb_type != MB_CACHE)
2876 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2877 else if (queuefp)
2878 fwrite(buf, 1, buflen+1, queuefp);
2879 size -= buflen + 1;
2881 if (mp->mb_type != MB_CACHE)
2882 swrite(&mp->mb_sock, "\r\n");
2883 else if (queuefp)
2884 fputs("\r\n", queuefp);
2885 while (mp->mb_active & MB_COMD) {
2886 rv = imap_answer(mp, 0);
2887 if (response_status == RESPONSE_NO /*&&
2888 ascncasecmp(responded_text,
2889 "[TRYCREATE] ", 12) == 0*/) {
2890 jtrycreate:
2891 if (twice) {
2892 rv = STOP;
2893 goto jleave;
2895 twice = TRU1;
2896 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2897 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2898 while (mp->mb_active & MB_COMD)
2899 rv = imap_answer(mp, 1);
2900 if (rv == STOP)
2901 goto jleave;
2902 imap_created_mailbox++;
2903 goto jagain;
2904 } else if (rv != OKAY)
2905 n_err(_("IMAP error: %s"), responded_text);
2906 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2907 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2909 jleave:
2910 if (queuefp != NULL)
2911 Fclose(queuefp);
2912 if (buf != NULL)
2913 free(buf);
2914 NYD_LEAVE;
2915 return rv;
2918 static enum okay
2919 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2921 char *buf, *bp, *lp;
2922 size_t bufsize, buflen, cnt;
2923 off_t off1 = -1, offs;
2924 int flag;
2925 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2926 time_t tim;
2927 long size;
2928 enum okay rv;
2929 NYD_ENTER;
2931 buf = smalloc(bufsize = LINESIZE);
2932 buflen = 0;
2933 cnt = fsize(fp);
2934 offs = ftell(fp);
2935 time(&tim);
2936 size = 0;
2938 for (flag = MNEW, state = _NLSEP;;) {
2939 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2941 if (bp == NULL ||
2942 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2943 is_head(buf, buflen, FAL0))) {
2944 if (off1 != (off_t)-1) {
2945 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2946 if (rv == STOP)
2947 goto jleave;
2948 fseek(fp, offs+buflen, SEEK_SET);
2950 off1 = offs + buflen;
2951 size = 0;
2952 flag = MNEW;
2953 state = _INHEAD;
2954 if (bp == NULL)
2955 break;
2956 tim = unixtime(buf);
2957 } else
2958 size += buflen+1;
2959 offs += buflen;
2961 state &= ~_NLSEP;
2962 if (buf[0] == '\n') {
2963 state &= ~_INHEAD;
2964 state |= _NLSEP;
2965 } else if (state & _INHEAD) {
2966 if (ascncasecmp(buf, "status", 6) == 0) {
2967 lp = &buf[6];
2968 while (whitechar(*lp))
2969 lp++;
2970 if (*lp == ':')
2971 while (*++lp != '\0')
2972 switch (*lp) {
2973 case 'R':
2974 flag |= MREAD;
2975 break;
2976 case 'O':
2977 flag &= ~MNEW;
2978 break;
2980 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2981 lp = &buf[8];
2982 while (whitechar(*lp))
2983 lp++;
2984 if (*lp == ':')
2985 while (*++lp != '\0')
2986 switch (*lp) {
2987 case 'F':
2988 flag |= MFLAGGED;
2989 break;
2990 case 'A':
2991 flag |= MANSWERED;
2992 break;
2993 case 'T':
2994 flag |= MDRAFTED;
2995 break;
3000 rv = OKAY;
3001 jleave:
3002 free(buf);
3003 NYD_LEAVE;
3004 return rv;
3007 FL enum okay
3008 imap_append(const char *xserver, FILE *fp)
3010 sighandler_type volatile saveint, savepipe;
3011 struct url url;
3012 struct ccred ccred;
3013 enum okay rv = STOP;
3014 NYD_ENTER;
3016 if (!url_parse(&url, CPROTO_IMAP, xserver))
3017 goto j_leave;
3018 if (!ok_blook(v15_compat) &&
3019 (!url.url_had_user || url.url_pass.s != NULL))
3020 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3021 assert(url.url_path.s != NULL);
3023 imaplock = 1;
3024 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3025 safe_signal(SIGINT, &_imap_maincatch);
3026 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3027 if (sigsetjmp(imapjmp, 1))
3028 goto jleave;
3029 if (savepipe != SIG_IGN)
3030 safe_signal(SIGPIPE, imapcatch);
3032 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3033 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3034 rv = imap_append0(&mb, url.url_path.s, fp);
3035 } else {
3036 struct mailbox mx;
3038 memset(&mx, 0, sizeof mx);
3040 if (!_imap_getcred(&mx, &ccred, &url))
3041 goto jleave;
3043 imap_delim_init(&mx, &url);
3044 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3046 if (disconnected(url.url_p_eu_h_p) == 0) {
3047 if (!sopen(&mx.mb_sock, &url))
3048 goto jfail;
3049 mx.mb_sock.s_desc = "IMAP";
3050 mx.mb_type = MB_IMAP;
3051 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3052 /* TODO the code now did
3053 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3054 * TODO though imap_mailbox is sfree()d and mbx
3055 * TODO is possibly even a constant
3056 * TODO i changed this to sstrdup() sofar, as is used
3057 * TODO somewhere else in this file for this! */
3058 if (imap_preauth(&mx, &url) != OKAY ||
3059 imap_auth(&mx, &ccred) != OKAY) {
3060 sclose(&mx.mb_sock);
3061 goto jfail;
3063 rv = imap_append0(&mx, url.url_path.s, fp);
3064 imap_exit(&mx);
3065 } else {
3066 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3067 mx.mb_type = MB_CACHE;
3068 rv = imap_append0(&mx, url.url_path.s, fp);
3070 jfail:
3074 jleave:
3075 safe_signal(SIGINT, saveint);
3076 safe_signal(SIGPIPE, savepipe);
3077 imaplock = 0;
3078 j_leave:
3079 NYD_LEAVE;
3080 if (interrupts)
3081 onintr(0);
3082 return rv;
3085 static enum okay
3086 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3087 struct list_item **lend, int level)
3089 char o[LINESIZE], *cp;
3090 struct list_item *lp;
3091 const char *qname, *bp;
3092 FILE *queuefp;
3093 enum okay ok;
3094 NYD_X;
3096 ok = STOP;
3097 queuefp = NULL;
3099 if((qname = imap_path_quote(mp, base)) == NULL)
3100 goto jleave;
3102 *list = *lend = NULL;
3103 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3104 IMAP_OUT(o, MB_COMD, goto jleave)
3105 while (mp->mb_active & MB_COMD) {
3106 ok = imap_answer(mp, 1);
3107 if (response_status == RESPONSE_OTHER &&
3108 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3109 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3110 lp = csalloc(1, sizeof *lp);
3111 lp->l_name = cp;
3112 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3113 ++cp;
3114 lp->l_base = *cp ? cp : savestr(base);
3115 lp->l_attr = list_attributes;
3116 lp->l_level = level+1;
3117 lp->l_delim = list_hierarchy_delimiter;
3118 if (*list && *lend) {
3119 (*lend)->l_next = lp;
3120 *lend = lp;
3121 } else
3122 *list = *lend = lp;
3125 jleave:
3126 return ok;
3129 static enum okay
3130 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3132 struct list_item *list, *lend, *lp, *lx, *ly;
3133 int n, depth;
3134 const char *bp;
3135 char *cp;
3136 enum okay rv;
3137 NYD_ENTER;
3139 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3140 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3141 goto jleave;
3142 rv = OKAY;
3143 if (list == NULL || lend == NULL)
3144 goto jleave;
3146 for (lp = list; lp; lp = lp->l_next)
3147 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3148 !(lp->l_attr & LIST_NOINFERIORS)) {
3149 cp = salloc((n = strlen(lp->l_name)) + 2);
3150 memcpy(cp, lp->l_name, n);
3151 cp[n] = lp->l_delim;
3152 cp[n+1] = '\0';
3153 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3154 lp->l_has_children = 1;
3155 if (strcmp(cp, lx->l_name) == 0)
3156 lx = lx->l_next;
3157 if (lx) {
3158 lend->l_next = lx;
3159 lend = ly;
3164 for (lp = list; lp; lp = lp->l_next) {
3165 if (strip) {
3166 cp = lp->l_name;
3167 for (bp = base; *bp && *bp == *cp; bp++)
3168 cp++;
3169 } else
3170 cp = lp->l_name;
3171 if (!(lp->l_attr & LIST_NOSELECT))
3172 fprintf(fp, "%s\n", *cp ? cp : base);
3173 else if (lp->l_has_children == 0)
3174 fprintf(fp, "%s%c\n", *cp ? cp : base,
3175 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3177 jleave:
3178 NYD_LEAVE;
3179 return rv;
3182 FL void
3183 imap_folders(const char * volatile name, int strip)
3185 sighandler_type saveint, savepipe;
3186 const char *fold, *cp, *sp;
3187 FILE * volatile fp;
3188 NYD_ENTER;
3190 cp = protbase(name);
3191 sp = mb.mb_imap_account;
3192 if (sp == NULL || strcmp(cp, sp)) {
3193 n_err(
3194 _("Cannot perform `folders' but when on the very IMAP "
3195 "account; the current one is\n `%s' -- "
3196 "try `folders @'\n"),
3197 (sp != NULL ? sp : _("[NONE]")));
3198 goto jleave;
3201 fold = imap_fileof(name);
3202 if (options & OPT_TTYOUT) {
3203 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
3204 0600)) == NULL) {
3205 n_perr(_("tmpfile"), 0);
3206 goto jleave;
3208 } else
3209 fp = stdout;
3211 imaplock = 1;
3212 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3213 safe_signal(SIGINT, &_imap_maincatch);
3214 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3215 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3216 goto junroll;
3217 if (savepipe != SIG_IGN)
3218 safe_signal(SIGPIPE, imapcatch);
3220 if (mb.mb_type == MB_CACHE)
3221 cache_list(&mb, fold, strip, fp);
3222 else
3223 imap_list(&mb, fold, strip, fp);
3225 imaplock = 0;
3226 if (interrupts) {
3227 if (options & OPT_TTYOUT)
3228 Fclose(fp);
3229 goto jleave;
3231 fflush(fp);
3233 if (options & OPT_TTYOUT) {
3234 rewind(fp);
3235 if (fsize(fp) > 0)
3236 dopr(fp);
3237 else
3238 n_err(_("Folder not found\n"));
3240 junroll:
3241 safe_signal(SIGINT, saveint);
3242 safe_signal(SIGPIPE, savepipe);
3243 if (options & OPT_TTYOUT)
3244 Fclose(fp);
3245 jleave:
3246 NYD_LEAVE;
3247 if (interrupts)
3248 onintr(0);
3251 static void
3252 dopr(FILE *fp)
3254 char o[LINESIZE];
3255 int c;
3256 long n = 0, mx = 0, columns, width;
3257 FILE *out;
3258 NYD_ENTER;
3260 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
3261 == NULL) {
3262 n_perr(_("tmpfile"), 0);
3263 goto jleave;
3266 while ((c = getc(fp)) != EOF) {
3267 if (c == '\n') {
3268 if (n > mx)
3269 mx = n;
3270 n = 0;
3271 } else
3272 ++n;
3274 rewind(fp);
3276 width = scrnwidth;
3277 if (mx < width / 2) {
3278 columns = width / (mx+2);
3279 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3280 } else
3281 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3282 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
3283 page_or_print(out, 0);
3284 Fclose(out);
3285 jleave:
3286 NYD_LEAVE;
3289 static enum okay
3290 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3292 char o[LINESIZE];
3293 const char *qname;
3294 bool_t twice, stored;
3295 FILE *queuefp;
3296 enum okay ok;
3297 NYD_X;
3299 ok = STOP;
3300 queuefp = NULL;
3301 twice = stored = FAL0;
3303 /* C99 */{
3304 size_t i;
3306 i = strlen(name = imap_fileof(name));
3307 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3308 name = savecat(name, "INBOX");
3309 if((qname = imap_path_quote(mp, name)) == NULL)
3310 goto jleave;
3313 if (mp->mb_type == MB_CACHE) {
3314 if ((queuefp = cache_queue(mp)) == NULL)
3315 goto jleave;
3316 ok = OKAY;
3319 /* Since it is not possible to set flags on the copy, recently
3320 * set flags must be set on the original to include it in the copy */
3321 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3322 imap_store(mp, m, n, '+', "\\Seen", 0);
3323 if (m->m_flag&MFLAG)
3324 imap_store(mp, m, n, '+', "\\Flagged", 0);
3325 if (m->m_flag&MUNFLAG)
3326 imap_store(mp, m, n, '-', "\\Flagged", 0);
3327 if (m->m_flag&MANSWER)
3328 imap_store(mp, m, n, '+', "\\Answered", 0);
3329 if (m->m_flag&MUNANSWER)
3330 imap_store(mp, m, n, '-', "\\Flagged", 0);
3331 if (m->m_flag&MDRAFT)
3332 imap_store(mp, m, n, '+', "\\Draft", 0);
3333 if (m->m_flag&MUNDRAFT)
3334 imap_store(mp, m, n, '-', "\\Draft", 0);
3335 again:
3336 if (m->m_uid)
3337 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3338 else {
3339 if (check_expunged() == STOP)
3340 goto out;
3341 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3343 IMAP_OUT(o, MB_COMD, goto out)
3344 while (mp->mb_active & MB_COMD)
3345 ok = imap_answer(mp, twice);
3347 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3348 response_status == RESPONSE_OK)
3349 imap_copyuid(mp, m, name);
3351 if (response_status == RESPONSE_NO && !twice) {
3352 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3353 IMAP_OUT(o, MB_COMD, goto out)
3354 while (mp->mb_active & MB_COMD)
3355 ok = imap_answer(mp, 1);
3356 if (ok == OKAY) {
3357 imap_created_mailbox++;
3358 goto again;
3361 twice = TRU1;
3363 if (queuefp != NULL)
3364 Fclose(queuefp);
3366 /* ... and reset the flag to its initial value so that the 'exit'
3367 * command still leaves the message unread */
3368 out:
3369 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3370 imap_store(mp, m, n, '-', "\\Seen", 0);
3371 stored = TRU1;
3373 if (m->m_flag & MFLAG) {
3374 imap_store(mp, m, n, '-', "\\Flagged", 0);
3375 stored = TRU1;
3377 if (m->m_flag & MUNFLAG) {
3378 imap_store(mp, m, n, '+', "\\Flagged", 0);
3379 stored = TRU1;
3381 if (m->m_flag & MANSWER) {
3382 imap_store(mp, m, n, '-', "\\Answered", 0);
3383 stored = TRU1;
3385 if (m->m_flag & MUNANSWER) {
3386 imap_store(mp, m, n, '+', "\\Answered", 0);
3387 stored = TRU1;
3389 if (m->m_flag & MDRAFT) {
3390 imap_store(mp, m, n, '-', "\\Draft", 0);
3391 stored = TRU1;
3393 if (m->m_flag & MUNDRAFT) {
3394 imap_store(mp, m, n, '+', "\\Draft", 0);
3395 stored = TRU1;
3397 if (stored) {
3398 mp->mb_active |= MB_COMD;
3399 (void)imap_finish(mp);
3401 jleave:
3402 return ok;
3405 FL enum okay
3406 imap_copy(struct message *m, int n, const char *name)
3408 sighandler_type saveint, savepipe;
3409 enum okay rv = STOP;
3410 NYD_ENTER;
3412 imaplock = 1;
3413 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3414 safe_signal(SIGINT, &_imap_maincatch);
3415 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3416 if (sigsetjmp(imapjmp, 1) == 0) {
3417 if (savepipe != SIG_IGN)
3418 safe_signal(SIGPIPE, imapcatch);
3420 rv = imap_copy1(&mb, m, n, name);
3422 safe_signal(SIGINT, saveint);
3423 safe_signal(SIGPIPE, savepipe);
3424 imaplock = 0;
3426 NYD_LEAVE;
3427 if (interrupts)
3428 onintr(0);
3429 return rv;
3432 static enum okay
3433 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3434 unsigned long *olduid, unsigned long *newuid)
3436 char *xp, *yp, *zp;
3437 enum okay rv;
3438 NYD_ENTER;
3440 *uidvalidity = strtoul(cp, &xp, 10);
3441 *olduid = strtoul(xp, &yp, 10);
3442 *newuid = strtoul(yp, &zp, 10);
3443 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3444 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3445 NYD_LEAVE;
3446 return rv;
3449 static enum okay
3450 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3451 unsigned long *uid)
3453 char *xp, *yp;
3454 enum okay rv;
3455 NYD_ENTER;
3457 *uidvalidity = strtoul(cp, &xp, 10);
3458 *uid = strtoul(xp, &yp, 10);
3459 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3460 *yp == ']');
3461 NYD_LEAVE;
3462 return rv;
3465 static enum okay
3466 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3468 struct mailbox xmb;
3469 struct message xm;
3470 const char *cp;
3471 unsigned long uidvalidity, olduid, newuid;
3472 enum okay rv;
3473 NYD_ENTER;
3475 rv = STOP;
3477 memset(&xmb, 0, sizeof xmb);
3479 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3480 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3481 goto jleave;
3483 rv = OKAY;
3485 xmb = *mp;
3486 xmb.mb_cache_directory = NULL;
3487 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3488 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3489 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3490 sizeof(xmb.mb_imap_delim));
3491 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3492 if (mp->mb_cache_directory != NULL)
3493 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3494 xmb.mb_uidvalidity = uidvalidity;
3495 initcache(&xmb);
3497 if (m == NULL) {
3498 memset(&xm, 0, sizeof xm);
3499 xm.m_uid = olduid;
3500 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3501 goto jleave;
3502 getcache(mp, &xm, NEED_HEADER);
3503 getcache(mp, &xm, NEED_BODY);
3504 } else {
3505 if ((m->m_flag & HAVE_HEADER) == 0)
3506 getcache(mp, m, NEED_HEADER);
3507 if ((m->m_flag & HAVE_BODY) == 0)
3508 getcache(mp, m, NEED_BODY);
3509 xm = *m;
3511 xm.m_uid = newuid;
3512 xm.m_flag &= ~MFULLYCACHED;
3513 putcache(&xmb, &xm);
3514 jleave:
3515 if (xmb.mb_cache_directory != NULL)
3516 free(xmb.mb_cache_directory);
3517 if (xmb.mb_imap_mailbox != NULL)
3518 free(xmb.mb_imap_mailbox);
3519 if (xmb.mb_imap_pass != NULL)
3520 free(xmb.mb_imap_pass);
3521 if (xmb.mb_imap_account != NULL)
3522 free(xmb.mb_imap_account);
3523 NYD_LEAVE;
3524 return rv;
3527 static enum okay
3528 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3529 long size, long lines, int flag, const char *name)
3531 struct mailbox xmb;
3532 struct message xm;
3533 const char *cp;
3534 unsigned long uidvalidity, uid;
3535 enum okay rv;
3536 NYD_ENTER;
3538 rv = STOP;
3540 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3541 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3542 goto jleave;
3544 rv = OKAY;
3546 xmb = *mp;
3547 xmb.mb_cache_directory = NULL;
3548 /* XXX mb_imap_delim reused */
3549 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3550 xmb.mb_uidvalidity = uidvalidity;
3551 xmb.mb_otf = xmb.mb_itf = fp;
3552 initcache(&xmb);
3553 memset(&xm, 0, sizeof xm);
3554 xm.m_flag = (flag & MREAD) | MNEW;
3555 xm.m_time = t;
3556 xm.m_block = mailx_blockof(off1);
3557 xm.m_offset = mailx_offsetof(off1);
3558 xm.m_size = size;
3559 xm.m_xsize = xsize;
3560 xm.m_lines = xm.m_xlines = lines;
3561 xm.m_uid = uid;
3562 xm.m_have = HAVE_HEADER | HAVE_BODY;
3563 putcache(&xmb, &xm);
3565 free(xmb.mb_imap_mailbox);
3566 jleave:
3567 NYD_LEAVE;
3568 return rv;
3571 static enum okay
3572 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3574 FILE *tp = NULL;
3575 time_t t;
3576 long size, xsize, ysize, lines;
3577 enum mflag flag = MNEW;
3578 char *name, *buf, *bp;
3579 char const *cp;
3580 size_t bufsize, buflen, cnt;
3581 enum okay rv = STOP;
3582 NYD_ENTER;
3584 buf = smalloc(bufsize = LINESIZE);
3585 buflen = 0;
3586 cnt = fsize(fp);
3587 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3588 goto jstop;
3590 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3592 while (*bp == ' ')
3593 ++bp;
3595 if ((cp = strrchr(bp, '{')) == NULL)
3596 goto jstop;
3598 xsize = atol(&cp[1]) + 2;
3599 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3600 goto jstop;
3601 while (*cp == ' ')
3602 cp++;
3604 if (*cp == '(') {
3605 imap_getflags(cp, &cp, &flag);
3606 while (*++cp == ' ')
3609 t = imap_read_date_time(cp);
3611 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3612 NULL)
3613 goto jstop;
3615 size = xsize;
3616 ysize = lines = 0;
3617 while (size > 0) {
3618 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3619 goto jstop;
3620 size -= buflen;
3621 buf[--buflen] = '\0';
3622 buf[buflen-1] = '\n';
3623 fwrite(buf, 1, buflen, tp);
3624 ysize += buflen;
3625 ++lines;
3627 fflush(tp);
3628 rewind(tp);
3630 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3631 imap_unquotestr(name));
3632 rv = OKAY;
3633 jstop:
3634 free(buf);
3635 if (tp)
3636 Fclose(tp);
3637 NYD_LEAVE;
3638 return rv;
3641 #ifdef HAVE_IMAP_SEARCH
3642 static enum okay
3643 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3644 int f)
3646 char *o, *xp, *cs, c;
3647 size_t osize;
3648 FILE *queuefp = NULL;
3649 int i;
3650 unsigned long n;
3651 const char *cp;
3652 enum okay ok = STOP;
3653 NYD_X;
3655 c = 0;
3656 for (cp = spec; *cp; cp++)
3657 c |= *cp;
3658 if (c & 0200) {
3659 cp = charset_get_lc();
3660 # ifdef HAVE_ICONV
3661 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3662 iconv_t it;
3663 char *nsp, *nspec;
3664 size_t sz, nsz;
3666 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3667 sz = strlen(spec) + 1;
3668 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3669 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3670 sz == 0) {
3671 spec = nspec;
3672 cp = "utf-8";
3674 n_iconv_close(it);
3677 # endif
3678 cp = imap_quotestr(cp);
3679 cs = salloc(n = strlen(cp) + 10);
3680 snprintf(cs, n, "CHARSET %s ", cp);
3681 } else
3682 cs = UNCONST("");
3684 o = ac_alloc(osize = strlen(spec) + 60);
3685 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3686 IMAP_OUT(o, MB_COMD, goto out)
3687 while (mp->mb_active & MB_COMD) {
3688 ok = imap_answer(mp, 0);
3689 if (response_status == RESPONSE_OTHER &&
3690 response_other == MAILBOX_DATA_SEARCH) {
3691 xp = responded_other_text;
3692 while (*xp && *xp != '\r') {
3693 n = strtoul(xp, &xp, 10);
3694 for (i = 0; i < cnt; i++)
3695 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3696 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3697 mark(i+1, f);
3701 out:
3702 ac_free(o);
3703 return ok;
3706 FL enum okay
3707 imap_search1(const char * volatile spec, int f)
3709 sighandler_type saveint, savepipe;
3710 enum okay volatile rv = STOP;
3711 NYD_ENTER;
3713 if (mb.mb_type != MB_IMAP)
3714 goto jleave;
3716 imaplock = 1;
3717 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3718 safe_signal(SIGINT, &_imap_maincatch);
3719 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3720 if (sigsetjmp(imapjmp, 1) == 0) {
3721 if (savepipe != SIG_IGN)
3722 safe_signal(SIGPIPE, imapcatch);
3724 rv = imap_search2(&mb, message, msgCount, spec, f);
3726 safe_signal(SIGINT, saveint);
3727 safe_signal(SIGPIPE, savepipe);
3728 imaplock = 0;
3729 jleave:
3730 NYD_LEAVE;
3731 if (interrupts)
3732 onintr(0);
3733 return rv;
3735 #endif /* HAVE_IMAP_SEARCH */
3737 FL int
3738 imap_thisaccount(const char *cp)
3740 int rv;
3741 NYD_ENTER;
3743 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3744 rv = 0;
3745 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3746 mb.mb_imap_account == NULL)
3747 rv = 0;
3748 else
3749 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3750 NYD_LEAVE;
3751 return rv;
3754 FL enum okay
3755 imap_remove(const char * volatile name)
3757 sighandler_type volatile saveint, savepipe;
3758 enum okay volatile rv = STOP;
3759 NYD_ENTER;
3761 if (mb.mb_type != MB_IMAP) {
3762 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3763 goto jleave;
3766 if (!imap_thisaccount(name)) {
3767 n_err(_("Can only remove mailboxes on current IMAP server: "
3768 "\"%s\" not removed\n"), name);
3769 goto jleave;
3772 imaplock = 1;
3773 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3774 safe_signal(SIGINT, &_imap_maincatch);
3775 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3776 if (sigsetjmp(imapjmp, 1) == 0) {
3777 if (savepipe != SIG_IGN)
3778 safe_signal(SIGPIPE, imapcatch);
3780 rv = imap_remove1(&mb, imap_fileof(name));
3782 safe_signal(SIGINT, saveint);
3783 safe_signal(SIGPIPE, savepipe);
3784 imaplock = 0;
3786 if (rv == OKAY)
3787 rv = cache_remove(name);
3788 jleave:
3789 NYD_LEAVE;
3790 if (interrupts)
3791 onintr(0);
3792 return rv;
3795 static enum okay
3796 imap_remove1(struct mailbox *mp, const char *name)
3798 char *o;
3799 int os;
3800 char const *qname;
3801 FILE *queuefp;
3802 enum okay ok;
3803 NYD_X;
3805 ok = STOP;
3806 queuefp = NULL;
3808 if((qname = imap_path_quote(mp, name)) != NULL){
3809 o = ac_alloc(os = strlen(qname) + 100);
3810 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3811 IMAP_OUT(o, MB_COMD, goto out)
3812 while (mp->mb_active & MB_COMD)
3813 ok = imap_answer(mp, 1);
3814 out:
3815 ac_free(o);
3817 return ok;
3820 FL enum okay
3821 imap_rename(const char *old, const char *new)
3823 sighandler_type saveint, savepipe;
3824 enum okay rv = STOP;
3825 NYD_ENTER;
3827 if (mb.mb_type != MB_IMAP) {
3828 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3829 goto jleave;
3832 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3833 n_err(_("Can only rename mailboxes on current IMAP "
3834 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3835 goto jleave;
3838 imaplock = 1;
3839 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3840 safe_signal(SIGINT, &_imap_maincatch);
3841 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3842 if (sigsetjmp(imapjmp, 1) == 0) {
3843 if (savepipe != SIG_IGN)
3844 safe_signal(SIGPIPE, imapcatch);
3846 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3848 safe_signal(SIGINT, saveint);
3849 safe_signal(SIGPIPE, savepipe);
3850 imaplock = 0;
3852 if (rv == OKAY)
3853 rv = cache_rename(old, new);
3854 jleave:
3855 NYD_LEAVE;
3856 if (interrupts)
3857 onintr(0);
3858 return rv;
3861 static enum okay
3862 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3864 char *o;
3865 int os;
3866 char const *qoname, *qnname;
3867 FILE *queuefp;
3868 enum okay ok;
3869 NYD_X;
3871 ok = STOP;
3872 queuefp = NULL;
3874 if((qoname = imap_path_quote(mp, old)) != NULL &&
3875 (qnname = imap_path_quote(mp, new)) != NULL){
3876 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3877 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3878 IMAP_OUT(o, MB_COMD, goto out)
3879 while (mp->mb_active & MB_COMD)
3880 ok = imap_answer(mp, 1);
3881 out:
3882 ac_free(o);
3884 return ok;
3887 FL enum okay
3888 imap_dequeue(struct mailbox *mp, FILE *fp)
3890 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3891 size_t bufsize, buflen, cnt;
3892 long offs, offs1, offs2, octets;
3893 int twice, gotcha = 0;
3894 FILE *queuefp = NULL;
3895 enum okay ok = OKAY, rok = OKAY;
3896 NYD_X;
3898 buf = smalloc(bufsize = LINESIZE);
3899 buflen = 0;
3900 cnt = fsize(fp);
3901 while ((offs1 = ftell(fp)) >= 0 &&
3902 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3903 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3905 while (*bp == ' ')
3906 ++bp;
3907 twice = 0;
3908 if ((offs = ftell(fp)) < 0)
3909 goto fail;
3910 again:
3911 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3912 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3913 cp = &bp[9];
3914 while (digitchar(*cp))
3915 cp++;
3916 if (*cp != ' ')
3917 goto fail;
3918 while (*cp == ' ')
3919 cp++;
3920 if ((newname = imap_strex(cp, NULL)) == NULL)
3921 goto fail;
3922 IMAP_OUT(o, MB_COMD, continue)
3923 while (mp->mb_active & MB_COMD)
3924 ok = imap_answer(mp, twice);
3925 if (response_status == RESPONSE_NO && twice++ == 0)
3926 goto trycreate;
3927 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3928 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3930 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3931 IMAP_OUT(o, MB_COMD, continue)
3932 while (mp->mb_active & MB_COMD)
3933 ok = imap_answer(mp, 1);
3934 if (ok == OKAY)
3935 gotcha++;
3936 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3937 if ((cp = strrchr(bp, '{')) == NULL)
3938 goto fail;
3939 octets = atol(&cp[1]) + 2;
3940 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3941 goto fail;
3942 IMAP_OUT(o, MB_COMD, continue)
3943 while (mp->mb_active & MB_COMD) {
3944 ok = imap_answer(mp, twice);
3945 if (response_type == RESPONSE_CONT)
3946 break;
3948 if (ok == STOP) {
3949 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3950 goto trycreate;
3951 goto fail;
3953 while (octets > 0) {
3954 size_t n = (UICMP(z, octets, >, sizeof iob)
3955 ? sizeof iob : (size_t)octets);
3956 octets -= n;
3957 if (n != fread(iob, 1, n, fp))
3958 goto fail;
3959 swrite1(&mp->mb_sock, iob, n, 1);
3961 swrite(&mp->mb_sock, "");
3962 while (mp->mb_active & MB_COMD) {
3963 ok = imap_answer(mp, 0);
3964 if (response_status == RESPONSE_NO && twice++ == 0) {
3965 if (fseek(fp, offs, SEEK_SET) < 0)
3966 goto fail;
3967 goto trycreate;
3970 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3971 if ((offs2 = ftell(fp)) < 0)
3972 goto fail;
3973 fseek(fp, offs1, SEEK_SET);
3974 if (imap_appenduid_cached(mp, fp) == STOP) {
3975 (void)fseek(fp, offs2, SEEK_SET);
3976 goto fail;
3979 } else {
3980 fail:
3981 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3982 rok = STOP;
3984 continue;
3985 trycreate:
3986 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3987 IMAP_OUT(o, MB_COMD, continue)
3988 while (mp->mb_active & MB_COMD)
3989 ok = imap_answer(mp, 1);
3990 if (ok == OKAY)
3991 goto again;
3993 fflush(fp);
3994 rewind(fp);
3995 ftruncate(fileno(fp), 0);
3996 if (gotcha)
3997 imap_close(mp);
3998 free(buf);
3999 return rok;
4002 static char *
4003 imap_strex(char const *cp, char const **xp)
4005 char const *cq;
4006 char *n = NULL;
4007 NYD_ENTER;
4009 if (*cp != '"')
4010 goto jleave;
4012 for (cq = cp + 1; *cq != '\0'; ++cq) {
4013 if (*cq == '\\')
4014 cq++;
4015 else if (*cq == '"')
4016 break;
4018 if (*cq != '"')
4019 goto jleave;
4021 n = salloc(cq - cp + 2);
4022 memcpy(n, cp, cq - cp +1);
4023 n[cq - cp + 1] = '\0';
4024 if (xp != NULL)
4025 *xp = cq + 1;
4026 jleave:
4027 NYD_LEAVE;
4028 return n;
4031 static enum okay
4032 check_expunged(void)
4034 enum okay rv;
4035 NYD_ENTER;
4037 if (expunged_messages > 0) {
4038 n_err(_("Command not executed - messages have been expunged\n"));
4039 rv = STOP;
4040 } else
4041 rv = OKAY;
4042 NYD_LEAVE;
4043 return rv;
4046 FL int
4047 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4049 struct url url;
4050 int rv, omsgCount = msgCount;
4051 NYD_ENTER;
4052 UNUSED(vp);
4054 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4055 n_err(_("Already connected\n"));
4056 rv = 1;
4057 goto jleave;
4060 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4061 rv = 1;
4062 goto jleave;
4064 ok_bclear(disconnected);
4065 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
4067 if (mb.mb_type == MB_CACHE) {
4068 enum fedit_mode fm = FEDIT_NONE;
4069 if (_imap_rdonly)
4070 fm |= FEDIT_RDONLY;
4071 if (!(pstate & PS_EDIT))
4072 fm |= FEDIT_SYSBOX;
4073 _imap_setfile1(&url, fm, 1);
4074 if (msgCount > omsgCount)
4075 newmailinfo(omsgCount);
4077 rv = 0;
4078 jleave:
4079 NYD_LEAVE;
4080 return rv;
4083 FL int
4084 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4086 struct url url;
4087 int rv = 1, *msgvec = vp;
4088 NYD_ENTER;
4090 if (mb.mb_type == MB_CACHE) {
4091 n_err(_("Not connected\n"));
4092 goto jleave;
4094 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4095 n_err(_("The current mailbox is not cached\n"));
4096 goto jleave;
4099 if (!url_parse(&url, CPROTO_IMAP, mailname))
4100 goto jleave;
4102 if (*msgvec)
4103 c_cache(vp);
4104 ok_bset(disconnected, TRU1);
4105 if (mb.mb_type == MB_IMAP) {
4106 enum fedit_mode fm = FEDIT_NONE;
4107 if (_imap_rdonly)
4108 fm |= FEDIT_RDONLY;
4109 if (!(pstate & PS_EDIT))
4110 fm |= FEDIT_SYSBOX;
4111 sclose(&mb.mb_sock);
4112 _imap_setfile1(&url, fm, 1);
4114 rv = 0;
4115 jleave:
4116 NYD_LEAVE;
4117 return rv;
4120 FL int
4121 c_cache(void *vp)
4123 int rv = 1, *msgvec = vp, *ip;
4124 struct message *mp;
4125 NYD_ENTER;
4127 if (mb.mb_type != MB_IMAP) {
4128 n_err(_("Not connected to an IMAP server\n"));
4129 goto jleave;
4131 if (cached_uidvalidity(&mb) == 0) {
4132 n_err(_("The current mailbox is not cached\n"));
4133 goto jleave;
4136 srelax_hold();
4137 for (ip = msgvec; *ip; ++ip) {
4138 mp = &message[*ip - 1];
4139 if (!(mp->m_have & HAVE_BODY)) {
4140 get_body(mp);
4141 srelax();
4144 srelax_rele();
4145 rv = 0;
4146 jleave:
4147 NYD_LEAVE;
4148 return rv;
4151 FL int
4152 disconnected(const char *file)
4154 struct url url;
4155 int rv = 1;
4156 NYD_ENTER;
4158 if (ok_blook(disconnected)) {
4159 rv = 1;
4160 goto jleave;
4163 if (!url_parse(&url, CPROTO_IMAP, file)) {
4164 rv = 0;
4165 goto jleave;
4167 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
4169 jleave:
4170 NYD_LEAVE;
4171 return rv;
4174 FL void
4175 transflags(struct message *omessage, long omsgCount, int transparent)
4177 struct message *omp, *nmp, *newdot, *newprevdot;
4178 int hf;
4179 NYD_ENTER;
4181 omp = omessage;
4182 nmp = message;
4183 newdot = message;
4184 newprevdot = NULL;
4185 while (PTRCMP(omp, <, omessage + omsgCount) &&
4186 PTRCMP(nmp, <, message + msgCount)) {
4187 if (dot && nmp->m_uid == dot->m_uid)
4188 newdot = nmp;
4189 if (prevdot && nmp->m_uid == prevdot->m_uid)
4190 newprevdot = nmp;
4191 if (omp->m_uid == nmp->m_uid) {
4192 hf = nmp->m_flag & MHIDDEN;
4193 if (transparent && mb.mb_type == MB_IMAP)
4194 omp->m_flag &= ~MHIDDEN;
4195 *nmp++ = *omp++;
4196 if (transparent && mb.mb_type == MB_CACHE)
4197 nmp[-1].m_flag |= hf;
4198 } else if (omp->m_uid < nmp->m_uid)
4199 ++omp;
4200 else
4201 ++nmp;
4203 dot = newdot;
4204 setdot(newdot);
4205 prevdot = newprevdot;
4206 free(omessage);
4207 NYD_LEAVE;
4210 FL time_t
4211 imap_read_date_time(const char *cp)
4213 char buf[3];
4214 time_t t;
4215 int i, year, month, day, hour, minute, second, sign = -1;
4216 NYD2_ENTER;
4218 /* "25-Jul-2004 15:33:44 +0200"
4219 * | | | | | |
4220 * 0 5 10 15 20 25 */
4221 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4222 goto jinvalid;
4223 day = strtol(&cp[1], NULL, 10);
4224 for (i = 0;;) {
4225 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
4226 break;
4227 if (month_names[++i][0] == '\0')
4228 goto jinvalid;
4230 month = i + 1;
4231 year = strtol(&cp[8], NULL, 10);
4232 hour = strtol(&cp[13], NULL, 10);
4233 minute = strtol(&cp[16], NULL, 10);
4234 second = strtol(&cp[19], NULL, 10);
4235 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4236 goto jinvalid;
4237 switch (cp[22]) {
4238 case '-':
4239 sign = 1;
4240 break;
4241 case '+':
4242 break;
4243 default:
4244 goto jinvalid;
4246 buf[2] = '\0';
4247 buf[0] = cp[23];
4248 buf[1] = cp[24];
4249 t += strtol(buf, NULL, 10) * sign * 3600;
4250 buf[0] = cp[25];
4251 buf[1] = cp[26];
4252 t += strtol(buf, NULL, 10) * sign * 60;
4253 jleave:
4254 NYD2_LEAVE;
4255 return t;
4256 jinvalid:
4257 time(&t);
4258 goto jleave;
4261 FL const char *
4262 imap_make_date_time(time_t t)
4264 static char s[30];
4265 struct tm *tmptr;
4266 int tzdiff, tzdiff_hour, tzdiff_min;
4267 NYD2_ENTER;
4269 tzdiff = t - mktime(gmtime(&t));
4270 tzdiff_hour = (int)(tzdiff / 60);
4271 tzdiff_min = tzdiff_hour % 60;
4272 tzdiff_hour /= 60;
4273 tmptr = localtime(&t);
4274 if (tmptr->tm_isdst > 0)
4275 tzdiff_hour++;
4276 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4277 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4278 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4279 NYD2_LEAVE;
4280 return s;
4282 #endif /* HAVE_IMAP */
4284 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
4285 FL char *
4286 imap_quotestr(char const *s)
4288 char *n, *np;
4289 NYD2_ENTER;
4291 np = n = salloc(2 * strlen(s) + 3);
4292 *np++ = '"';
4293 while (*s) {
4294 if (*s == '"' || *s == '\\')
4295 *np++ = '\\';
4296 *np++ = *s++;
4298 *np++ = '"';
4299 *np = '\0';
4300 NYD2_LEAVE;
4301 return n;
4304 FL char *
4305 imap_unquotestr(char const *s)
4307 char *n, *np;
4308 NYD2_ENTER;
4310 if (*s != '"') {
4311 n = savestr(s);
4312 goto jleave;
4315 np = n = salloc(strlen(s) + 1);
4316 while (*++s) {
4317 if (*s == '\\')
4318 s++;
4319 else if (*s == '"')
4320 break;
4321 *np++ = *s;
4323 *np = '\0';
4324 jleave:
4325 NYD2_LEAVE;
4326 return n;
4328 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
4330 /* s-it-mode */