Do not use non-compiler-builtin alloca(3)s..
[s-mailx.git] / imap.c
blob5bbdf728456decbecfd2a11980f8ea0a755ce099
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "config.h"
43 * Mail -- a mail program
45 * IMAP v4r1 client following RFC 2060.
48 #include "rcv.h"
49 #include <errno.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <time.h>
54 #ifdef USE_IMAP
55 # ifdef USE_MD5
56 # include "md5.h"
57 # endif
59 #include <sys/socket.h>
60 #include <netdb.h>
61 #include <netinet/in.h>
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
64 #endif /* HAVE_ARPA_INET_H */
66 #include "extern.h"
68 static int verbose;
70 #define IMAP_ANSWER() { \
71 if (mp->mb_type != MB_CACHE) { \
72 enum okay ok = OKAY; \
73 while (mp->mb_active & MB_COMD) \
74 ok = imap_answer(mp, 1); \
75 if (ok == STOP) \
76 return STOP; \
77 } \
79 #define IMAP_OUT(x, y, action) \
80 { \
81 if (mp->mb_type != MB_CACHE) { \
82 if (imap_finish(mp) == STOP) \
83 return STOP; \
84 if (verbose) \
85 fprintf(stderr, ">>> %s", x); \
86 mp->mb_active |= (y); \
87 if (swrite(&mp->mb_sock, x) == STOP) \
88 action; \
89 } else { \
90 if (queuefp != NULL) \
91 fputs(x, queuefp); \
92 } \
95 static struct record {
96 struct record *rec_next;
97 unsigned long rec_count;
98 enum rec_type {
99 REC_EXISTS,
100 REC_EXPUNGE
101 } rec_type;
102 } *record, *recend;
104 static enum {
105 RESPONSE_TAGGED,
106 RESPONSE_DATA,
107 RESPONSE_FATAL,
108 RESPONSE_CONT,
109 RESPONSE_ILLEGAL
110 } response_type;
112 static enum {
113 RESPONSE_OK,
114 RESPONSE_NO,
115 RESPONSE_BAD,
116 RESPONSE_PREAUTH,
117 RESPONSE_BYE,
118 RESPONSE_OTHER,
119 RESPONSE_UNKNOWN
120 } response_status;
122 static char *responded_tag;
123 static char *responded_text;
124 static char *responded_other_text;
125 static long responded_other_number;
127 static enum {
128 MAILBOX_DATA_FLAGS,
129 MAILBOX_DATA_LIST,
130 MAILBOX_DATA_LSUB,
131 MAILBOX_DATA_MAILBOX,
132 MAILBOX_DATA_SEARCH,
133 MAILBOX_DATA_STATUS,
134 MAILBOX_DATA_EXISTS,
135 MAILBOX_DATA_RECENT,
136 MESSAGE_DATA_EXPUNGE,
137 MESSAGE_DATA_FETCH,
138 CAPABILITY_DATA,
139 RESPONSE_OTHER_UNKNOWN
140 } response_other;
142 static enum list_attributes {
143 LIST_NONE = 000,
144 LIST_NOINFERIORS = 001,
145 LIST_NOSELECT = 002,
146 LIST_MARKED = 004,
147 LIST_UNMARKED = 010
148 } list_attributes;
150 static int list_hierarchy_delimiter;
151 static char *list_name;
153 struct list_item {
154 struct list_item *l_next;
155 char *l_name;
156 char *l_base;
157 enum list_attributes l_attr;
158 int l_delim;
159 int l_level;
160 int l_has_children;
163 static char *imapbuf;
164 static size_t imapbufsize;
165 static sigjmp_buf imapjmp;
166 static sighandler_type savealrm;
167 static int reset_tio;
168 static struct termios otio;
169 static int imapkeepalive;
170 static long had_exists = -1;
171 static long had_expunge = -1;
172 static long expunged_messages;
173 static volatile int imaplock;
175 static int same_imap_account;
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 maincatch(int s);
186 static enum okay imap_noop1(struct mailbox *mp);
187 static void rec_queue(enum rec_type type, unsigned long count);
188 static enum okay rec_dequeue(void);
189 static void rec_rmqueue(void);
190 static void imapalarm(int s);
191 static int imap_use_starttls(const char *uhp);
192 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
193 const char *uhp);
194 static enum okay imap_capability(struct mailbox *mp);
195 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
196 char *xuser, const char *pass);
197 #ifdef USE_MD5
198 static enum okay imap_cram_md5(struct mailbox *mp,
199 char *xuser, const char *xpass);
200 #endif
201 static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass);
202 #ifdef USE_GSSAPI
203 static enum okay imap_gss(struct mailbox *mp, char *user);
204 #endif /* USE_GSSAPI */
205 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
206 static void imap_init(struct mailbox *mp, int n);
207 static void imap_setptr(struct mailbox *mp, int newmail, int transparent,
208 int *prevcount);
209 static char *imap_have_password(const char *server);
210 static void imap_split(char **server, const char **sp, int *use_ssl,
211 const char **cp, char **uhp, char **mbx,
212 const char **pass, char **user);
213 static int imap_setfile1(const char *xserver, int newmail, int isedit,
214 int transparent);
215 static int imap_fetchdata(struct mailbox *mp, struct message *m,
216 size_t expected, int need,
217 const char *head, size_t headsize, long headlines);
218 static void imap_putstr(struct mailbox *mp, struct message *m,
219 const char *str,
220 const char *head, size_t headsize, long headlines);
221 static enum okay imap_get(struct mailbox *mp, struct message *m,
222 enum needspec need);
223 static void commitmsg(struct mailbox *mp, struct message *to,
224 struct message from, enum havespec have);
225 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
226 int bot, int top);
227 static enum okay imap_exit(struct mailbox *mp);
228 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
229 needstat);
230 static enum okay imap_close(struct mailbox *mp);
231 static enum okay imap_update(struct mailbox *mp);
232 static enum okay imap_store(struct mailbox *mp, struct message *m,
233 int n, int c, const char *sp, int needstat);
234 static enum okay imap_unstore(struct message *m, int n, const char *flag);
235 static const char *tag(int new);
236 static char *imap_putflags(int f);
237 static void imap_getflags(const char *cp, char **xp, enum mflag *f);
238 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
239 off_t off1, long xsize, enum mflag flag, time_t t);
240 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend, int level);
243 static enum okay imap_list(struct mailbox *mp, const char *base,
244 int strip, FILE *fp);
245 static void dopr(FILE *fp);
246 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
247 const char *name);
248 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
249 unsigned long *olduid, unsigned long *newuid);
250 static enum okay imap_appenduid_parse(const char *cp,
251 unsigned long *uidvalidity, unsigned long *uid);
252 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
253 const char *name);
254 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
255 long off1, long xsize, long size, long lines,
256 int flag, const char *name);
257 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
258 static enum okay imap_search2(struct mailbox *mp, struct message *m,
259 int count, const char *spec, int f);
260 static enum okay imap_remove1(struct mailbox *mp, const char *name);
261 static enum okay imap_rename1(struct mailbox *mp, const char *old,
262 const char *new);
263 static char *imap_strex(const char *cp, char **xp);
264 static enum okay check_expunged(void);
266 static void
267 imap_other_get(char *pp)
269 char *xp;
271 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
272 pp += 6;
273 response_other = MAILBOX_DATA_FLAGS;
274 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
275 pp += 5;
276 response_other = MAILBOX_DATA_LIST;
277 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
278 pp += 5;
279 response_other = MAILBOX_DATA_LSUB;
280 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
281 pp += 8;
282 response_other = MAILBOX_DATA_MAILBOX;
283 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
284 pp += 7;
285 response_other = MAILBOX_DATA_SEARCH;
286 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
287 pp += 7;
288 response_other = MAILBOX_DATA_STATUS;
289 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
290 pp += 11;
291 response_other = CAPABILITY_DATA;
292 } else {
293 responded_other_number = strtol(pp, &xp, 10);
294 while (*xp == ' ')
295 xp++;
296 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
297 response_other = MAILBOX_DATA_EXISTS;
298 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
299 response_other = MAILBOX_DATA_RECENT;
300 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
301 response_other = MESSAGE_DATA_EXPUNGE;
302 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
303 pp = &xp[6];
304 response_other = MESSAGE_DATA_FETCH;
305 } else
306 response_other = RESPONSE_OTHER_UNKNOWN;
308 responded_other_text = pp;
311 static void
312 imap_response_get(const char **cp)
314 if (ascncasecmp(*cp, "OK ", 3) == 0) {
315 *cp += 3;
316 response_status = RESPONSE_OK;
317 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
318 *cp += 3;
319 response_status = RESPONSE_NO;
320 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
321 *cp += 4;
322 response_status = RESPONSE_BAD;
323 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
324 *cp += 8;
325 response_status = RESPONSE_PREAUTH;
326 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
327 *cp += 4;
328 response_status = RESPONSE_BYE;
329 } else
330 response_status = RESPONSE_OTHER;
333 static void
334 imap_response_parse(void)
336 static char *parsebuf;
337 static size_t parsebufsize;
338 const char *ip = imapbuf;
339 char *pp;
341 if (parsebufsize < imapbufsize) {
342 free(parsebuf);
343 parsebuf = smalloc(parsebufsize = imapbufsize);
345 strcpy(parsebuf, imapbuf);
346 pp = parsebuf;
347 switch (*ip) {
348 case '+':
349 response_type = RESPONSE_CONT;
350 ip++;
351 pp++;
352 while (*ip == ' ') {
353 ip++;
354 pp++;
356 break;
357 case '*':
358 ip++;
359 pp++;
360 while (*ip == ' ') {
361 ip++;
362 pp++;
364 imap_response_get(&ip);
365 pp = &parsebuf[ip - imapbuf];
366 switch (response_status) {
367 case RESPONSE_BYE:
368 response_type = RESPONSE_FATAL;
369 break;
370 default:
371 response_type = RESPONSE_DATA;
373 break;
374 default:
375 responded_tag = parsebuf;
376 while (*pp && *pp != ' ')
377 pp++;
378 if (*pp == '\0') {
379 response_type = RESPONSE_ILLEGAL;
380 break;
382 *pp++ = '\0';
383 while (*pp && *pp == ' ')
384 pp++;
385 if (*pp == '\0') {
386 response_type = RESPONSE_ILLEGAL;
387 break;
389 ip = &imapbuf[pp - parsebuf];
390 response_type = RESPONSE_TAGGED;
391 imap_response_get(&ip);
392 pp = &parsebuf[ip - imapbuf];
394 responded_text = pp;
395 if (response_type != RESPONSE_CONT &&
396 response_type != RESPONSE_ILLEGAL &&
397 response_status == RESPONSE_OTHER)
398 imap_other_get(pp);
401 static enum okay
402 imap_answer(struct mailbox *mp, int errprnt)
404 int i, complete;
405 enum okay ok = STOP;
407 if (mp->mb_type == MB_CACHE)
408 return OKAY;
409 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
410 if (verbose)
411 fputs(imapbuf, stderr);
412 imap_response_parse();
413 if (response_type == RESPONSE_ILLEGAL)
414 goto again;
415 if (response_type == RESPONSE_CONT)
416 return OKAY;
417 if (response_status == RESPONSE_OTHER) {
418 if (response_other == MAILBOX_DATA_EXISTS) {
419 had_exists = responded_other_number;
420 rec_queue(REC_EXISTS, responded_other_number);
421 if (had_expunge > 0)
422 had_expunge = 0;
423 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
424 rec_queue(REC_EXPUNGE, responded_other_number);
425 if (had_expunge < 0)
426 had_expunge = 0;
427 had_expunge++;
428 expunged_messages++;
431 complete = 0;
432 if (response_type == RESPONSE_TAGGED) {
433 if (asccasecmp(responded_tag, tag(0)) == 0)
434 complete |= 1;
435 else
436 goto again;
438 switch (response_status) {
439 case RESPONSE_PREAUTH:
440 mp->mb_active &= ~MB_PREAUTH;
441 /*FALLTHRU*/
442 case RESPONSE_OK:
443 okay: ok = OKAY;
444 complete |= 2;
445 break;
446 case RESPONSE_NO:
447 case RESPONSE_BAD:
448 stop: ok = STOP;
449 complete |= 2;
450 if (errprnt)
451 fprintf(stderr, catgets(catd, CATSET, 218,
452 "IMAP error: %s"), responded_text);
453 break;
454 case RESPONSE_UNKNOWN: /* does not happen */
455 case RESPONSE_BYE:
456 i = mp->mb_active;
457 mp->mb_active = MB_NONE;
458 if (i & MB_BYE)
459 goto okay;
460 else
461 goto stop;
462 case RESPONSE_OTHER:
463 ok = OKAY;
465 if (response_status != RESPONSE_OTHER &&
466 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
467 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
468 if (complete == 3)
469 mp->mb_active &= ~MB_COMD;
470 } else {
471 ok = STOP;
472 mp->mb_active = MB_NONE;
474 return ok;
477 static enum okay
478 imap_parse_list(void)
480 char *cp;
482 cp = responded_other_text;
483 list_attributes = LIST_NONE;
484 if (*cp == '(') {
485 while (*cp && *cp != ')') {
486 if (*cp == '\\') {
487 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
488 == 0) {
489 list_attributes |= LIST_NOINFERIORS;
490 cp += 12;
491 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
492 == 0) {
493 list_attributes |= LIST_NOSELECT;
494 cp += 9;
495 } else if (ascncasecmp(&cp[1], "Marked ", 7)
496 == 0) {
497 list_attributes |= LIST_MARKED;
498 cp += 7;
499 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
500 == 0) {
501 list_attributes |= LIST_UNMARKED;
502 cp += 9;
505 cp++;
507 if (*++cp != ' ')
508 return STOP;
509 while (*cp == ' ')
510 cp++;
512 list_hierarchy_delimiter = EOF;
513 if (*cp == '"') {
514 if (*++cp == '\\')
515 cp++;
516 list_hierarchy_delimiter = *cp++ & 0377;
517 if (cp[0] != '"' || cp[1] != ' ')
518 return STOP;
519 cp++;
520 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
521 cp[3] == ' ') {
522 list_hierarchy_delimiter = EOF;
523 cp += 3;
525 while (*cp == ' ')
526 cp++;
527 list_name = cp;
528 while (*cp && *cp != '\r')
529 cp++;
530 *cp = '\0';
531 return OKAY;
534 static enum okay
535 imap_finish(struct mailbox *mp)
537 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
538 imap_answer(mp, 1);
539 return OKAY;
542 static void
543 imap_timer_off(void)
545 if (imapkeepalive > 0) {
546 alarm(0);
547 safe_signal(SIGALRM, savealrm);
551 static void
552 imapcatch(int s)
554 if (reset_tio)
555 tcsetattr(0, TCSADRAIN, &otio);
556 switch (s) {
557 case SIGINT:
558 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
559 siglongjmp(imapjmp, 1);
560 /*NOTREACHED*/
561 case SIGPIPE:
562 fprintf(stderr, "Received SIGPIPE during IMAP operation\n");
563 break;
567 static void
568 maincatch(int s)
570 (void)s;
571 if (interrupts++ == 0) {
572 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
573 return;
575 onintr(0);
578 static enum okay
579 imap_noop1(struct mailbox *mp)
581 char o[LINESIZE];
582 FILE *queuefp = NULL;
584 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
585 IMAP_OUT(o, MB_COMD, return STOP)
586 IMAP_ANSWER()
587 return OKAY;
590 enum okay
591 imap_noop(void)
593 sighandler_type saveint, savepipe;
594 enum okay ok = STOP;
596 (void)&saveint;
597 (void)&savepipe;
598 (void)&ok;
599 if (mb.mb_type != MB_IMAP)
600 return STOP;
601 verbose = value("verbose") != NULL;
602 imaplock = 1;
603 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
604 safe_signal(SIGINT, maincatch);
605 savepipe = safe_signal(SIGPIPE, SIG_IGN);
606 if (sigsetjmp(imapjmp, 1) == 0) {
607 if (savepipe != SIG_IGN)
608 safe_signal(SIGPIPE, imapcatch);
609 ok = imap_noop1(&mb);
611 safe_signal(SIGINT, saveint);
612 safe_signal(SIGPIPE, savepipe);
613 imaplock = 0;
614 if (interrupts)
615 onintr(0);
616 return ok;
619 static void
620 rec_queue(enum rec_type type, unsigned long count)
622 struct record *rp;
624 rp = scalloc(1, sizeof *rp);
625 rp->rec_type = type;
626 rp->rec_count = count;
627 if (record && recend) {
628 recend->rec_next = rp;
629 recend = rp;
630 } else
631 record = recend = rp;
634 static enum okay
635 rec_dequeue(void)
637 struct message *omessage;
638 enum okay ok = OKAY;
639 struct record *rp = record, *rq = NULL;
640 unsigned long exists = 0, i;
642 if (record == NULL)
643 return STOP;
644 omessage = message;
645 message = smalloc((msgCount+1) * sizeof *message);
646 if (msgCount)
647 memcpy(message, omessage, msgCount * sizeof *message);
648 memset(&message[msgCount], 0, sizeof *message);
649 while (rp) {
650 switch (rp->rec_type) {
651 case REC_EXISTS:
652 exists = rp->rec_count;
653 break;
654 case REC_EXPUNGE:
655 if (rp->rec_count == 0) {
656 ok = STOP;
657 break;
659 if (rp->rec_count > (unsigned long)msgCount) {
660 if (exists == 0 || rp->rec_count > exists--)
661 ok = STOP;
662 break;
664 if (exists > 0)
665 exists--;
666 delcache(&mb, &message[rp->rec_count-1]);
667 memmove(&message[rp->rec_count-1],
668 &message[rp->rec_count],
669 (msgCount - rp->rec_count + 1) *
670 sizeof *message);
671 msgCount--;
673 * If the message was part of a collapsed thread,
674 * the m_collapsed field of one of its ancestors
675 * should be incremented. It seems hardly possible
676 * to do this with the current message structure,
677 * though. The result is that a '+' may be shown
678 * in the header summary even if no collapsed
679 * children exists.
681 break;
683 free(rq);
684 rq = rp;
685 rp = rp->rec_next;
687 free(rq);
688 record = recend = NULL;
689 if (ok == OKAY && exists > (unsigned long)msgCount) {
690 message = srealloc(message,
691 (exists + 1) * sizeof *message);
692 memset(&message[msgCount], 0,
693 (exists - msgCount + 1) * sizeof *message);
694 for (i = msgCount; i < exists; i++)
695 imap_init(&mb, i);
696 imap_flags(&mb, msgCount+1, exists);
697 msgCount = exists;
699 if (ok == STOP) {
700 free(message);
701 message = omessage;
703 return ok;
706 static void
707 rec_rmqueue(void)
709 struct record *rp, *rq = NULL;
711 for (rp = record; rp; rp = rp->rec_next) {
712 free(rq);
713 rq = rp;
715 free(rq);
716 record = recend = NULL;
719 /*ARGSUSED*/
720 static void
721 imapalarm(int s)
723 sighandler_type saveint;
724 sighandler_type savepipe;
725 (void)s;
727 if (imaplock++ == 0) {
728 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
729 safe_signal(SIGINT, maincatch);
730 savepipe = safe_signal(SIGPIPE, SIG_IGN);
731 if (sigsetjmp(imapjmp, 1)) {
732 safe_signal(SIGINT, saveint);
733 safe_signal(SIGPIPE, savepipe);
734 goto brk;
736 if (savepipe != SIG_IGN)
737 safe_signal(SIGPIPE, imapcatch);
738 if (imap_noop1(&mb) != OKAY) {
739 safe_signal(SIGINT, saveint);
740 safe_signal(SIGPIPE, savepipe);
741 goto out;
743 safe_signal(SIGINT, saveint);
744 safe_signal(SIGPIPE, savepipe);
746 brk: alarm(imapkeepalive);
747 out: imaplock--;
750 static int
751 imap_use_starttls(const char *uhp)
753 char *var;
755 if (value("imap-use-starttls"))
756 return 1;
757 var = savecat("imap-use-starttls-", uhp);
758 return value(var) != NULL;
761 static enum okay
762 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
764 char *server, *cp;
766 mp->mb_active |= MB_PREAUTH;
767 imap_answer(mp, 1);
768 if ((cp = strchr(xserver, ':')) != NULL) {
769 server = salloc(cp - xserver + 1);
770 memcpy(server, xserver, cp - xserver);
771 server[cp - xserver] = '\0';
772 } else
773 server = (char *)xserver;
774 #ifdef USE_SSL
775 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
776 FILE *queuefp = NULL;
777 char o[LINESIZE];
779 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
780 IMAP_OUT(o, MB_COMD, return STOP);
781 IMAP_ANSWER()
782 if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
783 return STOP;
785 #else /* !USE_SSL */
786 if (imap_use_starttls(uhp)) {
787 fprintf(stderr, "No SSL support compiled in.\n");
788 return STOP;
790 #endif /* !USE_SSL */
791 imap_capability(mp);
792 return OKAY;
795 static enum okay
796 imap_capability(struct mailbox *mp)
798 char o[LINESIZE];
799 FILE *queuefp = NULL;
800 enum okay ok = STOP;
801 const char *cp;
803 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
804 IMAP_OUT(o, MB_COMD, return STOP)
805 while (mp->mb_active & MB_COMD) {
806 ok = imap_answer(mp, 0);
807 if (response_status == RESPONSE_OTHER &&
808 response_other == CAPABILITY_DATA) {
809 cp = responded_other_text;
810 while (*cp) {
811 while (spacechar(*cp&0377))
812 cp++;
813 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
814 spacechar(cp[7]&0377))
815 /* RFC 2359 */
816 mp->mb_flags |= MB_UIDPLUS;
817 while (*cp && !spacechar(*cp&0377))
818 cp++;
822 return ok;
825 static enum okay
826 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass)
828 char *var;
829 char *auth;
831 if (!(mp->mb_active & MB_PREAUTH))
832 return OKAY;
833 if ((auth = value("imap-auth")) == NULL) {
834 var = ac_alloc(strlen(uhp) + 11);
835 strcpy(var, "imap-auth-");
836 strcpy(&var[10], uhp);
837 auth = value(var);
838 ac_free(var);
840 if (auth == NULL || strcmp(auth, "login") == 0)
841 return imap_login(mp, xuser, pass);
842 if (strcmp(auth, "cram-md5") == 0) {
843 #ifdef USE_MD5
844 return imap_cram_md5(mp, xuser, pass);
845 #else
846 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
847 return (STOP);
848 #endif
850 if (strcmp(auth, "gssapi") == 0) {
851 #ifdef USE_GSSAPI
852 return imap_gss(mp, xuser);
853 #else /* !USE_GSSAPI */
854 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
855 return STOP;
856 #endif /* !USE_GSSAPI */
858 fprintf(stderr, tr(273, "Unknown IMAP authentication method: %s\n"),
859 auth);
860 return STOP;
864 * Implementation of RFC 2194.
866 #ifdef USE_MD5
867 static enum okay
868 imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
870 char o[LINESIZE];
871 const char *user, *pass;
872 char *cp;
873 FILE *queuefp = NULL;
874 enum okay ok = STOP;
876 retry: if (xuser == NULL) {
877 if ((user = getuser()) == NULL)
878 return STOP;
879 } else
880 user = xuser;
881 if (xpass == NULL) {
882 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
883 return STOP;
884 } else
885 pass = xpass;
886 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
887 IMAP_OUT(o, 0, return STOP)
888 imap_answer(mp, 1);
889 if (response_type != RESPONSE_CONT)
890 return STOP;
891 cp = cram_md5_string(user, pass, responded_text);
892 IMAP_OUT(cp, MB_COMD, return STOP)
893 while (mp->mb_active & MB_COMD)
894 ok = imap_answer(mp, 1);
895 if (ok == STOP) {
896 xpass = NULL;
897 goto retry;
899 return ok;
901 #endif /* USE_MD5 */
903 static enum okay
904 imap_login(struct mailbox *mp, char *xuser, const char *xpass)
906 char o[LINESIZE];
907 const char *user, *pass;
908 FILE *queuefp = NULL;
909 enum okay ok = STOP;
911 retry: if (xuser == NULL) {
912 if ((user = getuser()) == NULL)
913 return STOP;
914 } else
915 user = xuser;
916 if (xpass == NULL) {
917 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
918 return STOP;
919 } else
920 pass = xpass;
921 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
922 tag(1), imap_quotestr(user), imap_quotestr(pass));
923 IMAP_OUT(o, MB_COMD, return STOP)
924 while (mp->mb_active & MB_COMD)
925 ok = imap_answer(mp, 1);
926 if (ok == STOP) {
927 xpass = NULL;
928 goto retry;
930 return OKAY;
933 #ifdef USE_GSSAPI
934 #include "imap_gssapi.c"
935 #endif /* USE_GSSAPI */
937 enum okay
938 imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
940 enum okay ok = OKAY;
941 char *cp;
942 char o[LINESIZE];
943 FILE *queuefp = NULL;
944 (void)size;
946 mp->mb_uidvalidity = 0;
947 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
948 IMAP_OUT(o, MB_COMD, return STOP)
949 while (mp->mb_active & MB_COMD) {
950 ok = imap_answer(mp, 1);
951 if (response_status != RESPONSE_OTHER &&
952 (cp = asccasestr(responded_text,
953 "[UIDVALIDITY ")) != NULL)
954 mp->mb_uidvalidity = atol(&cp[13]);
956 *count = had_exists > 0 ? had_exists : 0;
957 if (response_status != RESPONSE_OTHER &&
958 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
959 == 0)
960 mp->mb_perm = 0;
961 return ok;
964 static enum okay
965 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
967 char o[LINESIZE];
968 FILE *queuefp = NULL;
969 char *cp;
970 struct message *m;
971 unsigned x = X, y = Y, n;
973 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
974 IMAP_OUT(o, MB_COMD, return STOP)
975 while (mp->mb_active & MB_COMD) {
976 imap_answer(mp, 1);
977 if (response_status == RESPONSE_OTHER &&
978 response_other == MESSAGE_DATA_FETCH) {
979 n = responded_other_number;
980 if (n < x || n > y)
981 continue;
982 m = &message[n-1];
983 m->m_xsize = 0;
984 } else
985 continue;
986 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
987 cp += 5;
988 while (*cp == ' ')
989 cp++;
990 if (*cp == '(')
991 imap_getflags(cp, &cp, &m->m_flag);
993 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
994 m->m_uid = strtoul(&cp[4], NULL, 10);
995 getcache1(mp, m, NEED_UNSPEC, 1);
996 m->m_flag &= ~MHIDDEN;
998 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
999 x++;
1000 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1001 y--;
1002 if (x <= y) {
1003 snprintf(o, sizeof o,
1004 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1005 tag(1), x, y);
1006 IMAP_OUT(o, MB_COMD, return STOP)
1007 while (mp->mb_active & MB_COMD) {
1008 imap_answer(mp, 1);
1009 if (response_status == RESPONSE_OTHER &&
1010 response_other == MESSAGE_DATA_FETCH) {
1011 n = responded_other_number;
1012 if (n < x || n > y)
1013 continue;
1014 m = &message[n-1];
1015 } else
1016 continue;
1017 if ((cp = asccasestr(responded_other_text,
1018 "RFC822.SIZE ")) != NULL)
1019 m->m_xsize = strtol(&cp[12], NULL, 10);
1020 if ((cp = asccasestr(responded_other_text,
1021 "INTERNALDATE ")) != NULL)
1022 m->m_time = imap_read_date_time(&cp[13]);
1025 for (n = X; n <= Y; n++)
1026 putcache(mp, &message[n-1]);
1027 return OKAY;
1030 static void
1031 imap_init(struct mailbox *mp, int n)
1033 struct message *m = &message[n];
1034 (void)mp;
1036 m->m_flag = MUSED|MNOFROM;
1037 m->m_block = 0;
1038 m->m_offset = 0;
1041 static void
1042 imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
1044 struct message *omessage = 0;
1045 int i, omsgCount = 0;
1046 enum okay dequeued = STOP;
1048 if (newmail || transparent) {
1049 omessage = message;
1050 omsgCount = msgCount;
1052 if (newmail)
1053 dequeued = rec_dequeue();
1054 if (had_exists >= 0) {
1055 if (dequeued != OKAY)
1056 msgCount = had_exists;
1057 had_exists = -1;
1059 if (had_expunge >= 0) {
1060 if (dequeued != OKAY)
1061 msgCount -= had_expunge;
1062 had_expunge = -1;
1064 if (newmail && expunged_messages)
1065 printf("Expunged %ld message%s.\n",
1066 expunged_messages,
1067 expunged_messages != 1 ? "s" : "");
1068 *prevcount = omsgCount - expunged_messages;
1069 expunged_messages = 0;
1070 if (msgCount < 0) {
1071 fputs("IMAP error: Negative message count\n", stderr);
1072 msgCount = 0;
1074 if (dequeued != OKAY) {
1075 message = scalloc(msgCount + 1, sizeof *message);
1076 for (i = 0; i < msgCount; i++)
1077 imap_init(mp, i);
1078 if (!newmail && mp->mb_type == MB_IMAP)
1079 initcache(mp);
1080 if (msgCount > 0)
1081 imap_flags(mp, 1, msgCount);
1082 message[msgCount].m_size = 0;
1083 message[msgCount].m_lines = 0;
1084 rec_rmqueue();
1086 if (newmail || transparent)
1087 transflags(omessage, omsgCount, transparent);
1088 else
1089 setdot(message);
1092 static char *
1093 imap_have_password(const char *server)
1095 char *var, *cp;
1097 var = ac_alloc(strlen(server) + 10);
1098 strcpy(var, "password-");
1099 strcpy(&var[9], server);
1100 if ((cp = value(var)) != NULL)
1101 cp = savestr(cp);
1102 ac_free(var);
1103 return cp;
1106 static void
1107 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1108 char **uhp, char **mbx, const char **pass, char **user)
1110 *sp = *server;
1111 if (strncmp(*sp, "imap://", 7) == 0) {
1112 *sp = &(*sp)[7];
1113 *use_ssl = 0;
1114 #ifdef USE_SSL
1115 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1116 *sp = &(*sp)[8];
1117 *use_ssl = 1;
1118 #endif /* USE_SSL */
1120 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1121 *uhp = savestr((char *)(*sp));
1122 (*uhp)[*cp - *sp] = '\0';
1123 *mbx = (char *)&(*cp)[1];
1124 } else {
1125 if (*cp)
1126 (*server)[*cp - *server] = '\0';
1127 *uhp = (char *)(*sp);
1128 *mbx = "INBOX";
1130 *pass = imap_have_password(*uhp);
1131 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1132 *user = salloc(*cp - *uhp + 1);
1133 memcpy(*user, *uhp, *cp - *uhp);
1134 (*user)[*cp - *uhp] = '\0';
1135 *sp = &(*cp)[1];
1136 *user = strdec(*user);
1137 } else {
1138 *user = NULL;
1139 *sp = *uhp;
1143 int
1144 imap_setfile(const char *xserver, int newmail, int isedit)
1146 return imap_setfile1(xserver, newmail, isedit, 0);
1149 static int
1150 imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
1152 struct sock so;
1153 sighandler_type volatile saveint, savepipe;
1154 char *server, *user, *account;
1155 const char *cp, *sp, *pass;
1156 char *uhp, *mbx;
1157 int use_ssl = 0;
1158 enum mbflags same_flags;
1159 int prevcount = 0;
1161 (void)&sp;
1162 (void)&use_ssl;
1163 (void)&saveint;
1164 (void)&savepipe;
1165 server = savestr((char *)xserver);
1166 verbose = value("verbose") != NULL;
1167 if (newmail) {
1168 saveint = safe_signal(SIGINT, SIG_IGN);
1169 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1170 if (saveint != SIG_IGN)
1171 safe_signal(SIGINT, imapcatch);
1172 if (savepipe != SIG_IGN)
1173 safe_signal(SIGPIPE, imapcatch);
1174 imaplock = 1;
1175 goto newmail;
1177 same_flags = mb.mb_flags;
1178 same_imap_account = 0;
1179 sp = protbase(server);
1180 if (mb.mb_imap_account) {
1181 if (mb.mb_sock.s_fd > 0 &&
1182 strcmp(mb.mb_imap_account, sp) == 0 &&
1183 disconnected(mb.mb_imap_account) == 0)
1184 same_imap_account = 1;
1186 account = sstrdup(sp);
1187 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1188 so.s_fd = -1;
1189 if (!same_imap_account) {
1190 if (!disconnected(account) &&
1191 sopen(sp, &so, use_ssl, uhp,
1192 use_ssl ? "imaps" : "imap", verbose) != OKAY)
1193 return -1;
1194 } else
1195 so = mb.mb_sock;
1196 if (!transparent)
1197 quit();
1198 edit = isedit;
1199 free(mb.mb_imap_account);
1200 mb.mb_imap_account = account;
1201 if (!same_imap_account) {
1202 if (mb.mb_sock.s_fd >= 0)
1203 sclose(&mb.mb_sock);
1205 same_imap_account = 0;
1206 if (!transparent) {
1207 if (mb.mb_itf) {
1208 fclose(mb.mb_itf);
1209 mb.mb_itf = NULL;
1211 if (mb.mb_otf) {
1212 fclose(mb.mb_otf);
1213 mb.mb_otf = NULL;
1215 free(mb.mb_imap_mailbox);
1216 mb.mb_imap_mailbox = sstrdup(mbx);
1217 initbox(server);
1219 mb.mb_type = MB_VOID;
1220 mb.mb_active = MB_NONE;;
1221 imaplock = 1;
1222 saveint = safe_signal(SIGINT, SIG_IGN);
1223 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1224 if (sigsetjmp(imapjmp, 1)) {
1225 sclose(&so);
1226 safe_signal(SIGINT, saveint);
1227 safe_signal(SIGPIPE, savepipe);
1228 imaplock = 0;
1229 return -1;
1231 if (saveint != SIG_IGN)
1232 safe_signal(SIGINT, imapcatch);
1233 if (savepipe != SIG_IGN)
1234 safe_signal(SIGPIPE, imapcatch);
1235 if (mb.mb_sock.s_fd < 0) {
1236 if (disconnected(mb.mb_imap_account)) {
1237 if (cache_setptr(transparent) == STOP)
1238 fprintf(stderr,
1239 "Mailbox \"%s\" is not cached.\n",
1240 server);
1241 goto done;
1243 if ((cp = value("imap-keepalive")) != NULL) {
1244 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1245 savealrm = safe_signal(SIGALRM, imapalarm);
1246 alarm(imapkeepalive);
1249 mb.mb_sock = so;
1250 mb.mb_sock.s_desc = "IMAP";
1251 mb.mb_sock.s_onclose = imap_timer_off;
1252 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1253 imap_auth(&mb, uhp, user, pass) != OKAY) {
1254 sclose(&mb.mb_sock);
1255 imap_timer_off();
1256 safe_signal(SIGINT, saveint);
1257 safe_signal(SIGPIPE, savepipe);
1258 imaplock = 0;
1259 return -1;
1261 } else /* same account */
1262 mb.mb_flags |= same_flags;
1263 mb.mb_perm = Rflag ? 0 : MB_DELE;
1264 mb.mb_type = MB_IMAP;
1265 cache_dequeue(&mb);
1266 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1267 /*sclose(&mb.mb_sock);
1268 imap_timer_off();*/
1269 safe_signal(SIGINT, saveint);
1270 safe_signal(SIGPIPE, savepipe);
1271 imaplock = 0;
1272 mb.mb_type = MB_VOID;
1273 return -1;
1275 newmail:
1276 imap_setptr(&mb, newmail, transparent, &prevcount);
1277 done: setmsize(msgCount);
1278 if (!newmail && !transparent)
1279 sawcom = 0;
1280 safe_signal(SIGINT, saveint);
1281 safe_signal(SIGPIPE, savepipe);
1282 imaplock = 0;
1283 if (!newmail && mb.mb_type == MB_IMAP)
1284 purgecache(&mb, message, msgCount);
1285 if ((newmail || transparent) && mb.mb_sorted) {
1286 mb.mb_threaded = 0;
1287 sort((void *)-1);
1289 if (!newmail && !edit && msgCount == 0) {
1290 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1291 value("emptystart") == NULL)
1292 fprintf(stderr, catgets(catd, CATSET, 258,
1293 "No mail at %s\n"), server);
1294 return 1;
1296 if (newmail)
1297 newmailinfo(prevcount);
1298 return 0;
1301 static int
1302 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1303 int need,
1304 const char *head, size_t headsize, long headlines)
1306 char *line = NULL, *lp;
1307 size_t linesize = 0, linelen, size = 0;
1308 int emptyline = 0, lines = 0, excess = 0;
1309 off_t offset;
1311 fseek(mp->mb_otf, 0L, SEEK_END);
1312 offset = ftell(mp->mb_otf);
1313 if (head)
1314 fwrite(head, 1, headsize, mp->mb_otf);
1315 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1316 lp = line;
1317 if (linelen > expected) {
1318 excess = linelen - expected;
1319 linelen = expected;
1322 * Need to mask 'From ' lines. This cannot be done properly
1323 * since some servers pass them as 'From ' and others as
1324 * '>From '. Although one could identify the first kind of
1325 * server in principle, it is not possible to identify the
1326 * second as '>From ' may also come from a server of the
1327 * first type as actual data. So do what is absolutely
1328 * necessary only - mask 'From '.
1330 * If the line is the first line of the message header, it
1331 * is likely a real 'From ' line. In this case, it is just
1332 * ignored since it violates all standards.
1334 if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
1335 lp[3] == 'm' && lp[4] == ' ') {
1336 if (lines + headlines != 0) {
1337 fputc('>', mp->mb_otf);
1338 size++;
1339 } else
1340 goto skip;
1342 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1343 lp[linelen-2] == '\r')) {
1344 emptyline = linelen <= 2;
1345 if (linelen > 2) {
1346 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1347 size += linelen - 1;
1348 } else
1349 size++;
1350 fputc('\n', mp->mb_otf);
1351 } else {
1352 emptyline = 0;
1353 fwrite(lp, 1, linelen, mp->mb_otf);
1354 size += linelen;
1356 lines++;
1357 skip: if ((expected -= linelen) <= 0)
1358 break;
1360 if (!emptyline) {
1362 * This is very ugly; but some IMAP daemons don't end a
1363 * message with \r\n\r\n, and we need \n\n for mbox format.
1365 fputc('\n', mp->mb_otf);
1366 lines++;
1367 size++;
1369 fflush(mp->mb_otf);
1370 if (m != NULL) {
1371 m->m_size = size + headsize;
1372 m->m_lines = lines + headlines;
1373 m->m_block = mailx_blockof(offset);
1374 m->m_offset = mailx_offsetof(offset);
1375 switch (need) {
1376 case NEED_HEADER:
1377 m->m_have |= HAVE_HEADER;
1378 break;
1379 case NEED_BODY:
1380 m->m_have |= HAVE_HEADER|HAVE_BODY;
1381 m->m_xlines = m->m_lines;
1382 m->m_xsize = m->m_size;
1383 break;
1386 free(line);
1387 return excess;
1390 static void
1391 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1392 const char *head, size_t headsize, long headlines)
1394 off_t offset;
1395 size_t len;
1397 len = strlen(str);
1398 fseek(mp->mb_otf, 0L, SEEK_END);
1399 offset = ftell(mp->mb_otf);
1400 if (head)
1401 fwrite(head, 1, headsize, mp->mb_otf);
1402 if (len > 0) {
1403 fwrite(str, 1, len, mp->mb_otf);
1404 fputc('\n', mp->mb_otf);
1405 len++;
1407 fflush(mp->mb_otf);
1408 if (m != NULL) {
1409 m->m_size = headsize + len;
1410 m->m_lines = headlines + 1;
1411 m->m_block = mailx_blockof(offset);
1412 m->m_offset = mailx_offsetof(offset);
1413 m->m_have |= HAVE_HEADER|HAVE_BODY;
1414 m->m_xlines = m->m_lines;
1415 m->m_xsize = m->m_size;
1419 static enum okay
1420 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1422 struct message mt;
1423 sighandler_type saveint = SIG_IGN, savepipe = SIG_IGN;
1424 char o[LINESIZE], *cp = NULL, *loc = NULL,
1425 *volatile item = NULL, *volatile resp = NULL,
1426 *volatile head = NULL;
1427 size_t expected;
1428 size_t volatile headsize = 0;
1429 int number = m - message + 1;
1430 enum okay ok = STOP;
1431 FILE *queuefp = NULL;
1432 long volatile headlines = 0;
1433 long n = -1;
1434 unsigned long u = 0;
1436 verbose = value("verbose") != NULL;
1437 if (getcache(mp, m, need) == OKAY)
1438 return OKAY;
1439 if (mp->mb_type == MB_CACHE) {
1440 fprintf(stderr, "Message %u not available.\n", number);
1441 return STOP;
1443 if (mp->mb_sock.s_fd < 0) {
1444 fprintf(stderr, "IMAP connection closed.\n");
1445 return STOP;
1447 switch (need) {
1448 case NEED_HEADER:
1449 resp = item = "RFC822.HEADER";
1450 break;
1451 case NEED_BODY:
1452 item = "BODY.PEEK[]";
1453 resp = "BODY[]";
1454 if (m->m_flag & HAVE_HEADER && m->m_size) {
1455 char *hdr = smalloc(m->m_size);
1456 fflush(mp->mb_otf);
1457 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1458 m->m_offset), SEEK_SET) < 0 ||
1459 fread(hdr, 1, m->m_size, mp->mb_itf)
1460 != m->m_size) {
1461 free(hdr);
1462 break;
1464 head = hdr;
1465 headsize = m->m_size;
1466 headlines = m->m_lines;
1467 item = "BODY.PEEK[TEXT]";
1468 resp = "BODY[TEXT]";
1470 break;
1471 case NEED_UNSPEC:
1472 return STOP;
1474 imaplock = 1;
1475 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1476 if (sigsetjmp(imapjmp, 1)) {
1477 safe_signal(SIGINT, saveint);
1478 safe_signal(SIGPIPE, savepipe);
1479 imaplock = 0;
1480 return STOP;
1482 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1483 safe_signal(SIGINT, maincatch);
1484 if (savepipe != SIG_IGN)
1485 safe_signal(SIGPIPE, imapcatch);
1486 if (m->m_uid)
1487 snprintf(o, sizeof o,
1488 "%s UID FETCH %lu (%s)\r\n",
1489 tag(1), m->m_uid, item);
1490 else {
1491 if (check_expunged() == STOP)
1492 goto out;
1493 snprintf(o, sizeof o,
1494 "%s FETCH %u (%s)\r\n",
1495 tag(1), number, item);
1497 IMAP_OUT(o, MB_COMD, goto out)
1498 for (;;) {
1499 ok = imap_answer(mp, 1);
1500 if (ok == STOP)
1501 break;
1502 if (response_status != RESPONSE_OTHER ||
1503 response_other != MESSAGE_DATA_FETCH)
1504 continue;
1505 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1506 continue;
1507 if (m->m_uid) {
1508 if ((cp = asccasestr(responded_other_text, "UID "))) {
1509 u = atol(&cp[4]);
1510 n = 0;
1511 } else {
1512 n = -1;
1513 u = 0;
1515 } else
1516 n = responded_other_number;
1517 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1518 if (m->m_uid ? m->m_uid != u : n != number)
1519 continue;
1520 if ((cp = strchr(loc, '"')) != NULL) {
1521 cp = imap_unquotestr(cp);
1522 imap_putstr(mp, m, cp,
1523 head, headsize, headlines);
1524 } else {
1525 m->m_have |= HAVE_HEADER|HAVE_BODY;
1526 m->m_xlines = m->m_lines;
1527 m->m_xsize = m->m_size;
1529 goto out;
1531 expected = atol(&cp[1]);
1532 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1533 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1534 continue;
1536 mt = *m;
1537 imap_fetchdata(mp, &mt, expected, need,
1538 head, headsize, headlines);
1539 if (n >= 0) {
1540 commitmsg(mp, m, mt, mt.m_have);
1541 break;
1543 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1544 &mp->mb_sock) > 0) {
1545 if (verbose)
1546 fputs(imapbuf, stderr);
1547 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1548 u = atol(&cp[4]);
1549 if (u == m->m_uid) {
1550 commitmsg(mp, m, mt, mt.m_have);
1551 break;
1556 out: while (mp->mb_active & MB_COMD)
1557 ok = imap_answer(mp, 1);
1558 if (saveint != SIG_IGN)
1559 safe_signal(SIGINT, saveint);
1560 if (savepipe != SIG_IGN)
1561 safe_signal(SIGPIPE, savepipe);
1562 imaplock--;
1563 if (ok == OKAY)
1564 putcache(mp, m);
1565 free(head);
1566 if (interrupts)
1567 onintr(0);
1568 return ok;
1571 enum okay
1572 imap_header(struct message *m)
1574 return imap_get(&mb, m, NEED_HEADER);
1578 enum okay
1579 imap_body(struct message *m)
1581 return imap_get(&mb, m, NEED_BODY);
1584 static void
1585 commitmsg(struct mailbox *mp, struct message *to,
1586 struct message from, enum havespec have)
1588 to->m_size = from.m_size;
1589 to->m_lines = from.m_lines;
1590 to->m_block = from.m_block;
1591 to->m_offset = from.m_offset;
1592 to->m_have = have;
1593 if (have & HAVE_BODY) {
1594 to->m_xlines = from.m_lines;
1595 to->m_xsize = from.m_size;
1597 putcache(mp, to);
1600 static enum okay
1601 imap_fetchheaders (
1602 struct mailbox *mp,
1603 struct message *m,
1604 int bot,
1605 int top /* bot > top */
1608 char o[LINESIZE], *cp;
1609 struct message mt;
1610 size_t expected;
1611 enum okay ok;
1612 int n = 0, u;
1613 FILE *queuefp = NULL;
1615 if (m[bot].m_uid)
1616 snprintf(o, sizeof o,
1617 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1618 tag(1), m[bot-1].m_uid, m[top-1].m_uid);
1619 else {
1620 if (check_expunged() == STOP)
1621 return STOP;
1622 snprintf(o, sizeof o,
1623 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1624 tag(1), bot, top);
1626 IMAP_OUT(o, MB_COMD, return STOP)
1627 for (;;) {
1628 ok = imap_answer(mp, 1);
1629 if (response_status != RESPONSE_OTHER)
1630 break;
1631 if (response_other != MESSAGE_DATA_FETCH)
1632 continue;
1633 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1634 return STOP;
1635 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1636 continue;
1637 expected = atol(&cp[1]);
1638 if (m[bot-1].m_uid) {
1639 if ((cp=asccasestr(responded_other_text, "UID "))) {
1640 u = atoi(&cp[4]);
1641 for (n = bot; n <= top; n++)
1642 if ((unsigned long)u == m[n-1].m_uid)
1643 break;
1644 if (n > top) {
1645 imap_fetchdata(mp, NULL, expected,
1646 NEED_HEADER,
1647 NULL, 0, 0);
1648 continue;
1650 } else
1651 n = -1;
1652 } else {
1653 n = responded_other_number;
1654 if (n <= 0 || n > msgCount) {
1655 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1656 NULL, 0, 0);
1657 continue;
1660 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1661 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1662 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1663 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1664 &mp->mb_sock) > 0) {
1665 if (verbose)
1666 fputs(imapbuf, stderr);
1667 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1668 u = atoi(&cp[4]);
1669 for (n = bot; n <= top; n++)
1670 if ((unsigned long)u == m[n-1].m_uid)
1671 break;
1672 if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
1673 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1674 n = 0;
1678 while (mp->mb_active & MB_COMD)
1679 ok = imap_answer(mp, 1);
1680 return ok;
1683 void
1684 imap_getheaders(int volatile bot, int top)
1686 sighandler_type saveint, savepipe;
1687 enum okay ok = STOP;
1688 int i, chunk = 256;
1690 verbose = value("verbose") != NULL;
1691 if (mb.mb_type == MB_CACHE)
1692 return;
1693 if (bot < 1)
1694 bot = 1;
1695 if (top > msgCount)
1696 top = msgCount;
1697 for (i = bot; i < top; i++) {
1698 if (message[i-1].m_have & HAVE_HEADER ||
1699 getcache(&mb, &message[i-1], NEED_HEADER)
1700 == OKAY)
1701 bot = i+1;
1702 else
1703 break;
1705 for (i = top; i > bot; i--) {
1706 if (message[i-1].m_have & HAVE_HEADER ||
1707 getcache(&mb, &message[i-1], NEED_HEADER)
1708 == OKAY)
1709 top = i-1;
1710 else
1711 break;
1713 if (bot >= top)
1714 return;
1715 imaplock = 1;
1716 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1717 safe_signal(SIGINT, maincatch);
1718 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1719 if (sigsetjmp(imapjmp, 1) == 0) {
1720 if (savepipe != SIG_IGN)
1721 safe_signal(SIGPIPE, imapcatch);
1722 for (i = bot; i <= top; i += chunk) {
1723 ok = imap_fetchheaders(&mb, message, i,
1724 i+chunk-1 < top ? i+chunk-1 : top);
1725 if (interrupts)
1726 onintr(0);
1729 safe_signal(SIGINT, saveint);
1730 safe_signal(SIGPIPE, savepipe);
1731 imaplock = 0;
1734 static enum okay
1735 imap_exit(struct mailbox *mp)
1737 char o[LINESIZE];
1738 FILE *queuefp = NULL;
1740 verbose = value("verbose") != NULL;
1741 mp->mb_active |= MB_BYE;
1742 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1743 IMAP_OUT(o, MB_COMD, return STOP)
1744 IMAP_ANSWER()
1745 return OKAY;
1748 static enum okay
1749 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1751 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1752 if (mp->mb_type == MB_IMAP)
1753 delcache(mp, m);
1754 return OKAY;
1757 static enum okay
1758 imap_close(struct mailbox *mp)
1760 char o[LINESIZE];
1761 FILE *queuefp = NULL;
1763 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1764 IMAP_OUT(o, MB_COMD, return STOP)
1765 IMAP_ANSWER()
1766 return OKAY;
1769 static enum okay
1770 imap_update(struct mailbox *mp)
1772 FILE *readstat = NULL;
1773 struct message *m;
1774 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1776 verbose = value("verbose") != NULL;
1777 if (Tflag != NULL) {
1778 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
1779 Tflag = NULL;
1781 if (!edit && mp->mb_perm != 0) {
1782 holdbits();
1783 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1784 if (m->m_flag & MBOX)
1785 c++;
1787 if (c > 0)
1788 if (makembox() == STOP)
1789 goto bypass;
1791 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1792 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
1793 char *id;
1795 if ((id = hfield1("message-id", m)) != NULL ||
1796 (id = hfieldX("article-id", m)) != NULL)
1797 fprintf(readstat, "%s\n", id);
1799 if (mp->mb_perm == 0) {
1800 dodel = 0;
1801 } else if (edit) {
1802 dodel = m->m_flag & MDELETED;
1803 } else {
1804 dodel = !((m->m_flag&MPRESERVE) ||
1805 (m->m_flag&MTOUCH) == 0);
1808 * Fetch the result after around each 800 STORE commands
1809 * sent (approx. 32k data sent). Otherwise, servers will
1810 * try to flush the return queue at some point, leading
1811 * to a deadlock if we are still writing commands but not
1812 * reading their results.
1814 needstat = stored > 0 && stored % 800 == 0;
1816 * Even if this message has been deleted, continue
1817 * to set further flags. This is necessary to support
1818 * Gmail semantics, where "delete" actually means
1819 * "archive", and the flags are applied to the copy
1820 * in "All Mail".
1822 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1823 imap_store(mp, m, m-message+1,
1824 '+', "\\Seen", needstat);
1825 stored++;
1827 if (m->m_flag & MFLAG) {
1828 imap_store(mp, m, m-message+1,
1829 '+', "\\Flagged", needstat);
1830 stored++;
1832 if (m->m_flag & MUNFLAG) {
1833 imap_store(mp, m, m-message+1,
1834 '-', "\\Flagged", needstat);
1835 stored++;
1837 if (m->m_flag & MANSWER) {
1838 imap_store(mp, m, m-message+1,
1839 '+', "\\Answered", needstat);
1840 stored++;
1842 if (m->m_flag & MUNANSWER) {
1843 imap_store(mp, m, m-message+1,
1844 '-', "\\Answered", needstat);
1845 stored++;
1847 if (m->m_flag & MDRAFT) {
1848 imap_store(mp, m, m-message+1,
1849 '+', "\\Draft", needstat);
1850 stored++;
1852 if (m->m_flag & MUNDRAFT) {
1853 imap_store(mp, m, m-message+1,
1854 '-', "\\Draft", needstat);
1855 stored++;
1857 if (dodel) {
1858 imap_delete(mp, m-message+1, m, needstat);
1859 stored++;
1860 gotcha++;
1861 } else if (mp->mb_type != MB_CACHE ||
1862 (! edit && ! (m->m_flag&(MBOXED|MSAVED|MDELETED))) ||
1863 (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) ==
1864 (MPRESERVE|MTOUCH) ||
1865 (edit && ! (m->m_flag & MDELETED)))
1866 held++;
1867 if (m->m_flag & MNEW) {
1868 m->m_flag &= ~MNEW;
1869 m->m_flag |= MSTATUS;
1872 bypass: if (readstat != NULL)
1873 Fclose(readstat);
1874 if (gotcha)
1875 imap_close(mp);
1876 for (m = &message[0]; m < &message[msgCount]; m++)
1877 if (!(m->m_flag&MUNLINKED) &&
1878 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1879 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1880 MDRAFT|MUNDRAFT)) {
1881 putcache(mp, m);
1882 modflags++;
1884 if ((gotcha || modflags) && edit) {
1885 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
1886 printf(value("bsdcompat") || value("bsdmsgs") ?
1887 catgets(catd, CATSET, 170, "complete\n") :
1888 catgets(catd, CATSET, 212, "updated.\n"));
1889 } else if (held && !edit && mp->mb_perm != 0) {
1890 if (held == 1)
1891 printf(catgets(catd, CATSET, 155,
1892 "Held 1 message in %s\n"), mailname);
1893 else if (held > 1)
1894 printf(catgets(catd, CATSET, 156,
1895 "Held %d messages in %s\n"), held, mailname);
1897 fflush(stdout);
1898 return OKAY;
1901 void
1902 imap_quit(void)
1904 sighandler_type saveint;
1905 sighandler_type savepipe;
1907 verbose = value("verbose") != NULL;
1908 if (mb.mb_type == MB_CACHE) {
1909 imap_update(&mb);
1910 return;
1912 if (mb.mb_sock.s_fd < 0) {
1913 fprintf(stderr, "IMAP connection closed.\n");
1914 return;
1916 imaplock = 1;
1917 saveint = safe_signal(SIGINT, SIG_IGN);
1918 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1919 if (sigsetjmp(imapjmp, 1)) {
1920 safe_signal(SIGINT, saveint);
1921 safe_signal(SIGPIPE, saveint);
1922 imaplock = 0;
1923 return;
1925 if (saveint != SIG_IGN)
1926 safe_signal(SIGINT, imapcatch);
1927 if (savepipe != SIG_IGN)
1928 safe_signal(SIGPIPE, imapcatch);
1929 imap_update(&mb);
1930 if (!same_imap_account) {
1931 imap_exit(&mb);
1932 sclose(&mb.mb_sock);
1934 safe_signal(SIGINT, saveint);
1935 safe_signal(SIGPIPE, savepipe);
1936 imaplock = 0;
1939 static enum okay
1940 imap_store(struct mailbox *mp, struct message *m, int n,
1941 int c, const char *sp, int needstat)
1943 char o[LINESIZE];
1944 FILE *queuefp = NULL;
1946 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1947 return STOP;
1948 if (m->m_uid)
1949 snprintf(o, sizeof o,
1950 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1951 tag(1), m->m_uid, c, sp);
1952 else {
1953 if (check_expunged() == STOP)
1954 return STOP;
1955 snprintf(o, sizeof o,
1956 "%s STORE %u %cFLAGS (%s)\r\n",
1957 tag(1), n, c, sp);
1959 IMAP_OUT(o, MB_COMD, return STOP)
1960 if (needstat)
1961 IMAP_ANSWER()
1962 else
1963 mb.mb_active &= ~MB_COMD;
1964 if (queuefp != NULL)
1965 Fclose(queuefp);
1966 return OKAY;
1969 enum okay
1970 imap_undelete(struct message *m, int n)
1972 return imap_unstore(m, n, "\\Deleted");
1975 enum okay
1976 imap_unread(struct message *m, int n)
1978 return imap_unstore(m, n, "\\Seen");
1981 static enum okay
1982 imap_unstore(struct message *m, int n, const char *flag)
1984 sighandler_type saveint, savepipe;
1985 enum okay ok = STOP;
1987 (void)&saveint;
1988 (void)&savepipe;
1989 (void)&ok;
1990 verbose = value("verbose") != NULL;
1991 imaplock = 1;
1992 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1993 safe_signal(SIGINT, maincatch);
1994 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1995 if (sigsetjmp(imapjmp, 1) == 0) {
1996 if (savepipe != SIG_IGN)
1997 safe_signal(SIGPIPE, imapcatch);
1998 ok = imap_store(&mb, m, n, '-', flag, 1);
2000 safe_signal(SIGINT, saveint);
2001 safe_signal(SIGPIPE, savepipe);
2002 imaplock = 0;
2003 if (interrupts)
2004 onintr(0);
2005 return ok;
2008 static const char *
2009 tag(int new)
2011 static char ts[20];
2012 static long n;
2014 if (new)
2015 n++;
2016 snprintf(ts, sizeof ts, "T%lu", n);
2017 return ts;
2020 int
2021 imap_imap(void *vp)
2023 sighandler_type saveint, savepipe;
2024 char o[LINESIZE];
2025 enum okay ok = STOP;
2026 struct mailbox *mp = &mb;
2027 FILE *queuefp = NULL;
2029 (void)&saveint;
2030 (void)&savepipe;
2031 (void)&ok;
2032 verbose = value("verbose") != NULL;
2033 if (mp->mb_type != MB_IMAP) {
2034 printf("Not operating on an IMAP mailbox.\n");
2035 return 1;
2037 imaplock = 1;
2038 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2039 safe_signal(SIGINT, maincatch);
2040 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2041 if (sigsetjmp(imapjmp, 1) == 0) {
2042 if (savepipe != SIG_IGN)
2043 safe_signal(SIGPIPE, imapcatch);
2044 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2045 IMAP_OUT(o, MB_COMD, goto out)
2046 while (mp->mb_active & MB_COMD) {
2047 ok = imap_answer(mp, 0);
2048 fputs(responded_text, stdout);
2051 out: safe_signal(SIGINT, saveint);
2052 safe_signal(SIGPIPE, savepipe);
2053 imaplock = 0;
2054 if (interrupts)
2055 onintr(0);
2056 return ok != OKAY;
2059 int
2060 imap_newmail(int autoinc)
2062 if (autoinc && had_exists < 0 && had_expunge < 0) {
2063 verbose = value("verbose") != NULL;
2064 imaplock = 1;
2065 imap_noop();
2066 imaplock = 0;
2068 if (had_exists == msgCount && had_expunge < 0)
2070 * Some servers always respond with EXISTS to NOOP. If
2071 * the mailbox has been changed but the number of messages
2072 * has not, an EXPUNGE must also had been sent; otherwise,
2073 * nothing has changed.
2075 had_exists = -1;
2076 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2079 static char *
2080 imap_putflags(int f)
2082 const char *cp;
2083 char *buf, *bp;
2085 bp = buf = salloc(100);
2086 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2087 *bp++ = '(';
2088 if (f & MREAD) {
2089 if (bp[-1] != '(')
2090 *bp++ = ' ';
2091 for (cp = "\\Seen"; *cp; cp++)
2092 *bp++ = *cp;
2094 if (f & MFLAGGED) {
2095 if (bp[-1] != '(')
2096 *bp++ = ' ';
2097 for (cp = "\\Flagged"; *cp; cp++)
2098 *bp++ = *cp;
2100 if (f & MANSWERED) {
2101 if (bp[-1] != '(')
2102 *bp++ = ' ';
2103 for (cp = "\\Answered"; *cp; cp++)
2104 *bp++ = *cp;
2106 if (f & MDRAFT) {
2107 if (bp[-1] != '(')
2108 *bp++ = ' ';
2109 for (cp = "\\Draft"; *cp; cp++)
2110 *bp++ = *cp;
2112 *bp++ = ')';
2113 *bp++ = ' ';
2115 *bp = '\0';
2116 return buf;
2119 static void
2120 imap_getflags(const char *cp, char **xp, enum mflag *f)
2122 while (*cp != ')') {
2123 if (*cp == '\\') {
2124 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2125 *f |= MREAD;
2126 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2127 *f |= MNEW;
2128 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2129 *f |= MDELETED;
2130 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2131 *f |= MFLAGGED;
2132 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2133 *f |= MANSWERED;
2134 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2135 *f |= MDRAFTED;
2137 cp++;
2139 if (xp)
2140 *xp = (char *)cp;
2143 static enum okay
2144 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2145 off_t off1, long xsize, enum mflag flag, time_t t)
2147 char o[LINESIZE];
2148 char *buf;
2149 size_t bufsize, buflen, count;
2150 enum okay ok = STOP;
2151 long size, lines, ysize;
2152 int twice = 0;
2153 FILE *queuefp = NULL;
2155 if (mp->mb_type == MB_CACHE) {
2156 queuefp = cache_queue(mp);
2157 if (queuefp == NULL)
2158 return STOP;
2159 ok = OKAY;
2161 buf = smalloc(bufsize = LINESIZE);
2162 buflen = 0;
2163 again: size = xsize;
2164 count = fsize(fp);
2165 fseek(fp, off1, SEEK_SET);
2166 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2167 tag(1), imap_quotestr(name),
2168 imap_putflags(flag),
2169 imap_make_date_time(t),
2170 size);
2171 IMAP_OUT(o, MB_COMD, goto out)
2172 while (mp->mb_active & MB_COMD) {
2173 ok = imap_answer(mp, twice);
2174 if (response_type == RESPONSE_CONT)
2175 break;
2177 if (mp->mb_type != MB_CACHE && ok == STOP) {
2178 if (twice == 0)
2179 goto trycreate;
2180 else
2181 goto out;
2183 lines = ysize = 0;
2184 while (size > 0) {
2185 fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2186 lines++;
2187 ysize += buflen;
2188 buf[buflen-1] = '\r';
2189 buf[buflen] = '\n';
2190 if (mp->mb_type != MB_CACHE)
2191 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2192 else if (queuefp)
2193 fwrite(buf, 1, buflen+1, queuefp);
2194 size -= buflen+1;
2196 if (mp->mb_type != MB_CACHE)
2197 swrite(&mp->mb_sock, "\r\n");
2198 else if (queuefp)
2199 fputs("\r\n", queuefp);
2200 while (mp->mb_active & MB_COMD) {
2201 ok = imap_answer(mp, 0);
2202 if (response_status == RESPONSE_NO /*&&
2203 ascncasecmp(responded_text,
2204 "[TRYCREATE] ", 12) == 0*/) {
2205 trycreate: if (twice++) {
2206 ok = STOP;
2207 goto out;
2209 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2210 tag(1),
2211 imap_quotestr(name));
2212 IMAP_OUT(o, MB_COMD, goto out);
2213 while (mp->mb_active & MB_COMD)
2214 ok = imap_answer(mp, 1);
2215 if (ok == STOP)
2216 goto out;
2217 imap_created_mailbox++;
2218 goto again;
2219 } else if (ok != OKAY)
2220 fprintf(stderr, "IMAP error: %s", responded_text);
2221 else if (response_status == RESPONSE_OK &&
2222 mp->mb_flags & MB_UIDPLUS)
2223 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2224 flag, name);
2226 out: if (queuefp != NULL)
2227 Fclose(queuefp);
2228 free(buf);
2229 return ok;
2232 static enum okay
2233 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2235 char *buf, *bp, *lp;
2236 size_t bufsize, buflen, count;
2237 off_t off1 = -1, offs;
2238 int inhead = 1;
2239 int flag = MNEW|MNEWEST;
2240 long size = 0;
2241 time_t tim;
2242 enum okay ok;
2244 buf = smalloc(bufsize = LINESIZE);
2245 buflen = 0;
2246 count = fsize(fp);
2247 offs = ftell(fp);
2248 time(&tim);
2249 do {
2250 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2251 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2252 if (off1 != (off_t)-1) {
2253 ok=imap_append1(mp, name, fp, off1,
2254 size, flag, tim);
2255 if (ok == STOP)
2256 return STOP;
2257 fseek(fp, offs+buflen, SEEK_SET);
2259 off1 = offs + buflen;
2260 size = 0;
2261 inhead = 1;
2262 flag = MNEW;
2263 if (bp != NULL)
2264 tim = unixtime(buf);
2265 } else
2266 size += buflen+1;
2267 offs += buflen;
2268 if (bp && buf[0] == '\n')
2269 inhead = 0;
2270 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2271 lp = &buf[6];
2272 while (whitechar(*lp&0377))
2273 lp++;
2274 if (*lp == ':')
2275 while (*++lp != '\0')
2276 switch (*lp) {
2277 case 'R':
2278 flag |= MREAD;
2279 break;
2280 case 'O':
2281 flag &= ~MNEW;
2282 break;
2284 } else if (bp && inhead &&
2285 ascncasecmp(buf, "x-status", 8) == 0) {
2286 lp = &buf[8];
2287 while (whitechar(*lp&0377))
2288 lp++;
2289 if (*lp == ':')
2290 while (*++lp != '\0')
2291 switch (*lp) {
2292 case 'F':
2293 flag |= MFLAGGED;
2294 break;
2295 case 'A':
2296 flag |= MANSWERED;
2297 break;
2298 case 'T':
2299 flag |= MDRAFTED;
2300 break;
2303 } while (bp != NULL);
2304 free(buf);
2305 return OKAY;
2308 enum okay
2309 imap_append(const char *xserver, FILE *fp)
2311 sighandler_type saveint, savepipe;
2312 char *server, *uhp, *mbx, *user;
2313 const char *sp, *cp, *pass;
2314 int use_ssl;
2315 enum okay ok = STOP;
2317 (void)&saveint;
2318 (void)&savepipe;
2319 (void)&ok;
2320 verbose = value("verbose") != NULL;
2321 server = savestr((char *)xserver);
2322 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2323 imaplock = 1;
2324 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2325 safe_signal(SIGINT, maincatch);
2326 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2327 if (sigsetjmp(imapjmp, 1))
2328 goto out;
2329 if (savepipe != SIG_IGN)
2330 safe_signal(SIGPIPE, imapcatch);
2331 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2332 mb.mb_imap_account &&
2333 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2334 ok = imap_append0(&mb, mbx, fp);
2336 else {
2337 struct mailbox mx;
2339 memset(&mx, 0, sizeof mx);
2340 if (disconnected(server) == 0) {
2341 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2342 use_ssl ? "imaps" : "imap",
2343 verbose) != OKAY)
2344 goto fail;
2345 mx.mb_sock.s_desc = "IMAP";
2346 mx.mb_type = MB_IMAP;
2347 mx.mb_imap_account = (char *)protbase(server);
2348 mx.mb_imap_mailbox = mbx;
2349 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2350 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2351 sclose(&mx.mb_sock);
2352 goto fail;
2354 ok = imap_append0(&mx, mbx, fp);
2355 imap_exit(&mx);
2356 sclose(&mx.mb_sock);
2357 } else {
2358 mx.mb_imap_account = (char *)protbase(server);
2359 mx.mb_imap_mailbox = mbx;
2360 mx.mb_type = MB_CACHE;
2361 ok = imap_append0(&mx, mbx, fp);
2363 fail:;
2365 out: safe_signal(SIGINT, saveint);
2366 safe_signal(SIGPIPE, savepipe);
2367 imaplock = 0;
2368 if (interrupts)
2369 onintr(0);
2370 return ok;
2373 static enum okay
2374 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2375 struct list_item **lend, int level)
2377 char o[LINESIZE];
2378 enum okay ok = STOP;
2379 char *cp;
2380 const char *bp;
2381 FILE *queuefp = NULL;
2382 struct list_item *lp;
2384 *list = *lend = NULL;
2385 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2386 tag(1), imap_quotestr(base));
2387 IMAP_OUT(o, MB_COMD, return STOP);
2388 while (mp->mb_active & MB_COMD) {
2389 ok = imap_answer(mp, 1);
2390 if (response_status == RESPONSE_OTHER &&
2391 response_other == MAILBOX_DATA_LIST &&
2392 imap_parse_list() == OKAY) {
2393 cp = imap_unquotestr(list_name);
2394 lp = csalloc(1, sizeof *lp);
2395 lp->l_name = cp;
2396 for (bp = base; *bp && *bp == *cp; bp++)
2397 cp++;
2398 lp->l_base = *cp ? cp : savestr(base);
2399 lp->l_attr = list_attributes;
2400 lp->l_level = level+1;
2401 lp->l_delim = list_hierarchy_delimiter;
2402 if (*list && *lend) {
2403 (*lend)->l_next = lp;
2404 *lend = lp;
2405 } else
2406 *list = *lend = lp;
2409 return ok;
2412 static enum okay
2413 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2415 struct list_item *list, *lend, *lp, *lx, *ly;
2416 int n;
2417 const char *bp;
2418 char *cp;
2419 int depth;
2421 verbose = value("verbose") != NULL;
2422 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2423 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2424 return STOP;
2425 if (list == NULL || lend == NULL)
2426 return OKAY;
2427 for (lp = list; lp; lp = lp->l_next)
2428 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2429 lp->l_level < depth &&
2430 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2431 cp = salloc((n = strlen(lp->l_name)) + 2);
2432 strcpy(cp, lp->l_name);
2433 cp[n] = lp->l_delim;
2434 cp[n+1] = '\0';
2435 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2436 lx && ly) {
2437 lp->l_has_children = 1;
2438 if (strcmp(cp, lx->l_name) == 0)
2439 lx = lx->l_next;
2440 if (lx) {
2441 lend->l_next = lx;
2442 lend = ly;
2446 for (lp = list; lp; lp = lp->l_next) {
2447 if (strip) {
2448 cp = lp->l_name;
2449 for (bp = base; *bp && *bp == *cp; bp++)
2450 cp++;
2451 } else
2452 cp = lp->l_name;
2453 if ((lp->l_attr&LIST_NOSELECT) == 0)
2454 fprintf(fp, "%s\n", *cp ? cp : base);
2455 else if (lp->l_has_children == 0)
2456 fprintf(fp, "%s%c\n", *cp ? cp : base,
2457 lp->l_delim != EOF ? lp->l_delim : '\n');
2459 return OKAY;
2462 void
2463 imap_folders(const char *name, int strip)
2465 sighandler_type saveint, savepipe;
2466 const char *fold, *cp, *sp;
2467 char *tempfn;
2468 FILE *volatile fp;
2469 int columnize = is_a_tty[1];
2471 cp = protbase(name);
2472 sp = mb.mb_imap_account;
2473 if (strcmp(cp, sp)) {
2474 fprintf(stderr, "Cannot list folders on other than the "
2475 "current IMAP account,\n\"%s\". "
2476 "Try \"folders @\".\n", sp);
2477 return;
2479 fold = protfile(name);
2480 if (columnize) {
2481 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2482 perror("tmpfile");
2483 return;
2485 rm(tempfn);
2486 Ftfree(&tempfn);
2487 } else
2488 fp = stdout;
2489 imaplock = 1;
2490 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2491 safe_signal(SIGINT, maincatch);
2492 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2493 if (sigsetjmp(imapjmp, 1))
2494 goto out;
2495 if (savepipe != SIG_IGN)
2496 safe_signal(SIGPIPE, imapcatch);
2497 if (mb.mb_type == MB_CACHE)
2498 cache_list(&mb, fold, strip, fp);
2499 else
2500 imap_list(&mb, fold, strip, fp);
2501 imaplock = 0;
2502 if (interrupts) {
2503 if (columnize)
2504 Fclose(fp);
2505 onintr(0);
2507 fflush(fp);
2508 if (columnize) {
2509 rewind(fp);
2510 if (fsize(fp) > 0)
2511 dopr(fp);
2512 else
2513 fprintf(stderr, "Folder not found.\n");
2515 out:
2516 safe_signal(SIGINT, saveint);
2517 safe_signal(SIGPIPE, savepipe);
2518 if (columnize)
2519 Fclose(fp);
2522 static void
2523 dopr(FILE *fp)
2525 char o[LINESIZE], *tempfn;
2526 int c;
2527 long n = 0, mx = 0, columns, width;
2528 FILE *out;
2530 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2531 perror("tmpfile");
2532 return;
2534 rm(tempfn);
2535 Ftfree(&tempfn);
2536 while ((c = getc(fp)) != EOF) {
2537 if (c == '\n') {
2538 if (n > mx)
2539 mx = n;
2540 n = 0;
2541 } else
2542 n++;
2544 rewind(fp);
2545 width = scrnwidth;
2546 if (mx < width / 2) {
2547 columns = width / (mx+2);
2548 snprintf(o, sizeof o,
2549 "sort | pr -%lu -w%lu -t",
2550 columns, width);
2551 } else
2552 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2553 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2554 try_pager(out);
2555 Fclose(out);
2558 static enum okay
2559 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2561 char o[LINESIZE];
2562 const char *qname;
2563 enum okay ok = STOP;
2564 int twice = 0;
2565 int stored = 0;
2566 FILE *queuefp = NULL;
2568 if (mp->mb_type == MB_CACHE) {
2569 if ((queuefp = cache_queue(mp)) == NULL)
2570 return STOP;
2571 ok = OKAY;
2573 qname = imap_quotestr(name = protfile(name));
2575 * Since it is not possible to set flags on the copy, recently
2576 * set flags must be set on the original to include it in the copy.
2578 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2579 imap_store(mp, m, n, '+', "\\Seen", 0);
2580 if (m->m_flag&MFLAG)
2581 imap_store(mp, m, n, '+', "\\Flagged", 0);
2582 if (m->m_flag&MUNFLAG)
2583 imap_store(mp, m, n, '-', "\\Flagged", 0);
2584 if (m->m_flag&MANSWER)
2585 imap_store(mp, m, n, '+', "\\Answered", 0);
2586 if (m->m_flag&MUNANSWER)
2587 imap_store(mp, m, n, '-', "\\Flagged", 0);
2588 if (m->m_flag&MDRAFT)
2589 imap_store(mp, m, n, '+', "\\Draft", 0);
2590 if (m->m_flag&MUNDRAFT)
2591 imap_store(mp, m, n, '-', "\\Draft", 0);
2592 again: if (m->m_uid)
2593 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2594 tag(1), m->m_uid, qname);
2595 else {
2596 if (check_expunged() == STOP)
2597 goto out;
2598 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2599 tag(1), n, qname);
2601 IMAP_OUT(o, MB_COMD, goto out)
2602 while (mp->mb_active & MB_COMD)
2603 ok = imap_answer(mp, twice);
2604 if (mp->mb_type == MB_IMAP &&
2605 mp->mb_flags & MB_UIDPLUS &&
2606 response_status == RESPONSE_OK)
2607 imap_copyuid(mp, m, name);
2608 if (response_status == RESPONSE_NO && twice++ == 0) {
2609 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2610 IMAP_OUT(o, MB_COMD, goto out)
2611 while (mp->mb_active & MB_COMD)
2612 ok = imap_answer(mp, 1);
2613 if (ok == OKAY) {
2614 imap_created_mailbox++;
2615 goto again;
2618 if (queuefp != NULL)
2619 Fclose(queuefp);
2621 * ... and reset the flag to its initial value so that
2622 * the 'exit' command still leaves the message unread.
2624 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2625 imap_store(mp, m, n, '-', "\\Seen", 0);
2626 stored++;
2628 if (m->m_flag&MFLAG) {
2629 imap_store(mp, m, n, '-', "\\Flagged", 0);
2630 stored++;
2632 if (m->m_flag&MUNFLAG) {
2633 imap_store(mp, m, n, '+', "\\Flagged", 0);
2634 stored++;
2636 if (m->m_flag&MANSWER) {
2637 imap_store(mp, m, n, '-', "\\Answered", 0);
2638 stored++;
2640 if (m->m_flag&MUNANSWER) {
2641 imap_store(mp, m, n, '+', "\\Answered", 0);
2642 stored++;
2644 if (m->m_flag&MDRAFT) {
2645 imap_store(mp, m, n, '-', "\\Draft", 0);
2646 stored++;
2648 if (m->m_flag&MUNDRAFT) {
2649 imap_store(mp, m, n, '+', "\\Draft", 0);
2650 stored++;
2652 if (stored) {
2653 mp->mb_active |= MB_COMD;
2654 imap_finish(mp);
2656 return ok;
2659 enum okay
2660 imap_copy(struct message *m, int n, const char *name)
2662 sighandler_type saveint, savepipe;
2663 enum okay ok = STOP;
2665 (void)&saveint;
2666 (void)&savepipe;
2667 (void)&ok;
2668 verbose = value("verbose") != NULL;
2669 imaplock = 1;
2670 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2671 safe_signal(SIGINT, maincatch);
2672 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2673 if (sigsetjmp(imapjmp, 1) == 0) {
2674 if (savepipe != SIG_IGN)
2675 safe_signal(SIGPIPE, imapcatch);
2676 ok = imap_copy1(&mb, m, n, name);
2678 safe_signal(SIGINT, saveint);
2679 safe_signal(SIGPIPE, savepipe);
2680 imaplock = 0;
2681 if (interrupts)
2682 onintr(0);
2683 return ok;
2686 static enum okay
2687 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2688 unsigned long *olduid, unsigned long *newuid)
2690 char *xp, *yp, *zp;
2692 *uidvalidity = strtoul(cp, &xp, 10);
2693 *olduid = strtoul(xp, &yp, 10);
2694 *newuid = strtoul(yp, &zp, 10);
2695 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2696 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2699 static enum okay
2700 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2701 unsigned long *uid)
2703 char *xp, *yp;
2705 *uidvalidity = strtoul(cp, &xp, 10);
2706 *uid = strtoul(xp, &yp, 10);
2707 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2708 yp > xp && *yp == ']';
2711 static enum okay
2712 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2714 const char *cp;
2715 unsigned long uidvalidity, olduid, newuid;
2716 struct mailbox xmb;
2717 struct message xm;
2719 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2720 imap_copyuid_parse(&cp[9], &uidvalidity,
2721 &olduid, &newuid) == STOP)
2722 return STOP;
2723 xmb = *mp;
2724 xmb.mb_cache_directory = NULL;
2725 xmb.mb_imap_mailbox = savestr((char *)name);
2726 xmb.mb_uidvalidity = uidvalidity;
2727 initcache(&xmb);
2728 if (m == NULL) {
2729 memset(&xm, 0, sizeof xm);
2730 xm.m_uid = olduid;
2731 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2732 return STOP;
2733 getcache(mp, &xm, NEED_HEADER);
2734 getcache(mp, &xm, NEED_BODY);
2735 } else {
2736 if ((m->m_flag & HAVE_HEADER) == 0)
2737 getcache(mp, m, NEED_HEADER);
2738 if ((m->m_flag & HAVE_BODY) == 0)
2739 getcache(mp, m, NEED_BODY);
2740 xm = *m;
2742 xm.m_uid = newuid;
2743 xm.m_flag &= ~MFULLYCACHED;
2744 putcache(&xmb, &xm);
2745 return OKAY;
2748 static enum okay
2749 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2750 long xsize, long size, long lines, int flag, const char *name)
2752 const char *cp;
2753 unsigned long uidvalidity, uid;
2754 struct mailbox xmb;
2755 struct message xm;
2757 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2758 imap_appenduid_parse(&cp[11], &uidvalidity,
2759 &uid) == STOP)
2760 return STOP;
2761 xmb = *mp;
2762 xmb.mb_cache_directory = NULL;
2763 xmb.mb_imap_mailbox = savestr((char *)name);
2764 xmb.mb_uidvalidity = uidvalidity;
2765 xmb.mb_otf = xmb.mb_itf = fp;
2766 initcache(&xmb);
2767 memset(&xm, 0, sizeof xm);
2768 xm.m_flag = (flag & MREAD) | MNEW;
2769 xm.m_time = t;
2770 xm.m_block = mailx_blockof(off1);
2771 xm.m_offset = mailx_offsetof(off1);
2772 xm.m_size = size;
2773 xm.m_xsize = xsize;
2774 xm.m_lines = xm.m_xlines = lines;
2775 xm.m_uid = uid;
2776 xm.m_have = HAVE_HEADER|HAVE_BODY;
2777 putcache(&xmb, &xm);
2778 return OKAY;
2781 static enum okay
2782 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2784 FILE *tp = NULL;
2785 time_t t;
2786 long size, xsize, ysize, lines;
2787 enum mflag flag = MNEW;
2788 char *name, *buf, *bp, *cp, *tempCopy;
2789 size_t bufsize, buflen, count;
2790 enum okay ok = STOP;
2792 buf = smalloc(bufsize = LINESIZE);
2793 buflen = 0;
2794 count = fsize(fp);
2795 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2796 goto stop;
2797 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2798 while (*bp == ' ')
2799 bp++;
2800 if ((cp = strrchr(bp, '{')) == NULL)
2801 goto stop;
2802 xsize = atol(&cp[1]) + 2;
2803 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2804 goto stop;
2805 while (*cp == ' ')
2806 cp++;
2807 if (*cp == '(') {
2808 imap_getflags(cp, &cp, &flag);
2809 while (*++cp == ' ');
2811 t = imap_read_date_time(cp);
2812 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2813 goto stop;
2814 rm(tempCopy);
2815 Ftfree(&tempCopy);
2816 size = xsize;
2817 ysize = lines = 0;
2818 while (size > 0) {
2819 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2820 goto stop;
2821 size -= buflen;
2822 buf[--buflen] = '\0';
2823 buf[buflen-1] = '\n';
2824 fwrite(buf, 1, buflen, tp);
2825 ysize += buflen;
2826 lines++;
2828 fflush(tp);
2829 rewind(tp);
2830 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2831 imap_unquotestr(name));
2832 ok = OKAY;
2833 stop: free(buf);
2834 if (tp)
2835 Fclose(tp);
2836 return ok;
2839 static enum okay
2840 imap_search2(struct mailbox *mp, struct message *m, int count,
2841 const char *spec, int f)
2843 char *o;
2844 size_t osize;
2845 FILE *queuefp = NULL;
2846 enum okay ok = STOP;
2847 int i;
2848 unsigned long n;
2849 const char *cp;
2850 char *xp, *cs, c;
2852 c = 0;
2853 for (cp = spec; *cp; cp++)
2854 c |= *cp;
2855 if (c & 0200) {
2856 cp = gettcharset();
2857 #ifdef HAVE_ICONV
2858 if (asccasecmp(cp, "utf-8")) {
2859 iconv_t it;
2860 char *sp, *nsp, *nspec;
2861 size_t sz, nsz;
2862 if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
2863 sz = strlen(spec) + 1;
2864 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2865 sp = (char *)spec;
2866 if (iconv_ft(it, &sp, &sz, &nsp, &nsz, 0)
2867 != (size_t)-1 && sz == 0) {
2868 spec = nspec;
2869 cp = "utf-8";
2871 iconv_close(it);
2874 #endif /* HAVE_ICONV */
2875 cp = imap_quotestr(cp);
2876 cs = salloc(n = strlen(cp) + 10);
2877 snprintf(cs, n, "CHARSET %s ", cp);
2878 } else
2879 cs = "";
2880 o = ac_alloc(osize = strlen(spec) + 60);
2881 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2882 IMAP_OUT(o, MB_COMD, goto out)
2883 while (mp->mb_active & MB_COMD) {
2884 ok = imap_answer(mp, 0);
2885 if (response_status == RESPONSE_OTHER &&
2886 response_other == MAILBOX_DATA_SEARCH) {
2887 xp = responded_other_text;
2888 while (*xp && *xp != '\r') {
2889 n = strtoul(xp, &xp, 10);
2890 for (i = 0; i < count; i++)
2891 if (m[i].m_uid == n &&
2892 (m[i].m_flag&MHIDDEN)
2893 == 0 &&
2894 (f == MDELETED ||
2895 (m[i].m_flag&MDELETED)
2896 == 0))
2897 mark(i+1, f);
2901 out: ac_free(o);
2902 return ok;
2905 enum okay
2906 imap_search1(const char *spec, int f)
2908 sighandler_type saveint, savepipe;
2909 enum okay ok = STOP;
2911 (void)&saveint;
2912 (void)&savepipe;
2913 (void)&ok;
2914 if (mb.mb_type != MB_IMAP)
2915 return STOP;
2916 verbose = value("verbose") != NULL;
2917 imaplock = 1;
2918 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2919 safe_signal(SIGINT, maincatch);
2920 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2921 if (sigsetjmp(imapjmp, 1) == 0) {
2922 if (savepipe != SIG_IGN)
2923 safe_signal(SIGPIPE, imapcatch);
2924 ok = imap_search2(&mb, message, msgCount, spec, f);
2926 safe_signal(SIGINT, saveint);
2927 safe_signal(SIGPIPE, savepipe);
2928 imaplock = 0;
2929 if (interrupts)
2930 onintr(0);
2931 return ok;
2934 int
2935 imap_thisaccount(const char *cp)
2937 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2938 return 0;
2939 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2940 mb.mb_imap_account == NULL)
2941 return 0;
2942 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2945 enum okay
2946 imap_remove(const char *name)
2948 sighandler_type saveint, savepipe;
2949 enum okay ok = STOP;
2951 (void)&saveint;
2952 (void)&savepipe;
2953 (void)&ok;
2954 verbose = value("verbose") != NULL;
2955 if (mb.mb_type != MB_IMAP) {
2956 fprintf(stderr, "Refusing to remove \"%s\" "
2957 "in disconnected mode.\n", name);
2958 return STOP;
2960 if (!imap_thisaccount(name)) {
2961 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2962 "server: \"%s\" not removed.\n", name);
2963 return STOP;
2965 imaplock = 1;
2966 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2967 safe_signal(SIGINT, maincatch);
2968 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2969 if (sigsetjmp(imapjmp, 1) == 0) {
2970 if (savepipe != SIG_IGN)
2971 safe_signal(SIGPIPE, imapcatch);
2972 ok = imap_remove1(&mb, protfile(name));
2974 safe_signal(SIGINT, saveint);
2975 safe_signal(SIGPIPE, savepipe);
2976 imaplock = 0;
2977 if (ok == OKAY)
2978 ok = cache_remove(name);
2979 if (interrupts)
2980 onintr(0);
2981 return ok;
2984 static enum okay
2985 imap_remove1(struct mailbox *mp, const char *name)
2987 FILE *queuefp = NULL;
2988 char *o;
2989 int os;
2990 enum okay ok = STOP;
2992 o = ac_alloc(os = 2*strlen(name) + 100);
2993 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
2994 IMAP_OUT(o, MB_COMD, goto out)
2995 while (mp->mb_active & MB_COMD)
2996 ok = imap_answer(mp, 1);
2997 out: ac_free(o);
2998 return ok;
3001 enum okay
3002 imap_rename(const char *old, const char *new)
3004 sighandler_type saveint, savepipe;
3005 enum okay ok = STOP;
3007 (void)&saveint;
3008 (void)&savepipe;
3009 (void)&ok;
3010 verbose = value("verbose") != NULL;
3011 if (mb.mb_type != MB_IMAP) {
3012 fprintf(stderr, "Refusing to rename mailboxes "
3013 "in disconnected mode.\n");
3014 return STOP;
3016 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3017 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3018 "server: \"%s\" not renamed to \"%s\".\n",
3019 old, new);
3020 return STOP;
3022 imaplock = 1;
3023 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3024 safe_signal(SIGINT, maincatch);
3025 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3026 if (sigsetjmp(imapjmp, 1) == 0) {
3027 if (savepipe != SIG_IGN)
3028 safe_signal(SIGPIPE, imapcatch);
3029 ok = imap_rename1(&mb, protfile(old), protfile(new));
3031 safe_signal(SIGINT, saveint);
3032 safe_signal(SIGPIPE, savepipe);
3033 imaplock = 0;
3034 if (ok == OKAY)
3035 ok = cache_rename(old, new);
3036 if (interrupts)
3037 onintr(0);
3038 return ok;
3041 static enum okay
3042 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3044 FILE *queuefp = NULL;
3045 char *o;
3046 int os;
3047 enum okay ok = STOP;
3049 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3050 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3051 imap_quotestr(old), imap_quotestr(new));
3052 IMAP_OUT(o, MB_COMD, goto out)
3053 while (mp->mb_active & MB_COMD)
3054 ok = imap_answer(mp, 1);
3055 out: ac_free(o);
3056 return ok;
3059 enum okay
3060 imap_dequeue(struct mailbox *mp, FILE *fp)
3062 FILE *queuefp = NULL;
3063 char o[LINESIZE], *newname;
3064 char *buf, *bp, *cp, iob[4096];
3065 size_t bufsize, buflen, count;
3066 enum okay ok = OKAY, rok = OKAY;
3067 long offs, offs1, offs2, octets;
3068 int twice, gotcha = 0;
3070 buf = smalloc(bufsize = LINESIZE);
3071 buflen = 0;
3072 count = fsize(fp);
3073 while (offs1 = ftell(fp),
3074 fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
3075 != NULL) {
3076 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3077 while (*bp == ' ')
3078 bp++;
3079 twice = 0;
3080 offs = ftell(fp);
3081 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3082 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3083 cp = &bp[9];
3084 while (digitchar(*cp&0377))
3085 cp++;
3086 if (*cp != ' ')
3087 goto fail;
3088 while (*cp == ' ')
3089 cp++;
3090 if ((newname = imap_strex(cp, NULL)) == NULL)
3091 goto fail;
3092 IMAP_OUT(o, MB_COMD, continue)
3093 while (mp->mb_active & MB_COMD)
3094 ok = imap_answer(mp, twice);
3095 if (response_status == RESPONSE_NO && twice++ == 0)
3096 goto trycreate;
3097 if (response_status == RESPONSE_OK &&
3098 mp->mb_flags & MB_UIDPLUS) {
3099 imap_copyuid(mp, NULL,
3100 imap_unquotestr(newname));
3102 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3103 IMAP_OUT(o, MB_COMD, continue)
3104 while (mp->mb_active & MB_COMD)
3105 ok = imap_answer(mp, 1);
3106 if (ok == OKAY)
3107 gotcha++;
3108 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3109 if ((cp = strrchr(bp, '{')) == NULL)
3110 goto fail;
3111 octets = atol(&cp[1]) + 2;
3112 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3113 goto fail;
3114 IMAP_OUT(o, MB_COMD, continue)
3115 while (mp->mb_active & MB_COMD) {
3116 ok = imap_answer(mp, twice);
3117 if (response_type == RESPONSE_CONT)
3118 break;
3120 if (ok == STOP) {
3121 if (twice++ == 0) {
3122 fseek(fp, offs, SEEK_SET);
3123 goto trycreate;
3125 goto fail;
3127 while (octets > 0) {
3128 size_t n = (size_t)octets > sizeof iob
3129 ? sizeof iob : (size_t)octets;
3130 octets -= n;
3131 if (n != fread(iob, 1, n, fp))
3132 goto fail;
3133 swrite1(&mp->mb_sock, iob, n, 1);
3135 swrite(&mp->mb_sock, "");
3136 while (mp->mb_active & MB_COMD) {
3137 ok = imap_answer(mp, 0);
3138 if (response_status == RESPONSE_NO &&
3139 twice++ == 0) {
3140 fseek(fp, offs, SEEK_SET);
3141 goto trycreate;
3144 if (response_status == RESPONSE_OK &&
3145 mp->mb_flags & MB_UIDPLUS) {
3146 offs2 = ftell(fp);
3147 fseek(fp, offs1, SEEK_SET);
3148 if (imap_appenduid_cached(mp, fp) == STOP) {
3149 fseek(fp, offs2, SEEK_SET);
3150 goto fail;
3153 } else {
3154 fail: fprintf(stderr,
3155 "Invalid command in IMAP cache queue: \"%s\"\n",
3156 bp);
3157 rok = STOP;
3159 continue;
3160 trycreate:
3161 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3162 tag(1), newname);
3163 IMAP_OUT(o, MB_COMD, continue)
3164 while (mp->mb_active & MB_COMD)
3165 ok = imap_answer(mp, 1);
3166 if (ok == OKAY)
3167 goto again;
3169 fflush(fp);
3170 rewind(fp);
3171 ftruncate(fileno(fp), 0);
3172 if (gotcha)
3173 imap_close(mp);
3174 return rok;
3177 static char *
3178 imap_strex(const char *cp, char **xp)
3180 const char *cq;
3181 char *n;
3183 if (*cp != '"')
3184 return NULL;
3185 for (cq = &cp[1]; *cq; cq++) {
3186 if (*cq == '\\')
3187 cq++;
3188 else if (*cq == '"')
3189 break;
3191 if (*cq != '"')
3192 return NULL;
3193 n = salloc(cq - cp + 2);
3194 memcpy(n, cp, cq - cp + 1);
3195 n[cq - cp + 1] = '\0';
3196 if (xp)
3197 *xp = (char *)&cq[1];
3198 return n;
3201 static enum okay
3202 check_expunged(void)
3204 if (expunged_messages > 0) {
3205 fprintf(stderr,
3206 "Command not executed - messages have been expunged\n");
3207 return STOP;
3209 return OKAY;
3212 /*ARGSUSED*/
3213 int
3214 cconnect(void *vp)
3216 char *cp, *cq;
3217 int omsgCount = msgCount;
3218 (void)vp;
3220 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3221 fprintf(stderr, "Already connected.\n");
3222 return 1;
3224 unset_allow_undefined = 1;
3225 unset_internal("disconnected");
3226 cp = protbase(mailname);
3227 if (strncmp(cp, "imap://", 7) == 0)
3228 cp += 7;
3229 else if (strncmp(cp, "imaps://", 8) == 0)
3230 cp += 8;
3231 if ((cq = strchr(cp, ':')) != NULL)
3232 *cq = '\0';
3233 unset_internal(savecat("disconnected-", cp));
3234 unset_allow_undefined = 0;
3235 if (mb.mb_type == MB_CACHE) {
3236 imap_setfile1(mailname, 0, edit, 1);
3237 if (msgCount > omsgCount)
3238 newmailinfo(omsgCount);
3240 return 0;
3243 int
3244 cdisconnect(void *vp)
3246 int *msgvec = vp;
3248 if (mb.mb_type == MB_CACHE) {
3249 fprintf(stderr, "Not connected.\n");
3250 return 1;
3251 } else if (mb.mb_type == MB_IMAP) {
3252 if (cached_uidvalidity(&mb) == 0) {
3253 fprintf(stderr, "The current mailbox is not cached.\n");
3254 return 1;
3257 if (*msgvec)
3258 ccache(vp);
3259 assign("disconnected", "");
3260 if (mb.mb_type == MB_IMAP) {
3261 sclose(&mb.mb_sock);
3262 imap_setfile1(mailname, 0, edit, 1);
3264 return 0;
3266 int
3267 ccache(void *vp)
3269 int *msgvec = vp, *ip;
3270 struct message *mp;
3272 if (mb.mb_type != MB_IMAP) {
3273 fprintf(stderr, "Not connected to an IMAP server.\n");
3274 return 1;
3276 if (cached_uidvalidity(&mb) == 0) {
3277 fprintf(stderr, "The current mailbox is not cached.\n");
3278 return 1;
3280 for (ip = msgvec; *ip; ip++) {
3281 mp = &message[*ip-1];
3282 if (!(mp->m_have & HAVE_BODY))
3283 get_body(mp);
3285 return 0;
3287 #else /* !USE_IMAP */
3289 #include "extern.h"
3291 static void
3292 noimap(void)
3294 fprintf(stderr, catgets(catd, CATSET, 269,
3295 "No IMAP support compiled in.\n"));
3298 int
3299 imap_setfile(const char *server, int newmail, int isedit)
3301 (void)server;
3302 (void)newmail;
3303 (void)isedit;
3304 noimap();
3305 return -1;
3308 enum okay
3309 imap_header(struct message *mp)
3311 (void)mp;
3312 noimap();
3313 return STOP;
3316 enum okay
3317 imap_body(struct message *mp)
3319 (void)mp;
3320 noimap();
3321 return STOP;
3324 void
3325 imap_getheaders(int bot, int top)
3327 (void)bot;
3328 (void)top;
3331 void
3332 imap_quit(void)
3334 noimap();
3337 /*ARGSUSED*/
3338 int
3339 imap_imap(void *vp)
3341 (void)vp;
3342 noimap();
3343 return 1;
3346 /*ARGSUSED*/
3347 int
3348 imap_newmail(int dummy)
3350 (void)dummy;
3351 return 0;
3354 /*ARGSUSED*/
3355 enum okay
3356 imap_undelete(struct message *m, int n)
3358 (void)m;
3359 (void)n;
3360 return STOP;
3363 /*ARGSUSED*/
3364 enum okay
3365 imap_unread(struct message *m, int n)
3367 (void)m;
3368 (void)n;
3369 return STOP;
3372 /*ARGSUSED*/
3373 enum okay
3374 imap_append(const char *server, FILE *fp)
3376 (void)server;
3377 (void)fp;
3378 noimap();
3379 return STOP;
3382 /*ARGSUSED*/
3383 void
3384 imap_folders(const char *name, int strip)
3386 (void)name;
3387 (void)strip;
3388 noimap();
3391 /*ARGSUSED*/
3392 enum okay
3393 imap_remove(const char *name)
3395 (void)name;
3396 noimap();
3397 return STOP;
3400 /*ARGSUSED*/
3401 enum okay
3402 imap_rename(const char *old, const char *new)
3404 (void)old;
3405 (void)new;
3406 noimap();
3407 return STOP;
3410 enum okay
3411 imap_copy(struct message *m, int n, const char *name)
3413 (void)m;
3414 (void)n;
3415 (void)name;
3416 noimap();
3417 return STOP;
3420 /*ARGSUSED*/
3421 enum okay
3422 imap_search1(const char *spec, int f)
3424 (void)spec;
3425 (void)f;
3426 return STOP;
3429 int
3430 imap_thisaccount(const char *cp)
3432 (void)cp;
3433 return 0;
3436 enum okay
3437 imap_noop(void)
3439 noimap();
3440 return STOP;
3443 /*ARGSUSED*/
3444 int
3445 cconnect(void *vp)
3447 (void)vp;
3448 noimap();
3449 return 1;
3452 /*ARGSUSED*/
3453 int
3454 cdisconnect(void *vp)
3456 (void)vp;
3457 noimap();
3458 return 1;
3461 /*ARGSUSED*/
3462 int
3463 ccache(void *vp)
3465 (void)vp;
3466 noimap();
3467 return 1;
3469 #endif /* USE_IMAP */
3471 time_t
3472 imap_read_date_time(const char *cp)
3474 time_t t;
3475 int i, year, month, day, hour, minute, second;
3476 int sign = -1;
3477 char buf[3];
3480 * "25-Jul-2004 15:33:44 +0200"
3481 * | | | | | |
3482 * 0 5 10 15 20 25
3484 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3485 goto invalid;
3486 day = strtol(&cp[1], NULL, 10);
3487 for (i = 0; month_names[i]; i++)
3488 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3489 break;
3490 if (month_names[i] == NULL)
3491 goto invalid;
3492 month = i + 1;
3493 year = strtol(&cp[8], NULL, 10);
3494 hour = strtol(&cp[13], NULL, 10);
3495 minute = strtol(&cp[16], NULL, 10);
3496 second = strtol(&cp[19], NULL, 10);
3497 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3498 (time_t)-1)
3499 goto invalid;
3500 switch (cp[22]) {
3501 case '-':
3502 sign = 1;
3503 break;
3504 case '+':
3505 break;
3506 default:
3507 goto invalid;
3509 buf[2] = '\0';
3510 buf[0] = cp[23];
3511 buf[1] = cp[24];
3512 t += strtol(buf, NULL, 10) * sign * 3600;
3513 buf[0] = cp[25];
3514 buf[1] = cp[26];
3515 t += strtol(buf, NULL, 10) * sign * 60;
3516 return t;
3517 invalid:
3518 time(&t);
3519 return t;
3522 time_t
3523 imap_read_date(const char *cp)
3525 time_t t;
3526 int year, month, day, i, tzdiff;
3527 struct tm *tmptr;
3528 char *xp, *yp;
3530 if (*cp == '"')
3531 cp++;
3532 day = strtol(cp, &xp, 10);
3533 if (day <= 0 || day > 31 || *xp++ != '-')
3534 return -1;
3535 for (i = 0; month_names[i]; i++)
3536 if (ascncasecmp(xp, month_names[i], 3) == 0)
3537 break;
3538 if (month_names[i] == NULL)
3539 return -1;
3540 month = i+1;
3541 if (xp[3] != '-')
3542 return -1;
3543 year = strtol(&xp[4], &yp, 10);
3544 if (year < 1970 || year > 2037 || yp != &xp[8])
3545 return -1;
3546 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3547 return -1;
3548 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3549 return -1;
3550 tzdiff = t - mktime(gmtime(&t));
3551 tmptr = localtime(&t);
3552 if (tmptr->tm_isdst > 0)
3553 tzdiff += 3600;
3554 t -= tzdiff;
3555 return t;
3558 const char *
3559 imap_make_date_time(time_t t)
3561 static char s[30];
3562 struct tm *tmptr;
3563 int tzdiff, tzdiff_hour, tzdiff_min;
3565 tzdiff = t - mktime(gmtime(&t));
3566 tzdiff_hour = (int)(tzdiff / 60);
3567 tzdiff_min = tzdiff_hour % 60;
3568 tzdiff_hour /= 60;
3569 tmptr = localtime(&t);
3570 if (tmptr->tm_isdst > 0)
3571 tzdiff_hour++;
3572 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3573 tmptr->tm_mday,
3574 month_names[tmptr->tm_mon],
3575 tmptr->tm_year + 1900,
3576 tmptr->tm_hour,
3577 tmptr->tm_min,
3578 tmptr->tm_sec,
3579 tzdiff_hour,
3580 tzdiff_min);
3581 return s;
3584 char *
3585 imap_quotestr(const char *s)
3587 char *n, *np;
3589 np = n = salloc(2 * strlen(s) + 3);
3590 *np++ = '"';
3591 while (*s) {
3592 if (*s == '"' || *s == '\\')
3593 *np++ = '\\';
3594 *np++ = *s++;
3596 *np++ = '"';
3597 *np = '\0';
3598 return n;
3601 char *
3602 imap_unquotestr(const char *s)
3604 char *n, *np;
3606 if (*s != '"')
3607 return savestr(s);
3608 np = n = salloc(strlen(s) + 1);
3609 while (*++s) {
3610 if (*s == '\\')
3611 s++;
3612 else if (*s == '"')
3613 break;
3614 *np++ = *s;
3616 *np = '\0';
3617 return n;