*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / imap.c
blob9e7a4a40b73958dac534db5b7f2dea028829f90f
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_normalize_path(struct mailbox *mp, char const *cp);
176 static char *imap_quotepath(struct mailbox *mp, char const *cp);
177 static void imap_other_get(char *pp);
178 static void imap_response_get(const char **cp);
179 static void imap_response_parse(void);
180 static enum okay imap_answer(struct mailbox *mp, int errprnt);
181 static enum okay imap_parse_list(void);
182 static enum okay imap_finish(struct mailbox *mp);
183 static void imap_timer_off(void);
184 static void imapcatch(int s);
185 static void _imap_maincatch(int s);
186 static enum okay imap_noop1(struct mailbox *mp);
187 static void rec_queue(enum rec_type type, unsigned long cnt);
188 static enum okay rec_dequeue(void);
189 static void rec_rmqueue(void);
190 static void imapalarm(int s);
191 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
192 static enum okay imap_capability(struct mailbox *mp);
193 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
194 #ifdef HAVE_MD5
195 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
196 #endif
197 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
198 #ifdef HAVE_GSSAPI
199 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
200 #endif
201 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
202 static void imap_init(struct mailbox *mp, int n);
203 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
204 int *prevcount);
205 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
206 struct url *urlp);
207 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
208 int transparent);
209 static int imap_fetchdata(struct mailbox *mp, struct message *m,
210 size_t expected, int need, const char *head,
211 size_t headsize, long headlines);
212 static void imap_putstr(struct mailbox *mp, struct message *m,
213 const char *str, const char *head, size_t headsize,
214 long headlines);
215 static enum okay imap_get(struct mailbox *mp, struct message *m,
216 enum needspec need);
217 static void commitmsg(struct mailbox *mp, struct message *to,
218 struct message *from, enum havespec have);
219 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
220 int bot, int top);
221 static enum okay imap_exit(struct mailbox *mp);
222 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
223 int needstat);
224 static enum okay imap_close(struct mailbox *mp);
225 static enum okay imap_update(struct mailbox *mp);
226 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
227 int c, const char *sp, int needstat);
228 static enum okay imap_unstore(struct message *m, int n, const char *flag);
229 static const char *tag(int new);
230 static char * imap_putflags(int f);
231 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
232 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
233 off_t off1, long xsize, enum mflag flag, time_t t);
234 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
235 static enum okay imap_list1(struct mailbox *mp, const char *base,
236 struct list_item **list, struct list_item **lend,
237 int level);
238 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
239 FILE *fp);
240 static void dopr(FILE *fp);
241 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
242 const char *name);
243 static enum okay imap_copyuid_parse(const char *cp,
244 unsigned long *uidvalidity, unsigned long *olduid,
245 unsigned long *newuid);
246 static enum okay imap_appenduid_parse(const char *cp,
247 unsigned long *uidvalidity, unsigned long *uid);
248 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
249 const char *name);
250 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
251 long off1, long xsize, long size, long lines, int flag,
252 const char *name);
253 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
254 #ifdef HAVE_IMAP_SEARCH
255 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
256 const char *spec, int f);
257 #endif
258 static enum okay imap_remove1(struct mailbox *mp, const char *name);
259 static enum okay imap_rename1(struct mailbox *mp, const char *old,
260 const char *new);
261 static char * imap_strex(char const *cp, char const **xp);
262 static enum okay check_expunged(void);
264 static void
265 imap_delim_init(struct mailbox *mp, struct url const *urlp){
266 size_t i;
267 char const *cp;
268 NYD2_ENTER;
270 mp->mb_imap_delim[0] = '\0';
272 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
273 i = strlen(cp);
275 if(i == 0){
276 cp = n_IMAP_DELIM;
277 i = sizeof(n_IMAP_DELIM) -1;
278 goto jcopy;
281 if(i < NELEM(mp->mb_imap_delim))
282 jcopy:
283 memcpy(&mb.mb_imap_delim[0], cp, i +1);
284 else
285 n_err(_("*imap-delim* for %s too long: %s\n"),
286 urlp->url_input, cp);
288 NYD2_LEAVE;
291 static char const *
292 imap_normalize_path(struct mailbox *mp, char const *cp){ /* TODO btw: no utf7 */
293 char *rv_base, *rv, dc2, dc, c, lc;
294 char const *dcp;
295 NYD2_ENTER;
297 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
298 * exactly what i have specified" */
299 dc2 = '\0';
300 if(mp == NULL)
301 dcp = n_IMAP_DELIM;
302 else if((dc = *(dcp = &mp->mb_imap_delim[0])) != '\0')
303 dc2 = *++dcp;
305 /* Plain names don't need path quoting */
306 /* C99 */{
307 size_t i, j;
308 char const *cpx;
310 for(cpx = cp;; ++cpx)
311 if((c = *cpx) == '\0')
312 goto jleave;
313 else if(dc == '\0'){
314 if(strchr(n_IMAP_DELIM, c)){
315 dc = c;
316 break;
318 }else if(c == dc)
319 break;
320 else if(dc2 && strchr(dcp, c) != NULL)
321 break;
323 /* And we don't need to reevaluate what we have seen yet */
324 i = PTR2SIZE(cpx - cp);
325 rv = rv_base = salloc(i + (j = strlen(cpx) +1));
326 if(i > 0)
327 memcpy(rv, cp, i);
328 memcpy(&rv[i], cpx, j);
329 rv += i;
330 cp = cpx;
333 /* Squeeze adjacent delimiters, convert remain to dc */
334 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
335 if(c == dc || (dc2 && strchr(dcp, c) != NULL))
336 c = dc;
337 if(c != dc || lc != dc)
338 *rv++ = c;
340 *rv = '\0';
342 cp = rv_base;
343 jleave:
344 NYD2_LEAVE;
345 return cp;
348 static char *
349 imap_quotepath(struct mailbox *mp, char const *cp){
350 char *rv;
351 NYD2_ENTER;
353 rv = imap_quotestr(imap_normalize_path(mp, cp));
354 NYD2_LEAVE;
355 return rv;
358 static void
359 imap_other_get(char *pp)
361 char *xp;
362 NYD2_ENTER;
364 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
365 pp += 6;
366 response_other = MAILBOX_DATA_FLAGS;
367 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
368 pp += 5;
369 response_other = MAILBOX_DATA_LIST;
370 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
371 pp += 5;
372 response_other = MAILBOX_DATA_LSUB;
373 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
374 pp += 8;
375 response_other = MAILBOX_DATA_MAILBOX;
376 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
377 pp += 7;
378 response_other = MAILBOX_DATA_SEARCH;
379 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
380 pp += 7;
381 response_other = MAILBOX_DATA_STATUS;
382 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
383 pp += 11;
384 response_other = CAPABILITY_DATA;
385 } else {
386 responded_other_number = strtol(pp, &xp, 10);
387 while (*xp == ' ')
388 ++xp;
389 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
390 response_other = MAILBOX_DATA_EXISTS;
391 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
392 response_other = MAILBOX_DATA_RECENT;
393 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
394 response_other = MESSAGE_DATA_EXPUNGE;
395 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
396 pp = &xp[6];
397 response_other = MESSAGE_DATA_FETCH;
398 } else
399 response_other = RESPONSE_OTHER_UNKNOWN;
401 responded_other_text = pp;
402 NYD2_LEAVE;
405 static void
406 imap_response_get(const char **cp)
408 NYD2_ENTER;
409 if (ascncasecmp(*cp, "OK ", 3) == 0) {
410 *cp += 3;
411 response_status = RESPONSE_OK;
412 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
413 *cp += 3;
414 response_status = RESPONSE_NO;
415 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
416 *cp += 4;
417 response_status = RESPONSE_BAD;
418 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
419 *cp += 8;
420 response_status = RESPONSE_PREAUTH;
421 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
422 *cp += 4;
423 response_status = RESPONSE_BYE;
424 } else
425 response_status = RESPONSE_OTHER;
426 NYD2_LEAVE;
429 static void
430 imap_response_parse(void)
432 static char *parsebuf; /* TODO Use pool */
433 static size_t parsebufsize;
435 const char *ip = imapbuf;
436 char *pp;
437 NYD2_ENTER;
439 if (parsebufsize < imapbufsize)
440 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
441 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
442 pp = parsebuf;
443 switch (*ip) {
444 case '+':
445 response_type = RESPONSE_CONT;
446 ip++;
447 pp++;
448 while (*ip == ' ') {
449 ip++;
450 pp++;
452 break;
453 case '*':
454 ip++;
455 pp++;
456 while (*ip == ' ') {
457 ip++;
458 pp++;
460 imap_response_get(&ip);
461 pp = &parsebuf[ip - imapbuf];
462 switch (response_status) {
463 case RESPONSE_BYE:
464 response_type = RESPONSE_FATAL;
465 break;
466 default:
467 response_type = RESPONSE_DATA;
469 break;
470 default:
471 responded_tag = parsebuf;
472 while (*pp && *pp != ' ')
473 pp++;
474 if (*pp == '\0') {
475 response_type = RESPONSE_ILLEGAL;
476 break;
478 *pp++ = '\0';
479 while (*pp && *pp == ' ')
480 pp++;
481 if (*pp == '\0') {
482 response_type = RESPONSE_ILLEGAL;
483 break;
485 ip = &imapbuf[pp - parsebuf];
486 response_type = RESPONSE_TAGGED;
487 imap_response_get(&ip);
488 pp = &parsebuf[ip - imapbuf];
490 responded_text = pp;
491 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
492 response_status == RESPONSE_OTHER)
493 imap_other_get(pp);
494 NYD2_LEAVE;
497 static enum okay
498 imap_answer(struct mailbox *mp, int errprnt)
500 int i, complete;
501 enum okay rv;
502 NYD2_ENTER;
504 rv = OKAY;
505 if (mp->mb_type == MB_CACHE)
506 goto jleave;
507 rv = STOP;
508 jagain:
509 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
510 if (options & OPT_VERBVERB)
511 fputs(imapbuf, stderr);
512 imap_response_parse();
513 if (response_type == RESPONSE_ILLEGAL)
514 goto jagain;
515 if (response_type == RESPONSE_CONT) {
516 rv = OKAY;
517 goto jleave;
519 if (response_status == RESPONSE_OTHER) {
520 if (response_other == MAILBOX_DATA_EXISTS) {
521 had_exists = responded_other_number;
522 rec_queue(REC_EXISTS, responded_other_number);
523 if (had_expunge > 0)
524 had_expunge = 0;
525 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
526 rec_queue(REC_EXPUNGE, responded_other_number);
527 if (had_expunge < 0)
528 had_expunge = 0;
529 had_expunge++;
530 expunged_messages++;
533 complete = 0;
534 if (response_type == RESPONSE_TAGGED) {
535 if (asccasecmp(responded_tag, tag(0)) == 0)
536 complete |= 1;
537 else
538 goto jagain;
540 switch (response_status) {
541 case RESPONSE_PREAUTH:
542 mp->mb_active &= ~MB_PREAUTH;
543 /*FALLTHRU*/
544 case RESPONSE_OK:
545 jokay:
546 rv = OKAY;
547 complete |= 2;
548 break;
549 case RESPONSE_NO:
550 case RESPONSE_BAD:
551 jstop:
552 rv = STOP;
553 complete |= 2;
554 if (errprnt)
555 n_err(_("IMAP error: %s"), responded_text);
556 break;
557 case RESPONSE_UNKNOWN: /* does not happen */
558 case RESPONSE_BYE:
559 i = mp->mb_active;
560 mp->mb_active = MB_NONE;
561 if (i & MB_BYE)
562 goto jokay;
563 goto jstop;
564 case RESPONSE_OTHER:
565 rv = OKAY;
566 break;
568 if (response_status != RESPONSE_OTHER &&
569 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
570 n_err(_("IMAP alert: %s"), &responded_text[8]);
571 if (complete == 3)
572 mp->mb_active &= ~MB_COMD;
573 } else {
574 rv = STOP;
575 mp->mb_active = MB_NONE;
577 jleave:
578 NYD2_LEAVE;
579 return rv;
582 static enum okay
583 imap_parse_list(void)
585 char *cp;
586 enum okay rv;
587 NYD2_ENTER;
589 rv = STOP;
591 cp = responded_other_text;
592 list_attributes = LIST_NONE;
593 if (*cp == '(') {
594 while (*cp && *cp != ')') {
595 if (*cp == '\\') {
596 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
597 list_attributes |= LIST_NOINFERIORS;
598 cp += 12;
599 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
600 list_attributes |= LIST_NOSELECT;
601 cp += 9;
602 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
603 list_attributes |= LIST_MARKED;
604 cp += 7;
605 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
606 list_attributes |= LIST_UNMARKED;
607 cp += 9;
610 cp++;
612 if (*++cp != ' ')
613 goto jleave;
614 while (*cp == ' ')
615 cp++;
618 list_hierarchy_delimiter = EOF;
619 if (*cp == '"') {
620 if (*++cp == '\\')
621 cp++;
622 list_hierarchy_delimiter = *cp++ & 0377;
623 if (cp[0] != '"' || cp[1] != ' ')
624 goto jleave;
625 cp++;
626 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
627 list_hierarchy_delimiter = EOF;
628 cp += 3;
631 while (*cp == ' ')
632 cp++;
633 list_name = cp;
634 while (*cp && *cp != '\r')
635 cp++;
636 *cp = '\0';
637 rv = OKAY;
638 jleave:
639 NYD2_LEAVE;
640 return rv;
643 static enum okay
644 imap_finish(struct mailbox *mp)
646 NYD_ENTER;
647 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
648 imap_answer(mp, 1);
649 NYD_LEAVE;
650 return OKAY;
653 static void
654 imap_timer_off(void)
656 NYD_ENTER;
657 if (imapkeepalive > 0) {
658 alarm(0);
659 safe_signal(SIGALRM, savealrm);
661 NYD_LEAVE;
664 static void
665 imapcatch(int s)
667 NYD_X; /* Signal handler */
668 switch (s) {
669 case SIGINT:
670 n_err_sighdl(_("Interrupt\n"));
671 siglongjmp(imapjmp, 1);
672 /*NOTREACHED*/
673 case SIGPIPE:
674 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
675 break;
679 static void
680 _imap_maincatch(int s)
682 NYD_X; /* Signal handler */
683 UNUSED(s);
684 if (interrupts++ == 0) {
685 n_err_sighdl(_("Interrupt\n"));
686 return;
688 onintr(0);
691 static enum okay
692 imap_noop1(struct mailbox *mp)
694 char o[LINESIZE];
695 FILE *queuefp = NULL;
696 NYD_X;
698 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
699 IMAP_OUT(o, MB_COMD, return STOP)
700 IMAP_ANSWER()
701 return OKAY;
704 FL char const *
705 imap_fileof(char const *xcp)
707 char const *cp = xcp;
708 int state = 0;
709 NYD_ENTER;
711 while (*cp) {
712 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
713 cp += 3;
714 state = 1;
716 if (cp[0] == '/' && state == 1) {
717 ++cp;
718 goto jleave;
720 if (cp[0] == '/') {
721 cp = xcp;
722 goto jleave;
724 ++cp;
726 jleave:
727 NYD_LEAVE;
728 return cp;
731 FL enum okay
732 imap_noop(void)
734 sighandler_type volatile oldint, oldpipe;
735 enum okay rv = STOP;
736 NYD_ENTER;
738 if (mb.mb_type != MB_IMAP)
739 goto jleave;
741 imaplock = 1;
742 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
743 safe_signal(SIGINT, &_imap_maincatch);
744 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
745 if (sigsetjmp(imapjmp, 1) == 0) {
746 if (oldpipe != SIG_IGN)
747 safe_signal(SIGPIPE, imapcatch);
749 rv = imap_noop1(&mb);
751 safe_signal(SIGINT, oldint);
752 safe_signal(SIGPIPE, oldpipe);
753 imaplock = 0;
754 jleave:
755 NYD_LEAVE;
756 if (interrupts)
757 onintr(0);
758 return rv;
761 static void
762 rec_queue(enum rec_type rt, unsigned long cnt)
764 struct record *rp;
765 NYD_ENTER;
767 rp = scalloc(1, sizeof *rp);
768 rp->rec_type = rt;
769 rp->rec_count = cnt;
770 if (record && recend) {
771 recend->rec_next = rp;
772 recend = rp;
773 } else
774 record = recend = rp;
775 NYD_LEAVE;
778 static enum okay
779 rec_dequeue(void)
781 struct message *omessage;
782 struct record *rp, *rq;
783 uiz_t exists = 0, i;
784 enum okay rv = STOP;
785 NYD_ENTER;
787 if (record == NULL)
788 goto jleave;
790 omessage = message;
791 message = smalloc((msgCount+1) * sizeof *message);
792 if (msgCount)
793 memcpy(message, omessage, msgCount * sizeof *message);
794 memset(&message[msgCount], 0, sizeof *message);
796 rp = record, rq = NULL;
797 rv = OKAY;
798 while (rp != NULL) {
799 switch (rp->rec_type) {
800 case REC_EXISTS:
801 exists = rp->rec_count;
802 break;
803 case REC_EXPUNGE:
804 if (rp->rec_count == 0) {
805 rv = STOP;
806 break;
808 if (rp->rec_count > (unsigned long)msgCount) {
809 if (exists == 0 || rp->rec_count > exists--)
810 rv = STOP;
811 break;
813 if (exists > 0)
814 exists--;
815 delcache(&mb, &message[rp->rec_count-1]);
816 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
817 ((msgCount - rp->rec_count + 1) * sizeof *message));
818 --msgCount;
819 /* If the message was part of a collapsed thread,
820 * the m_collapsed field of one of its ancestors
821 * should be incremented. It seems hardly possible
822 * to do this with the current message structure,
823 * though. The result is that a '+' may be shown
824 * in the header summary even if no collapsed
825 * children exists */
826 break;
828 if (rq != NULL)
829 free(rq);
830 rq = rp;
831 rp = rp->rec_next;
833 if (rq != NULL)
834 free(rq);
836 record = recend = NULL;
837 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
838 message = srealloc(message, (exists + 1) * sizeof *message);
839 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
840 for (i = msgCount; i < exists; ++i)
841 imap_init(&mb, i);
842 imap_flags(&mb, msgCount+1, exists);
843 msgCount = exists;
846 if (rv == STOP) {
847 free(message);
848 message = omessage;
850 jleave:
851 NYD_LEAVE;
852 return rv;
855 static void
856 rec_rmqueue(void)
858 struct record *rp;
859 NYD_ENTER;
861 for (rp = record; rp != NULL;) {
862 struct record *tmp = rp;
863 rp = rp->rec_next;
864 free(tmp);
866 record = recend = NULL;
867 NYD_LEAVE;
870 /*ARGSUSED*/
871 static void
872 imapalarm(int s)
874 sighandler_type volatile saveint, savepipe;
875 NYD_X; /* Signal handler */
876 UNUSED(s);
878 if (imaplock++ == 0) {
879 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
880 safe_signal(SIGINT, &_imap_maincatch);
881 savepipe = safe_signal(SIGPIPE, SIG_IGN);
882 if (sigsetjmp(imapjmp, 1)) {
883 safe_signal(SIGINT, saveint);
884 safe_signal(SIGPIPE, savepipe);
885 goto jbrk;
887 if (savepipe != SIG_IGN)
888 safe_signal(SIGPIPE, imapcatch);
889 if (imap_noop1(&mb) != OKAY) {
890 safe_signal(SIGINT, saveint);
891 safe_signal(SIGPIPE, savepipe);
892 goto jleave;
894 safe_signal(SIGINT, saveint);
895 safe_signal(SIGPIPE, savepipe);
897 jbrk:
898 alarm(imapkeepalive);
899 jleave:
900 --imaplock;
903 static enum okay
904 imap_preauth(struct mailbox *mp, struct url const *urlp)
906 NYD_X;
908 mp->mb_active |= MB_PREAUTH;
909 imap_answer(mp, 1);
911 #ifdef HAVE_SSL
912 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
913 FILE *queuefp = NULL;
914 char o[LINESIZE];
916 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
917 IMAP_OUT(o, MB_COMD, return STOP)
918 IMAP_ANSWER()
919 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
920 return STOP;
922 #else
923 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
924 n_err(_("No SSL support compiled in\n"));
925 return STOP;
927 #endif
929 imap_capability(mp);
930 return OKAY;
933 static enum okay
934 imap_capability(struct mailbox *mp)
936 char o[LINESIZE];
937 FILE *queuefp = NULL;
938 enum okay ok = STOP;
939 const char *cp;
940 NYD_X;
942 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
943 IMAP_OUT(o, MB_COMD, return STOP)
944 while (mp->mb_active & MB_COMD) {
945 ok = imap_answer(mp, 0);
946 if (response_status == RESPONSE_OTHER &&
947 response_other == CAPABILITY_DATA) {
948 cp = responded_other_text;
949 while (*cp) {
950 while (spacechar(*cp))
951 ++cp;
952 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
953 /* RFC 2359 */
954 mp->mb_flags |= MB_UIDPLUS;
955 while (*cp && !spacechar(*cp))
956 ++cp;
960 return ok;
963 static enum okay
964 imap_auth(struct mailbox *mp, struct ccred *ccred)
966 enum okay rv;
967 NYD_ENTER;
969 if (!(mp->mb_active & MB_PREAUTH)) {
970 rv = OKAY;
971 goto jleave;
974 switch (ccred->cc_authtype) {
975 case AUTHTYPE_LOGIN:
976 rv = imap_login(mp, ccred);
977 break;
978 #ifdef HAVE_MD5
979 case AUTHTYPE_CRAM_MD5:
980 rv = imap_cram_md5(mp, ccred);
981 break;
982 #endif
983 #ifdef HAVE_GSSAPI
984 case AUTHTYPE_GSSAPI:
985 rv = _imap_gssapi(mp, ccred);
986 break;
987 #endif
988 default:
989 rv = STOP;
990 break;
992 jleave:
993 NYD_LEAVE;
994 return rv;
997 #ifdef HAVE_MD5
998 static enum okay
999 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
1001 char o[LINESIZE], *cp;
1002 FILE *queuefp = NULL;
1003 enum okay rv = STOP;
1004 NYD_ENTER;
1006 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1007 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1008 imap_answer(mp, 1);
1009 if (response_type != RESPONSE_CONT)
1010 goto jleave;
1012 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1013 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1014 while (mp->mb_active & MB_COMD)
1015 rv = imap_answer(mp, 1);
1016 jleave:
1017 NYD_LEAVE;
1018 return rv;
1020 #endif /* HAVE_MD5 */
1022 static enum okay
1023 imap_login(struct mailbox *mp, struct ccred *ccred)
1025 char o[LINESIZE];
1026 FILE *queuefp = NULL;
1027 enum okay rv = STOP;
1028 NYD_ENTER;
1030 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1031 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
1032 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1033 while (mp->mb_active & MB_COMD)
1034 rv = imap_answer(mp, 1);
1035 jleave:
1036 NYD_LEAVE;
1037 return rv;
1040 #ifdef HAVE_GSSAPI
1041 # include "imap_gssapi.h"
1042 #endif
1044 FL enum okay
1045 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1046 enum fedit_mode fm)
1048 enum okay ok = OKAY;
1049 char const *cp;
1050 char o[LINESIZE];
1051 FILE *queuefp = NULL;
1052 NYD_X;
1053 UNUSED(size);
1055 mp->mb_uidvalidity = 0;
1056 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1057 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), imap_quotepath(mp, mbx));
1058 IMAP_OUT(o, MB_COMD, return STOP)
1059 while (mp->mb_active & MB_COMD) {
1060 ok = imap_answer(mp, 1);
1061 if (response_status != RESPONSE_OTHER &&
1062 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1063 mp->mb_uidvalidity = atol(&cp[13]);
1065 *cnt = (had_exists > 0) ? had_exists : 0;
1066 if (response_status != RESPONSE_OTHER &&
1067 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1068 mp->mb_perm = 0;
1069 return ok;
1072 static enum okay
1073 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1075 char o[LINESIZE];
1076 FILE *queuefp = NULL;
1077 char const *cp;
1078 struct message *m;
1079 unsigned x = X, y = Y, n;
1080 NYD_X;
1082 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1083 IMAP_OUT(o, MB_COMD, return STOP)
1084 while (mp->mb_active & MB_COMD) {
1085 imap_answer(mp, 1);
1086 if (response_status == RESPONSE_OTHER &&
1087 response_other == MESSAGE_DATA_FETCH) {
1088 n = responded_other_number;
1089 if (n < x || n > y)
1090 continue;
1091 m = &message[n-1];
1092 m->m_xsize = 0;
1093 } else
1094 continue;
1096 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1097 cp += 5;
1098 while (*cp == ' ')
1099 cp++;
1100 if (*cp == '(')
1101 imap_getflags(cp, &cp, &m->m_flag);
1104 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1105 m->m_uid = strtoul(&cp[4], NULL, 10);
1106 getcache1(mp, m, NEED_UNSPEC, 1);
1107 m->m_flag &= ~MHIDDEN;
1110 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1111 x++;
1112 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1113 y--;
1114 if (x <= y) {
1115 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1116 tag(1), x, y);
1117 IMAP_OUT(o, MB_COMD, return STOP)
1118 while (mp->mb_active & MB_COMD) {
1119 imap_answer(mp, 1);
1120 if (response_status == RESPONSE_OTHER &&
1121 response_other == MESSAGE_DATA_FETCH) {
1122 n = responded_other_number;
1123 if (n < x || n > y)
1124 continue;
1125 m = &message[n-1];
1126 } else
1127 continue;
1128 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1129 m->m_xsize = strtol(&cp[12], NULL, 10);
1130 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1131 m->m_time = imap_read_date_time(&cp[13]);
1135 srelax_hold();
1136 for (n = X; n <= Y; ++n) {
1137 putcache(mp, &message[n-1]);
1138 srelax();
1140 srelax_rele();
1141 return OKAY;
1144 static void
1145 imap_init(struct mailbox *mp, int n)
1147 struct message *m;
1148 NYD_ENTER;
1149 UNUSED(mp);
1151 m = message + n;
1152 m->m_flag = MUSED | MNOFROM;
1153 m->m_block = 0;
1154 m->m_offset = 0;
1155 NYD_LEAVE;
1158 static void
1159 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1161 struct message *omessage = 0;
1162 int i, omsgCount = 0;
1163 enum okay dequeued = STOP;
1164 NYD_ENTER;
1166 if (nmail || transparent) {
1167 omessage = message;
1168 omsgCount = msgCount;
1170 if (nmail)
1171 dequeued = rec_dequeue();
1173 if (had_exists >= 0) {
1174 if (dequeued != OKAY)
1175 msgCount = had_exists;
1176 had_exists = -1;
1178 if (had_expunge >= 0) {
1179 if (dequeued != OKAY)
1180 msgCount -= had_expunge;
1181 had_expunge = -1;
1184 if (nmail && expunged_messages)
1185 printf("Expunged %ld message%s.\n", expunged_messages,
1186 (expunged_messages != 1 ? "s" : ""));
1187 *prevcount = omsgCount - expunged_messages;
1188 expunged_messages = 0;
1189 if (msgCount < 0) {
1190 fputs("IMAP error: Negative message count\n", stderr);
1191 msgCount = 0;
1194 if (dequeued != OKAY) {
1195 message = scalloc(msgCount + 1, sizeof *message);
1196 for (i = 0; i < msgCount; i++)
1197 imap_init(mp, i);
1198 if (!nmail && mp->mb_type == MB_IMAP)
1199 initcache(mp);
1200 if (msgCount > 0)
1201 imap_flags(mp, 1, msgCount);
1202 message[msgCount].m_size = 0;
1203 message[msgCount].m_lines = 0;
1204 rec_rmqueue();
1206 if (nmail || transparent)
1207 transflags(omessage, omsgCount, transparent);
1208 else
1209 setdot(message);
1210 NYD_LEAVE;
1213 FL int
1214 imap_setfile(const char *xserver, enum fedit_mode fm)
1216 struct url url;
1217 int rv;
1218 NYD_ENTER;
1220 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1221 rv = 1;
1222 goto jleave;
1224 if (!ok_blook(v15_compat) &&
1225 (!url.url_had_user || url.url_pass.s != NULL))
1226 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1228 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1229 rv = _imap_setfile1(&url, fm, 0);
1230 jleave:
1231 NYD_LEAVE;
1232 return rv;
1235 static bool_t
1236 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1238 bool_t rv = FAL0;
1239 NYD_ENTER;
1241 if (ok_blook(v15_compat))
1242 rv = ccred_lookup(ccredp, urlp);
1243 else {
1244 char *var, *old,
1245 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1247 if ((var = mbp->mb_imap_pass) != NULL) {
1248 var = savecat("password-", xuhp);
1249 if ((old = vok_vlook(var)) != NULL)
1250 old = sstrdup(old);
1251 vok_vset(var, mbp->mb_imap_pass);
1253 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1254 if (var != NULL) {
1255 if (old != NULL) {
1256 vok_vset(var, old);
1257 free(old);
1258 } else
1259 vok_vclear(var);
1263 NYD_LEAVE;
1264 return rv;
1267 static int
1268 _imap_setfile1(struct url *urlp, enum fedit_mode fm, int volatile transparent)
1270 struct sock so;
1271 struct ccred ccred;
1272 sighandler_type volatile saveint, savepipe;
1273 char const *cp;
1274 int rv;
1275 int volatile prevcount = 0;
1276 enum mbflags same_flags;
1277 NYD_ENTER;
1279 if (fm & FEDIT_NEWMAIL) {
1280 saveint = safe_signal(SIGINT, SIG_IGN);
1281 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1282 if (saveint != SIG_IGN)
1283 safe_signal(SIGINT, imapcatch);
1284 if (savepipe != SIG_IGN)
1285 safe_signal(SIGPIPE, imapcatch);
1286 imaplock = 1;
1287 goto jnmail;
1290 same_flags = mb.mb_flags;
1291 same_imap_account = 0;
1292 if (mb.mb_imap_account != NULL &&
1293 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1294 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1295 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1296 disconnected(mb.mb_imap_account) == 0) {
1297 same_imap_account = 1;
1298 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1300 goto jduppass;
1301 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1302 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1303 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1304 jduppass:
1306 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1310 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1311 free(mb.mb_imap_pass);
1312 mb.mb_imap_pass = NULL;
1314 if (!_imap_getcred(&mb, &ccred, urlp)) {
1315 rv = -1;
1316 goto jleave;
1319 so.s_fd = -1;
1320 if (!same_imap_account) {
1321 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1322 rv = -1;
1323 goto jleave;
1325 } else
1326 so = mb.mb_sock;
1327 if (!transparent)
1328 quit();
1330 if (fm & FEDIT_SYSBOX)
1331 pstate &= ~PS_EDIT;
1332 else
1333 pstate |= PS_EDIT;
1334 if (mb.mb_imap_account != NULL)
1335 free(mb.mb_imap_account);
1336 if (mb.mb_imap_pass != NULL)
1337 free(mb.mb_imap_pass);
1338 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1339 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1340 * TODO object, and mailbox will naturally have an URL and credentials */
1341 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1343 if (!same_imap_account) {
1344 if (mb.mb_sock.s_fd >= 0)
1345 sclose(&mb.mb_sock);
1347 same_imap_account = 0;
1349 if (!transparent) {
1350 if (mb.mb_itf) {
1351 fclose(mb.mb_itf);
1352 mb.mb_itf = NULL;
1354 if (mb.mb_otf) {
1355 fclose(mb.mb_otf);
1356 mb.mb_otf = NULL;
1358 if (mb.mb_imap_mailbox != NULL)
1359 free(mb.mb_imap_mailbox);
1360 assert(urlp->url_path.s != NULL);
1361 imap_delim_init(&mb, urlp);
1362 mb.mb_imap_mailbox = sstrdup(imap_normalize_path(&mb, urlp->url_path.s));
1363 initbox(savecatsep(urlp->url_p_eu_h_p,
1364 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
1365 mb.mb_imap_mailbox));
1367 mb.mb_type = MB_VOID;
1368 mb.mb_active = MB_NONE;
1370 imaplock = 1;
1371 saveint = safe_signal(SIGINT, SIG_IGN);
1372 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1373 if (sigsetjmp(imapjmp, 1)) {
1374 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1375 sclose(&mb.mb_sock);
1376 safe_signal(SIGINT, saveint);
1377 safe_signal(SIGPIPE, savepipe);
1378 imaplock = 0;
1380 mb.mb_type = MB_VOID;
1381 mb.mb_active = MB_NONE;
1382 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1383 goto jleave;
1385 if (saveint != SIG_IGN)
1386 safe_signal(SIGINT, imapcatch);
1387 if (savepipe != SIG_IGN)
1388 safe_signal(SIGPIPE, imapcatch);
1390 if (mb.mb_sock.s_fd < 0) {
1391 if (disconnected(mb.mb_imap_account)) {
1392 if (cache_setptr(fm, transparent) == STOP)
1393 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1394 goto jdone;
1396 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1397 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1398 savealrm = safe_signal(SIGALRM, imapalarm);
1399 alarm(imapkeepalive);
1403 mb.mb_sock = so;
1404 mb.mb_sock.s_desc = "IMAP";
1405 mb.mb_sock.s_onclose = imap_timer_off;
1406 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1407 sclose(&mb.mb_sock);
1408 imap_timer_off();
1409 safe_signal(SIGINT, saveint);
1410 safe_signal(SIGPIPE, savepipe);
1411 imaplock = 0;
1412 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1413 goto jleave;
1415 } else /* same account */
1416 mb.mb_flags |= same_flags;
1418 if (options & OPT_R_FLAG)
1419 fm |= FEDIT_RDONLY;
1420 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1421 mb.mb_type = MB_IMAP;
1422 cache_dequeue(&mb);
1423 assert(urlp->url_path.s != NULL);
1424 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
1425 /*sclose(&mb.mb_sock);
1426 imap_timer_off();*/
1427 safe_signal(SIGINT, saveint);
1428 safe_signal(SIGPIPE, savepipe);
1429 imaplock = 0;
1430 mb.mb_type = MB_VOID;
1431 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1432 goto jleave;
1435 jnmail:
1436 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1437 UNVOLATILE(&prevcount));
1438 jdone:
1439 setmsize(msgCount);
1440 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1441 pstate &= ~PS_SAW_COMMAND;
1442 safe_signal(SIGINT, saveint);
1443 safe_signal(SIGPIPE, savepipe);
1444 imaplock = 0;
1446 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1447 purgecache(&mb, message, msgCount);
1448 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1449 mb.mb_threaded = 0;
1450 c_sort((void*)-1);
1453 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1454 mb.mb_type == MB_CACHE)) {
1455 rv = (msgCount == 0);
1456 goto jleave;
1459 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1460 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1461 !ok_blook(emptystart))
1462 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1463 rv = 1;
1464 goto jleave;
1467 if (fm & FEDIT_NEWMAIL)
1468 newmailinfo(prevcount);
1469 rv = 0;
1470 jleave:
1471 NYD_LEAVE;
1472 return rv;
1475 static int
1476 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1477 int need, const char *head, size_t headsize, long headlines)
1479 char *line = NULL, *lp;
1480 size_t linesize = 0, linelen, size = 0;
1481 int emptyline = 0, lines = 0, excess = 0;
1482 off_t offset;
1483 NYD_ENTER;
1485 fseek(mp->mb_otf, 0L, SEEK_END);
1486 offset = ftell(mp->mb_otf);
1488 if (head)
1489 fwrite(head, 1, headsize, mp->mb_otf);
1491 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1492 lp = line;
1493 if (linelen > expected) {
1494 excess = linelen - expected;
1495 linelen = expected;
1497 /* TODO >>
1498 * Need to mask 'From ' lines. This cannot be done properly
1499 * since some servers pass them as 'From ' and others as
1500 * '>From '. Although one could identify the first kind of
1501 * server in principle, it is not possible to identify the
1502 * second as '>From ' may also come from a server of the
1503 * first type as actual data. So do what is absolutely
1504 * necessary only - mask 'From '.
1506 * If the line is the first line of the message header, it
1507 * is likely a real 'From ' line. In this case, it is just
1508 * ignored since it violates all standards.
1509 * TODO can the latter *really* happen??
1510 * TODO <<
1512 /* Since we simply copy over data without doing any transfer
1513 * encoding reclassification/adjustment we *have* to perform
1514 * RFC 4155 compliant From_ quoting here */
1515 if (emptyline && is_head(lp, linelen, FAL0)) {
1516 fputc('>', mp->mb_otf);
1517 ++size;
1519 emptyline = 0;
1520 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1521 if (linelen > 2) {
1522 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1523 size += linelen - 1;
1524 } else {
1525 emptyline = 1;
1526 ++size;
1528 fputc('\n', mp->mb_otf);
1529 } else {
1530 fwrite(lp, 1, linelen, mp->mb_otf);
1531 size += linelen;
1533 ++lines;
1534 if ((expected -= linelen) <= 0)
1535 break;
1537 if (!emptyline) {
1538 /* This is very ugly; but some IMAP daemons don't end a
1539 * message with \r\n\r\n, and we need \n\n for mbox format */
1540 fputc('\n', mp->mb_otf);
1541 ++lines;
1542 ++size;
1544 fflush(mp->mb_otf);
1546 if (m != NULL) {
1547 m->m_size = size + headsize;
1548 m->m_lines = lines + headlines;
1549 m->m_block = mailx_blockof(offset);
1550 m->m_offset = mailx_offsetof(offset);
1551 switch (need) {
1552 case NEED_HEADER:
1553 m->m_have |= HAVE_HEADER;
1554 break;
1555 case NEED_BODY:
1556 m->m_have |= HAVE_HEADER | HAVE_BODY;
1557 m->m_xlines = m->m_lines;
1558 m->m_xsize = m->m_size;
1559 break;
1562 free(line);
1563 NYD_LEAVE;
1564 return excess;
1567 static void
1568 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1569 const char *head, size_t headsize, long headlines)
1571 off_t offset;
1572 size_t len;
1573 NYD_ENTER;
1575 len = strlen(str);
1576 fseek(mp->mb_otf, 0L, SEEK_END);
1577 offset = ftell(mp->mb_otf);
1578 if (head)
1579 fwrite(head, 1, headsize, mp->mb_otf);
1580 if (len > 0) {
1581 fwrite(str, 1, len, mp->mb_otf);
1582 fputc('\n', mp->mb_otf);
1583 ++len;
1585 fflush(mp->mb_otf);
1587 if (m != NULL) {
1588 m->m_size = headsize + len;
1589 m->m_lines = headlines + 1;
1590 m->m_block = mailx_blockof(offset);
1591 m->m_offset = mailx_offsetof(offset);
1592 m->m_have |= HAVE_HEADER | HAVE_BODY;
1593 m->m_xlines = m->m_lines;
1594 m->m_xsize = m->m_size;
1596 NYD_LEAVE;
1599 static enum okay
1600 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1602 char o[LINESIZE];
1603 struct message mt;
1604 sighandler_type volatile saveint, savepipe;
1605 char * volatile head;
1606 char const *cp, *loc, * volatile item, * volatile resp;
1607 size_t expected;
1608 size_t volatile headsize;
1609 int number;
1610 FILE *queuefp;
1611 long volatile headlines;
1612 long n;
1613 ul_i volatile u;
1614 enum okay ok;
1615 NYD_X;
1617 saveint = savepipe = SIG_IGN;
1618 head = NULL;
1619 cp = loc = item = resp = NULL;
1620 headsize = 0;
1621 number = (int)PTR2SIZE(m - message + 1);
1622 queuefp = NULL;
1623 headlines = 0;
1624 n = -1;
1625 u = 0;
1626 ok = STOP;
1628 if (getcache(mp, m, need) == OKAY)
1629 return OKAY;
1630 if (mp->mb_type == MB_CACHE) {
1631 n_err(_("Message %lu not available\n"), (ul_i)number);
1632 return STOP;
1635 if (mp->mb_sock.s_fd < 0) {
1636 n_err(_("IMAP connection closed\n"));
1637 return STOP;
1640 switch (need) {
1641 case NEED_HEADER:
1642 resp = item = "RFC822.HEADER";
1643 break;
1644 case NEED_BODY:
1645 item = "BODY.PEEK[]";
1646 resp = "BODY[]";
1647 if (m->m_flag & HAVE_HEADER && m->m_size) {
1648 char *hdr = smalloc(m->m_size);
1649 fflush(mp->mb_otf);
1650 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1651 SEEK_SET) < 0 ||
1652 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1653 free(hdr);
1654 break;
1656 head = hdr;
1657 headsize = m->m_size;
1658 headlines = m->m_lines;
1659 item = "BODY.PEEK[TEXT]";
1660 resp = "BODY[TEXT]";
1662 break;
1663 case NEED_UNSPEC:
1664 return STOP;
1667 imaplock = 1;
1668 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1669 if (sigsetjmp(imapjmp, 1)) {
1670 safe_signal(SIGINT, saveint);
1671 safe_signal(SIGPIPE, savepipe);
1672 imaplock = 0;
1673 return STOP;
1675 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1676 safe_signal(SIGINT, &_imap_maincatch);
1677 if (savepipe != SIG_IGN)
1678 safe_signal(SIGPIPE, imapcatch);
1680 if (m->m_uid)
1681 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1682 tag(1), m->m_uid, item);
1683 else {
1684 if (check_expunged() == STOP)
1685 goto out;
1686 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1688 IMAP_OUT(o, MB_COMD, goto out)
1689 for (;;) {
1690 ok = imap_answer(mp, 1);
1691 if (ok == STOP)
1692 break;
1693 if (response_status != RESPONSE_OTHER ||
1694 response_other != MESSAGE_DATA_FETCH)
1695 continue;
1696 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1697 continue;
1698 if (m->m_uid) {
1699 if ((cp = asccasestr(responded_other_text, "UID "))) {
1700 u = atol(&cp[4]);
1701 n = 0;
1702 } else {
1703 n = -1;
1704 u = 0;
1706 } else
1707 n = responded_other_number;
1708 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1709 if (m->m_uid ? m->m_uid != u : n != number)
1710 continue;
1711 if ((cp = strchr(loc, '"')) != NULL) {
1712 cp = imap_unquotestr(cp);
1713 imap_putstr(mp, m, cp, head, headsize, headlines);
1714 } else {
1715 m->m_have |= HAVE_HEADER|HAVE_BODY;
1716 m->m_xlines = m->m_lines;
1717 m->m_xsize = m->m_size;
1719 goto out;
1721 expected = atol(&cp[1]);
1722 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1723 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1724 continue;
1726 mt = *m;
1727 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1728 if (n >= 0) {
1729 commitmsg(mp, m, &mt, mt.m_have);
1730 break;
1732 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1733 if (options & OPT_VERBVERB)
1734 fputs(imapbuf, stderr);
1735 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1736 u = atol(&cp[4]);
1737 if (u == m->m_uid) {
1738 commitmsg(mp, m, &mt, mt.m_have);
1739 break;
1744 out:
1745 while (mp->mb_active & MB_COMD)
1746 ok = imap_answer(mp, 1);
1748 if (saveint != SIG_IGN)
1749 safe_signal(SIGINT, saveint);
1750 if (savepipe != SIG_IGN)
1751 safe_signal(SIGPIPE, savepipe);
1752 imaplock--;
1754 if (ok == OKAY)
1755 putcache(mp, m);
1756 if (head != NULL)
1757 free(head);
1758 if (interrupts)
1759 onintr(0);
1760 return ok;
1763 FL enum okay
1764 imap_header(struct message *m)
1766 enum okay rv;
1767 NYD_ENTER;
1769 rv = imap_get(&mb, m, NEED_HEADER);
1770 NYD_LEAVE;
1771 return rv;
1775 FL enum okay
1776 imap_body(struct message *m)
1778 enum okay rv;
1779 NYD_ENTER;
1781 rv = imap_get(&mb, m, NEED_BODY);
1782 NYD_LEAVE;
1783 return rv;
1786 static void
1787 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1788 enum havespec have)
1790 NYD_ENTER;
1791 tomp->m_size = frommp->m_size;
1792 tomp->m_lines = frommp->m_lines;
1793 tomp->m_block = frommp->m_block;
1794 tomp->m_offset = frommp->m_offset;
1795 tomp->m_have = have;
1796 if (have & HAVE_BODY) {
1797 tomp->m_xlines = frommp->m_lines;
1798 tomp->m_xsize = frommp->m_size;
1800 putcache(mp, tomp);
1801 NYD_LEAVE;
1804 static enum okay
1805 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1807 /* bot > topp */
1808 char o[LINESIZE];
1809 char const *cp;
1810 struct message mt;
1811 size_t expected;
1812 int n = 0, u;
1813 FILE *queuefp = NULL;
1814 enum okay ok;
1815 NYD_X;
1817 if (m[bot].m_uid)
1818 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1819 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1820 else {
1821 if (check_expunged() == STOP)
1822 return STOP;
1823 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1824 tag(1), bot, topp);
1826 IMAP_OUT(o, MB_COMD, return STOP)
1828 srelax_hold();
1829 for (;;) {
1830 ok = imap_answer(mp, 1);
1831 if (response_status != RESPONSE_OTHER)
1832 break;
1833 if (response_other != MESSAGE_DATA_FETCH)
1834 continue;
1835 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
1836 srelax_rele();
1837 return STOP;
1839 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1840 continue;
1841 expected = atol(&cp[1]);
1842 if (m[bot-1].m_uid) {
1843 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1844 u = atoi(&cp[4]);
1845 for (n = bot; n <= topp; n++)
1846 if ((unsigned long)u == m[n-1].m_uid)
1847 break;
1848 if (n > topp) {
1849 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1850 continue;
1852 } else
1853 n = -1;
1854 } else {
1855 n = responded_other_number;
1856 if (n <= 0 || n > msgCount) {
1857 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1858 continue;
1861 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1862 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1863 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1864 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1865 if (options & OPT_VERBVERB)
1866 fputs(imapbuf, stderr);
1867 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1868 u = atoi(&cp[4]);
1869 for (n = bot; n <= topp; n++)
1870 if ((unsigned long)u == m[n-1].m_uid)
1871 break;
1872 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1873 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1874 n = 0;
1877 srelax();
1879 srelax_rele();
1881 while (mp->mb_active & MB_COMD)
1882 ok = imap_answer(mp, 1);
1883 return ok;
1886 FL void
1887 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1889 sighandler_type saveint, savepipe;
1890 /*enum okay ok = STOP;*/
1891 int i, chunk = 256;
1892 NYD_X;
1894 if (mb.mb_type == MB_CACHE)
1895 return;
1896 if (bot < 1)
1897 bot = 1;
1898 if (topp > msgCount)
1899 topp = msgCount;
1900 for (i = bot; i < topp; i++) {
1901 if (message[i-1].m_have & HAVE_HEADER ||
1902 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1903 bot = i+1;
1904 else
1905 break;
1907 for (i = topp; i > bot; i--) {
1908 if (message[i-1].m_have & HAVE_HEADER ||
1909 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1910 topp = i-1;
1911 else
1912 break;
1914 if (bot >= topp)
1915 return;
1917 imaplock = 1;
1918 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1919 safe_signal(SIGINT, &_imap_maincatch);
1920 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1921 if (sigsetjmp(imapjmp, 1) == 0) {
1922 if (savepipe != SIG_IGN)
1923 safe_signal(SIGPIPE, imapcatch);
1925 for (i = bot; i <= topp; i += chunk) {
1926 int j = i + chunk - 1;
1927 j = MIN(j, topp);
1928 if (visible(message + j))
1929 /*ok = */imap_fetchheaders(&mb, message, i, j);
1930 if (interrupts)
1931 onintr(0); /* XXX imaplock? */
1934 safe_signal(SIGINT, saveint);
1935 safe_signal(SIGPIPE, savepipe);
1936 imaplock = 0;
1939 static enum okay
1940 __imap_exit(struct mailbox *mp)
1942 char o[LINESIZE];
1943 FILE *queuefp = NULL;
1944 NYD_X;
1946 mp->mb_active |= MB_BYE;
1947 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1948 IMAP_OUT(o, MB_COMD, return STOP)
1949 IMAP_ANSWER()
1950 return OKAY;
1953 static enum okay
1954 imap_exit(struct mailbox *mp)
1956 enum okay rv;
1957 NYD_ENTER;
1959 rv = __imap_exit(mp);
1960 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1961 free(mp->mb_imap_pass);
1962 free(mp->mb_imap_account);
1963 free(mp->mb_imap_mailbox);
1964 if (mp->mb_cache_directory != NULL)
1965 free(mp->mb_cache_directory);
1966 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1967 mp->mb_imap_account =
1968 mp->mb_imap_mailbox =
1969 mp->mb_cache_directory = "";
1970 #else
1971 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1972 mp->mb_imap_mailbox = NULL;
1973 mp->mb_cache_directory = NULL;
1974 #endif
1975 #endif
1976 sclose(&mp->mb_sock);
1977 NYD_LEAVE;
1978 return rv;
1981 static enum okay
1982 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1984 NYD_ENTER;
1985 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1986 if (mp->mb_type == MB_IMAP)
1987 delcache(mp, m);
1988 NYD_LEAVE;
1989 return OKAY;
1992 static enum okay
1993 imap_close(struct mailbox *mp)
1995 char o[LINESIZE];
1996 FILE *queuefp = NULL;
1997 NYD_X;
1999 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2000 IMAP_OUT(o, MB_COMD, return STOP)
2001 IMAP_ANSWER()
2002 return OKAY;
2005 static enum okay
2006 imap_update(struct mailbox *mp)
2008 struct message *m;
2009 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2010 NYD_ENTER;
2012 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
2013 holdbits();
2014 c = 0;
2015 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
2016 if (m->m_flag & MBOX)
2017 ++c;
2018 if (c > 0)
2019 if (makembox() == STOP)
2020 goto jbypass;
2023 gotcha = held = 0;
2024 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
2025 if (mp->mb_perm == 0)
2026 dodel = 0;
2027 else if (pstate & PS_EDIT)
2028 dodel = ((m->m_flag & MDELETED) != 0);
2029 else
2030 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2032 /* Fetch the result after around each 800 STORE commands
2033 * sent (approx. 32k data sent). Otherwise, servers will
2034 * try to flush the return queue at some point, leading
2035 * to a deadlock if we are still writing commands but not
2036 * reading their results */
2037 needstat = stored > 0 && stored % 800 == 0;
2038 /* Even if this message has been deleted, continue
2039 * to set further flags. This is necessary to support
2040 * Gmail semantics, where "delete" actually means
2041 * "archive", and the flags are applied to the copy
2042 * in "All Mail" */
2043 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2044 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2045 stored++;
2047 if (m->m_flag & MFLAG) {
2048 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2049 stored++;
2051 if (m->m_flag & MUNFLAG) {
2052 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2053 stored++;
2055 if (m->m_flag & MANSWER) {
2056 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2057 stored++;
2059 if (m->m_flag & MUNANSWER) {
2060 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2061 stored++;
2063 if (m->m_flag & MDRAFT) {
2064 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2065 stored++;
2067 if (m->m_flag & MUNDRAFT) {
2068 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2069 stored++;
2072 if (dodel) {
2073 imap_delete(mp, m-message+1, m, needstat);
2074 stored++;
2075 gotcha++;
2076 } else if (mp->mb_type != MB_CACHE ||
2077 (!(pstate & PS_EDIT) &&
2078 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2079 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2080 (MPRESERVE | MTOUCH) ||
2081 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
2082 held++;
2083 if (m->m_flag & MNEW) {
2084 m->m_flag &= ~MNEW;
2085 m->m_flag |= MSTATUS;
2088 jbypass:
2089 if (gotcha)
2090 imap_close(mp);
2092 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2093 if (!(m->m_flag & MUNLINKED) &&
2094 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2095 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2096 putcache(mp, m);
2097 modflags++;
2100 /* XXX should be readonly (but our IMAP code is weird...) */
2101 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST)) &&
2102 mb.mb_perm != 0) {
2103 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2104 printf(_("\"%s\" "), displayname);
2105 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2106 ? _("complete\n") : _("updated.\n"));
2107 } else if (held && !(pstate & PS_EDIT)) {
2108 if (held == 1)
2109 printf(_("Held 1 message in %s\n"), displayname);
2110 else
2111 printf(_("Held %d messages in %s\n"), held, displayname);
2113 fflush(stdout);
2115 NYD_LEAVE;
2116 return OKAY;
2119 FL void
2120 imap_quit(void)
2122 sighandler_type volatile saveint, savepipe;
2123 NYD_ENTER;
2125 if (mb.mb_type == MB_CACHE) {
2126 imap_update(&mb);
2127 goto jleave;
2130 if (mb.mb_sock.s_fd < 0) {
2131 n_err(_("IMAP connection closed\n"));
2132 goto jleave;
2135 imaplock = 1;
2136 saveint = safe_signal(SIGINT, SIG_IGN);
2137 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2138 if (sigsetjmp(imapjmp, 1)) {
2139 safe_signal(SIGINT, saveint);
2140 safe_signal(SIGPIPE, saveint);
2141 imaplock = 0;
2142 goto jleave;
2144 if (saveint != SIG_IGN)
2145 safe_signal(SIGINT, imapcatch);
2146 if (savepipe != SIG_IGN)
2147 safe_signal(SIGPIPE, imapcatch);
2149 imap_update(&mb);
2150 if (!same_imap_account)
2151 imap_exit(&mb);
2153 safe_signal(SIGINT, saveint);
2154 safe_signal(SIGPIPE, savepipe);
2155 imaplock = 0;
2156 jleave:
2157 NYD_LEAVE;
2160 static enum okay
2161 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2162 int needstat)
2164 char o[LINESIZE];
2165 FILE *queuefp = NULL;
2166 NYD_X;
2168 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2169 return STOP;
2170 if (m->m_uid)
2171 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2172 tag(1), m->m_uid, c, sp);
2173 else {
2174 if (check_expunged() == STOP)
2175 return STOP;
2176 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2178 IMAP_OUT(o, MB_COMD, return STOP)
2179 if (needstat)
2180 IMAP_ANSWER()
2181 else
2182 mb.mb_active &= ~MB_COMD;
2183 if (queuefp != NULL)
2184 Fclose(queuefp);
2185 return OKAY;
2188 FL enum okay
2189 imap_undelete(struct message *m, int n)
2191 enum okay rv;
2192 NYD_ENTER;
2194 rv = imap_unstore(m, n, "\\Deleted");
2195 NYD_LEAVE;
2196 return rv;
2199 FL enum okay
2200 imap_unread(struct message *m, int n)
2202 enum okay rv;
2203 NYD_ENTER;
2205 rv = imap_unstore(m, n, "\\Seen");
2206 NYD_LEAVE;
2207 return rv;
2210 static enum okay
2211 imap_unstore(struct message *m, int n, const char *flag)
2213 sighandler_type saveint, savepipe;
2214 enum okay rv = STOP;
2215 NYD_ENTER;
2217 imaplock = 1;
2218 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2219 safe_signal(SIGINT, &_imap_maincatch);
2220 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2221 if (sigsetjmp(imapjmp, 1) == 0) {
2222 if (savepipe != SIG_IGN)
2223 safe_signal(SIGPIPE, imapcatch);
2225 rv = imap_store(&mb, m, n, '-', flag, 1);
2227 safe_signal(SIGINT, saveint);
2228 safe_signal(SIGPIPE, savepipe);
2229 imaplock = 0;
2231 NYD_LEAVE;
2232 if (interrupts)
2233 onintr(0);
2234 return rv;
2237 static const char *
2238 tag(int new)
2240 static char ts[20];
2241 static long n;
2242 NYD2_ENTER;
2244 if (new)
2245 ++n;
2246 snprintf(ts, sizeof ts, "T%lu", n);
2247 NYD2_LEAVE;
2248 return ts;
2251 FL int
2252 c_imap_imap(void *vp)
2254 char o[LINESIZE];
2255 sighandler_type saveint, savepipe;
2256 struct mailbox *mp = &mb;
2257 FILE *queuefp = NULL;
2258 enum okay volatile ok = STOP;
2259 NYD_X;
2261 if (mp->mb_type != MB_IMAP) {
2262 printf("Not operating on an IMAP mailbox.\n");
2263 return 1;
2265 imaplock = 1;
2266 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2267 safe_signal(SIGINT, &_imap_maincatch);
2268 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2269 if (sigsetjmp(imapjmp, 1) == 0) {
2270 if (savepipe != SIG_IGN)
2271 safe_signal(SIGPIPE, imapcatch);
2273 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2274 IMAP_OUT(o, MB_COMD, goto out)
2275 while (mp->mb_active & MB_COMD) {
2276 ok = imap_answer(mp, 0);
2277 fputs(responded_text, stdout);
2280 out:
2281 safe_signal(SIGINT, saveint);
2282 safe_signal(SIGPIPE, savepipe);
2283 imaplock = 0;
2285 if (interrupts)
2286 onintr(0);
2287 return ok != OKAY;
2290 FL int
2291 imap_newmail(int nmail)
2293 NYD_ENTER;
2295 if (nmail && had_exists < 0 && had_expunge < 0) {
2296 imaplock = 1;
2297 imap_noop();
2298 imaplock = 0;
2301 if (had_exists == msgCount && had_expunge < 0)
2302 /* Some servers always respond with EXISTS to NOOP. If
2303 * the mailbox has been changed but the number of messages
2304 * has not, an EXPUNGE must also had been sent; otherwise,
2305 * nothing has changed */
2306 had_exists = -1;
2307 NYD_LEAVE;
2308 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2311 static char *
2312 imap_putflags(int f)
2314 const char *cp;
2315 char *buf, *bp;
2316 NYD2_ENTER;
2318 bp = buf = salloc(100);
2319 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2320 *bp++ = '(';
2321 if (f & MREAD) {
2322 if (bp[-1] != '(')
2323 *bp++ = ' ';
2324 for (cp = "\\Seen"; *cp; cp++)
2325 *bp++ = *cp;
2327 if (f & MFLAGGED) {
2328 if (bp[-1] != '(')
2329 *bp++ = ' ';
2330 for (cp = "\\Flagged"; *cp; cp++)
2331 *bp++ = *cp;
2333 if (f & MANSWERED) {
2334 if (bp[-1] != '(')
2335 *bp++ = ' ';
2336 for (cp = "\\Answered"; *cp; cp++)
2337 *bp++ = *cp;
2339 if (f & MDRAFT) {
2340 if (bp[-1] != '(')
2341 *bp++ = ' ';
2342 for (cp = "\\Draft"; *cp; cp++)
2343 *bp++ = *cp;
2345 *bp++ = ')';
2346 *bp++ = ' ';
2348 *bp = '\0';
2349 NYD2_LEAVE;
2350 return buf;
2353 static void
2354 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2356 NYD2_ENTER;
2357 while (*cp != ')') {
2358 if (*cp == '\\') {
2359 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2360 *f |= MREAD;
2361 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2362 *f |= MNEW;
2363 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2364 *f |= MDELETED;
2365 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2366 *f |= MFLAGGED;
2367 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2368 *f |= MANSWERED;
2369 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2370 *f |= MDRAFTED;
2372 cp++;
2375 if (xp != NULL)
2376 *xp = cp;
2377 NYD2_LEAVE;
2380 static enum okay
2381 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2382 long xsize, enum mflag flag, time_t t)
2384 char o[LINESIZE], *buf;
2385 size_t bufsize, buflen, cnt;
2386 long size, lines, ysize;
2387 int twice = 0;
2388 FILE *queuefp = NULL;
2389 enum okay rv;
2390 NYD_ENTER;
2392 if (mp->mb_type == MB_CACHE) {
2393 queuefp = cache_queue(mp);
2394 if (queuefp == NULL) {
2395 rv = STOP;
2396 buf = NULL;
2397 goto jleave;
2399 rv = OKAY;
2400 } else
2401 rv = STOP;
2403 buf = smalloc(bufsize = LINESIZE);
2404 buflen = 0;
2405 jagain:
2406 size = xsize;
2407 cnt = fsize(fp);
2408 if (fseek(fp, off1, SEEK_SET) < 0) {
2409 rv = STOP;
2410 goto jleave;
2413 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2414 tag(1), imap_quotepath(mp, name), imap_putflags(flag),
2415 imap_make_date_time(t), size);
2416 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2417 while (mp->mb_active & MB_COMD) {
2418 rv = imap_answer(mp, twice);
2419 if (response_type == RESPONSE_CONT)
2420 break;
2423 if (mp->mb_type != MB_CACHE && rv == STOP) {
2424 if (twice == 0)
2425 goto jtrycreate;
2426 else
2427 goto jleave;
2430 lines = ysize = 0;
2431 while (size > 0) {
2432 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2433 lines++;
2434 ysize += buflen;
2435 buf[buflen - 1] = '\r';
2436 buf[buflen] = '\n';
2437 if (mp->mb_type != MB_CACHE)
2438 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2439 else if (queuefp)
2440 fwrite(buf, 1, buflen+1, queuefp);
2441 size -= buflen + 1;
2443 if (mp->mb_type != MB_CACHE)
2444 swrite(&mp->mb_sock, "\r\n");
2445 else if (queuefp)
2446 fputs("\r\n", queuefp);
2447 while (mp->mb_active & MB_COMD) {
2448 rv = imap_answer(mp, 0);
2449 if (response_status == RESPONSE_NO /*&&
2450 ascncasecmp(responded_text,
2451 "[TRYCREATE] ", 12) == 0*/) {
2452 jtrycreate:
2453 if (twice++) {
2454 rv = STOP;
2455 goto jleave;
2457 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2458 tag(1), imap_quotepath(mp, name));
2459 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2460 while (mp->mb_active & MB_COMD)
2461 rv = imap_answer(mp, 1);
2462 if (rv == STOP)
2463 goto jleave;
2464 imap_created_mailbox++;
2465 goto jagain;
2466 } else if (rv != OKAY)
2467 n_err(_("IMAP error: %s"), responded_text);
2468 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2469 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2471 jleave:
2472 if (queuefp != NULL)
2473 Fclose(queuefp);
2474 if (buf != NULL)
2475 free(buf);
2476 NYD_LEAVE;
2477 return rv;
2480 static enum okay
2481 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2483 char *buf, *bp, *lp;
2484 size_t bufsize, buflen, cnt;
2485 off_t off1 = -1, offs;
2486 int flag;
2487 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2488 time_t tim;
2489 long size;
2490 enum okay rv;
2491 NYD_ENTER;
2493 buf = smalloc(bufsize = LINESIZE);
2494 buflen = 0;
2495 cnt = fsize(fp);
2496 offs = ftell(fp);
2497 time(&tim);
2498 size = 0;
2500 for (flag = MNEW, state = _NLSEP;;) {
2501 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2503 if (bp == NULL ||
2504 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2505 is_head(buf, buflen, FAL0))) {
2506 if (off1 != (off_t)-1) {
2507 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2508 if (rv == STOP)
2509 goto jleave;
2510 fseek(fp, offs+buflen, SEEK_SET);
2512 off1 = offs + buflen;
2513 size = 0;
2514 flag = MNEW;
2515 state = _INHEAD;
2516 if (bp == NULL)
2517 break;
2518 tim = unixtime(buf);
2519 } else
2520 size += buflen+1;
2521 offs += buflen;
2523 state &= ~_NLSEP;
2524 if (buf[0] == '\n') {
2525 state &= ~_INHEAD;
2526 state |= _NLSEP;
2527 } else if (state & _INHEAD) {
2528 if (ascncasecmp(buf, "status", 6) == 0) {
2529 lp = &buf[6];
2530 while (whitechar(*lp))
2531 lp++;
2532 if (*lp == ':')
2533 while (*++lp != '\0')
2534 switch (*lp) {
2535 case 'R':
2536 flag |= MREAD;
2537 break;
2538 case 'O':
2539 flag &= ~MNEW;
2540 break;
2542 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2543 lp = &buf[8];
2544 while (whitechar(*lp))
2545 lp++;
2546 if (*lp == ':')
2547 while (*++lp != '\0')
2548 switch (*lp) {
2549 case 'F':
2550 flag |= MFLAGGED;
2551 break;
2552 case 'A':
2553 flag |= MANSWERED;
2554 break;
2555 case 'T':
2556 flag |= MDRAFTED;
2557 break;
2562 rv = OKAY;
2563 jleave:
2564 free(buf);
2565 NYD_LEAVE;
2566 return rv;
2569 FL enum okay
2570 imap_append(const char *xserver, FILE *fp)
2572 sighandler_type volatile saveint, savepipe;
2573 struct url url;
2574 struct ccred ccred;
2575 char const * volatile mbx;
2576 enum okay rv = STOP;
2577 NYD_ENTER;
2579 if (!url_parse(&url, CPROTO_IMAP, xserver))
2580 goto j_leave;
2581 if (!ok_blook(v15_compat) &&
2582 (!url.url_had_user || url.url_pass.s != NULL))
2583 n_err(_("New-style URL used without *v15-compat* being set!\n"));
2584 assert(url.url_path.s != NULL);
2586 imaplock = 1;
2587 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2588 safe_signal(SIGINT, &_imap_maincatch);
2589 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2590 if (sigsetjmp(imapjmp, 1))
2591 goto jleave;
2592 if (savepipe != SIG_IGN)
2593 safe_signal(SIGPIPE, imapcatch);
2595 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2596 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
2597 rv = imap_append0(&mb, url.url_path.s, fp);
2598 } else {
2599 struct mailbox mx;
2601 memset(&mx, 0, sizeof mx);
2603 if (!_imap_getcred(&mx, &ccred, &url))
2604 goto jleave;
2606 imap_delim_init(&mx, &url);
2607 mx.mb_imap_mailbox = sstrdup(imap_normalize_path(&mx, url.url_path.s));
2609 if (disconnected(url.url_p_eu_h_p) == 0) {
2610 if (!sopen(&mx.mb_sock, &url))
2611 goto jfail;
2612 mx.mb_sock.s_desc = "IMAP";
2613 mx.mb_type = MB_IMAP;
2614 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2615 /* TODO the code now did
2616 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
2617 * TODO though imap_mailbox is sfree()d and mbx
2618 * TODO is possibly even a constant
2619 * TODO i changed this to sstrdup() sofar, as is used
2620 * TODO somewhere else in this file for this! */
2621 if (imap_preauth(&mx, &url) != OKAY ||
2622 imap_auth(&mx, &ccred) != OKAY) {
2623 sclose(&mx.mb_sock);
2624 goto jfail;
2626 rv = imap_append0(&mx, url.url_path.s, fp);
2627 imap_exit(&mx);
2628 } else {
2629 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2630 mx.mb_type = MB_CACHE;
2631 rv = imap_append0(&mx, url.url_path.s, fp);
2633 jfail:
2637 jleave:
2638 safe_signal(SIGINT, saveint);
2639 safe_signal(SIGPIPE, savepipe);
2640 imaplock = 0;
2641 j_leave:
2642 NYD_LEAVE;
2643 if (interrupts)
2644 onintr(0);
2645 return rv;
2648 static enum okay
2649 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2650 struct list_item **lend, int level)
2652 char o[LINESIZE], *cp;
2653 const char *bp;
2654 FILE *queuefp = NULL;
2655 struct list_item *lp;
2656 enum okay ok = STOP;
2657 NYD_X;
2659 *list = *lend = NULL;
2660 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotepath(mp, base));
2661 IMAP_OUT(o, MB_COMD, return STOP)
2662 while (mp->mb_active & MB_COMD) {
2663 ok = imap_answer(mp, 1);
2664 if (response_status == RESPONSE_OTHER &&
2665 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2666 cp = imap_unquotestr(list_name);
2667 lp = csalloc(1, sizeof *lp);
2668 lp->l_name = cp;
2669 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2670 ++cp;
2671 lp->l_base = *cp ? cp : savestr(base);
2672 lp->l_attr = list_attributes;
2673 lp->l_level = level+1;
2674 lp->l_delim = list_hierarchy_delimiter;
2675 if (*list && *lend) {
2676 (*lend)->l_next = lp;
2677 *lend = lp;
2678 } else
2679 *list = *lend = lp;
2682 return ok;
2685 static enum okay
2686 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2688 struct list_item *list, *lend, *lp, *lx, *ly;
2689 int n, depth;
2690 const char *bp;
2691 char *cp;
2692 enum okay rv;
2693 NYD_ENTER;
2695 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2696 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2697 goto jleave;
2698 rv = OKAY;
2699 if (list == NULL || lend == NULL)
2700 goto jleave;
2702 for (lp = list; lp; lp = lp->l_next)
2703 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2704 !(lp->l_attr & LIST_NOINFERIORS)) {
2705 cp = salloc((n = strlen(lp->l_name)) + 2);
2706 memcpy(cp, lp->l_name, n);
2707 cp[n] = lp->l_delim;
2708 cp[n+1] = '\0';
2709 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2710 lp->l_has_children = 1;
2711 if (strcmp(cp, lx->l_name) == 0)
2712 lx = lx->l_next;
2713 if (lx) {
2714 lend->l_next = lx;
2715 lend = ly;
2720 for (lp = list; lp; lp = lp->l_next) {
2721 if (strip) {
2722 cp = lp->l_name;
2723 for (bp = base; *bp && *bp == *cp; bp++)
2724 cp++;
2725 } else
2726 cp = lp->l_name;
2727 if (!(lp->l_attr & LIST_NOSELECT))
2728 fprintf(fp, "%s\n", *cp ? cp : base);
2729 else if (lp->l_has_children == 0)
2730 fprintf(fp, "%s%c\n", *cp ? cp : base,
2731 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2733 jleave:
2734 NYD_LEAVE;
2735 return rv;
2738 FL void
2739 imap_folders(const char * volatile name, int strip)
2741 sighandler_type saveint, savepipe;
2742 const char *fold, *cp, *sp;
2743 FILE * volatile fp;
2744 NYD_ENTER;
2746 cp = protbase(name);
2747 sp = mb.mb_imap_account;
2748 if (sp == NULL || strcmp(cp, sp)) {
2749 n_err(
2750 _("Cannot perform `folders' but when on the very IMAP "
2751 "account; the current one is\n `%s' -- "
2752 "try `folders @'\n"),
2753 (sp != NULL ? sp : _("[NONE]")));
2754 goto jleave;
2757 fold = imap_fileof(name);
2758 if (options & OPT_TTYOUT) {
2759 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2760 0600)) == NULL) {
2761 n_perr(_("tmpfile"), 0);
2762 goto jleave;
2764 } else
2765 fp = stdout;
2767 imaplock = 1;
2768 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2769 safe_signal(SIGINT, &_imap_maincatch);
2770 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2771 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2772 goto junroll;
2773 if (savepipe != SIG_IGN)
2774 safe_signal(SIGPIPE, imapcatch);
2776 if (mb.mb_type == MB_CACHE)
2777 cache_list(&mb, fold, strip, fp);
2778 else
2779 imap_list(&mb, fold, strip, fp);
2781 imaplock = 0;
2782 if (interrupts) {
2783 if (options & OPT_TTYOUT)
2784 Fclose(fp);
2785 goto jleave;
2787 fflush(fp);
2789 if (options & OPT_TTYOUT) {
2790 rewind(fp);
2791 if (fsize(fp) > 0)
2792 dopr(fp);
2793 else
2794 n_err(_("Folder not found\n"));
2796 junroll:
2797 safe_signal(SIGINT, saveint);
2798 safe_signal(SIGPIPE, savepipe);
2799 if (options & OPT_TTYOUT)
2800 Fclose(fp);
2801 jleave:
2802 NYD_LEAVE;
2803 if (interrupts)
2804 onintr(0);
2807 static void
2808 dopr(FILE *fp)
2810 char o[LINESIZE];
2811 int c;
2812 long n = 0, mx = 0, columns, width;
2813 FILE *out;
2814 NYD_ENTER;
2816 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2817 == NULL) {
2818 n_perr(_("tmpfile"), 0);
2819 goto jleave;
2822 while ((c = getc(fp)) != EOF) {
2823 if (c == '\n') {
2824 if (n > mx)
2825 mx = n;
2826 n = 0;
2827 } else
2828 ++n;
2830 rewind(fp);
2832 width = scrnwidth;
2833 if (mx < width / 2) {
2834 columns = width / (mx+2);
2835 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2836 } else
2837 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2838 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2839 page_or_print(out, 0);
2840 Fclose(out);
2841 jleave:
2842 NYD_LEAVE;
2845 static enum okay
2846 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2848 char o[LINESIZE];
2849 const char *qname;
2850 int twice = 0, stored = 0;
2851 FILE *queuefp = NULL;
2852 enum okay ok = STOP;
2853 NYD_X;
2855 if (mp->mb_type == MB_CACHE) {
2856 if ((queuefp = cache_queue(mp)) == NULL)
2857 return STOP;
2858 ok = OKAY;
2861 /* C99 */{
2862 size_t i;
2864 i = strlen(name = imap_fileof(name));
2865 if(i == 0 || (i > 0 && name[i - 1] == '/'))
2866 name = savecat(name, "INBOX");
2867 qname = imap_quotepath(mp, name);
2870 /* Since it is not possible to set flags on the copy, recently
2871 * set flags must be set on the original to include it in the copy */
2872 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2873 imap_store(mp, m, n, '+', "\\Seen", 0);
2874 if (m->m_flag&MFLAG)
2875 imap_store(mp, m, n, '+', "\\Flagged", 0);
2876 if (m->m_flag&MUNFLAG)
2877 imap_store(mp, m, n, '-', "\\Flagged", 0);
2878 if (m->m_flag&MANSWER)
2879 imap_store(mp, m, n, '+', "\\Answered", 0);
2880 if (m->m_flag&MUNANSWER)
2881 imap_store(mp, m, n, '-', "\\Flagged", 0);
2882 if (m->m_flag&MDRAFT)
2883 imap_store(mp, m, n, '+', "\\Draft", 0);
2884 if (m->m_flag&MUNDRAFT)
2885 imap_store(mp, m, n, '-', "\\Draft", 0);
2886 again:
2887 if (m->m_uid)
2888 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2889 else {
2890 if (check_expunged() == STOP)
2891 goto out;
2892 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2894 IMAP_OUT(o, MB_COMD, goto out)
2895 while (mp->mb_active & MB_COMD)
2896 ok = imap_answer(mp, twice);
2898 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2899 response_status == RESPONSE_OK)
2900 imap_copyuid(mp, m, name);
2902 if (response_status == RESPONSE_NO && twice++ == 0) {
2903 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2904 IMAP_OUT(o, MB_COMD, goto out)
2905 while (mp->mb_active & MB_COMD)
2906 ok = imap_answer(mp, 1);
2907 if (ok == OKAY) {
2908 imap_created_mailbox++;
2909 goto again;
2913 if (queuefp != NULL)
2914 Fclose(queuefp);
2916 /* ... and reset the flag to its initial value so that the 'exit'
2917 * command still leaves the message unread */
2918 out:
2919 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2920 imap_store(mp, m, n, '-', "\\Seen", 0);
2921 stored++;
2923 if (m->m_flag & MFLAG) {
2924 imap_store(mp, m, n, '-', "\\Flagged", 0);
2925 stored++;
2927 if (m->m_flag & MUNFLAG) {
2928 imap_store(mp, m, n, '+', "\\Flagged", 0);
2929 stored++;
2931 if (m->m_flag & MANSWER) {
2932 imap_store(mp, m, n, '-', "\\Answered", 0);
2933 stored++;
2935 if (m->m_flag & MUNANSWER) {
2936 imap_store(mp, m, n, '+', "\\Answered", 0);
2937 stored++;
2939 if (m->m_flag & MDRAFT) {
2940 imap_store(mp, m, n, '-', "\\Draft", 0);
2941 stored++;
2943 if (m->m_flag & MUNDRAFT) {
2944 imap_store(mp, m, n, '+', "\\Draft", 0);
2945 stored++;
2947 if (stored) {
2948 mp->mb_active |= MB_COMD;
2949 (void)imap_finish(mp);
2951 return ok;
2954 FL enum okay
2955 imap_copy(struct message *m, int n, const char *name)
2957 sighandler_type saveint, savepipe;
2958 enum okay rv = STOP;
2959 NYD_ENTER;
2961 imaplock = 1;
2962 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2963 safe_signal(SIGINT, &_imap_maincatch);
2964 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2965 if (sigsetjmp(imapjmp, 1) == 0) {
2966 if (savepipe != SIG_IGN)
2967 safe_signal(SIGPIPE, imapcatch);
2969 rv = imap_copy1(&mb, m, n, name);
2971 safe_signal(SIGINT, saveint);
2972 safe_signal(SIGPIPE, savepipe);
2973 imaplock = 0;
2975 NYD_LEAVE;
2976 if (interrupts)
2977 onintr(0);
2978 return rv;
2981 static enum okay
2982 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2983 unsigned long *olduid, unsigned long *newuid)
2985 char *xp, *yp, *zp;
2986 enum okay rv;
2987 NYD_ENTER;
2989 *uidvalidity = strtoul(cp, &xp, 10);
2990 *olduid = strtoul(xp, &yp, 10);
2991 *newuid = strtoul(yp, &zp, 10);
2992 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2993 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2994 NYD_LEAVE;
2995 return rv;
2998 static enum okay
2999 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
3000 unsigned long *uid)
3002 char *xp, *yp;
3003 enum okay rv;
3004 NYD_ENTER;
3006 *uidvalidity = strtoul(cp, &xp, 10);
3007 *uid = strtoul(xp, &yp, 10);
3008 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3009 *yp == ']');
3010 NYD_LEAVE;
3011 return rv;
3014 static enum okay
3015 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3017 struct mailbox xmb;
3018 struct message xm;
3019 const char *cp;
3020 unsigned long uidvalidity, olduid, newuid;
3021 enum okay rv;
3022 NYD_ENTER;
3024 rv = STOP;
3026 memset(&xmb, 0, sizeof xmb);
3028 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
3029 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3030 goto jleave;
3032 rv = OKAY;
3034 xmb = *mp;
3035 xmb.mb_cache_directory = NULL;
3036 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
3037 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
3038 memcpy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3039 sizeof(xmb.mb_imap_delim));
3040 xmb.mb_imap_mailbox = sstrdup(imap_normalize_path(&xmb, name));
3041 if (mp->mb_cache_directory != NULL)
3042 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
3043 xmb.mb_uidvalidity = uidvalidity;
3044 initcache(&xmb);
3046 if (m == NULL) {
3047 memset(&xm, 0, sizeof xm);
3048 xm.m_uid = olduid;
3049 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3050 goto jleave;
3051 getcache(mp, &xm, NEED_HEADER);
3052 getcache(mp, &xm, NEED_BODY);
3053 } else {
3054 if ((m->m_flag & HAVE_HEADER) == 0)
3055 getcache(mp, m, NEED_HEADER);
3056 if ((m->m_flag & HAVE_BODY) == 0)
3057 getcache(mp, m, NEED_BODY);
3058 xm = *m;
3060 xm.m_uid = newuid;
3061 xm.m_flag &= ~MFULLYCACHED;
3062 putcache(&xmb, &xm);
3063 jleave:
3064 if (xmb.mb_cache_directory != NULL)
3065 free(xmb.mb_cache_directory);
3066 if (xmb.mb_imap_mailbox != NULL)
3067 free(xmb.mb_imap_mailbox);
3068 if (xmb.mb_imap_pass != NULL)
3069 free(xmb.mb_imap_pass);
3070 if (xmb.mb_imap_account != NULL)
3071 free(xmb.mb_imap_account);
3072 NYD_LEAVE;
3073 return rv;
3076 static enum okay
3077 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3078 long size, long lines, int flag, const char *name)
3080 struct mailbox xmb;
3081 struct message xm;
3082 const char *cp;
3083 unsigned long uidvalidity, uid;
3084 enum okay rv;
3085 NYD_ENTER;
3087 rv = STOP;
3089 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
3090 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3091 goto jleave;
3093 rv = OKAY;
3095 xmb = *mp;
3096 xmb.mb_cache_directory = NULL;
3097 /* XXX mb_imap_delim reused */
3098 xmb.mb_imap_mailbox = sstrdup(imap_normalize_path(&xmb, name));
3099 xmb.mb_uidvalidity = uidvalidity;
3100 xmb.mb_otf = xmb.mb_itf = fp;
3101 initcache(&xmb);
3102 memset(&xm, 0, sizeof xm);
3103 xm.m_flag = (flag & MREAD) | MNEW;
3104 xm.m_time = t;
3105 xm.m_block = mailx_blockof(off1);
3106 xm.m_offset = mailx_offsetof(off1);
3107 xm.m_size = size;
3108 xm.m_xsize = xsize;
3109 xm.m_lines = xm.m_xlines = lines;
3110 xm.m_uid = uid;
3111 xm.m_have = HAVE_HEADER | HAVE_BODY;
3112 putcache(&xmb, &xm);
3114 free(xmb.mb_imap_mailbox);
3115 jleave:
3116 NYD_LEAVE;
3117 return rv;
3120 static enum okay
3121 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3123 FILE *tp = NULL;
3124 time_t t;
3125 long size, xsize, ysize, lines;
3126 enum mflag flag = MNEW;
3127 char *name, *buf, *bp;
3128 char const *cp;
3129 size_t bufsize, buflen, cnt;
3130 enum okay rv = STOP;
3131 NYD_ENTER;
3133 buf = smalloc(bufsize = LINESIZE);
3134 buflen = 0;
3135 cnt = fsize(fp);
3136 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3137 goto jstop;
3139 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3141 while (*bp == ' ')
3142 ++bp;
3144 if ((cp = strrchr(bp, '{')) == NULL)
3145 goto jstop;
3147 xsize = atol(&cp[1]) + 2;
3148 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3149 goto jstop;
3150 while (*cp == ' ')
3151 cp++;
3153 if (*cp == '(') {
3154 imap_getflags(cp, &cp, &flag);
3155 while (*++cp == ' ')
3158 t = imap_read_date_time(cp);
3160 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3161 NULL)
3162 goto jstop;
3164 size = xsize;
3165 ysize = lines = 0;
3166 while (size > 0) {
3167 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3168 goto jstop;
3169 size -= buflen;
3170 buf[--buflen] = '\0';
3171 buf[buflen-1] = '\n';
3172 fwrite(buf, 1, buflen, tp);
3173 ysize += buflen;
3174 ++lines;
3176 fflush(tp);
3177 rewind(tp);
3179 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3180 imap_unquotestr(name));
3181 rv = OKAY;
3182 jstop:
3183 free(buf);
3184 if (tp)
3185 Fclose(tp);
3186 NYD_LEAVE;
3187 return rv;
3190 #ifdef HAVE_IMAP_SEARCH
3191 static enum okay
3192 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3193 int f)
3195 char *o, *xp, *cs, c;
3196 size_t osize;
3197 FILE *queuefp = NULL;
3198 int i;
3199 unsigned long n;
3200 const char *cp;
3201 enum okay ok = STOP;
3202 NYD_X;
3204 c = 0;
3205 for (cp = spec; *cp; cp++)
3206 c |= *cp;
3207 if (c & 0200) {
3208 cp = charset_get_lc();
3209 # ifdef HAVE_ICONV
3210 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3211 iconv_t it;
3212 char *nsp, *nspec;
3213 size_t sz, nsz;
3215 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3216 sz = strlen(spec) + 1;
3217 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3218 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3219 sz == 0) {
3220 spec = nspec;
3221 cp = "utf-8";
3223 n_iconv_close(it);
3226 # endif
3227 cp = imap_quotestr(cp);
3228 cs = salloc(n = strlen(cp) + 10);
3229 snprintf(cs, n, "CHARSET %s ", cp);
3230 } else
3231 cs = UNCONST("");
3233 o = ac_alloc(osize = strlen(spec) + 60);
3234 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3235 IMAP_OUT(o, MB_COMD, goto out)
3236 while (mp->mb_active & MB_COMD) {
3237 ok = imap_answer(mp, 0);
3238 if (response_status == RESPONSE_OTHER &&
3239 response_other == MAILBOX_DATA_SEARCH) {
3240 xp = responded_other_text;
3241 while (*xp && *xp != '\r') {
3242 n = strtoul(xp, &xp, 10);
3243 for (i = 0; i < cnt; i++)
3244 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3245 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3246 mark(i+1, f);
3250 out:
3251 ac_free(o);
3252 return ok;
3255 FL enum okay
3256 imap_search1(const char * volatile spec, int f)
3258 sighandler_type saveint, savepipe;
3259 enum okay volatile rv = STOP;
3260 NYD_ENTER;
3262 if (mb.mb_type != MB_IMAP)
3263 goto jleave;
3265 imaplock = 1;
3266 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3267 safe_signal(SIGINT, &_imap_maincatch);
3268 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3269 if (sigsetjmp(imapjmp, 1) == 0) {
3270 if (savepipe != SIG_IGN)
3271 safe_signal(SIGPIPE, imapcatch);
3273 rv = imap_search2(&mb, message, msgCount, spec, f);
3275 safe_signal(SIGINT, saveint);
3276 safe_signal(SIGPIPE, savepipe);
3277 imaplock = 0;
3278 jleave:
3279 NYD_LEAVE;
3280 if (interrupts)
3281 onintr(0);
3282 return rv;
3284 #endif /* HAVE_IMAP_SEARCH */
3286 FL int
3287 imap_thisaccount(const char *cp)
3289 int rv;
3290 NYD_ENTER;
3292 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3293 rv = 0;
3294 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3295 mb.mb_imap_account == NULL)
3296 rv = 0;
3297 else
3298 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3299 NYD_LEAVE;
3300 return rv;
3303 FL enum okay
3304 imap_remove(const char * volatile name)
3306 sighandler_type volatile saveint, savepipe;
3307 enum okay volatile rv = STOP;
3308 NYD_ENTER;
3310 if (mb.mb_type != MB_IMAP) {
3311 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3312 goto jleave;
3315 if (!imap_thisaccount(name)) {
3316 n_err(_("Can only remove mailboxes on current IMAP server: "
3317 "\"%s\" not removed\n"), name);
3318 goto jleave;
3321 imaplock = 1;
3322 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3323 safe_signal(SIGINT, &_imap_maincatch);
3324 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3325 if (sigsetjmp(imapjmp, 1) == 0) {
3326 if (savepipe != SIG_IGN)
3327 safe_signal(SIGPIPE, imapcatch);
3329 rv = imap_remove1(&mb, imap_fileof(name));
3331 safe_signal(SIGINT, saveint);
3332 safe_signal(SIGPIPE, savepipe);
3333 imaplock = 0;
3335 if (rv == OKAY)
3336 rv = cache_remove(name);
3337 jleave:
3338 NYD_LEAVE;
3339 if (interrupts)
3340 onintr(0);
3341 return rv;
3344 static enum okay
3345 imap_remove1(struct mailbox *mp, const char *name)
3347 FILE *queuefp = NULL;
3348 char *o;
3349 int os;
3350 enum okay ok = STOP;
3351 NYD_X;
3353 o = ac_alloc(os = 2*strlen(name) + 100);
3354 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotepath(mp, name));
3355 IMAP_OUT(o, MB_COMD, goto out)
3356 while (mp->mb_active & MB_COMD)
3357 ok = imap_answer(mp, 1);
3358 out:
3359 ac_free(o);
3360 return ok;
3363 FL enum okay
3364 imap_rename(const char *old, const char *new)
3366 sighandler_type saveint, savepipe;
3367 enum okay rv = STOP;
3368 NYD_ENTER;
3370 if (mb.mb_type != MB_IMAP) {
3371 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3372 goto jleave;
3375 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3376 n_err(_("Can only rename mailboxes on current IMAP "
3377 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3378 goto jleave;
3381 imaplock = 1;
3382 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3383 safe_signal(SIGINT, &_imap_maincatch);
3384 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3385 if (sigsetjmp(imapjmp, 1) == 0) {
3386 if (savepipe != SIG_IGN)
3387 safe_signal(SIGPIPE, imapcatch);
3389 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3391 safe_signal(SIGINT, saveint);
3392 safe_signal(SIGPIPE, savepipe);
3393 imaplock = 0;
3395 if (rv == OKAY)
3396 rv = cache_rename(old, new);
3397 jleave:
3398 NYD_LEAVE;
3399 if (interrupts)
3400 onintr(0);
3401 return rv;
3404 static enum okay
3405 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3407 FILE *queuefp = NULL;
3408 char *o;
3409 int os;
3410 enum okay ok = STOP;
3411 NYD_X;
3413 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3414 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotepath(mp, old),
3415 imap_quotepath(mp, new));
3416 IMAP_OUT(o, MB_COMD, goto out)
3417 while (mp->mb_active & MB_COMD)
3418 ok = imap_answer(mp, 1);
3419 out:
3420 ac_free(o);
3421 return ok;
3424 FL enum okay
3425 imap_dequeue(struct mailbox *mp, FILE *fp)
3427 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3428 size_t bufsize, buflen, cnt;
3429 long offs, offs1, offs2, octets;
3430 int twice, gotcha = 0;
3431 FILE *queuefp = NULL;
3432 enum okay ok = OKAY, rok = OKAY;
3433 NYD_X;
3435 buf = smalloc(bufsize = LINESIZE);
3436 buflen = 0;
3437 cnt = fsize(fp);
3438 while ((offs1 = ftell(fp)) >= 0 &&
3439 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3440 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3442 while (*bp == ' ')
3443 ++bp;
3444 twice = 0;
3445 if ((offs = ftell(fp)) < 0)
3446 goto fail;
3447 again:
3448 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3449 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3450 cp = &bp[9];
3451 while (digitchar(*cp))
3452 cp++;
3453 if (*cp != ' ')
3454 goto fail;
3455 while (*cp == ' ')
3456 cp++;
3457 if ((newname = imap_strex(cp, NULL)) == NULL)
3458 goto fail;
3459 IMAP_OUT(o, MB_COMD, continue)
3460 while (mp->mb_active & MB_COMD)
3461 ok = imap_answer(mp, twice);
3462 if (response_status == RESPONSE_NO && twice++ == 0)
3463 goto trycreate;
3464 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3465 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3467 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3468 IMAP_OUT(o, MB_COMD, continue)
3469 while (mp->mb_active & MB_COMD)
3470 ok = imap_answer(mp, 1);
3471 if (ok == OKAY)
3472 gotcha++;
3473 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3474 if ((cp = strrchr(bp, '{')) == NULL)
3475 goto fail;
3476 octets = atol(&cp[1]) + 2;
3477 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3478 goto fail;
3479 IMAP_OUT(o, MB_COMD, continue)
3480 while (mp->mb_active & MB_COMD) {
3481 ok = imap_answer(mp, twice);
3482 if (response_type == RESPONSE_CONT)
3483 break;
3485 if (ok == STOP) {
3486 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3487 goto trycreate;
3488 goto fail;
3490 while (octets > 0) {
3491 size_t n = (UICMP(z, octets, >, sizeof iob)
3492 ? sizeof iob : (size_t)octets);
3493 octets -= n;
3494 if (n != fread(iob, 1, n, fp))
3495 goto fail;
3496 swrite1(&mp->mb_sock, iob, n, 1);
3498 swrite(&mp->mb_sock, "");
3499 while (mp->mb_active & MB_COMD) {
3500 ok = imap_answer(mp, 0);
3501 if (response_status == RESPONSE_NO && twice++ == 0) {
3502 if (fseek(fp, offs, SEEK_SET) < 0)
3503 goto fail;
3504 goto trycreate;
3507 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3508 if ((offs2 = ftell(fp)) < 0)
3509 goto fail;
3510 fseek(fp, offs1, SEEK_SET);
3511 if (imap_appenduid_cached(mp, fp) == STOP) {
3512 (void)fseek(fp, offs2, SEEK_SET);
3513 goto fail;
3516 } else {
3517 fail:
3518 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3519 rok = STOP;
3521 continue;
3522 trycreate:
3523 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3524 IMAP_OUT(o, MB_COMD, continue)
3525 while (mp->mb_active & MB_COMD)
3526 ok = imap_answer(mp, 1);
3527 if (ok == OKAY)
3528 goto again;
3530 fflush(fp);
3531 rewind(fp);
3532 ftruncate(fileno(fp), 0);
3533 if (gotcha)
3534 imap_close(mp);
3535 free(buf);
3536 return rok;
3539 static char *
3540 imap_strex(char const *cp, char const **xp)
3542 char const *cq;
3543 char *n = NULL;
3544 NYD_ENTER;
3546 if (*cp != '"')
3547 goto jleave;
3549 for (cq = cp + 1; *cq != '\0'; ++cq) {
3550 if (*cq == '\\')
3551 cq++;
3552 else if (*cq == '"')
3553 break;
3555 if (*cq != '"')
3556 goto jleave;
3558 n = salloc(cq - cp + 2);
3559 memcpy(n, cp, cq - cp +1);
3560 n[cq - cp + 1] = '\0';
3561 if (xp != NULL)
3562 *xp = cq + 1;
3563 jleave:
3564 NYD_LEAVE;
3565 return n;
3568 static enum okay
3569 check_expunged(void)
3571 enum okay rv;
3572 NYD_ENTER;
3574 if (expunged_messages > 0) {
3575 n_err(_("Command not executed - messages have been expunged\n"));
3576 rv = STOP;
3577 } else
3578 rv = OKAY;
3579 NYD_LEAVE;
3580 return rv;
3583 FL int
3584 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3586 struct url url;
3587 int rv, omsgCount = msgCount;
3588 NYD_ENTER;
3589 UNUSED(vp);
3591 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3592 n_err(_("Already connected\n"));
3593 rv = 1;
3594 goto jleave;
3597 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3598 rv = 1;
3599 goto jleave;
3601 ok_bclear(disconnected);
3602 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
3604 if (mb.mb_type == MB_CACHE) {
3605 enum fedit_mode fm = FEDIT_NONE;
3606 if (_imap_rdonly)
3607 fm |= FEDIT_RDONLY;
3608 if (!(pstate & PS_EDIT))
3609 fm |= FEDIT_SYSBOX;
3610 _imap_setfile1(&url, fm, 1);
3611 if (msgCount > omsgCount)
3612 newmailinfo(omsgCount);
3614 rv = 0;
3615 jleave:
3616 NYD_LEAVE;
3617 return rv;
3620 FL int
3621 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3623 struct url url;
3624 int rv = 1, *msgvec = vp;
3625 NYD_ENTER;
3627 if (mb.mb_type == MB_CACHE) {
3628 n_err(_("Not connected\n"));
3629 goto jleave;
3631 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3632 n_err(_("The current mailbox is not cached\n"));
3633 goto jleave;
3636 if (!url_parse(&url, CPROTO_IMAP, mailname))
3637 goto jleave;
3639 if (*msgvec)
3640 c_cache(vp);
3641 ok_bset(disconnected, TRU1);
3642 if (mb.mb_type == MB_IMAP) {
3643 enum fedit_mode fm = FEDIT_NONE;
3644 if (_imap_rdonly)
3645 fm |= FEDIT_RDONLY;
3646 if (!(pstate & PS_EDIT))
3647 fm |= FEDIT_SYSBOX;
3648 sclose(&mb.mb_sock);
3649 _imap_setfile1(&url, fm, 1);
3651 rv = 0;
3652 jleave:
3653 NYD_LEAVE;
3654 return rv;
3657 FL int
3658 c_cache(void *vp)
3660 int rv = 1, *msgvec = vp, *ip;
3661 struct message *mp;
3662 NYD_ENTER;
3664 if (mb.mb_type != MB_IMAP) {
3665 n_err(_("Not connected to an IMAP server\n"));
3666 goto jleave;
3668 if (cached_uidvalidity(&mb) == 0) {
3669 n_err(_("The current mailbox is not cached\n"));
3670 goto jleave;
3673 srelax_hold();
3674 for (ip = msgvec; *ip; ++ip) {
3675 mp = &message[*ip - 1];
3676 if (!(mp->m_have & HAVE_BODY)) {
3677 get_body(mp);
3678 srelax();
3681 srelax_rele();
3682 rv = 0;
3683 jleave:
3684 NYD_LEAVE;
3685 return rv;
3688 FL int
3689 disconnected(const char *file)
3691 struct url url;
3692 int rv = 1;
3693 NYD_ENTER;
3695 if (ok_blook(disconnected)) {
3696 rv = 1;
3697 goto jleave;
3700 if (!url_parse(&url, CPROTO_IMAP, file)) {
3701 rv = 0;
3702 goto jleave;
3704 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
3706 jleave:
3707 NYD_LEAVE;
3708 return rv;
3711 FL void
3712 transflags(struct message *omessage, long omsgCount, int transparent)
3714 struct message *omp, *nmp, *newdot, *newprevdot;
3715 int hf;
3716 NYD_ENTER;
3718 omp = omessage;
3719 nmp = message;
3720 newdot = message;
3721 newprevdot = NULL;
3722 while (PTRCMP(omp, <, omessage + omsgCount) &&
3723 PTRCMP(nmp, <, message + msgCount)) {
3724 if (dot && nmp->m_uid == dot->m_uid)
3725 newdot = nmp;
3726 if (prevdot && nmp->m_uid == prevdot->m_uid)
3727 newprevdot = nmp;
3728 if (omp->m_uid == nmp->m_uid) {
3729 hf = nmp->m_flag & MHIDDEN;
3730 if (transparent && mb.mb_type == MB_IMAP)
3731 omp->m_flag &= ~MHIDDEN;
3732 *nmp++ = *omp++;
3733 if (transparent && mb.mb_type == MB_CACHE)
3734 nmp[-1].m_flag |= hf;
3735 } else if (omp->m_uid < nmp->m_uid)
3736 ++omp;
3737 else
3738 ++nmp;
3740 dot = newdot;
3741 setdot(newdot);
3742 prevdot = newprevdot;
3743 free(omessage);
3744 NYD_LEAVE;
3747 FL time_t
3748 imap_read_date_time(const char *cp)
3750 char buf[3];
3751 time_t t;
3752 int i, year, month, day, hour, minute, second, sign = -1;
3753 NYD2_ENTER;
3755 /* "25-Jul-2004 15:33:44 +0200"
3756 * | | | | | |
3757 * 0 5 10 15 20 25 */
3758 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3759 goto jinvalid;
3760 day = strtol(&cp[1], NULL, 10);
3761 for (i = 0;;) {
3762 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3763 break;
3764 if (month_names[++i][0] == '\0')
3765 goto jinvalid;
3767 month = i + 1;
3768 year = strtol(&cp[8], NULL, 10);
3769 hour = strtol(&cp[13], NULL, 10);
3770 minute = strtol(&cp[16], NULL, 10);
3771 second = strtol(&cp[19], NULL, 10);
3772 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3773 goto jinvalid;
3774 switch (cp[22]) {
3775 case '-':
3776 sign = 1;
3777 break;
3778 case '+':
3779 break;
3780 default:
3781 goto jinvalid;
3783 buf[2] = '\0';
3784 buf[0] = cp[23];
3785 buf[1] = cp[24];
3786 t += strtol(buf, NULL, 10) * sign * 3600;
3787 buf[0] = cp[25];
3788 buf[1] = cp[26];
3789 t += strtol(buf, NULL, 10) * sign * 60;
3790 jleave:
3791 NYD2_LEAVE;
3792 return t;
3793 jinvalid:
3794 time(&t);
3795 goto jleave;
3798 FL const char *
3799 imap_make_date_time(time_t t)
3801 static char s[30];
3802 struct tm *tmptr;
3803 int tzdiff, tzdiff_hour, tzdiff_min;
3804 NYD2_ENTER;
3806 tzdiff = t - mktime(gmtime(&t));
3807 tzdiff_hour = (int)(tzdiff / 60);
3808 tzdiff_min = tzdiff_hour % 60;
3809 tzdiff_hour /= 60;
3810 tmptr = localtime(&t);
3811 if (tmptr->tm_isdst > 0)
3812 tzdiff_hour++;
3813 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3814 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3815 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3816 NYD2_LEAVE;
3817 return s;
3819 #endif /* HAVE_IMAP */
3821 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3822 FL char *
3823 imap_quotestr(char const *s)
3825 char *n, *np;
3826 NYD2_ENTER;
3828 np = n = salloc(2 * strlen(s) + 3);
3829 *np++ = '"';
3830 while (*s) {
3831 if (*s == '"' || *s == '\\')
3832 *np++ = '\\';
3833 *np++ = *s++;
3835 *np++ = '"';
3836 *np = '\0';
3837 NYD2_LEAVE;
3838 return n;
3841 FL char *
3842 imap_unquotestr(char const *s)
3844 char *n, *np;
3845 NYD2_ENTER;
3847 if (*s != '"') {
3848 n = savestr(s);
3849 goto jleave;
3852 np = n = salloc(strlen(s) + 1);
3853 while (*++s) {
3854 if (*s == '\\')
3855 s++;
3856 else if (*s == '"')
3857 break;
3858 *np++ = *s;
3860 *np = '\0';
3861 jleave:
3862 NYD2_LEAVE;
3863 return n;
3865 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3867 /* s-it-mode */