NEWS: v14.8.13 ("Cyrustrupidae")
[s-mailx.git] / imap.c
blob740b1246691f8c5fd26ebabf60debfd3734b8ad6
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 = savestrbuf(path, l << 1);
525 /* xxx Don't check for invalid characters from malicious servers */
526 if(l == 0 || (cp = memchr(path, '&', l)) == NULL)
527 goto jleave;
529 *err_or_null = TRU1;
531 emsg = N_("Invalid mUTF-7 encoding");
532 i = PTR2SIZE(cp - path);
533 rv += i;
534 l -= i;
535 mb64p_base = NULL;
537 while(l > 0){
538 if((c = *cp) != '&'){
539 if(c <= 0x1F || c >= 0x7F){
540 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
541 goto jerr;
543 *rv++ = c;
544 ++cp;
545 --l;
546 }else if(--l == 0)
547 goto jeincpl;
548 else if(*++cp == '-'){
549 *rv++ = '&';
550 ++cp;
551 --l;
552 }else if(l < 3){
553 jeincpl:
554 emsg = N_("Invalid mUTF-7: incomplete input");
555 goto jerr;
556 }else{
557 /* mUTF-7 -> UTF-16BE -> UTF-8 */
558 static ui8_t const mb64dt[256] = {
559 #undef XX
560 #define XX 0xFFu
561 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
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,62, 63,XX,XX,XX,
564 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
565 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
566 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
567 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
568 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
569 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,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
579 if(mb64p_base == NULL)
580 mb64p_base = salloc(l);
582 /* Decode the mUTF-7 to what is indeed UTF-16BE */
583 for(mb64p = mb64p_base;;){
584 assert(l >= 3);
585 if((mb64p[0] = mb64dt[(ui8_t)cp[0]]) == XX ||
586 (mb64p[1] = mb64dt[(ui8_t)cp[1]]) == XX)
587 goto jerr;
588 mb64p += 2;
590 c = cp[2];
591 cp += 3;
592 l -= 3;
593 if(c == '-')
594 break;
595 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
596 goto jerr;
598 if(l == 0)
599 goto jerr;
600 --l;
601 if((c = *cp++) == '-')
602 break;
603 if((*mb64p++ = mb64dt[(ui8_t)c]) == XX)
604 goto jerr;
606 if(l < 3){
607 if(l > 0 && *cp == '-'){
608 --l;
609 ++cp;
610 break;
612 goto jerr;
615 #undef XX
617 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
618 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
619 goto jerr;
622 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
623 i = PTR2SIZE(mb64p - mb64p_base);
624 mb64p = mb64xp = mb64p_base;
626 while(i > 0){
627 ui8_t ul, u0, u1, u2, u3;
629 ul = (i >= 4) ? 4 : i & 0x3;
630 i -= ul;
631 u0 = mb64xp[0];
632 u1 = mb64xp[1];
633 u2 = (ul < 3) ? 0 : mb64xp[2];
634 u3 = (ul < 4) ? 0 : mb64xp[3];
635 mb64xp += ul;
636 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
637 if(ul < 3)
638 break;
639 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
640 if(ul < 4)
641 break;
642 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
645 /* UTF-16BE we convert to UTF-8 */
646 i = PTR2SIZE(mb64p - mb64p_base);
647 if(i & 1){
648 emsg = N_("Odd bytecount for UTF-16BE input");
649 goto jerr;
652 /* TODO S-CText: magic utf16 conversions */
653 emsg = N_("Invalid UTF-16BE encoding");
655 for(mb64p = mb64p_base; i > 0;){
656 ui32_t utf32;
657 ui16_t uhi, ulo;
659 uhi = mb64p[0];
660 uhi <<= 8;
661 uhi |= mb64p[1];
663 /* Not a surrogate? */
664 if(uhi < 0xD800 || uhi > 0xDFFF){
665 utf32 = uhi;
666 mb64p += 2;
667 i -= 2;
668 }else if(uhi > 0xDBFF)
669 goto jerr;
670 else if(i < 4){
671 emsg = N_("Incomplete UTF-16BE surrogate pair");
672 goto jerr;
673 }else{
674 ulo = mb64p[2];
675 ulo <<= 8;
676 ulo |= mb64p[3];
677 if(ulo < 0xDC00 || ulo > 0xDFFF){
678 goto jerr;
681 uhi &= 0x03FF;
682 uhi <<= 10;
683 ulo &= 0x03FF;
684 uhi |= ulo;
686 utf32 = 0x10000u + uhi;
687 mb64p += 4;
688 i -= 4;
691 utf32 = n_utf32_to_utf8(utf32, rv);
692 rv += utf32;
696 *rv = '\0';
698 /* We can skip the UTF-8 conversion occasionally */
699 if(!(options & OPT_UNICODE)){
700 int ir;
701 iconv_t icd;
703 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
705 if((icd = iconv_open(charset_get_lc(), "utf-8")) == (iconv_t)-1)
706 goto jerr;
708 out.s = NULL, out.l = 0;
709 in.l = strlen(in.s = rv_base);
710 if((ir = n_iconv_str(icd, &out, &in, NULL, FAL0)) == 0)
711 /* Because the length of this is unpredictable, copy*/
712 rv_base = savestrbuf(out.s, out.l);
714 if(out.s != NULL)
715 free(out.s);
716 iconv_close(icd);
718 if(ir != 0)
719 goto jerr;
722 *err_or_null = FAL0;
723 rv = rv_base;
724 jleave:
725 NYD2_LEAVE;
726 return rv;
727 jerr:
728 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
729 memcpy(rv = rv_base, path, ++l_orig);
730 goto jleave;
733 static char *
734 imap_path_quote(struct mailbox *mp, char const *cp){
735 bool_t err;
736 char *rv;
737 NYD2_ENTER;
739 cp = imap_path_normalize(mp, cp);
740 cp = imap_path_encode(cp, &err);
741 rv = err ? NULL : imap_quotestr(cp);
742 NYD2_LEAVE;
743 return rv;
746 static void
747 imap_other_get(char *pp)
749 char *xp;
750 NYD2_ENTER;
752 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
753 pp += 6;
754 response_other = MAILBOX_DATA_FLAGS;
755 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
756 pp += 5;
757 response_other = MAILBOX_DATA_LIST;
758 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
759 pp += 5;
760 response_other = MAILBOX_DATA_LSUB;
761 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
762 pp += 8;
763 response_other = MAILBOX_DATA_MAILBOX;
764 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
765 pp += 7;
766 response_other = MAILBOX_DATA_SEARCH;
767 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
768 pp += 7;
769 response_other = MAILBOX_DATA_STATUS;
770 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
771 pp += 11;
772 response_other = CAPABILITY_DATA;
773 } else {
774 responded_other_number = strtol(pp, &xp, 10);
775 while (*xp == ' ')
776 ++xp;
777 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
778 response_other = MAILBOX_DATA_EXISTS;
779 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
780 response_other = MAILBOX_DATA_RECENT;
781 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
782 response_other = MESSAGE_DATA_EXPUNGE;
783 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
784 pp = &xp[6];
785 response_other = MESSAGE_DATA_FETCH;
786 } else
787 response_other = RESPONSE_OTHER_UNKNOWN;
789 responded_other_text = pp;
790 NYD2_LEAVE;
793 static void
794 imap_response_get(const char **cp)
796 NYD2_ENTER;
797 if (ascncasecmp(*cp, "OK ", 3) == 0) {
798 *cp += 3;
799 response_status = RESPONSE_OK;
800 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
801 *cp += 3;
802 response_status = RESPONSE_NO;
803 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
804 *cp += 4;
805 response_status = RESPONSE_BAD;
806 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
807 *cp += 8;
808 response_status = RESPONSE_PREAUTH;
809 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
810 *cp += 4;
811 response_status = RESPONSE_BYE;
812 } else
813 response_status = RESPONSE_OTHER;
814 NYD2_LEAVE;
817 static void
818 imap_response_parse(void)
820 static char *parsebuf; /* TODO Use pool */
821 static size_t parsebufsize;
823 const char *ip = imapbuf;
824 char *pp;
825 NYD2_ENTER;
827 if (parsebufsize < imapbufsize)
828 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
829 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
830 pp = parsebuf;
831 switch (*ip) {
832 case '+':
833 response_type = RESPONSE_CONT;
834 ip++;
835 pp++;
836 while (*ip == ' ') {
837 ip++;
838 pp++;
840 break;
841 case '*':
842 ip++;
843 pp++;
844 while (*ip == ' ') {
845 ip++;
846 pp++;
848 imap_response_get(&ip);
849 pp = &parsebuf[ip - imapbuf];
850 switch (response_status) {
851 case RESPONSE_BYE:
852 response_type = RESPONSE_FATAL;
853 break;
854 default:
855 response_type = RESPONSE_DATA;
857 break;
858 default:
859 responded_tag = parsebuf;
860 while (*pp && *pp != ' ')
861 pp++;
862 if (*pp == '\0') {
863 response_type = RESPONSE_ILLEGAL;
864 break;
866 *pp++ = '\0';
867 while (*pp && *pp == ' ')
868 pp++;
869 if (*pp == '\0') {
870 response_type = RESPONSE_ILLEGAL;
871 break;
873 ip = &imapbuf[pp - parsebuf];
874 response_type = RESPONSE_TAGGED;
875 imap_response_get(&ip);
876 pp = &parsebuf[ip - imapbuf];
878 responded_text = pp;
879 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
880 response_status == RESPONSE_OTHER)
881 imap_other_get(pp);
882 NYD2_LEAVE;
885 static enum okay
886 imap_answer(struct mailbox *mp, int errprnt)
888 int i, complete;
889 enum okay rv;
890 NYD2_ENTER;
892 rv = OKAY;
893 if (mp->mb_type == MB_CACHE)
894 goto jleave;
895 rv = STOP;
896 jagain:
897 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
898 if (options & OPT_VERBVERB)
899 fputs(imapbuf, stderr);
900 imap_response_parse();
901 if (response_type == RESPONSE_ILLEGAL)
902 goto jagain;
903 if (response_type == RESPONSE_CONT) {
904 rv = OKAY;
905 goto jleave;
907 if (response_status == RESPONSE_OTHER) {
908 if (response_other == MAILBOX_DATA_EXISTS) {
909 had_exists = responded_other_number;
910 rec_queue(REC_EXISTS, responded_other_number);
911 if (had_expunge > 0)
912 had_expunge = 0;
913 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
914 rec_queue(REC_EXPUNGE, responded_other_number);
915 if (had_expunge < 0)
916 had_expunge = 0;
917 had_expunge++;
918 expunged_messages++;
921 complete = 0;
922 if (response_type == RESPONSE_TAGGED) {
923 if (asccasecmp(responded_tag, tag(0)) == 0)
924 complete |= 1;
925 else
926 goto jagain;
928 switch (response_status) {
929 case RESPONSE_PREAUTH:
930 mp->mb_active &= ~MB_PREAUTH;
931 /*FALLTHRU*/
932 case RESPONSE_OK:
933 jokay:
934 rv = OKAY;
935 complete |= 2;
936 break;
937 case RESPONSE_NO:
938 case RESPONSE_BAD:
939 jstop:
940 rv = STOP;
941 complete |= 2;
942 if (errprnt)
943 n_err(_("IMAP error: %s"), responded_text);
944 break;
945 case RESPONSE_UNKNOWN: /* does not happen */
946 case RESPONSE_BYE:
947 i = mp->mb_active;
948 mp->mb_active = MB_NONE;
949 if (i & MB_BYE)
950 goto jokay;
951 goto jstop;
952 case RESPONSE_OTHER:
953 rv = OKAY;
954 break;
956 if (response_status != RESPONSE_OTHER &&
957 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
958 n_err(_("IMAP alert: %s"), &responded_text[8]);
959 if (complete == 3)
960 mp->mb_active &= ~MB_COMD;
961 } else {
962 rv = STOP;
963 mp->mb_active = MB_NONE;
965 jleave:
966 NYD2_LEAVE;
967 return rv;
970 static enum okay
971 imap_parse_list(void)
973 char *cp;
974 enum okay rv;
975 NYD2_ENTER;
977 rv = STOP;
979 cp = responded_other_text;
980 list_attributes = LIST_NONE;
981 if (*cp == '(') {
982 while (*cp && *cp != ')') {
983 if (*cp == '\\') {
984 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
985 list_attributes |= LIST_NOINFERIORS;
986 cp += 12;
987 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
988 list_attributes |= LIST_NOSELECT;
989 cp += 9;
990 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
991 list_attributes |= LIST_MARKED;
992 cp += 7;
993 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
994 list_attributes |= LIST_UNMARKED;
995 cp += 9;
998 cp++;
1000 if (*++cp != ' ')
1001 goto jleave;
1002 while (*cp == ' ')
1003 cp++;
1006 list_hierarchy_delimiter = EOF;
1007 if (*cp == '"') {
1008 if (*++cp == '\\')
1009 cp++;
1010 list_hierarchy_delimiter = *cp++ & 0377;
1011 if (cp[0] != '"' || cp[1] != ' ')
1012 goto jleave;
1013 cp++;
1014 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1015 list_hierarchy_delimiter = EOF;
1016 cp += 3;
1019 while (*cp == ' ')
1020 cp++;
1021 list_name = cp;
1022 while (*cp && *cp != '\r')
1023 cp++;
1024 *cp = '\0';
1025 rv = OKAY;
1026 jleave:
1027 NYD2_LEAVE;
1028 return rv;
1031 static enum okay
1032 imap_finish(struct mailbox *mp)
1034 NYD_ENTER;
1035 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
1036 imap_answer(mp, 1);
1037 NYD_LEAVE;
1038 return OKAY;
1041 static void
1042 imap_timer_off(void)
1044 NYD_ENTER;
1045 if (imapkeepalive > 0) {
1046 alarm(0);
1047 safe_signal(SIGALRM, savealrm);
1049 NYD_LEAVE;
1052 static void
1053 imapcatch(int s)
1055 NYD_X; /* Signal handler */
1056 switch (s) {
1057 case SIGINT:
1058 n_err_sighdl(_("Interrupt\n"));
1059 siglongjmp(imapjmp, 1);
1060 /*NOTREACHED*/
1061 case SIGPIPE:
1062 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1063 break;
1067 static void
1068 _imap_maincatch(int s)
1070 NYD_X; /* Signal handler */
1071 UNUSED(s);
1072 if (interrupts++ == 0) {
1073 n_err_sighdl(_("Interrupt\n"));
1074 return;
1076 onintr(0);
1079 static enum okay
1080 imap_noop1(struct mailbox *mp)
1082 char o[LINESIZE];
1083 FILE *queuefp = NULL;
1084 NYD_X;
1086 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1087 IMAP_OUT(o, MB_COMD, return STOP)
1088 IMAP_ANSWER()
1089 return OKAY;
1092 FL char const *
1093 imap_fileof(char const *xcp)
1095 char const *cp = xcp;
1096 int state = 0;
1097 NYD_ENTER;
1099 while (*cp) {
1100 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1101 cp += 3;
1102 state = 1;
1104 if (cp[0] == '/' && state == 1) {
1105 ++cp;
1106 goto jleave;
1108 if (cp[0] == '/') {
1109 cp = xcp;
1110 goto jleave;
1112 ++cp;
1114 jleave:
1115 NYD_LEAVE;
1116 return cp;
1119 FL enum okay
1120 imap_noop(void)
1122 sighandler_type volatile oldint, oldpipe;
1123 enum okay rv = STOP;
1124 NYD_ENTER;
1126 if (mb.mb_type != MB_IMAP)
1127 goto jleave;
1129 imaplock = 1;
1130 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1131 safe_signal(SIGINT, &_imap_maincatch);
1132 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1133 if (sigsetjmp(imapjmp, 1) == 0) {
1134 if (oldpipe != SIG_IGN)
1135 safe_signal(SIGPIPE, imapcatch);
1137 rv = imap_noop1(&mb);
1139 safe_signal(SIGINT, oldint);
1140 safe_signal(SIGPIPE, oldpipe);
1141 imaplock = 0;
1142 jleave:
1143 NYD_LEAVE;
1144 if (interrupts)
1145 onintr(0);
1146 return rv;
1149 static void
1150 rec_queue(enum rec_type rt, unsigned long cnt)
1152 struct record *rp;
1153 NYD_ENTER;
1155 rp = scalloc(1, sizeof *rp);
1156 rp->rec_type = rt;
1157 rp->rec_count = cnt;
1158 if (record && recend) {
1159 recend->rec_next = rp;
1160 recend = rp;
1161 } else
1162 record = recend = rp;
1163 NYD_LEAVE;
1166 static enum okay
1167 rec_dequeue(void)
1169 struct message *omessage;
1170 struct record *rp, *rq;
1171 uiz_t exists = 0, i;
1172 enum okay rv = STOP;
1173 NYD_ENTER;
1175 if (record == NULL)
1176 goto jleave;
1178 omessage = message;
1179 message = smalloc((msgCount+1) * sizeof *message);
1180 if (msgCount)
1181 memcpy(message, omessage, msgCount * sizeof *message);
1182 memset(&message[msgCount], 0, sizeof *message);
1184 rp = record, rq = NULL;
1185 rv = OKAY;
1186 while (rp != NULL) {
1187 switch (rp->rec_type) {
1188 case REC_EXISTS:
1189 exists = rp->rec_count;
1190 break;
1191 case REC_EXPUNGE:
1192 if (rp->rec_count == 0) {
1193 rv = STOP;
1194 break;
1196 if (rp->rec_count > (unsigned long)msgCount) {
1197 if (exists == 0 || rp->rec_count > exists--)
1198 rv = STOP;
1199 break;
1201 if (exists > 0)
1202 exists--;
1203 delcache(&mb, &message[rp->rec_count-1]);
1204 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
1205 ((msgCount - rp->rec_count + 1) * sizeof *message));
1206 --msgCount;
1207 /* If the message was part of a collapsed thread,
1208 * the m_collapsed field of one of its ancestors
1209 * should be incremented. It seems hardly possible
1210 * to do this with the current message structure,
1211 * though. The result is that a '+' may be shown
1212 * in the header summary even if no collapsed
1213 * children exists */
1214 break;
1216 if (rq != NULL)
1217 free(rq);
1218 rq = rp;
1219 rp = rp->rec_next;
1221 if (rq != NULL)
1222 free(rq);
1224 record = recend = NULL;
1225 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
1226 message = srealloc(message, (exists + 1) * sizeof *message);
1227 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
1228 for (i = msgCount; i < exists; ++i)
1229 imap_init(&mb, i);
1230 imap_flags(&mb, msgCount+1, exists);
1231 msgCount = exists;
1234 if (rv == STOP) {
1235 free(message);
1236 message = omessage;
1238 jleave:
1239 NYD_LEAVE;
1240 return rv;
1243 static void
1244 rec_rmqueue(void)
1246 struct record *rp;
1247 NYD_ENTER;
1249 for (rp = record; rp != NULL;) {
1250 struct record *tmp = rp;
1251 rp = rp->rec_next;
1252 free(tmp);
1254 record = recend = NULL;
1255 NYD_LEAVE;
1258 /*ARGSUSED*/
1259 static void
1260 imapalarm(int s)
1262 sighandler_type volatile saveint, savepipe;
1263 NYD_X; /* Signal handler */
1264 UNUSED(s);
1266 if (imaplock++ == 0) {
1267 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1268 safe_signal(SIGINT, &_imap_maincatch);
1269 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1270 if (sigsetjmp(imapjmp, 1)) {
1271 safe_signal(SIGINT, saveint);
1272 safe_signal(SIGPIPE, savepipe);
1273 goto jbrk;
1275 if (savepipe != SIG_IGN)
1276 safe_signal(SIGPIPE, imapcatch);
1277 if (imap_noop1(&mb) != OKAY) {
1278 safe_signal(SIGINT, saveint);
1279 safe_signal(SIGPIPE, savepipe);
1280 goto jleave;
1282 safe_signal(SIGINT, saveint);
1283 safe_signal(SIGPIPE, savepipe);
1285 jbrk:
1286 alarm(imapkeepalive);
1287 jleave:
1288 --imaplock;
1291 static enum okay
1292 imap_preauth(struct mailbox *mp, struct url const *urlp)
1294 NYD_X;
1296 mp->mb_active |= MB_PREAUTH;
1297 imap_answer(mp, 1);
1299 #ifdef HAVE_SSL
1300 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1301 FILE *queuefp = NULL;
1302 char o[LINESIZE];
1304 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1305 IMAP_OUT(o, MB_COMD, return STOP)
1306 IMAP_ANSWER()
1307 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
1308 return STOP;
1310 #else
1311 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
1312 n_err(_("No SSL support compiled in\n"));
1313 return STOP;
1315 #endif
1317 imap_capability(mp);
1318 return OKAY;
1321 static enum okay
1322 imap_capability(struct mailbox *mp)
1324 char o[LINESIZE];
1325 FILE *queuefp = NULL;
1326 enum okay ok = STOP;
1327 const char *cp;
1328 NYD_X;
1330 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1331 IMAP_OUT(o, MB_COMD, return STOP)
1332 while (mp->mb_active & MB_COMD) {
1333 ok = imap_answer(mp, 0);
1334 if (response_status == RESPONSE_OTHER &&
1335 response_other == CAPABILITY_DATA) {
1336 cp = responded_other_text;
1337 while (*cp) {
1338 while (spacechar(*cp))
1339 ++cp;
1340 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
1341 /* RFC 2359 */
1342 mp->mb_flags |= MB_UIDPLUS;
1343 while (*cp && !spacechar(*cp))
1344 ++cp;
1348 return ok;
1351 static enum okay
1352 imap_auth(struct mailbox *mp, struct ccred *ccred)
1354 enum okay rv;
1355 NYD_ENTER;
1357 if (!(mp->mb_active & MB_PREAUTH)) {
1358 rv = OKAY;
1359 goto jleave;
1362 switch (ccred->cc_authtype) {
1363 case AUTHTYPE_LOGIN:
1364 rv = imap_login(mp, ccred);
1365 break;
1366 #ifdef HAVE_MD5
1367 case AUTHTYPE_CRAM_MD5:
1368 rv = imap_cram_md5(mp, ccred);
1369 break;
1370 #endif
1371 #ifdef HAVE_GSSAPI
1372 case AUTHTYPE_GSSAPI:
1373 rv = _imap_gssapi(mp, ccred);
1374 break;
1375 #endif
1376 default:
1377 rv = STOP;
1378 break;
1380 jleave:
1381 NYD_LEAVE;
1382 return rv;
1385 #ifdef HAVE_MD5
1386 static enum okay
1387 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1389 char o[LINESIZE], *cp;
1390 FILE *queuefp = NULL;
1391 enum okay rv = STOP;
1392 NYD_ENTER;
1394 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1395 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1396 imap_answer(mp, 1);
1397 if (response_type != RESPONSE_CONT)
1398 goto jleave;
1400 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1401 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1402 while (mp->mb_active & MB_COMD)
1403 rv = imap_answer(mp, 1);
1404 jleave:
1405 NYD_LEAVE;
1406 return rv;
1408 #endif /* HAVE_MD5 */
1410 static enum okay
1411 imap_login(struct mailbox *mp, struct ccred *ccred)
1413 char o[LINESIZE];
1414 FILE *queuefp = NULL;
1415 enum okay rv = STOP;
1416 NYD_ENTER;
1418 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1419 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1420 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1421 while (mp->mb_active & MB_COMD)
1422 rv = imap_answer(mp, 1);
1423 jleave:
1424 NYD_LEAVE;
1425 return rv;
1428 #ifdef HAVE_GSSAPI
1429 # include "imap_gssapi.h"
1430 #endif
1432 FL enum okay
1433 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1434 enum fedit_mode fm)
1436 char o[LINESIZE];
1437 char const *qname, *cp;
1438 FILE *queuefp;
1439 enum okay ok;
1440 NYD_X;
1441 UNUSED(size);
1443 ok = STOP;
1444 queuefp = NULL;
1446 if((qname = imap_path_quote(mp, mbx)) == NULL)
1447 goto jleave;
1449 ok = OKAY;
1451 mp->mb_uidvalidity = 0;
1452 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1453 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1454 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1455 while (mp->mb_active & MB_COMD) {
1456 ok = imap_answer(mp, 1);
1457 if (response_status != RESPONSE_OTHER &&
1458 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1459 mp->mb_uidvalidity = atol(&cp[13]);
1461 *cnt = (had_exists > 0) ? had_exists : 0;
1462 if (response_status != RESPONSE_OTHER &&
1463 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1464 mp->mb_perm = 0;
1465 jleave:
1466 return ok;
1469 static enum okay
1470 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1472 char o[LINESIZE];
1473 FILE *queuefp = NULL;
1474 char const *cp;
1475 struct message *m;
1476 unsigned x = X, y = Y, n;
1477 NYD_X;
1479 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1480 IMAP_OUT(o, MB_COMD, return STOP)
1481 while (mp->mb_active & MB_COMD) {
1482 imap_answer(mp, 1);
1483 if (response_status == RESPONSE_OTHER &&
1484 response_other == MESSAGE_DATA_FETCH) {
1485 n = responded_other_number;
1486 if (n < x || n > y)
1487 continue;
1488 m = &message[n-1];
1489 m->m_xsize = 0;
1490 } else
1491 continue;
1493 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1494 cp += 5;
1495 while (*cp == ' ')
1496 cp++;
1497 if (*cp == '(')
1498 imap_getflags(cp, &cp, &m->m_flag);
1501 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1502 m->m_uid = strtoul(&cp[4], NULL, 10);
1503 getcache1(mp, m, NEED_UNSPEC, 1);
1504 m->m_flag &= ~MHIDDEN;
1507 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1508 x++;
1509 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1510 y--;
1511 if (x <= y) {
1512 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1513 tag(1), x, y);
1514 IMAP_OUT(o, MB_COMD, return STOP)
1515 while (mp->mb_active & MB_COMD) {
1516 imap_answer(mp, 1);
1517 if (response_status == RESPONSE_OTHER &&
1518 response_other == MESSAGE_DATA_FETCH) {
1519 n = responded_other_number;
1520 if (n < x || n > y)
1521 continue;
1522 m = &message[n-1];
1523 } else
1524 continue;
1525 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1526 m->m_xsize = strtol(&cp[12], NULL, 10);
1527 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1528 m->m_time = imap_read_date_time(&cp[13]);
1532 srelax_hold();
1533 for (n = X; n <= Y; ++n) {
1534 putcache(mp, &message[n-1]);
1535 srelax();
1537 srelax_rele();
1538 return OKAY;
1541 static void
1542 imap_init(struct mailbox *mp, int n)
1544 struct message *m;
1545 NYD_ENTER;
1546 UNUSED(mp);
1548 m = message + n;
1549 m->m_flag = MUSED | MNOFROM;
1550 m->m_block = 0;
1551 m->m_offset = 0;
1552 NYD_LEAVE;
1555 static void
1556 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1558 struct message *omessage = 0;
1559 int i, omsgCount = 0;
1560 enum okay dequeued = STOP;
1561 NYD_ENTER;
1563 if (nmail || transparent) {
1564 omessage = message;
1565 omsgCount = msgCount;
1567 if (nmail)
1568 dequeued = rec_dequeue();
1570 if (had_exists >= 0) {
1571 if (dequeued != OKAY)
1572 msgCount = had_exists;
1573 had_exists = -1;
1575 if (had_expunge >= 0) {
1576 if (dequeued != OKAY)
1577 msgCount -= had_expunge;
1578 had_expunge = -1;
1581 if (nmail && expunged_messages)
1582 printf("Expunged %ld message%s.\n", expunged_messages,
1583 (expunged_messages != 1 ? "s" : ""));
1584 *prevcount = omsgCount - expunged_messages;
1585 expunged_messages = 0;
1586 if (msgCount < 0) {
1587 fputs("IMAP error: Negative message count\n", stderr);
1588 msgCount = 0;
1591 if (dequeued != OKAY) {
1592 message = scalloc(msgCount + 1, sizeof *message);
1593 for (i = 0; i < msgCount; i++)
1594 imap_init(mp, i);
1595 if (!nmail && mp->mb_type == MB_IMAP)
1596 initcache(mp);
1597 if (msgCount > 0)
1598 imap_flags(mp, 1, msgCount);
1599 message[msgCount].m_size = 0;
1600 message[msgCount].m_lines = 0;
1601 rec_rmqueue();
1603 if (nmail || transparent)
1604 transflags(omessage, omsgCount, transparent);
1605 else
1606 setdot(message);
1607 NYD_LEAVE;
1610 FL int
1611 imap_setfile(const char *xserver, enum fedit_mode fm)
1613 struct url url;
1614 int rv;
1615 NYD_ENTER;
1617 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1618 rv = 1;
1619 goto jleave;
1621 if (!ok_blook(v15_compat) &&
1622 (!url.url_had_user || url.url_pass.s != NULL))
1623 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1625 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1626 rv = _imap_setfile1(&url, fm, 0);
1627 jleave:
1628 NYD_LEAVE;
1629 return rv;
1632 static bool_t
1633 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1635 bool_t rv = FAL0;
1636 NYD_ENTER;
1638 if (ok_blook(v15_compat))
1639 rv = ccred_lookup(ccredp, urlp);
1640 else {
1641 char *var, *old,
1642 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1644 if ((var = mbp->mb_imap_pass) != NULL) {
1645 var = savecat("password-", xuhp);
1646 if ((old = vok_vlook(var)) != NULL)
1647 old = sstrdup(old);
1648 vok_vset(var, mbp->mb_imap_pass);
1650 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1651 if (var != NULL) {
1652 if (old != NULL) {
1653 vok_vset(var, old);
1654 free(old);
1655 } else
1656 vok_vclear(var);
1660 NYD_LEAVE;
1661 return rv;
1664 static int
1665 _imap_setfile1(struct url *urlp, enum fedit_mode volatile fm,
1666 int volatile transparent)
1668 struct sock so;
1669 struct ccred ccred;
1670 sighandler_type volatile saveint, savepipe;
1671 char const *cp;
1672 int rv;
1673 int volatile prevcount = 0;
1674 enum mbflags same_flags;
1675 NYD_ENTER;
1677 if (fm & FEDIT_NEWMAIL) {
1678 saveint = safe_signal(SIGINT, SIG_IGN);
1679 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1680 if (saveint != SIG_IGN)
1681 safe_signal(SIGINT, imapcatch);
1682 if (savepipe != SIG_IGN)
1683 safe_signal(SIGPIPE, imapcatch);
1684 imaplock = 1;
1685 goto jnmail;
1688 same_flags = mb.mb_flags;
1689 same_imap_account = 0;
1690 if (mb.mb_imap_account != NULL &&
1691 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1692 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1693 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1694 disconnected(mb.mb_imap_account) == 0) {
1695 same_imap_account = 1;
1696 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1698 goto jduppass;
1699 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1700 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1701 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1702 jduppass:
1704 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1708 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1709 free(mb.mb_imap_pass);
1710 mb.mb_imap_pass = NULL;
1712 if (!_imap_getcred(&mb, &ccred, urlp)) {
1713 rv = -1;
1714 goto jleave;
1717 so.s_fd = -1;
1718 if (!same_imap_account) {
1719 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1720 rv = -1;
1721 goto jleave;
1723 } else
1724 so = mb.mb_sock;
1725 if (!transparent)
1726 quit();
1728 if (fm & FEDIT_SYSBOX)
1729 pstate &= ~PS_EDIT;
1730 else
1731 pstate |= PS_EDIT;
1732 if (mb.mb_imap_account != NULL)
1733 free(mb.mb_imap_account);
1734 if (mb.mb_imap_pass != NULL)
1735 free(mb.mb_imap_pass);
1736 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1737 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1738 * TODO object, and mailbox will naturally have an URL and credentials */
1739 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1741 if (!same_imap_account) {
1742 if (mb.mb_sock.s_fd >= 0)
1743 sclose(&mb.mb_sock);
1745 same_imap_account = 0;
1747 if (!transparent) {
1748 if (mb.mb_itf) {
1749 fclose(mb.mb_itf);
1750 mb.mb_itf = NULL;
1752 if (mb.mb_otf) {
1753 fclose(mb.mb_otf);
1754 mb.mb_otf = NULL;
1756 if (mb.mb_imap_mailbox != NULL)
1757 free(mb.mb_imap_mailbox);
1758 assert(urlp->url_path.s != NULL);
1759 imap_delim_init(&mb, urlp);
1760 mb.mb_imap_mailbox = sstrdup(imap_path_normalize(&mb, urlp->url_path.s));
1761 initbox(savecatsep(urlp->url_p_eu_h_p,
1762 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1763 mb.mb_imap_mailbox));
1765 mb.mb_type = MB_VOID;
1766 mb.mb_active = MB_NONE;
1768 imaplock = 1;
1769 saveint = safe_signal(SIGINT, SIG_IGN);
1770 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1771 if (sigsetjmp(imapjmp, 1)) {
1772 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1773 sclose(&mb.mb_sock);
1774 safe_signal(SIGINT, saveint);
1775 safe_signal(SIGPIPE, savepipe);
1776 imaplock = 0;
1778 mb.mb_type = MB_VOID;
1779 mb.mb_active = MB_NONE;
1780 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1781 goto jleave;
1783 if (saveint != SIG_IGN)
1784 safe_signal(SIGINT, imapcatch);
1785 if (savepipe != SIG_IGN)
1786 safe_signal(SIGPIPE, imapcatch);
1788 if (mb.mb_sock.s_fd < 0) {
1789 if (disconnected(mb.mb_imap_account)) {
1790 if (cache_setptr(fm, transparent) == STOP)
1791 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1792 goto jdone;
1794 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1795 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1796 savealrm = safe_signal(SIGALRM, imapalarm);
1797 alarm(imapkeepalive);
1801 mb.mb_sock = so;
1802 mb.mb_sock.s_desc = "IMAP";
1803 mb.mb_sock.s_onclose = imap_timer_off;
1804 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1805 sclose(&mb.mb_sock);
1806 imap_timer_off();
1807 safe_signal(SIGINT, saveint);
1808 safe_signal(SIGPIPE, savepipe);
1809 imaplock = 0;
1810 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1811 goto jleave;
1813 } else /* same account */
1814 mb.mb_flags |= same_flags;
1816 if (options & OPT_R_FLAG)
1817 fm |= FEDIT_RDONLY;
1818 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1819 mb.mb_type = MB_IMAP;
1820 cache_dequeue(&mb);
1821 assert(urlp->url_path.s != NULL);
1822 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1823 /*sclose(&mb.mb_sock);
1824 imap_timer_off();*/
1825 safe_signal(SIGINT, saveint);
1826 safe_signal(SIGPIPE, savepipe);
1827 imaplock = 0;
1828 mb.mb_type = MB_VOID;
1829 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1830 goto jleave;
1833 jnmail:
1834 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1835 UNVOLATILE(&prevcount));
1836 jdone:
1837 setmsize(msgCount);
1838 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1839 pstate &= ~PS_SAW_COMMAND;
1840 safe_signal(SIGINT, saveint);
1841 safe_signal(SIGPIPE, savepipe);
1842 imaplock = 0;
1844 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1845 purgecache(&mb, message, msgCount);
1846 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1847 mb.mb_threaded = 0;
1848 c_sort((void*)-1);
1851 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1852 mb.mb_type == MB_CACHE)) {
1853 rv = (msgCount == 0);
1854 goto jleave;
1857 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1858 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1859 !ok_blook(emptystart))
1860 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1861 rv = 1;
1862 goto jleave;
1865 if (fm & FEDIT_NEWMAIL)
1866 newmailinfo(prevcount);
1867 rv = 0;
1868 jleave:
1869 NYD_LEAVE;
1870 return rv;
1873 static int
1874 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1875 int need, const char *head, size_t headsize, long headlines)
1877 char *line = NULL, *lp;
1878 size_t linesize = 0, linelen, size = 0;
1879 int emptyline = 0, lines = 0, excess = 0;
1880 off_t offset;
1881 NYD_ENTER;
1883 fseek(mp->mb_otf, 0L, SEEK_END);
1884 offset = ftell(mp->mb_otf);
1886 if (head)
1887 fwrite(head, 1, headsize, mp->mb_otf);
1889 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1890 lp = line;
1891 if (linelen > expected) {
1892 excess = linelen - expected;
1893 linelen = expected;
1895 /* TODO >>
1896 * Need to mask 'From ' lines. This cannot be done properly
1897 * since some servers pass them as 'From ' and others as
1898 * '>From '. Although one could identify the first kind of
1899 * server in principle, it is not possible to identify the
1900 * second as '>From ' may also come from a server of the
1901 * first type as actual data. So do what is absolutely
1902 * necessary only - mask 'From '.
1904 * If the line is the first line of the message header, it
1905 * is likely a real 'From ' line. In this case, it is just
1906 * ignored since it violates all standards.
1907 * TODO can the latter *really* happen??
1908 * TODO <<
1910 /* Since we simply copy over data without doing any transfer
1911 * encoding reclassification/adjustment we *have* to perform
1912 * RFC 4155 compliant From_ quoting here */
1913 if (emptyline && is_head(lp, linelen, FAL0)) {
1914 fputc('>', mp->mb_otf);
1915 ++size;
1917 emptyline = 0;
1918 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1919 if (linelen > 2) {
1920 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1921 size += linelen - 1;
1922 } else {
1923 emptyline = 1;
1924 ++size;
1926 fputc('\n', mp->mb_otf);
1927 } else {
1928 fwrite(lp, 1, linelen, mp->mb_otf);
1929 size += linelen;
1931 ++lines;
1932 if ((expected -= linelen) <= 0)
1933 break;
1935 if (!emptyline) {
1936 /* This is very ugly; but some IMAP daemons don't end a
1937 * message with \r\n\r\n, and we need \n\n for mbox format */
1938 fputc('\n', mp->mb_otf);
1939 ++lines;
1940 ++size;
1942 fflush(mp->mb_otf);
1944 if (m != NULL) {
1945 m->m_size = size + headsize;
1946 m->m_lines = lines + headlines;
1947 m->m_block = mailx_blockof(offset);
1948 m->m_offset = mailx_offsetof(offset);
1949 switch (need) {
1950 case NEED_HEADER:
1951 m->m_have |= HAVE_HEADER;
1952 break;
1953 case NEED_BODY:
1954 m->m_have |= HAVE_HEADER | HAVE_BODY;
1955 m->m_xlines = m->m_lines;
1956 m->m_xsize = m->m_size;
1957 break;
1960 free(line);
1961 NYD_LEAVE;
1962 return excess;
1965 static void
1966 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1967 const char *head, size_t headsize, long headlines)
1969 off_t offset;
1970 size_t len;
1971 NYD_ENTER;
1973 len = strlen(str);
1974 fseek(mp->mb_otf, 0L, SEEK_END);
1975 offset = ftell(mp->mb_otf);
1976 if (head)
1977 fwrite(head, 1, headsize, mp->mb_otf);
1978 if (len > 0) {
1979 fwrite(str, 1, len, mp->mb_otf);
1980 fputc('\n', mp->mb_otf);
1981 ++len;
1983 fflush(mp->mb_otf);
1985 if (m != NULL) {
1986 m->m_size = headsize + len;
1987 m->m_lines = headlines + 1;
1988 m->m_block = mailx_blockof(offset);
1989 m->m_offset = mailx_offsetof(offset);
1990 m->m_have |= HAVE_HEADER | HAVE_BODY;
1991 m->m_xlines = m->m_lines;
1992 m->m_xsize = m->m_size;
1994 NYD_LEAVE;
1997 static enum okay
1998 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2000 char o[LINESIZE];
2001 struct message mt;
2002 sighandler_type volatile saveint, savepipe;
2003 char * volatile head;
2004 char const *cp, *loc, * volatile item, * volatile resp;
2005 size_t expected;
2006 size_t volatile headsize;
2007 int number;
2008 FILE *queuefp;
2009 long volatile headlines;
2010 long n;
2011 ul_i volatile u;
2012 enum okay ok;
2013 NYD_X;
2015 saveint = savepipe = SIG_IGN;
2016 head = NULL;
2017 cp = loc = item = resp = NULL;
2018 headsize = 0;
2019 number = (int)PTR2SIZE(m - message + 1);
2020 queuefp = NULL;
2021 headlines = 0;
2022 n = -1;
2023 u = 0;
2024 ok = STOP;
2026 if (getcache(mp, m, need) == OKAY)
2027 return OKAY;
2028 if (mp->mb_type == MB_CACHE) {
2029 n_err(_("Message %lu not available\n"), (ul_i)number);
2030 return STOP;
2033 if (mp->mb_sock.s_fd < 0) {
2034 n_err(_("IMAP connection closed\n"));
2035 return STOP;
2038 switch (need) {
2039 case NEED_HEADER:
2040 resp = item = "RFC822.HEADER";
2041 break;
2042 case NEED_BODY:
2043 item = "BODY.PEEK[]";
2044 resp = "BODY[]";
2045 if (m->m_flag & HAVE_HEADER && m->m_size) {
2046 char *hdr = smalloc(m->m_size);
2047 fflush(mp->mb_otf);
2048 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2049 SEEK_SET) < 0 ||
2050 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2051 free(hdr);
2052 break;
2054 head = hdr;
2055 headsize = m->m_size;
2056 headlines = m->m_lines;
2057 item = "BODY.PEEK[TEXT]";
2058 resp = "BODY[TEXT]";
2060 break;
2061 case NEED_UNSPEC:
2062 return STOP;
2065 imaplock = 1;
2066 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2067 if (sigsetjmp(imapjmp, 1)) {
2068 safe_signal(SIGINT, saveint);
2069 safe_signal(SIGPIPE, savepipe);
2070 imaplock = 0;
2071 return STOP;
2073 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2074 safe_signal(SIGINT, &_imap_maincatch);
2075 if (savepipe != SIG_IGN)
2076 safe_signal(SIGPIPE, imapcatch);
2078 if (m->m_uid)
2079 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
2080 tag(1), m->m_uid, item);
2081 else {
2082 if (check_expunged() == STOP)
2083 goto out;
2084 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
2086 IMAP_OUT(o, MB_COMD, goto out)
2087 for (;;) {
2088 ok = imap_answer(mp, 1);
2089 if (ok == STOP)
2090 break;
2091 if (response_status != RESPONSE_OTHER ||
2092 response_other != MESSAGE_DATA_FETCH)
2093 continue;
2094 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
2095 continue;
2096 if (m->m_uid) {
2097 if ((cp = asccasestr(responded_other_text, "UID "))) {
2098 u = atol(&cp[4]);
2099 n = 0;
2100 } else {
2101 n = -1;
2102 u = 0;
2104 } else
2105 n = responded_other_number;
2106 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
2107 if (m->m_uid ? m->m_uid != u : n != number)
2108 continue;
2109 if ((cp = strchr(loc, '"')) != NULL) {
2110 cp = imap_unquotestr(cp);
2111 imap_putstr(mp, m, cp, head, headsize, headlines);
2112 } else {
2113 m->m_have |= HAVE_HEADER|HAVE_BODY;
2114 m->m_xlines = m->m_lines;
2115 m->m_xsize = m->m_size;
2117 goto out;
2119 expected = atol(&cp[1]);
2120 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
2121 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2122 continue;
2124 mt = *m;
2125 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2126 if (n >= 0) {
2127 commitmsg(mp, m, &mt, mt.m_have);
2128 break;
2130 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2131 if (options & OPT_VERBVERB)
2132 fputs(imapbuf, stderr);
2133 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2134 u = atol(&cp[4]);
2135 if (u == m->m_uid) {
2136 commitmsg(mp, m, &mt, mt.m_have);
2137 break;
2142 out:
2143 while (mp->mb_active & MB_COMD)
2144 ok = imap_answer(mp, 1);
2146 if (saveint != SIG_IGN)
2147 safe_signal(SIGINT, saveint);
2148 if (savepipe != SIG_IGN)
2149 safe_signal(SIGPIPE, savepipe);
2150 imaplock--;
2152 if (ok == OKAY)
2153 putcache(mp, m);
2154 if (head != NULL)
2155 free(head);
2156 if (interrupts)
2157 onintr(0);
2158 return ok;
2161 FL enum okay
2162 imap_header(struct message *m)
2164 enum okay rv;
2165 NYD_ENTER;
2167 rv = imap_get(&mb, m, NEED_HEADER);
2168 NYD_LEAVE;
2169 return rv;
2173 FL enum okay
2174 imap_body(struct message *m)
2176 enum okay rv;
2177 NYD_ENTER;
2179 rv = imap_get(&mb, m, NEED_BODY);
2180 NYD_LEAVE;
2181 return rv;
2184 static void
2185 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2186 enum havespec have)
2188 NYD_ENTER;
2189 tomp->m_size = frommp->m_size;
2190 tomp->m_lines = frommp->m_lines;
2191 tomp->m_block = frommp->m_block;
2192 tomp->m_offset = frommp->m_offset;
2193 tomp->m_have = have;
2194 if (have & HAVE_BODY) {
2195 tomp->m_xlines = frommp->m_lines;
2196 tomp->m_xsize = frommp->m_size;
2198 putcache(mp, tomp);
2199 NYD_LEAVE;
2202 static enum okay
2203 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2205 /* bot > topp */
2206 char o[LINESIZE];
2207 char const *cp;
2208 struct message mt;
2209 size_t expected;
2210 int n = 0, u;
2211 FILE *queuefp = NULL;
2212 enum okay ok;
2213 NYD_X;
2215 if (m[bot].m_uid)
2216 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2217 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2218 else {
2219 if (check_expunged() == STOP)
2220 return STOP;
2221 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2222 tag(1), bot, topp);
2224 IMAP_OUT(o, MB_COMD, return STOP)
2226 srelax_hold();
2227 for (;;) {
2228 ok = imap_answer(mp, 1);
2229 if (response_status != RESPONSE_OTHER)
2230 break;
2231 if (response_other != MESSAGE_DATA_FETCH)
2232 continue;
2233 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
2234 srelax_rele();
2235 return STOP;
2237 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
2238 continue;
2239 expected = atol(&cp[1]);
2240 if (m[bot-1].m_uid) {
2241 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
2242 u = atoi(&cp[4]);
2243 for (n = bot; n <= topp; n++)
2244 if ((unsigned long)u == m[n-1].m_uid)
2245 break;
2246 if (n > topp) {
2247 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2248 continue;
2250 } else
2251 n = -1;
2252 } else {
2253 n = responded_other_number;
2254 if (n <= 0 || n > msgCount) {
2255 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2256 continue;
2259 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2260 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
2261 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
2262 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
2263 if (options & OPT_VERBVERB)
2264 fputs(imapbuf, stderr);
2265 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
2266 u = atoi(&cp[4]);
2267 for (n = bot; n <= topp; n++)
2268 if ((unsigned long)u == m[n-1].m_uid)
2269 break;
2270 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
2271 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
2272 n = 0;
2275 srelax();
2277 srelax_rele();
2279 while (mp->mb_active & MB_COMD)
2280 ok = imap_answer(mp, 1);
2281 return ok;
2284 FL void
2285 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2287 sighandler_type saveint, savepipe;
2288 /*enum okay ok = STOP;*/
2289 int i, chunk = 256;
2290 NYD_X;
2292 if (mb.mb_type == MB_CACHE)
2293 return;
2294 if (bot < 1)
2295 bot = 1;
2296 if (topp > msgCount)
2297 topp = msgCount;
2298 for (i = bot; i < topp; i++) {
2299 if (message[i-1].m_have & HAVE_HEADER ||
2300 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2301 bot = i+1;
2302 else
2303 break;
2305 for (i = topp; i > bot; i--) {
2306 if (message[i-1].m_have & HAVE_HEADER ||
2307 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2308 topp = i-1;
2309 else
2310 break;
2312 if (bot >= topp)
2313 return;
2315 imaplock = 1;
2316 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2317 safe_signal(SIGINT, &_imap_maincatch);
2318 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2319 if (sigsetjmp(imapjmp, 1) == 0) {
2320 if (savepipe != SIG_IGN)
2321 safe_signal(SIGPIPE, imapcatch);
2323 for (i = bot; i <= topp; i += chunk) {
2324 int j = i + chunk - 1;
2325 j = MIN(j, topp);
2326 if (visible(message + j))
2327 /*ok = */imap_fetchheaders(&mb, message, i, j);
2328 if (interrupts)
2329 onintr(0); /* XXX imaplock? */
2332 safe_signal(SIGINT, saveint);
2333 safe_signal(SIGPIPE, savepipe);
2334 imaplock = 0;
2337 static enum okay
2338 __imap_exit(struct mailbox *mp)
2340 char o[LINESIZE];
2341 FILE *queuefp = NULL;
2342 NYD_X;
2344 mp->mb_active |= MB_BYE;
2345 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2346 IMAP_OUT(o, MB_COMD, return STOP)
2347 IMAP_ANSWER()
2348 return OKAY;
2351 static enum okay
2352 imap_exit(struct mailbox *mp)
2354 enum okay rv;
2355 NYD_ENTER;
2357 rv = __imap_exit(mp);
2358 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2359 free(mp->mb_imap_pass);
2360 free(mp->mb_imap_account);
2361 free(mp->mb_imap_mailbox);
2362 if (mp->mb_cache_directory != NULL)
2363 free(mp->mb_cache_directory);
2364 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2365 mp->mb_imap_account =
2366 mp->mb_imap_mailbox =
2367 mp->mb_cache_directory = "";
2368 #else
2369 mp->mb_imap_account = NULL; /* for assert legacy time.. */
2370 mp->mb_imap_mailbox = NULL;
2371 mp->mb_cache_directory = NULL;
2372 #endif
2373 #endif
2374 sclose(&mp->mb_sock);
2375 NYD_LEAVE;
2376 return rv;
2379 static enum okay
2380 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2382 NYD_ENTER;
2383 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2384 if (mp->mb_type == MB_IMAP)
2385 delcache(mp, m);
2386 NYD_LEAVE;
2387 return OKAY;
2390 static enum okay
2391 imap_close(struct mailbox *mp)
2393 char o[LINESIZE];
2394 FILE *queuefp = NULL;
2395 NYD_X;
2397 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2398 IMAP_OUT(o, MB_COMD, return STOP)
2399 IMAP_ANSWER()
2400 return OKAY;
2403 static enum okay
2404 imap_update(struct mailbox *mp)
2406 struct message *m;
2407 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2408 NYD_ENTER;
2410 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
2411 holdbits();
2412 c = 0;
2413 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2414 if (m->m_flag & MBOX)
2415 ++c;
2416 if (c > 0)
2417 if (makembox() == STOP)
2418 goto jbypass;
2421 gotcha = held = 0;
2422 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2423 if (mp->mb_perm == 0)
2424 dodel = 0;
2425 else if (pstate & PS_EDIT)
2426 dodel = ((m->m_flag & MDELETED) != 0);
2427 else
2428 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2430 /* Fetch the result after around each 800 STORE commands
2431 * sent (approx. 32k data sent). Otherwise, servers will
2432 * try to flush the return queue at some point, leading
2433 * to a deadlock if we are still writing commands but not
2434 * reading their results */
2435 needstat = stored > 0 && stored % 800 == 0;
2436 /* Even if this message has been deleted, continue
2437 * to set further flags. This is necessary to support
2438 * Gmail semantics, where "delete" actually means
2439 * "archive", and the flags are applied to the copy
2440 * in "All Mail" */
2441 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2442 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2443 stored++;
2445 if (m->m_flag & MFLAG) {
2446 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2447 stored++;
2449 if (m->m_flag & MUNFLAG) {
2450 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2451 stored++;
2453 if (m->m_flag & MANSWER) {
2454 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2455 stored++;
2457 if (m->m_flag & MUNANSWER) {
2458 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2459 stored++;
2461 if (m->m_flag & MDRAFT) {
2462 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2463 stored++;
2465 if (m->m_flag & MUNDRAFT) {
2466 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2467 stored++;
2470 if (dodel) {
2471 imap_delete(mp, m-message+1, m, needstat);
2472 stored++;
2473 gotcha++;
2474 } else if (mp->mb_type != MB_CACHE ||
2475 (!(pstate & PS_EDIT) &&
2476 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2477 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2478 (MPRESERVE | MTOUCH) ||
2479 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
2480 held++;
2481 if (m->m_flag & MNEW) {
2482 m->m_flag &= ~MNEW;
2483 m->m_flag |= MSTATUS;
2486 jbypass:
2487 if (gotcha)
2488 imap_close(mp);
2490 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2491 if (!(m->m_flag & MUNLINKED) &&
2492 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2493 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2494 putcache(mp, m);
2495 modflags++;
2498 /* XXX should be readonly (but our IMAP code is weird...) */
2499 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST)) &&
2500 mb.mb_perm != 0) {
2501 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2502 printf(_("\"%s\" "), displayname);
2503 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2504 ? _("complete\n") : _("updated.\n"));
2505 } else if (held && !(pstate & PS_EDIT)) {
2506 if (held == 1)
2507 printf(_("Held 1 message in %s\n"), displayname);
2508 else
2509 printf(_("Held %d messages in %s\n"), held, displayname);
2511 fflush(stdout);
2513 NYD_LEAVE;
2514 return OKAY;
2517 FL void
2518 imap_quit(void)
2520 sighandler_type volatile saveint, savepipe;
2521 NYD_ENTER;
2523 if (mb.mb_type == MB_CACHE) {
2524 imap_update(&mb);
2525 goto jleave;
2528 if (mb.mb_sock.s_fd < 0) {
2529 n_err(_("IMAP connection closed\n"));
2530 goto jleave;
2533 imaplock = 1;
2534 saveint = safe_signal(SIGINT, SIG_IGN);
2535 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2536 if (sigsetjmp(imapjmp, 1)) {
2537 safe_signal(SIGINT, saveint);
2538 safe_signal(SIGPIPE, saveint);
2539 imaplock = 0;
2540 goto jleave;
2542 if (saveint != SIG_IGN)
2543 safe_signal(SIGINT, imapcatch);
2544 if (savepipe != SIG_IGN)
2545 safe_signal(SIGPIPE, imapcatch);
2547 imap_update(&mb);
2548 if (!same_imap_account)
2549 imap_exit(&mb);
2551 safe_signal(SIGINT, saveint);
2552 safe_signal(SIGPIPE, savepipe);
2553 imaplock = 0;
2554 jleave:
2555 NYD_LEAVE;
2558 static enum okay
2559 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2560 int needstat)
2562 char o[LINESIZE];
2563 FILE *queuefp = NULL;
2564 NYD_X;
2566 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2567 return STOP;
2568 if (m->m_uid)
2569 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2570 tag(1), m->m_uid, c, sp);
2571 else {
2572 if (check_expunged() == STOP)
2573 return STOP;
2574 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2576 IMAP_OUT(o, MB_COMD, return STOP)
2577 if (needstat)
2578 IMAP_ANSWER()
2579 else
2580 mb.mb_active &= ~MB_COMD;
2581 if (queuefp != NULL)
2582 Fclose(queuefp);
2583 return OKAY;
2586 FL enum okay
2587 imap_undelete(struct message *m, int n)
2589 enum okay rv;
2590 NYD_ENTER;
2592 rv = imap_unstore(m, n, "\\Deleted");
2593 NYD_LEAVE;
2594 return rv;
2597 FL enum okay
2598 imap_unread(struct message *m, int n)
2600 enum okay rv;
2601 NYD_ENTER;
2603 rv = imap_unstore(m, n, "\\Seen");
2604 NYD_LEAVE;
2605 return rv;
2608 static enum okay
2609 imap_unstore(struct message *m, int n, const char *flag)
2611 sighandler_type saveint, savepipe;
2612 enum okay rv = STOP;
2613 NYD_ENTER;
2615 imaplock = 1;
2616 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2617 safe_signal(SIGINT, &_imap_maincatch);
2618 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2619 if (sigsetjmp(imapjmp, 1) == 0) {
2620 if (savepipe != SIG_IGN)
2621 safe_signal(SIGPIPE, imapcatch);
2623 rv = imap_store(&mb, m, n, '-', flag, 1);
2625 safe_signal(SIGINT, saveint);
2626 safe_signal(SIGPIPE, savepipe);
2627 imaplock = 0;
2629 NYD_LEAVE;
2630 if (interrupts)
2631 onintr(0);
2632 return rv;
2635 static const char *
2636 tag(int new)
2638 static char ts[20];
2639 static long n;
2640 NYD2_ENTER;
2642 if (new)
2643 ++n;
2644 snprintf(ts, sizeof ts, "T%lu", n);
2645 NYD2_LEAVE;
2646 return ts;
2649 FL int
2650 c_imapcodec(void *v){
2651 bool_t err;
2652 char const **argv, *cp, *res;
2653 NYD_ENTER;
2655 if(is_prefix(cp = *(argv = v), "encode")){
2656 while((cp = *++argv) != NULL){
2657 res = imap_path_normalize(NULL, cp);
2658 res = imap_path_encode(res, &err);
2659 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2660 cp, strlen(cp), (err ? "ERROR " : ""), res, strlen(res));
2662 }else if(is_prefix(cp, "decode")){
2663 struct str in, out;
2665 while((cp = *++argv) != NULL){
2666 res = imap_path_normalize(NULL, cp);
2667 res = imap_path_decode(res, &err);
2668 in.l = strlen(in.s = UNCONST(res)); /* logical */
2669 makeprint(&in, &out);
2670 printf(" in: %s (%" PRIuZ " bytes)\nout: %s%s (%" PRIuZ " bytes)\n",
2671 cp, strlen(cp), (err ? "ERROR " : ""), out.s, in.l);
2672 free(out.s);
2674 }else{
2675 n_err(_("`imapcodec': invalid subcommand: %s\n"), *argv);
2676 cp = NULL;
2678 NYD_LEAVE;
2679 return (cp != NULL ? OKAY : STOP);
2682 FL int
2683 c_imap_imap(void *vp)
2685 char o[LINESIZE];
2686 sighandler_type saveint, savepipe;
2687 struct mailbox *mp = &mb;
2688 FILE *queuefp = NULL;
2689 enum okay volatile ok = STOP;
2690 NYD_X;
2692 if (mp->mb_type != MB_IMAP) {
2693 printf("Not operating on an IMAP mailbox.\n");
2694 return 1;
2696 imaplock = 1;
2697 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2698 safe_signal(SIGINT, &_imap_maincatch);
2699 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2700 if (sigsetjmp(imapjmp, 1) == 0) {
2701 if (savepipe != SIG_IGN)
2702 safe_signal(SIGPIPE, imapcatch);
2704 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2705 IMAP_OUT(o, MB_COMD, goto out)
2706 while (mp->mb_active & MB_COMD) {
2707 ok = imap_answer(mp, 0);
2708 fputs(responded_text, stdout);
2711 out:
2712 safe_signal(SIGINT, saveint);
2713 safe_signal(SIGPIPE, savepipe);
2714 imaplock = 0;
2716 if (interrupts)
2717 onintr(0);
2718 return ok != OKAY;
2721 FL int
2722 imap_newmail(int nmail)
2724 NYD_ENTER;
2726 if (nmail && had_exists < 0 && had_expunge < 0) {
2727 imaplock = 1;
2728 imap_noop();
2729 imaplock = 0;
2732 if (had_exists == msgCount && had_expunge < 0)
2733 /* Some servers always respond with EXISTS to NOOP. If
2734 * the mailbox has been changed but the number of messages
2735 * has not, an EXPUNGE must also had been sent; otherwise,
2736 * nothing has changed */
2737 had_exists = -1;
2738 NYD_LEAVE;
2739 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2742 static char *
2743 imap_putflags(int f)
2745 const char *cp;
2746 char *buf, *bp;
2747 NYD2_ENTER;
2749 bp = buf = salloc(100);
2750 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2751 *bp++ = '(';
2752 if (f & MREAD) {
2753 if (bp[-1] != '(')
2754 *bp++ = ' ';
2755 for (cp = "\\Seen"; *cp; cp++)
2756 *bp++ = *cp;
2758 if (f & MFLAGGED) {
2759 if (bp[-1] != '(')
2760 *bp++ = ' ';
2761 for (cp = "\\Flagged"; *cp; cp++)
2762 *bp++ = *cp;
2764 if (f & MANSWERED) {
2765 if (bp[-1] != '(')
2766 *bp++ = ' ';
2767 for (cp = "\\Answered"; *cp; cp++)
2768 *bp++ = *cp;
2770 if (f & MDRAFT) {
2771 if (bp[-1] != '(')
2772 *bp++ = ' ';
2773 for (cp = "\\Draft"; *cp; cp++)
2774 *bp++ = *cp;
2776 *bp++ = ')';
2777 *bp++ = ' ';
2779 *bp = '\0';
2780 NYD2_LEAVE;
2781 return buf;
2784 static void
2785 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2787 NYD2_ENTER;
2788 while (*cp != ')') {
2789 if (*cp == '\\') {
2790 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2791 *f |= MREAD;
2792 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2793 *f |= MNEW;
2794 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2795 *f |= MDELETED;
2796 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2797 *f |= MFLAGGED;
2798 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2799 *f |= MANSWERED;
2800 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2801 *f |= MDRAFTED;
2803 cp++;
2806 if (xp != NULL)
2807 *xp = cp;
2808 NYD2_LEAVE;
2811 static enum okay
2812 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2813 long xsize, enum mflag flag, time_t t)
2815 char o[LINESIZE], *buf;
2816 size_t bufsize, buflen, cnt;
2817 long size, lines, ysize;
2818 char const *qname;
2819 bool_t twice;
2820 FILE *queuefp;
2821 enum okay rv;
2822 NYD_ENTER;
2824 rv = STOP;
2825 queuefp = NULL;
2826 twice = FAL0;
2827 buf = NULL;
2829 if((qname = imap_path_quote(mp, name)) == NULL)
2830 goto jleave;
2832 if (mp->mb_type == MB_CACHE) {
2833 queuefp = cache_queue(mp);
2834 if (queuefp == NULL) {
2835 buf = NULL;
2836 goto jleave;
2838 rv = OKAY;
2841 buf = smalloc(bufsize = LINESIZE);
2842 buflen = 0;
2843 jagain:
2844 size = xsize;
2845 cnt = fsize(fp);
2846 if (fseek(fp, off1, SEEK_SET) < 0) {
2847 rv = STOP;
2848 goto jleave;
2851 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2852 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
2853 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2854 while (mp->mb_active & MB_COMD) {
2855 rv = imap_answer(mp, twice);
2856 if (response_type == RESPONSE_CONT)
2857 break;
2860 if (mp->mb_type != MB_CACHE && rv == STOP) {
2861 if (!twice)
2862 goto jtrycreate;
2863 else
2864 goto jleave;
2867 lines = ysize = 0;
2868 while (size > 0) {
2869 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2870 lines++;
2871 ysize += buflen;
2872 buf[buflen - 1] = '\r';
2873 buf[buflen] = '\n';
2874 if (mp->mb_type != MB_CACHE)
2875 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2876 else if (queuefp)
2877 fwrite(buf, 1, buflen+1, queuefp);
2878 size -= buflen + 1;
2880 if (mp->mb_type != MB_CACHE)
2881 swrite(&mp->mb_sock, "\r\n");
2882 else if (queuefp)
2883 fputs("\r\n", queuefp);
2884 while (mp->mb_active & MB_COMD) {
2885 rv = imap_answer(mp, 0);
2886 if (response_status == RESPONSE_NO /*&&
2887 ascncasecmp(responded_text,
2888 "[TRYCREATE] ", 12) == 0*/) {
2889 jtrycreate:
2890 if (twice) {
2891 rv = STOP;
2892 goto jleave;
2894 twice = TRU1;
2895 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2896 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2897 while (mp->mb_active & MB_COMD)
2898 rv = imap_answer(mp, 1);
2899 if (rv == STOP)
2900 goto jleave;
2901 imap_created_mailbox++;
2902 goto jagain;
2903 } else if (rv != OKAY)
2904 n_err(_("IMAP error: %s"), responded_text);
2905 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2906 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2908 jleave:
2909 if (queuefp != NULL)
2910 Fclose(queuefp);
2911 if (buf != NULL)
2912 free(buf);
2913 NYD_LEAVE;
2914 return rv;
2917 static enum okay
2918 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2920 char *buf, *bp, *lp;
2921 size_t bufsize, buflen, cnt;
2922 off_t off1 = -1, offs;
2923 int flag;
2924 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2925 time_t tim;
2926 long size;
2927 enum okay rv;
2928 NYD_ENTER;
2930 buf = smalloc(bufsize = LINESIZE);
2931 buflen = 0;
2932 cnt = fsize(fp);
2933 offs = ftell(fp);
2934 time(&tim);
2935 size = 0;
2937 for (flag = MNEW, state = _NLSEP;;) {
2938 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2940 if (bp == NULL ||
2941 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2942 is_head(buf, buflen, FAL0))) {
2943 if (off1 != (off_t)-1) {
2944 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2945 if (rv == STOP)
2946 goto jleave;
2947 fseek(fp, offs+buflen, SEEK_SET);
2949 off1 = offs + buflen;
2950 size = 0;
2951 flag = MNEW;
2952 state = _INHEAD;
2953 if (bp == NULL)
2954 break;
2955 tim = unixtime(buf);
2956 } else
2957 size += buflen+1;
2958 offs += buflen;
2960 state &= ~_NLSEP;
2961 if (buf[0] == '\n') {
2962 state &= ~_INHEAD;
2963 state |= _NLSEP;
2964 } else if (state & _INHEAD) {
2965 if (ascncasecmp(buf, "status", 6) == 0) {
2966 lp = &buf[6];
2967 while (whitechar(*lp))
2968 lp++;
2969 if (*lp == ':')
2970 while (*++lp != '\0')
2971 switch (*lp) {
2972 case 'R':
2973 flag |= MREAD;
2974 break;
2975 case 'O':
2976 flag &= ~MNEW;
2977 break;
2979 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2980 lp = &buf[8];
2981 while (whitechar(*lp))
2982 lp++;
2983 if (*lp == ':')
2984 while (*++lp != '\0')
2985 switch (*lp) {
2986 case 'F':
2987 flag |= MFLAGGED;
2988 break;
2989 case 'A':
2990 flag |= MANSWERED;
2991 break;
2992 case 'T':
2993 flag |= MDRAFTED;
2994 break;
2999 rv = OKAY;
3000 jleave:
3001 free(buf);
3002 NYD_LEAVE;
3003 return rv;
3006 FL enum okay
3007 imap_append(const char *xserver, FILE *fp)
3009 sighandler_type volatile saveint, savepipe;
3010 struct url url;
3011 struct ccred ccred;
3012 enum okay rv = STOP;
3013 NYD_ENTER;
3015 if (!url_parse(&url, CPROTO_IMAP, xserver))
3016 goto j_leave;
3017 if (!ok_blook(v15_compat) &&
3018 (!url.url_had_user || url.url_pass.s != NULL))
3019 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3020 assert(url.url_path.s != NULL);
3022 imaplock = 1;
3023 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3024 safe_signal(SIGINT, &_imap_maincatch);
3025 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3026 if (sigsetjmp(imapjmp, 1))
3027 goto jleave;
3028 if (savepipe != SIG_IGN)
3029 safe_signal(SIGPIPE, imapcatch);
3031 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
3032 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3033 rv = imap_append0(&mb, url.url_path.s, fp);
3034 } else {
3035 struct mailbox mx;
3037 memset(&mx, 0, sizeof mx);
3039 if (!_imap_getcred(&mx, &ccred, &url))
3040 goto jleave;
3042 imap_delim_init(&mx, &url);
3043 mx.mb_imap_mailbox = sstrdup(imap_path_normalize(&mx, url.url_path.s));
3045 if (disconnected(url.url_p_eu_h_p) == 0) {
3046 if (!sopen(&mx.mb_sock, &url))
3047 goto jfail;
3048 mx.mb_sock.s_desc = "IMAP";
3049 mx.mb_type = MB_IMAP;
3050 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3051 /* TODO the code now did
3052 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3053 * TODO though imap_mailbox is sfree()d and mbx
3054 * TODO is possibly even a constant
3055 * TODO i changed this to sstrdup() sofar, as is used
3056 * TODO somewhere else in this file for this! */
3057 if (imap_preauth(&mx, &url) != OKAY ||
3058 imap_auth(&mx, &ccred) != OKAY) {
3059 sclose(&mx.mb_sock);
3060 goto jfail;
3062 rv = imap_append0(&mx, url.url_path.s, fp);
3063 imap_exit(&mx);
3064 } else {
3065 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
3066 mx.mb_type = MB_CACHE;
3067 rv = imap_append0(&mx, url.url_path.s, fp);
3069 jfail:
3073 jleave:
3074 safe_signal(SIGINT, saveint);
3075 safe_signal(SIGPIPE, savepipe);
3076 imaplock = 0;
3077 j_leave:
3078 NYD_LEAVE;
3079 if (interrupts)
3080 onintr(0);
3081 return rv;
3084 static enum okay
3085 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3086 struct list_item **lend, int level)
3088 char o[LINESIZE], *cp;
3089 struct list_item *lp;
3090 const char *qname, *bp;
3091 FILE *queuefp;
3092 enum okay ok;
3093 NYD_X;
3095 ok = STOP;
3096 queuefp = NULL;
3098 if((qname = imap_path_quote(mp, base)) == NULL)
3099 goto jleave;
3101 *list = *lend = NULL;
3102 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3103 IMAP_OUT(o, MB_COMD, goto jleave)
3104 while (mp->mb_active & MB_COMD) {
3105 ok = imap_answer(mp, 1);
3106 if (response_status == RESPONSE_OTHER &&
3107 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3108 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3109 lp = csalloc(1, sizeof *lp);
3110 lp->l_name = cp;
3111 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3112 ++cp;
3113 lp->l_base = *cp ? cp : savestr(base);
3114 lp->l_attr = list_attributes;
3115 lp->l_level = level+1;
3116 lp->l_delim = list_hierarchy_delimiter;
3117 if (*list && *lend) {
3118 (*lend)->l_next = lp;
3119 *lend = lp;
3120 } else
3121 *list = *lend = lp;
3124 jleave:
3125 return ok;
3128 static enum okay
3129 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3131 struct list_item *list, *lend, *lp, *lx, *ly;
3132 int n, depth;
3133 const char *bp;
3134 char *cp;
3135 enum okay rv;
3136 NYD_ENTER;
3138 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3139 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3140 goto jleave;
3141 rv = OKAY;
3142 if (list == NULL || lend == NULL)
3143 goto jleave;
3145 for (lp = list; lp; lp = lp->l_next)
3146 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3147 !(lp->l_attr & LIST_NOINFERIORS)) {
3148 cp = salloc((n = strlen(lp->l_name)) + 2);
3149 memcpy(cp, lp->l_name, n);
3150 cp[n] = lp->l_delim;
3151 cp[n+1] = '\0';
3152 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3153 lp->l_has_children = 1;
3154 if (strcmp(cp, lx->l_name) == 0)
3155 lx = lx->l_next;
3156 if (lx) {
3157 lend->l_next = lx;
3158 lend = ly;
3163 for (lp = list; lp; lp = lp->l_next) {
3164 if (strip) {
3165 cp = lp->l_name;
3166 for (bp = base; *bp && *bp == *cp; bp++)
3167 cp++;
3168 } else
3169 cp = lp->l_name;
3170 if (!(lp->l_attr & LIST_NOSELECT))
3171 fprintf(fp, "%s\n", *cp ? cp : base);
3172 else if (lp->l_has_children == 0)
3173 fprintf(fp, "%s%c\n", *cp ? cp : base,
3174 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3176 jleave:
3177 NYD_LEAVE;
3178 return rv;
3181 FL void
3182 imap_folders(const char * volatile name, int strip)
3184 sighandler_type saveint, savepipe;
3185 const char *fold, *cp, *sp;
3186 FILE * volatile fp;
3187 NYD_ENTER;
3189 cp = protbase(name);
3190 sp = mb.mb_imap_account;
3191 if (sp == NULL || strcmp(cp, sp)) {
3192 n_err(
3193 _("Cannot perform `folders' but when on the very IMAP "
3194 "account; the current one is\n `%s' -- "
3195 "try `folders @'\n"),
3196 (sp != NULL ? sp : _("[NONE]")));
3197 goto jleave;
3200 fold = imap_fileof(name);
3201 if (options & OPT_TTYOUT) {
3202 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
3203 0600)) == NULL) {
3204 n_perr(_("tmpfile"), 0);
3205 goto jleave;
3207 } else
3208 fp = stdout;
3210 imaplock = 1;
3211 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3212 safe_signal(SIGINT, &_imap_maincatch);
3213 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3214 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3215 goto junroll;
3216 if (savepipe != SIG_IGN)
3217 safe_signal(SIGPIPE, imapcatch);
3219 if (mb.mb_type == MB_CACHE)
3220 cache_list(&mb, fold, strip, fp);
3221 else
3222 imap_list(&mb, fold, strip, fp);
3224 imaplock = 0;
3225 if (interrupts) {
3226 if (options & OPT_TTYOUT)
3227 Fclose(fp);
3228 goto jleave;
3230 fflush(fp);
3232 if (options & OPT_TTYOUT) {
3233 rewind(fp);
3234 if (fsize(fp) > 0)
3235 dopr(fp);
3236 else
3237 n_err(_("Folder not found\n"));
3239 junroll:
3240 safe_signal(SIGINT, saveint);
3241 safe_signal(SIGPIPE, savepipe);
3242 if (options & OPT_TTYOUT)
3243 Fclose(fp);
3244 jleave:
3245 NYD_LEAVE;
3246 if (interrupts)
3247 onintr(0);
3250 static void
3251 dopr(FILE *fp)
3253 char o[LINESIZE];
3254 int c;
3255 long n = 0, mx = 0, columns, width;
3256 FILE *out;
3257 NYD_ENTER;
3259 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
3260 == NULL) {
3261 n_perr(_("tmpfile"), 0);
3262 goto jleave;
3265 while ((c = getc(fp)) != EOF) {
3266 if (c == '\n') {
3267 if (n > mx)
3268 mx = n;
3269 n = 0;
3270 } else
3271 ++n;
3273 rewind(fp);
3275 width = scrnwidth;
3276 if (mx < width / 2) {
3277 columns = width / (mx+2);
3278 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
3279 } else
3280 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
3281 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
3282 page_or_print(out, 0);
3283 Fclose(out);
3284 jleave:
3285 NYD_LEAVE;
3288 static enum okay
3289 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3291 char o[LINESIZE];
3292 const char *qname;
3293 bool_t twice, stored;
3294 FILE *queuefp;
3295 enum okay ok;
3296 NYD_X;
3298 ok = STOP;
3299 queuefp = NULL;
3300 twice = stored = FAL0;
3302 /* C99 */{
3303 size_t i;
3305 i = strlen(name = imap_fileof(name));
3306 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3307 name = savecat(name, "INBOX");
3308 if((qname = imap_path_quote(mp, name)) == NULL)
3309 goto jleave;
3312 if (mp->mb_type == MB_CACHE) {
3313 if ((queuefp = cache_queue(mp)) == NULL)
3314 goto jleave;
3315 ok = OKAY;
3318 /* Since it is not possible to set flags on the copy, recently
3319 * set flags must be set on the original to include it in the copy */
3320 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3321 imap_store(mp, m, n, '+', "\\Seen", 0);
3322 if (m->m_flag&MFLAG)
3323 imap_store(mp, m, n, '+', "\\Flagged", 0);
3324 if (m->m_flag&MUNFLAG)
3325 imap_store(mp, m, n, '-', "\\Flagged", 0);
3326 if (m->m_flag&MANSWER)
3327 imap_store(mp, m, n, '+', "\\Answered", 0);
3328 if (m->m_flag&MUNANSWER)
3329 imap_store(mp, m, n, '-', "\\Flagged", 0);
3330 if (m->m_flag&MDRAFT)
3331 imap_store(mp, m, n, '+', "\\Draft", 0);
3332 if (m->m_flag&MUNDRAFT)
3333 imap_store(mp, m, n, '-', "\\Draft", 0);
3334 again:
3335 if (m->m_uid)
3336 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
3337 else {
3338 if (check_expunged() == STOP)
3339 goto out;
3340 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
3342 IMAP_OUT(o, MB_COMD, goto out)
3343 while (mp->mb_active & MB_COMD)
3344 ok = imap_answer(mp, twice);
3346 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3347 response_status == RESPONSE_OK)
3348 imap_copyuid(mp, m, name);
3350 if (response_status == RESPONSE_NO && !twice) {
3351 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3352 IMAP_OUT(o, MB_COMD, goto out)
3353 while (mp->mb_active & MB_COMD)
3354 ok = imap_answer(mp, 1);
3355 if (ok == OKAY) {
3356 imap_created_mailbox++;
3357 goto again;
3360 twice = TRU1;
3362 if (queuefp != NULL)
3363 Fclose(queuefp);
3365 /* ... and reset the flag to its initial value so that the 'exit'
3366 * command still leaves the message unread */
3367 out:
3368 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3369 imap_store(mp, m, n, '-', "\\Seen", 0);
3370 stored = TRU1;
3372 if (m->m_flag & MFLAG) {
3373 imap_store(mp, m, n, '-', "\\Flagged", 0);
3374 stored = TRU1;
3376 if (m->m_flag & MUNFLAG) {
3377 imap_store(mp, m, n, '+', "\\Flagged", 0);
3378 stored = TRU1;
3380 if (m->m_flag & MANSWER) {
3381 imap_store(mp, m, n, '-', "\\Answered", 0);
3382 stored = TRU1;
3384 if (m->m_flag & MUNANSWER) {
3385 imap_store(mp, m, n, '+', "\\Answered", 0);
3386 stored = TRU1;
3388 if (m->m_flag & MDRAFT) {
3389 imap_store(mp, m, n, '-', "\\Draft", 0);
3390 stored = TRU1;
3392 if (m->m_flag & MUNDRAFT) {
3393 imap_store(mp, m, n, '+', "\\Draft", 0);
3394 stored = TRU1;
3396 if (stored) {
3397 mp->mb_active |= MB_COMD;
3398 (void)imap_finish(mp);
3400 jleave:
3401 return ok;
3404 FL enum okay
3405 imap_copy(struct message *m, int n, const char *name)
3407 sighandler_type saveint, savepipe;
3408 enum okay rv = STOP;
3409 NYD_ENTER;
3411 imaplock = 1;
3412 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3413 safe_signal(SIGINT, &_imap_maincatch);
3414 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3415 if (sigsetjmp(imapjmp, 1) == 0) {
3416 if (savepipe != SIG_IGN)
3417 safe_signal(SIGPIPE, imapcatch);
3419 rv = imap_copy1(&mb, m, n, name);
3421 safe_signal(SIGINT, saveint);
3422 safe_signal(SIGPIPE, savepipe);
3423 imaplock = 0;
3425 NYD_LEAVE;
3426 if (interrupts)
3427 onintr(0);
3428 return rv;
3431 static enum okay
3432 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
3433 unsigned long *olduid, unsigned long *newuid)
3435 char *xp, *yp, *zp;
3436 enum okay rv;
3437 NYD_ENTER;
3439 *uidvalidity = strtoul(cp, &xp, 10);
3440 *olduid = strtoul(xp, &yp, 10);
3441 *newuid = strtoul(yp, &zp, 10);
3442 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3443 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3444 NYD_LEAVE;
3445 return rv;
3448 static enum okay
3449 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3450 unsigned long *uid)
3452 char *xp, *yp;
3453 enum okay rv;
3454 NYD_ENTER;
3456 *uidvalidity = strtoul(cp, &xp, 10);
3457 *uid = strtoul(xp, &yp, 10);
3458 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3459 *yp == ']');
3460 NYD_LEAVE;
3461 return rv;
3464 static enum okay
3465 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3467 struct mailbox xmb;
3468 struct message xm;
3469 const char *cp;
3470 unsigned long uidvalidity, olduid, newuid;
3471 enum okay rv;
3472 NYD_ENTER;
3474 rv = STOP;
3476 memset(&xmb, 0, sizeof xmb);
3478 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3479 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3480 goto jleave;
3482 rv = OKAY;
3484 xmb = *mp;
3485 xmb.mb_cache_directory = NULL;
3486 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3487 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3488 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3489 sizeof(xmb.mb_imap_delim));
3490 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3491 if (mp->mb_cache_directory != NULL)
3492 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3493 xmb.mb_uidvalidity = uidvalidity;
3494 initcache(&xmb);
3496 if (m == NULL) {
3497 memset(&xm, 0, sizeof xm);
3498 xm.m_uid = olduid;
3499 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3500 goto jleave;
3501 getcache(mp, &xm, NEED_HEADER);
3502 getcache(mp, &xm, NEED_BODY);
3503 } else {
3504 if ((m->m_flag & HAVE_HEADER) == 0)
3505 getcache(mp, m, NEED_HEADER);
3506 if ((m->m_flag & HAVE_BODY) == 0)
3507 getcache(mp, m, NEED_BODY);
3508 xm = *m;
3510 xm.m_uid = newuid;
3511 xm.m_flag &= ~MFULLYCACHED;
3512 putcache(&xmb, &xm);
3513 jleave:
3514 if (xmb.mb_cache_directory != NULL)
3515 free(xmb.mb_cache_directory);
3516 if (xmb.mb_imap_mailbox != NULL)
3517 free(xmb.mb_imap_mailbox);
3518 if (xmb.mb_imap_pass != NULL)
3519 free(xmb.mb_imap_pass);
3520 if (xmb.mb_imap_account != NULL)
3521 free(xmb.mb_imap_account);
3522 NYD_LEAVE;
3523 return rv;
3526 static enum okay
3527 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3528 long size, long lines, int flag, const char *name)
3530 struct mailbox xmb;
3531 struct message xm;
3532 const char *cp;
3533 unsigned long uidvalidity, uid;
3534 enum okay rv;
3535 NYD_ENTER;
3537 rv = STOP;
3539 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3540 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3541 goto jleave;
3543 rv = OKAY;
3545 xmb = *mp;
3546 xmb.mb_cache_directory = NULL;
3547 /* XXX mb_imap_delim reused */
3548 xmb.mb_imap_mailbox = sstrdup(imap_path_normalize(&xmb, name));
3549 xmb.mb_uidvalidity = uidvalidity;
3550 xmb.mb_otf = xmb.mb_itf = fp;
3551 initcache(&xmb);
3552 memset(&xm, 0, sizeof xm);
3553 xm.m_flag = (flag & MREAD) | MNEW;
3554 xm.m_time = t;
3555 xm.m_block = mailx_blockof(off1);
3556 xm.m_offset = mailx_offsetof(off1);
3557 xm.m_size = size;
3558 xm.m_xsize = xsize;
3559 xm.m_lines = xm.m_xlines = lines;
3560 xm.m_uid = uid;
3561 xm.m_have = HAVE_HEADER | HAVE_BODY;
3562 putcache(&xmb, &xm);
3564 free(xmb.mb_imap_mailbox);
3565 jleave:
3566 NYD_LEAVE;
3567 return rv;
3570 static enum okay
3571 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3573 FILE *tp = NULL;
3574 time_t t;
3575 long size, xsize, ysize, lines;
3576 enum mflag flag = MNEW;
3577 char *name, *buf, *bp;
3578 char const *cp;
3579 size_t bufsize, buflen, cnt;
3580 enum okay rv = STOP;
3581 NYD_ENTER;
3583 buf = smalloc(bufsize = LINESIZE);
3584 buflen = 0;
3585 cnt = fsize(fp);
3586 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3587 goto jstop;
3589 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3591 while (*bp == ' ')
3592 ++bp;
3594 if ((cp = strrchr(bp, '{')) == NULL)
3595 goto jstop;
3597 xsize = atol(&cp[1]) + 2;
3598 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3599 goto jstop;
3600 while (*cp == ' ')
3601 cp++;
3603 if (*cp == '(') {
3604 imap_getflags(cp, &cp, &flag);
3605 while (*++cp == ' ')
3608 t = imap_read_date_time(cp);
3610 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3611 NULL)
3612 goto jstop;
3614 size = xsize;
3615 ysize = lines = 0;
3616 while (size > 0) {
3617 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3618 goto jstop;
3619 size -= buflen;
3620 buf[--buflen] = '\0';
3621 buf[buflen-1] = '\n';
3622 fwrite(buf, 1, buflen, tp);
3623 ysize += buflen;
3624 ++lines;
3626 fflush(tp);
3627 rewind(tp);
3629 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3630 imap_unquotestr(name));
3631 rv = OKAY;
3632 jstop:
3633 free(buf);
3634 if (tp)
3635 Fclose(tp);
3636 NYD_LEAVE;
3637 return rv;
3640 #ifdef HAVE_IMAP_SEARCH
3641 static enum okay
3642 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3643 int f)
3645 char *o, *xp, *cs, c;
3646 size_t osize;
3647 FILE *queuefp = NULL;
3648 int i;
3649 unsigned long n;
3650 const char *cp;
3651 enum okay ok = STOP;
3652 NYD_X;
3654 c = 0;
3655 for (cp = spec; *cp; cp++)
3656 c |= *cp;
3657 if (c & 0200) {
3658 cp = charset_get_lc();
3659 # ifdef HAVE_ICONV
3660 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3661 iconv_t it;
3662 char *nsp, *nspec;
3663 size_t sz, nsz;
3665 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3666 sz = strlen(spec) + 1;
3667 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3668 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3669 sz == 0) {
3670 spec = nspec;
3671 cp = "utf-8";
3673 n_iconv_close(it);
3676 # endif
3677 cp = imap_quotestr(cp);
3678 cs = salloc(n = strlen(cp) + 10);
3679 snprintf(cs, n, "CHARSET %s ", cp);
3680 } else
3681 cs = UNCONST("");
3683 o = ac_alloc(osize = strlen(spec) + 60);
3684 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3685 IMAP_OUT(o, MB_COMD, goto out)
3686 while (mp->mb_active & MB_COMD) {
3687 ok = imap_answer(mp, 0);
3688 if (response_status == RESPONSE_OTHER &&
3689 response_other == MAILBOX_DATA_SEARCH) {
3690 xp = responded_other_text;
3691 while (*xp && *xp != '\r') {
3692 n = strtoul(xp, &xp, 10);
3693 for (i = 0; i < cnt; i++)
3694 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3695 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3696 mark(i+1, f);
3700 out:
3701 ac_free(o);
3702 return ok;
3705 FL enum okay
3706 imap_search1(const char * volatile spec, int f)
3708 sighandler_type saveint, savepipe;
3709 enum okay volatile rv = STOP;
3710 NYD_ENTER;
3712 if (mb.mb_type != MB_IMAP)
3713 goto jleave;
3715 imaplock = 1;
3716 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3717 safe_signal(SIGINT, &_imap_maincatch);
3718 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3719 if (sigsetjmp(imapjmp, 1) == 0) {
3720 if (savepipe != SIG_IGN)
3721 safe_signal(SIGPIPE, imapcatch);
3723 rv = imap_search2(&mb, message, msgCount, spec, f);
3725 safe_signal(SIGINT, saveint);
3726 safe_signal(SIGPIPE, savepipe);
3727 imaplock = 0;
3728 jleave:
3729 NYD_LEAVE;
3730 if (interrupts)
3731 onintr(0);
3732 return rv;
3734 #endif /* HAVE_IMAP_SEARCH */
3736 FL int
3737 imap_thisaccount(const char *cp)
3739 int rv;
3740 NYD_ENTER;
3742 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3743 rv = 0;
3744 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3745 mb.mb_imap_account == NULL)
3746 rv = 0;
3747 else
3748 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3749 NYD_LEAVE;
3750 return rv;
3753 FL enum okay
3754 imap_remove(const char * volatile name)
3756 sighandler_type volatile saveint, savepipe;
3757 enum okay volatile rv = STOP;
3758 NYD_ENTER;
3760 if (mb.mb_type != MB_IMAP) {
3761 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3762 goto jleave;
3765 if (!imap_thisaccount(name)) {
3766 n_err(_("Can only remove mailboxes on current IMAP server: "
3767 "\"%s\" not removed\n"), name);
3768 goto jleave;
3771 imaplock = 1;
3772 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3773 safe_signal(SIGINT, &_imap_maincatch);
3774 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3775 if (sigsetjmp(imapjmp, 1) == 0) {
3776 if (savepipe != SIG_IGN)
3777 safe_signal(SIGPIPE, imapcatch);
3779 rv = imap_remove1(&mb, imap_fileof(name));
3781 safe_signal(SIGINT, saveint);
3782 safe_signal(SIGPIPE, savepipe);
3783 imaplock = 0;
3785 if (rv == OKAY)
3786 rv = cache_remove(name);
3787 jleave:
3788 NYD_LEAVE;
3789 if (interrupts)
3790 onintr(0);
3791 return rv;
3794 static enum okay
3795 imap_remove1(struct mailbox *mp, const char *name)
3797 char *o;
3798 int os;
3799 char const *qname;
3800 FILE *queuefp;
3801 enum okay ok;
3802 NYD_X;
3804 ok = STOP;
3805 queuefp = NULL;
3807 if((qname = imap_path_quote(mp, name)) != NULL){
3808 o = ac_alloc(os = strlen(qname) + 100);
3809 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
3810 IMAP_OUT(o, MB_COMD, goto out)
3811 while (mp->mb_active & MB_COMD)
3812 ok = imap_answer(mp, 1);
3813 out:
3814 ac_free(o);
3816 return ok;
3819 FL enum okay
3820 imap_rename(const char *old, const char *new)
3822 sighandler_type saveint, savepipe;
3823 enum okay rv = STOP;
3824 NYD_ENTER;
3826 if (mb.mb_type != MB_IMAP) {
3827 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3828 goto jleave;
3831 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3832 n_err(_("Can only rename mailboxes on current IMAP "
3833 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3834 goto jleave;
3837 imaplock = 1;
3838 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3839 safe_signal(SIGINT, &_imap_maincatch);
3840 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3841 if (sigsetjmp(imapjmp, 1) == 0) {
3842 if (savepipe != SIG_IGN)
3843 safe_signal(SIGPIPE, imapcatch);
3845 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3847 safe_signal(SIGINT, saveint);
3848 safe_signal(SIGPIPE, savepipe);
3849 imaplock = 0;
3851 if (rv == OKAY)
3852 rv = cache_rename(old, new);
3853 jleave:
3854 NYD_LEAVE;
3855 if (interrupts)
3856 onintr(0);
3857 return rv;
3860 static enum okay
3861 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3863 char *o;
3864 int os;
3865 char const *qoname, *qnname;
3866 FILE *queuefp;
3867 enum okay ok;
3868 NYD_X;
3870 ok = STOP;
3871 queuefp = NULL;
3873 if((qoname = imap_path_quote(mp, old)) != NULL &&
3874 (qnname = imap_path_quote(mp, new)) != NULL){
3875 o = ac_alloc(os = strlen(qoname) + strlen(qnname) + 100);
3876 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
3877 IMAP_OUT(o, MB_COMD, goto out)
3878 while (mp->mb_active & MB_COMD)
3879 ok = imap_answer(mp, 1);
3880 out:
3881 ac_free(o);
3883 return ok;
3886 FL enum okay
3887 imap_dequeue(struct mailbox *mp, FILE *fp)
3889 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3890 size_t bufsize, buflen, cnt;
3891 long offs, offs1, offs2, octets;
3892 int twice, gotcha = 0;
3893 FILE *queuefp = NULL;
3894 enum okay ok = OKAY, rok = OKAY;
3895 NYD_X;
3897 buf = smalloc(bufsize = LINESIZE);
3898 buflen = 0;
3899 cnt = fsize(fp);
3900 while ((offs1 = ftell(fp)) >= 0 &&
3901 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3902 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3904 while (*bp == ' ')
3905 ++bp;
3906 twice = 0;
3907 if ((offs = ftell(fp)) < 0)
3908 goto fail;
3909 again:
3910 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3911 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3912 cp = &bp[9];
3913 while (digitchar(*cp))
3914 cp++;
3915 if (*cp != ' ')
3916 goto fail;
3917 while (*cp == ' ')
3918 cp++;
3919 if ((newname = imap_strex(cp, NULL)) == NULL)
3920 goto fail;
3921 IMAP_OUT(o, MB_COMD, continue)
3922 while (mp->mb_active & MB_COMD)
3923 ok = imap_answer(mp, twice);
3924 if (response_status == RESPONSE_NO && twice++ == 0)
3925 goto trycreate;
3926 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3927 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3929 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3930 IMAP_OUT(o, MB_COMD, continue)
3931 while (mp->mb_active & MB_COMD)
3932 ok = imap_answer(mp, 1);
3933 if (ok == OKAY)
3934 gotcha++;
3935 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3936 if ((cp = strrchr(bp, '{')) == NULL)
3937 goto fail;
3938 octets = atol(&cp[1]) + 2;
3939 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3940 goto fail;
3941 IMAP_OUT(o, MB_COMD, continue)
3942 while (mp->mb_active & MB_COMD) {
3943 ok = imap_answer(mp, twice);
3944 if (response_type == RESPONSE_CONT)
3945 break;
3947 if (ok == STOP) {
3948 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3949 goto trycreate;
3950 goto fail;
3952 while (octets > 0) {
3953 size_t n = (UICMP(z, octets, >, sizeof iob)
3954 ? sizeof iob : (size_t)octets);
3955 octets -= n;
3956 if (n != fread(iob, 1, n, fp))
3957 goto fail;
3958 swrite1(&mp->mb_sock, iob, n, 1);
3960 swrite(&mp->mb_sock, "");
3961 while (mp->mb_active & MB_COMD) {
3962 ok = imap_answer(mp, 0);
3963 if (response_status == RESPONSE_NO && twice++ == 0) {
3964 if (fseek(fp, offs, SEEK_SET) < 0)
3965 goto fail;
3966 goto trycreate;
3969 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3970 if ((offs2 = ftell(fp)) < 0)
3971 goto fail;
3972 fseek(fp, offs1, SEEK_SET);
3973 if (imap_appenduid_cached(mp, fp) == STOP) {
3974 (void)fseek(fp, offs2, SEEK_SET);
3975 goto fail;
3978 } else {
3979 fail:
3980 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3981 rok = STOP;
3983 continue;
3984 trycreate:
3985 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3986 IMAP_OUT(o, MB_COMD, continue)
3987 while (mp->mb_active & MB_COMD)
3988 ok = imap_answer(mp, 1);
3989 if (ok == OKAY)
3990 goto again;
3992 fflush(fp);
3993 rewind(fp);
3994 ftruncate(fileno(fp), 0);
3995 if (gotcha)
3996 imap_close(mp);
3997 free(buf);
3998 return rok;
4001 static char *
4002 imap_strex(char const *cp, char const **xp)
4004 char const *cq;
4005 char *n = NULL;
4006 NYD_ENTER;
4008 if (*cp != '"')
4009 goto jleave;
4011 for (cq = cp + 1; *cq != '\0'; ++cq) {
4012 if (*cq == '\\')
4013 cq++;
4014 else if (*cq == '"')
4015 break;
4017 if (*cq != '"')
4018 goto jleave;
4020 n = salloc(cq - cp + 2);
4021 memcpy(n, cp, cq - cp +1);
4022 n[cq - cp + 1] = '\0';
4023 if (xp != NULL)
4024 *xp = cq + 1;
4025 jleave:
4026 NYD_LEAVE;
4027 return n;
4030 static enum okay
4031 check_expunged(void)
4033 enum okay rv;
4034 NYD_ENTER;
4036 if (expunged_messages > 0) {
4037 n_err(_("Command not executed - messages have been expunged\n"));
4038 rv = STOP;
4039 } else
4040 rv = OKAY;
4041 NYD_LEAVE;
4042 return rv;
4045 FL int
4046 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4048 struct url url;
4049 int rv, omsgCount = msgCount;
4050 NYD_ENTER;
4051 UNUSED(vp);
4053 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
4054 n_err(_("Already connected\n"));
4055 rv = 1;
4056 goto jleave;
4059 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
4060 rv = 1;
4061 goto jleave;
4063 ok_bclear(disconnected);
4064 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
4066 if (mb.mb_type == MB_CACHE) {
4067 enum fedit_mode fm = FEDIT_NONE;
4068 if (_imap_rdonly)
4069 fm |= FEDIT_RDONLY;
4070 if (!(pstate & PS_EDIT))
4071 fm |= FEDIT_SYSBOX;
4072 _imap_setfile1(&url, fm, 1);
4073 if (msgCount > omsgCount)
4074 newmailinfo(omsgCount);
4076 rv = 0;
4077 jleave:
4078 NYD_LEAVE;
4079 return rv;
4082 FL int
4083 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4085 struct url url;
4086 int rv = 1, *msgvec = vp;
4087 NYD_ENTER;
4089 if (mb.mb_type == MB_CACHE) {
4090 n_err(_("Not connected\n"));
4091 goto jleave;
4093 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4094 n_err(_("The current mailbox is not cached\n"));
4095 goto jleave;
4098 if (!url_parse(&url, CPROTO_IMAP, mailname))
4099 goto jleave;
4101 if (*msgvec)
4102 c_cache(vp);
4103 ok_bset(disconnected, TRU1);
4104 if (mb.mb_type == MB_IMAP) {
4105 enum fedit_mode fm = FEDIT_NONE;
4106 if (_imap_rdonly)
4107 fm |= FEDIT_RDONLY;
4108 if (!(pstate & PS_EDIT))
4109 fm |= FEDIT_SYSBOX;
4110 sclose(&mb.mb_sock);
4111 _imap_setfile1(&url, fm, 1);
4113 rv = 0;
4114 jleave:
4115 NYD_LEAVE;
4116 return rv;
4119 FL int
4120 c_cache(void *vp)
4122 int rv = 1, *msgvec = vp, *ip;
4123 struct message *mp;
4124 NYD_ENTER;
4126 if (mb.mb_type != MB_IMAP) {
4127 n_err(_("Not connected to an IMAP server\n"));
4128 goto jleave;
4130 if (cached_uidvalidity(&mb) == 0) {
4131 n_err(_("The current mailbox is not cached\n"));
4132 goto jleave;
4135 srelax_hold();
4136 for (ip = msgvec; *ip; ++ip) {
4137 mp = &message[*ip - 1];
4138 if (!(mp->m_have & HAVE_BODY)) {
4139 get_body(mp);
4140 srelax();
4143 srelax_rele();
4144 rv = 0;
4145 jleave:
4146 NYD_LEAVE;
4147 return rv;
4150 FL int
4151 disconnected(const char *file)
4153 struct url url;
4154 int rv = 1;
4155 NYD_ENTER;
4157 if (ok_blook(disconnected)) {
4158 rv = 1;
4159 goto jleave;
4162 if (!url_parse(&url, CPROTO_IMAP, file)) {
4163 rv = 0;
4164 goto jleave;
4166 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
4168 jleave:
4169 NYD_LEAVE;
4170 return rv;
4173 FL void
4174 transflags(struct message *omessage, long omsgCount, int transparent)
4176 struct message *omp, *nmp, *newdot, *newprevdot;
4177 int hf;
4178 NYD_ENTER;
4180 omp = omessage;
4181 nmp = message;
4182 newdot = message;
4183 newprevdot = NULL;
4184 while (PTRCMP(omp, <, omessage + omsgCount) &&
4185 PTRCMP(nmp, <, message + msgCount)) {
4186 if (dot && nmp->m_uid == dot->m_uid)
4187 newdot = nmp;
4188 if (prevdot && nmp->m_uid == prevdot->m_uid)
4189 newprevdot = nmp;
4190 if (omp->m_uid == nmp->m_uid) {
4191 hf = nmp->m_flag & MHIDDEN;
4192 if (transparent && mb.mb_type == MB_IMAP)
4193 omp->m_flag &= ~MHIDDEN;
4194 *nmp++ = *omp++;
4195 if (transparent && mb.mb_type == MB_CACHE)
4196 nmp[-1].m_flag |= hf;
4197 } else if (omp->m_uid < nmp->m_uid)
4198 ++omp;
4199 else
4200 ++nmp;
4202 dot = newdot;
4203 setdot(newdot);
4204 prevdot = newprevdot;
4205 free(omessage);
4206 NYD_LEAVE;
4209 FL time_t
4210 imap_read_date_time(const char *cp)
4212 char buf[3];
4213 time_t t;
4214 int i, year, month, day, hour, minute, second, sign = -1;
4215 NYD2_ENTER;
4217 /* "25-Jul-2004 15:33:44 +0200"
4218 * | | | | | |
4219 * 0 5 10 15 20 25 */
4220 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
4221 goto jinvalid;
4222 day = strtol(&cp[1], NULL, 10);
4223 for (i = 0;;) {
4224 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
4225 break;
4226 if (month_names[++i][0] == '\0')
4227 goto jinvalid;
4229 month = i + 1;
4230 year = strtol(&cp[8], NULL, 10);
4231 hour = strtol(&cp[13], NULL, 10);
4232 minute = strtol(&cp[16], NULL, 10);
4233 second = strtol(&cp[19], NULL, 10);
4234 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4235 goto jinvalid;
4236 switch (cp[22]) {
4237 case '-':
4238 sign = 1;
4239 break;
4240 case '+':
4241 break;
4242 default:
4243 goto jinvalid;
4245 buf[2] = '\0';
4246 buf[0] = cp[23];
4247 buf[1] = cp[24];
4248 t += strtol(buf, NULL, 10) * sign * 3600;
4249 buf[0] = cp[25];
4250 buf[1] = cp[26];
4251 t += strtol(buf, NULL, 10) * sign * 60;
4252 jleave:
4253 NYD2_LEAVE;
4254 return t;
4255 jinvalid:
4256 time(&t);
4257 goto jleave;
4260 FL const char *
4261 imap_make_date_time(time_t t)
4263 static char s[30];
4264 struct tm *tmptr;
4265 int tzdiff, tzdiff_hour, tzdiff_min;
4266 NYD2_ENTER;
4268 tzdiff = t - mktime(gmtime(&t));
4269 tzdiff_hour = (int)(tzdiff / 60);
4270 tzdiff_min = tzdiff_hour % 60;
4271 tzdiff_hour /= 60;
4272 tmptr = localtime(&t);
4273 if (tmptr->tm_isdst > 0)
4274 tzdiff_hour++;
4275 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4276 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
4277 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
4278 NYD2_LEAVE;
4279 return s;
4281 #endif /* HAVE_IMAP */
4283 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
4284 FL char *
4285 imap_quotestr(char const *s)
4287 char *n, *np;
4288 NYD2_ENTER;
4290 np = n = salloc(2 * strlen(s) + 3);
4291 *np++ = '"';
4292 while (*s) {
4293 if (*s == '"' || *s == '\\')
4294 *np++ = '\\';
4295 *np++ = *s++;
4297 *np++ = '"';
4298 *np = '\0';
4299 NYD2_LEAVE;
4300 return n;
4303 FL char *
4304 imap_unquotestr(char const *s)
4306 char *n, *np;
4307 NYD2_ENTER;
4309 if (*s != '"') {
4310 n = savestr(s);
4311 goto jleave;
4314 np = n = salloc(strlen(s) + 1);
4315 while (*++s) {
4316 if (*s == '\\')
4317 s++;
4318 else if (*s == '"')
4319 break;
4320 *np++ = *s;
4322 *np = '\0';
4323 jleave:
4324 NYD2_LEAVE;
4325 return n;
4327 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
4329 /* s-it-mode */