cmd3.c: fix compiler warnings..
[s-mailx.git] / imap.c
blob45499b83d0c1a372dd6469219dac8fea68fb1bff
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 2004
8 * Gunnar Ritter. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gunnar Ritter
21 * and his contributors.
22 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)imap.c 1.222 (gritter) 3/13/09";
42 #endif
43 #endif /* not lint */
45 #include "config.h"
48 * Mail -- a mail program
50 * IMAP v4r1 client following RFC 2060.
53 #include "rcv.h"
54 #include <errno.h>
55 #include <sys/stat.h>
56 #include <unistd.h>
57 #include <time.h>
59 #ifdef USE_IMAP
61 #include "md5.h"
63 #include <sys/socket.h>
64 #include <netdb.h>
65 #include <netinet/in.h>
66 #ifdef HAVE_ARPA_INET_H
67 #include <arpa/inet.h>
68 #endif /* HAVE_ARPA_INET_H */
70 #include "extern.h"
72 static int verbose;
74 #define IMAP_ANSWER() { \
75 if (mp->mb_type != MB_CACHE) { \
76 enum okay ok = OKAY; \
77 while (mp->mb_active & MB_COMD) \
78 ok = imap_answer(mp, 1); \
79 if (ok == STOP) \
80 return STOP; \
81 } \
83 #define IMAP_OUT(x, y, action) \
84 { \
85 if (mp->mb_type != MB_CACHE) { \
86 if (imap_finish(mp) == STOP) \
87 return STOP; \
88 if (verbose) \
89 fprintf(stderr, ">>> %s", x); \
90 mp->mb_active |= (y); \
91 if (swrite(&mp->mb_sock, x) == STOP) \
92 action; \
93 } else { \
94 if (queuefp != NULL) \
95 fputs(x, queuefp); \
96 } \
99 static struct record {
100 struct record *rec_next;
101 unsigned long rec_count;
102 enum rec_type {
103 REC_EXISTS,
104 REC_EXPUNGE
105 } rec_type;
106 } *record, *recend;
108 static enum {
109 RESPONSE_TAGGED,
110 RESPONSE_DATA,
111 RESPONSE_FATAL,
112 RESPONSE_CONT,
113 RESPONSE_ILLEGAL
114 } response_type;
116 static enum {
117 RESPONSE_OK,
118 RESPONSE_NO,
119 RESPONSE_BAD,
120 RESPONSE_PREAUTH,
121 RESPONSE_BYE,
122 RESPONSE_OTHER,
123 RESPONSE_UNKNOWN
124 } response_status;
126 static char *responded_tag;
127 static char *responded_text;
128 static char *responded_other_text;
129 static long responded_other_number;
131 static enum {
132 MAILBOX_DATA_FLAGS,
133 MAILBOX_DATA_LIST,
134 MAILBOX_DATA_LSUB,
135 MAILBOX_DATA_MAILBOX,
136 MAILBOX_DATA_SEARCH,
137 MAILBOX_DATA_STATUS,
138 MAILBOX_DATA_EXISTS,
139 MAILBOX_DATA_RECENT,
140 MESSAGE_DATA_EXPUNGE,
141 MESSAGE_DATA_FETCH,
142 CAPABILITY_DATA,
143 RESPONSE_OTHER_UNKNOWN
144 } response_other;
146 static enum list_attributes {
147 LIST_NONE = 000,
148 LIST_NOINFERIORS = 001,
149 LIST_NOSELECT = 002,
150 LIST_MARKED = 004,
151 LIST_UNMARKED = 010
152 } list_attributes;
154 static int list_hierarchy_delimiter;
155 static char *list_name;
157 struct list_item {
158 struct list_item *l_next;
159 char *l_name;
160 char *l_base;
161 enum list_attributes l_attr;
162 int l_delim;
163 int l_level;
164 int l_has_children;
167 static char *imapbuf;
168 static size_t imapbufsize;
169 static sigjmp_buf imapjmp;
170 static sighandler_type savealrm;
171 static int reset_tio;
172 static struct termios otio;
173 static int imapkeepalive;
174 static long had_exists = -1;
175 static long had_expunge = -1;
176 static long expunged_messages;
177 static volatile int imaplock;
179 static int same_imap_account;
181 static void imap_other_get(char *pp);
182 static void imap_response_get(const char **cp);
183 static void imap_response_parse(void);
184 static enum okay imap_answer(struct mailbox *mp, int errprnt);
185 static enum okay imap_parse_list(void);
186 static enum okay imap_finish(struct mailbox *mp);
187 static void imap_timer_off(void);
188 static void imapcatch(int s);
189 static void maincatch(int s);
190 static enum okay imap_noop1(struct mailbox *mp);
191 static void rec_queue(enum rec_type type, unsigned long count);
192 static enum okay rec_dequeue(void);
193 static void rec_rmqueue(void);
194 static void imapalarm(int s);
195 static int imap_use_starttls(const char *uhp);
196 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
197 const char *uhp);
198 static enum okay imap_capability(struct mailbox *mp);
199 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
200 char *xuser, const char *pass);
201 static enum okay imap_cram_md5(struct mailbox *mp,
202 char *xuser, const char *xpass);
203 static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass);
204 #ifdef USE_GSSAPI
205 static enum okay imap_gss(struct mailbox *mp, char *user);
206 #endif /* USE_GSSAPI */
207 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
208 static void imap_init(struct mailbox *mp, int n);
209 static void imap_setptr(struct mailbox *mp, int newmail, int transparent,
210 int *prevcount);
211 static char *imap_have_password(const char *server);
212 static void imap_split(char **server, const char **sp, int *use_ssl,
213 const char **cp, char **uhp, char **mbx,
214 const char **pass, char **user);
215 static int imap_setfile1(const char *xserver, int newmail, int isedit,
216 int transparent);
217 static int imap_fetchdata(struct mailbox *mp, struct message *m,
218 size_t expected, int need,
219 const char *head, size_t headsize, long headlines);
220 static void imap_putstr(struct mailbox *mp, struct message *m,
221 const char *str,
222 const char *head, size_t headsize, long headlines);
223 static enum okay imap_get(struct mailbox *mp, struct message *m,
224 enum needspec need);
225 static void commitmsg(struct mailbox *mp, struct message *to,
226 struct message from, enum havespec have);
227 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
228 int bot, int top);
229 static enum okay imap_exit(struct mailbox *mp);
230 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
231 needstat);
232 static enum okay imap_close(struct mailbox *mp);
233 static enum okay imap_update(struct mailbox *mp);
234 static enum okay imap_store(struct mailbox *mp, struct message *m,
235 int n, int c, const char *sp, int needstat);
236 static enum okay imap_unstore(struct message *m, int n, const char *flag);
237 static const char *tag(int new);
238 static char *imap_putflags(int f);
239 static void imap_getflags(const char *cp, char **xp, enum mflag *f);
240 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
241 off_t off1, long xsize, enum mflag flag, time_t t);
242 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
243 static enum okay imap_list1(struct mailbox *mp, const char *base,
244 struct list_item **list, struct list_item **lend, int level);
245 static enum okay imap_list(struct mailbox *mp, const char *base,
246 int strip, FILE *fp);
247 static void dopr(FILE *fp);
248 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
249 const char *name);
250 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
251 unsigned long *olduid, unsigned long *newuid);
252 static enum okay imap_appenduid_parse(const char *cp,
253 unsigned long *uidvalidity, unsigned long *uid);
254 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
255 const char *name);
256 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
257 long off1, long xsize, long size, long lines,
258 int flag, const char *name);
259 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
260 static enum okay imap_search2(struct mailbox *mp, struct message *m,
261 int count, const char *spec, int f);
262 static enum okay imap_remove1(struct mailbox *mp, const char *name);
263 static enum okay imap_rename1(struct mailbox *mp, const char *old,
264 const char *new);
265 static char *imap_strex(const char *cp, char **xp);
266 static enum okay check_expunged(void);
268 static void
269 imap_other_get(char *pp)
271 char *xp;
273 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
274 pp += 6;
275 response_other = MAILBOX_DATA_FLAGS;
276 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
277 pp += 5;
278 response_other = MAILBOX_DATA_LIST;
279 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
280 pp += 5;
281 response_other = MAILBOX_DATA_LSUB;
282 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
283 pp += 8;
284 response_other = MAILBOX_DATA_MAILBOX;
285 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
286 pp += 7;
287 response_other = MAILBOX_DATA_SEARCH;
288 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
289 pp += 7;
290 response_other = MAILBOX_DATA_STATUS;
291 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
292 pp += 11;
293 response_other = CAPABILITY_DATA;
294 } else {
295 responded_other_number = strtol(pp, &xp, 10);
296 while (*xp == ' ')
297 xp++;
298 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
299 response_other = MAILBOX_DATA_EXISTS;
300 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
301 response_other = MAILBOX_DATA_RECENT;
302 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
303 response_other = MESSAGE_DATA_EXPUNGE;
304 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
305 pp = &xp[6];
306 response_other = MESSAGE_DATA_FETCH;
307 } else
308 response_other = RESPONSE_OTHER_UNKNOWN;
310 responded_other_text = pp;
313 static void
314 imap_response_get(const char **cp)
316 if (ascncasecmp(*cp, "OK ", 3) == 0) {
317 *cp += 3;
318 response_status = RESPONSE_OK;
319 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
320 *cp += 3;
321 response_status = RESPONSE_NO;
322 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
323 *cp += 4;
324 response_status = RESPONSE_BAD;
325 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
326 *cp += 8;
327 response_status = RESPONSE_PREAUTH;
328 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
329 *cp += 4;
330 response_status = RESPONSE_BYE;
331 } else
332 response_status = RESPONSE_OTHER;
335 static void
336 imap_response_parse(void)
338 static char *parsebuf;
339 static size_t parsebufsize;
340 const char *ip = imapbuf;
341 char *pp;
343 if (parsebufsize < imapbufsize) {
344 free(parsebuf);
345 parsebuf = smalloc(parsebufsize = imapbufsize);
347 strcpy(parsebuf, imapbuf);
348 pp = parsebuf;
349 switch (*ip) {
350 case '+':
351 response_type = RESPONSE_CONT;
352 ip++;
353 pp++;
354 while (*ip == ' ') {
355 ip++;
356 pp++;
358 break;
359 case '*':
360 ip++;
361 pp++;
362 while (*ip == ' ') {
363 ip++;
364 pp++;
366 imap_response_get(&ip);
367 pp = &parsebuf[ip - imapbuf];
368 switch (response_status) {
369 case RESPONSE_BYE:
370 response_type = RESPONSE_FATAL;
371 break;
372 default:
373 response_type = RESPONSE_DATA;
375 break;
376 default:
377 responded_tag = parsebuf;
378 while (*pp && *pp != ' ')
379 pp++;
380 if (*pp == '\0') {
381 response_type = RESPONSE_ILLEGAL;
382 break;
384 *pp++ = '\0';
385 while (*pp && *pp == ' ')
386 pp++;
387 if (*pp == '\0') {
388 response_type = RESPONSE_ILLEGAL;
389 break;
391 ip = &imapbuf[pp - parsebuf];
392 response_type = RESPONSE_TAGGED;
393 imap_response_get(&ip);
394 pp = &parsebuf[ip - imapbuf];
396 responded_text = pp;
397 if (response_type != RESPONSE_CONT &&
398 response_type != RESPONSE_ILLEGAL &&
399 response_status == RESPONSE_OTHER)
400 imap_other_get(pp);
403 static enum okay
404 imap_answer(struct mailbox *mp, int errprnt)
406 int i, complete;
407 enum okay ok = STOP;
409 if (mp->mb_type == MB_CACHE)
410 return OKAY;
411 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
412 if (verbose)
413 fputs(imapbuf, stderr);
414 imap_response_parse();
415 if (response_type == RESPONSE_ILLEGAL)
416 goto again;
417 if (response_type == RESPONSE_CONT)
418 return OKAY;
419 if (response_status == RESPONSE_OTHER) {
420 if (response_other == MAILBOX_DATA_EXISTS) {
421 had_exists = responded_other_number;
422 rec_queue(REC_EXISTS, responded_other_number);
423 if (had_expunge > 0)
424 had_expunge = 0;
425 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
426 rec_queue(REC_EXPUNGE, responded_other_number);
427 if (had_expunge < 0)
428 had_expunge = 0;
429 had_expunge++;
430 expunged_messages++;
433 complete = 0;
434 if (response_type == RESPONSE_TAGGED) {
435 if (asccasecmp(responded_tag, tag(0)) == 0)
436 complete |= 1;
437 else
438 goto again;
440 switch (response_status) {
441 case RESPONSE_PREAUTH:
442 mp->mb_active &= ~MB_PREAUTH;
443 /*FALLTHRU*/
444 case RESPONSE_OK:
445 okay: ok = OKAY;
446 complete |= 2;
447 break;
448 case RESPONSE_NO:
449 case RESPONSE_BAD:
450 stop: ok = STOP;
451 complete |= 2;
452 if (errprnt)
453 fprintf(stderr, catgets(catd, CATSET, 218,
454 "IMAP error: %s"), responded_text);
455 break;
456 case RESPONSE_UNKNOWN: /* does not happen */
457 case RESPONSE_BYE:
458 i = mp->mb_active;
459 mp->mb_active = MB_NONE;
460 if (i & MB_BYE)
461 goto okay;
462 else
463 goto stop;
464 case RESPONSE_OTHER:
465 ok = OKAY;
467 if (response_status != RESPONSE_OTHER &&
468 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
469 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
470 if (complete == 3)
471 mp->mb_active &= ~MB_COMD;
472 } else {
473 ok = STOP;
474 mp->mb_active = MB_NONE;
476 return ok;
479 static enum okay
480 imap_parse_list(void)
482 char *cp;
484 cp = responded_other_text;
485 list_attributes = LIST_NONE;
486 if (*cp == '(') {
487 while (*cp && *cp != ')') {
488 if (*cp == '\\') {
489 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
490 == 0) {
491 list_attributes |= LIST_NOINFERIORS;
492 cp += 12;
493 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
494 == 0) {
495 list_attributes |= LIST_NOSELECT;
496 cp += 9;
497 } else if (ascncasecmp(&cp[1], "Marked ", 7)
498 == 0) {
499 list_attributes |= LIST_MARKED;
500 cp += 7;
501 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
502 == 0) {
503 list_attributes |= LIST_UNMARKED;
504 cp += 9;
507 cp++;
509 if (*++cp != ' ')
510 return STOP;
511 while (*cp == ' ')
512 cp++;
514 list_hierarchy_delimiter = EOF;
515 if (*cp == '"') {
516 if (*++cp == '\\')
517 cp++;
518 list_hierarchy_delimiter = *cp++ & 0377;
519 if (cp[0] != '"' || cp[1] != ' ')
520 return STOP;
521 cp++;
522 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
523 cp[3] == ' ') {
524 list_hierarchy_delimiter = EOF;
525 cp += 3;
527 while (*cp == ' ')
528 cp++;
529 list_name = cp;
530 while (*cp && *cp != '\r')
531 cp++;
532 *cp = '\0';
533 return OKAY;
536 static enum okay
537 imap_finish(struct mailbox *mp)
539 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
540 imap_answer(mp, 1);
541 return OKAY;
544 static void
545 imap_timer_off(void)
547 if (imapkeepalive > 0) {
548 alarm(0);
549 safe_signal(SIGALRM, savealrm);
553 static void
554 imapcatch(int s)
556 if (reset_tio)
557 tcsetattr(0, TCSADRAIN, &otio);
558 switch (s) {
559 case SIGINT:
560 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
561 siglongjmp(imapjmp, 1);
562 /*NOTREACHED*/
563 case SIGPIPE:
564 fprintf(stderr, "Received SIGPIPE during IMAP operation\n");
565 break;
569 static void
570 maincatch(int s)
572 if (interrupts++ == 0) {
573 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
574 return;
576 onintr(0);
579 static enum okay
580 imap_noop1(struct mailbox *mp)
582 char o[LINESIZE];
583 FILE *queuefp = NULL;
585 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
586 IMAP_OUT(o, MB_COMD, return STOP)
587 IMAP_ANSWER()
588 return OKAY;
591 enum okay
592 imap_noop(void)
594 sighandler_type saveint, savepipe;
595 enum okay ok = STOP;
597 (void)&saveint;
598 (void)&savepipe;
599 (void)&ok;
600 if (mb.mb_type != MB_IMAP)
601 return STOP;
602 verbose = value("verbose") != NULL;
603 imaplock = 1;
604 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
605 safe_signal(SIGINT, maincatch);
606 savepipe = safe_signal(SIGPIPE, SIG_IGN);
607 if (sigsetjmp(imapjmp, 1) == 0) {
608 if (savepipe != SIG_IGN)
609 safe_signal(SIGPIPE, imapcatch);
610 ok = imap_noop1(&mb);
612 safe_signal(SIGINT, saveint);
613 safe_signal(SIGPIPE, savepipe);
614 imaplock = 0;
615 if (interrupts)
616 onintr(0);
617 return ok;
620 static void
621 rec_queue(enum rec_type type, unsigned long count)
623 struct record *rp;
625 rp = scalloc(1, sizeof *rp);
626 rp->rec_type = type;
627 rp->rec_count = count;
628 if (record && recend) {
629 recend->rec_next = rp;
630 recend = rp;
631 } else
632 record = recend = rp;
635 static enum okay
636 rec_dequeue(void)
638 struct message *omessage;
639 enum okay ok = OKAY;
640 struct record *rp = record, *rq = NULL;
641 unsigned long exists = 0;
642 long i;
644 if (record == NULL)
645 return STOP;
646 omessage = message;
647 message = smalloc((msgCount+1) * sizeof *message);
648 if (msgCount)
649 memcpy(message, omessage, msgCount * sizeof *message);
650 memset(&message[msgCount], 0, sizeof *message);
651 while (rp) {
652 switch (rp->rec_type) {
653 case REC_EXISTS:
654 exists = rp->rec_count;
655 break;
656 case REC_EXPUNGE:
657 if (rp->rec_count == 0) {
658 ok = STOP;
659 break;
661 if (rp->rec_count > msgCount) {
662 if (exists == 0 || rp->rec_count > exists--)
663 ok = STOP;
664 break;
666 if (exists > 0)
667 exists--;
668 delcache(&mb, &message[rp->rec_count-1]);
669 memmove(&message[rp->rec_count-1],
670 &message[rp->rec_count],
671 (msgCount - rp->rec_count + 1) *
672 sizeof *message);
673 msgCount--;
675 * If the message was part of a collapsed thread,
676 * the m_collapsed field of one of its ancestors
677 * should be incremented. It seems hardly possible
678 * to do this with the current message structure,
679 * though. The result is that a '+' may be shown
680 * in the header summary even if no collapsed
681 * children exists.
683 break;
685 free(rq);
686 rq = rp;
687 rp = rp->rec_next;
689 free(rq);
690 record = recend = NULL;
691 if (ok == OKAY && exists > msgCount) {
692 message = srealloc(message,
693 (exists + 1) * sizeof *message);
694 memset(&message[msgCount], 0,
695 (exists - msgCount + 1) * sizeof *message);
696 for (i = msgCount; i < exists; i++)
697 imap_init(&mb, i);
698 imap_flags(&mb, msgCount+1, exists);
699 msgCount = exists;
701 if (ok == STOP) {
702 free(message);
703 message = omessage;
705 return ok;
708 static void
709 rec_rmqueue(void)
711 struct record *rp, *rq = NULL;
713 for (rp = record; rp; rp = rp->rec_next) {
714 free(rq);
715 rq = rp;
717 free(rq);
718 record = recend = NULL;
721 /*ARGSUSED*/
722 static void
723 imapalarm(int s)
725 sighandler_type saveint;
726 sighandler_type savepipe;
728 if (imaplock++ == 0) {
729 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
730 safe_signal(SIGINT, maincatch);
731 savepipe = safe_signal(SIGPIPE, SIG_IGN);
732 if (sigsetjmp(imapjmp, 1)) {
733 safe_signal(SIGINT, saveint);
734 safe_signal(SIGPIPE, savepipe);
735 goto brk;
737 if (savepipe != SIG_IGN)
738 safe_signal(SIGPIPE, imapcatch);
739 if (imap_noop1(&mb) != OKAY) {
740 safe_signal(SIGINT, saveint);
741 safe_signal(SIGPIPE, savepipe);
742 goto out;
744 safe_signal(SIGINT, saveint);
745 safe_signal(SIGPIPE, savepipe);
747 brk: alarm(imapkeepalive);
748 out: imaplock--;
751 static int
752 imap_use_starttls(const char *uhp)
754 char *var;
756 if (value("imap-use-starttls"))
757 return 1;
758 var = savecat("imap-use-starttls-", uhp);
759 return value(var) != NULL;
762 static enum okay
763 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
765 char *server, *cp;
767 mp->mb_active |= MB_PREAUTH;
768 imap_answer(mp, 1);
769 if ((cp = strchr(xserver, ':')) != NULL) {
770 server = salloc(cp - xserver + 1);
771 memcpy(server, xserver, cp - xserver);
772 server[cp - xserver] = '\0';
773 } else
774 server = (char *)xserver;
775 #ifdef USE_SSL
776 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
777 FILE *queuefp = NULL;
778 char o[LINESIZE];
780 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
781 IMAP_OUT(o, MB_COMD, return STOP);
782 IMAP_ANSWER()
783 if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
784 return STOP;
786 #else /* !USE_SSL */
787 if (imap_use_starttls(uhp)) {
788 fprintf(stderr, "No SSL support compiled in.\n");
789 return STOP;
791 #endif /* !USE_SSL */
792 imap_capability(mp);
793 return OKAY;
796 static enum okay
797 imap_capability(struct mailbox *mp)
799 char o[LINESIZE];
800 FILE *queuefp = NULL;
801 enum okay ok = STOP;
802 const char *cp;
804 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
805 IMAP_OUT(o, MB_COMD, return STOP)
806 while (mp->mb_active & MB_COMD) {
807 ok = imap_answer(mp, 0);
808 if (response_status == RESPONSE_OTHER &&
809 response_other == CAPABILITY_DATA) {
810 cp = responded_other_text;
811 while (*cp) {
812 while (spacechar(*cp&0377))
813 cp++;
814 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
815 spacechar(cp[7]&0377))
816 /* RFC 2359 */
817 mp->mb_flags |= MB_UIDPLUS;
818 while (*cp && !spacechar(*cp&0377))
819 cp++;
823 return ok;
826 static enum okay
827 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass)
829 char *var;
830 char *auth;
832 if (!(mp->mb_active & MB_PREAUTH))
833 return OKAY;
834 if ((auth = value("imap-auth")) == NULL) {
835 var = ac_alloc(strlen(uhp) + 11);
836 strcpy(var, "imap-auth-");
837 strcpy(&var[10], uhp);
838 auth = value(var);
839 ac_free(var);
841 if (auth == NULL || strcmp(auth, "login") == 0)
842 return imap_login(mp, xuser, pass);
843 if (strcmp(auth, "cram-md5") == 0)
844 return imap_cram_md5(mp, xuser, pass);
845 if (strcmp(auth, "gssapi") == 0) {
846 #ifdef USE_GSSAPI
847 return imap_gss(mp, xuser);
848 #else /* !USE_GSSAPI */
849 fprintf(stderr, "No GSSAPI support compiled in.\n");
850 return STOP;
851 #endif /* !USE_GSSAPI */
853 fprintf(stderr, "Unknown IMAP authentication method: \"%s\"\n", auth);
854 return STOP;
858 * Implementation of RFC 2194.
860 static enum okay
861 imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
863 char o[LINESIZE];
864 const char *user, *pass;
865 char *cp;
866 FILE *queuefp = NULL;
867 enum okay ok = STOP;
869 retry: if (xuser == NULL) {
870 if ((user = getuser()) == NULL)
871 return STOP;
872 } else
873 user = xuser;
874 if (xpass == NULL) {
875 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
876 return STOP;
877 } else
878 pass = xpass;
879 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
880 IMAP_OUT(o, 0, return STOP)
881 imap_answer(mp, 1);
882 if (response_type != RESPONSE_CONT)
883 return STOP;
884 cp = cram_md5_string(user, pass, responded_text);
885 IMAP_OUT(cp, MB_COMD, return STOP)
886 while (mp->mb_active & MB_COMD)
887 ok = imap_answer(mp, 1);
888 if (ok == STOP) {
889 xpass = NULL;
890 goto retry;
892 return ok;
895 static enum okay
896 imap_login(struct mailbox *mp, char *xuser, const char *xpass)
898 char o[LINESIZE];
899 const char *user, *pass;
900 FILE *queuefp = NULL;
901 enum okay ok = STOP;
903 retry: if (xuser == NULL) {
904 if ((user = getuser()) == NULL)
905 return STOP;
906 } else
907 user = xuser;
908 if (xpass == NULL) {
909 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
910 return STOP;
911 } else
912 pass = xpass;
913 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
914 tag(1), imap_quotestr(user), imap_quotestr(pass));
915 IMAP_OUT(o, MB_COMD, return STOP)
916 while (mp->mb_active & MB_COMD)
917 ok = imap_answer(mp, 1);
918 if (ok == STOP) {
919 xpass = NULL;
920 goto retry;
922 return OKAY;
925 #ifdef USE_GSSAPI
926 #include "imap_gssapi.c"
927 #endif /* USE_GSSAPI */
929 enum okay
930 imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
932 enum okay ok = OKAY;
933 char *cp;
934 char o[LINESIZE];
935 FILE *queuefp = NULL;
937 mp->mb_uidvalidity = 0;
938 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
939 IMAP_OUT(o, MB_COMD, return STOP)
940 while (mp->mb_active & MB_COMD) {
941 ok = imap_answer(mp, 1);
942 if (response_status != RESPONSE_OTHER &&
943 (cp = asccasestr(responded_text,
944 "[UIDVALIDITY ")) != NULL)
945 mp->mb_uidvalidity = atol(&cp[13]);
947 *count = had_exists > 0 ? had_exists : 0;
948 if (response_status != RESPONSE_OTHER &&
949 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
950 == 0)
951 mp->mb_perm = 0;
952 return ok;
955 static enum okay
956 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
958 char o[LINESIZE];
959 FILE *queuefp = NULL;
960 char *cp;
961 struct message *m;
962 unsigned x = X, y = Y;
963 int n;
965 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
966 IMAP_OUT(o, MB_COMD, return STOP)
967 while (mp->mb_active & MB_COMD) {
968 imap_answer(mp, 1);
969 if (response_status == RESPONSE_OTHER &&
970 response_other == MESSAGE_DATA_FETCH) {
971 n = responded_other_number;
972 if (n < x || n > y)
973 continue;
974 m = &message[n-1];
975 m->m_xsize = 0;
976 } else
977 continue;
978 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
979 cp += 5;
980 while (*cp == ' ')
981 cp++;
982 if (*cp == '(')
983 imap_getflags(cp, &cp, &m->m_flag);
985 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
986 m->m_uid = strtoul(&cp[4], NULL, 10);
987 getcache1(mp, m, NEED_UNSPEC, 1);
988 m->m_flag &= ~MHIDDEN;
990 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
991 x++;
992 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
993 y--;
994 if (x <= y) {
995 snprintf(o, sizeof o,
996 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
997 tag(1), x, y);
998 IMAP_OUT(o, MB_COMD, return STOP)
999 while (mp->mb_active & MB_COMD) {
1000 imap_answer(mp, 1);
1001 if (response_status == RESPONSE_OTHER &&
1002 response_other == MESSAGE_DATA_FETCH) {
1003 n = responded_other_number;
1004 if (n < x || n > y)
1005 continue;
1006 m = &message[n-1];
1007 } else
1008 continue;
1009 if ((cp = asccasestr(responded_other_text,
1010 "RFC822.SIZE ")) != NULL)
1011 m->m_xsize = strtol(&cp[12], NULL, 10);
1012 if ((cp = asccasestr(responded_other_text,
1013 "INTERNALDATE ")) != NULL)
1014 m->m_time = imap_read_date_time(&cp[13]);
1017 for (n = X; n <= Y; n++)
1018 putcache(mp, &message[n-1]);
1019 return OKAY;
1022 static void
1023 imap_init(struct mailbox *mp, int n)
1025 struct message *m = &message[n];
1027 m->m_flag = MUSED|MNOFROM;
1028 m->m_block = 0;
1029 m->m_offset = 0;
1032 static void
1033 imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
1035 struct message *omessage = 0;
1036 int i, omsgCount = 0;
1037 enum okay dequeued = STOP;
1039 if (newmail || transparent) {
1040 omessage = message;
1041 omsgCount = msgCount;
1043 if (newmail)
1044 dequeued = rec_dequeue();
1045 if (had_exists >= 0) {
1046 if (dequeued != OKAY)
1047 msgCount = had_exists;
1048 had_exists = -1;
1050 if (had_expunge >= 0) {
1051 if (dequeued != OKAY)
1052 msgCount -= had_expunge;
1053 had_expunge = -1;
1055 if (newmail && expunged_messages)
1056 printf("Expunged %ld message%s.\n",
1057 expunged_messages,
1058 expunged_messages != 1 ? "s" : "");
1059 *prevcount = omsgCount - expunged_messages;
1060 expunged_messages = 0;
1061 if (msgCount < 0) {
1062 fputs("IMAP error: Negative message count\n", stderr);
1063 msgCount = 0;
1065 if (dequeued != OKAY) {
1066 message = scalloc(msgCount + 1, sizeof *message);
1067 for (i = 0; i < msgCount; i++)
1068 imap_init(mp, i);
1069 if (!newmail && mp->mb_type == MB_IMAP)
1070 initcache(mp);
1071 if (msgCount > 0)
1072 imap_flags(mp, 1, msgCount);
1073 message[msgCount].m_size = 0;
1074 message[msgCount].m_lines = 0;
1075 rec_rmqueue();
1077 if (newmail || transparent)
1078 transflags(omessage, omsgCount, transparent);
1079 else
1080 setdot(message);
1083 static char *
1084 imap_have_password(const char *server)
1086 char *var, *cp;
1088 var = ac_alloc(strlen(server) + 10);
1089 strcpy(var, "password-");
1090 strcpy(&var[9], server);
1091 if ((cp = value(var)) != NULL)
1092 cp = savestr(cp);
1093 ac_free(var);
1094 return cp;
1097 static void
1098 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1099 char **uhp, char **mbx, const char **pass, char **user)
1101 *sp = *server;
1102 if (strncmp(*sp, "imap://", 7) == 0) {
1103 *sp = &(*sp)[7];
1104 *use_ssl = 0;
1105 #ifdef USE_SSL
1106 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1107 *sp = &(*sp)[8];
1108 *use_ssl = 1;
1109 #endif /* USE_SSL */
1111 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1112 *uhp = savestr((char *)(*sp));
1113 (*uhp)[*cp - *sp] = '\0';
1114 *mbx = (char *)&(*cp)[1];
1115 } else {
1116 if (*cp)
1117 (*server)[*cp - *server] = '\0';
1118 *uhp = (char *)(*sp);
1119 *mbx = "INBOX";
1121 *pass = imap_have_password(*uhp);
1122 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1123 *user = salloc(*cp - *uhp + 1);
1124 memcpy(*user, *uhp, *cp - *uhp);
1125 (*user)[*cp - *uhp] = '\0';
1126 *sp = &(*cp)[1];
1127 *user = strdec(*user);
1128 } else {
1129 *user = NULL;
1130 *sp = *uhp;
1134 int
1135 imap_setfile(const char *xserver, int newmail, int isedit)
1137 return imap_setfile1(xserver, newmail, isedit, 0);
1140 static int
1141 imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
1143 struct sock so;
1144 sighandler_type saveint;
1145 sighandler_type savepipe;
1146 char *server, *user, *account;
1147 const char *cp, *sp, *pass;
1148 char *uhp, *mbx;
1149 int use_ssl = 0;
1150 enum mbflags same_flags;
1151 int prevcount = 0;
1153 (void)&sp;
1154 (void)&use_ssl;
1155 (void)&saveint;
1156 (void)&savepipe;
1157 server = savestr((char *)xserver);
1158 verbose = value("verbose") != NULL;
1159 if (newmail) {
1160 saveint = safe_signal(SIGINT, SIG_IGN);
1161 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1162 if (saveint != SIG_IGN)
1163 safe_signal(SIGINT, imapcatch);
1164 if (savepipe != SIG_IGN)
1165 safe_signal(SIGPIPE, imapcatch);
1166 imaplock = 1;
1167 goto newmail;
1169 same_flags = mb.mb_flags;
1170 same_imap_account = 0;
1171 sp = protbase(server);
1172 if (mb.mb_imap_account) {
1173 if (mb.mb_sock.s_fd > 0 &&
1174 strcmp(mb.mb_imap_account, sp) == 0 &&
1175 disconnected(mb.mb_imap_account) == 0)
1176 same_imap_account = 1;
1178 account = sstrdup(sp);
1179 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1180 so.s_fd = -1;
1181 if (!same_imap_account) {
1182 if (!disconnected(account) &&
1183 sopen(sp, &so, use_ssl, uhp,
1184 use_ssl ? "imaps" : "imap", verbose) != OKAY)
1185 return -1;
1186 } else
1187 so = mb.mb_sock;
1188 if (!transparent)
1189 quit();
1190 edit = isedit;
1191 free(mb.mb_imap_account);
1192 mb.mb_imap_account = account;
1193 if (!same_imap_account) {
1194 if (mb.mb_sock.s_fd >= 0)
1195 sclose(&mb.mb_sock);
1197 same_imap_account = 0;
1198 if (!transparent) {
1199 if (mb.mb_itf) {
1200 fclose(mb.mb_itf);
1201 mb.mb_itf = NULL;
1203 if (mb.mb_otf) {
1204 fclose(mb.mb_otf);
1205 mb.mb_otf = NULL;
1207 free(mb.mb_imap_mailbox);
1208 mb.mb_imap_mailbox = sstrdup(mbx);
1209 initbox(server);
1211 mb.mb_type = MB_VOID;
1212 mb.mb_active = MB_NONE;;
1213 imaplock = 1;
1214 saveint = safe_signal(SIGINT, SIG_IGN);
1215 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1216 if (sigsetjmp(imapjmp, 1)) {
1217 sclose(&so);
1218 safe_signal(SIGINT, saveint);
1219 safe_signal(SIGPIPE, savepipe);
1220 imaplock = 0;
1221 return -1;
1223 if (saveint != SIG_IGN)
1224 safe_signal(SIGINT, imapcatch);
1225 if (savepipe != SIG_IGN)
1226 safe_signal(SIGPIPE, imapcatch);
1227 if (mb.mb_sock.s_fd < 0) {
1228 if (disconnected(mb.mb_imap_account)) {
1229 if (cache_setptr(transparent) == STOP)
1230 fprintf(stderr,
1231 "Mailbox \"%s\" is not cached.\n",
1232 server);
1233 goto done;
1235 if ((cp = value("imap-keepalive")) != NULL) {
1236 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1237 savealrm = safe_signal(SIGALRM, imapalarm);
1238 alarm(imapkeepalive);
1241 mb.mb_sock = so;
1242 mb.mb_sock.s_desc = "IMAP";
1243 mb.mb_sock.s_onclose = imap_timer_off;
1244 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1245 imap_auth(&mb, uhp, user, pass) != OKAY) {
1246 sclose(&mb.mb_sock);
1247 imap_timer_off();
1248 safe_signal(SIGINT, saveint);
1249 safe_signal(SIGPIPE, savepipe);
1250 imaplock = 0;
1251 return -1;
1253 } else /* same account */
1254 mb.mb_flags |= same_flags;
1255 mb.mb_perm = Rflag ? 0 : MB_DELE;
1256 mb.mb_type = MB_IMAP;
1257 cache_dequeue(&mb);
1258 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1259 /*sclose(&mb.mb_sock);
1260 imap_timer_off();*/
1261 safe_signal(SIGINT, saveint);
1262 safe_signal(SIGPIPE, savepipe);
1263 imaplock = 0;
1264 mb.mb_type = MB_VOID;
1265 return -1;
1267 newmail:
1268 imap_setptr(&mb, newmail, transparent, &prevcount);
1269 done: setmsize(msgCount);
1270 if (!newmail && !transparent)
1271 sawcom = 0;
1272 safe_signal(SIGINT, saveint);
1273 safe_signal(SIGPIPE, savepipe);
1274 imaplock = 0;
1275 if (!newmail && mb.mb_type == MB_IMAP)
1276 purgecache(&mb, message, msgCount);
1277 if ((newmail || transparent) && mb.mb_sorted) {
1278 mb.mb_threaded = 0;
1279 sort((void *)-1);
1281 if (!newmail && !edit && msgCount == 0) {
1282 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1283 value("emptystart") == NULL)
1284 fprintf(stderr, catgets(catd, CATSET, 258,
1285 "No mail at %s\n"), server);
1286 return 1;
1288 if (newmail)
1289 newmailinfo(prevcount);
1290 return 0;
1293 static int
1294 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1295 int need,
1296 const char *head, size_t headsize, long headlines)
1298 char *line = NULL, *lp;
1299 size_t linesize = 0, linelen, size = 0;
1300 int emptyline = 0, lines = 0, excess = 0;
1301 off_t offset;
1303 fseek(mp->mb_otf, 0L, SEEK_END);
1304 offset = ftell(mp->mb_otf);
1305 if (head)
1306 fwrite(head, 1, headsize, mp->mb_otf);
1307 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1308 lp = line;
1309 if (linelen > expected) {
1310 excess = linelen - expected;
1311 linelen = expected;
1314 * Need to mask 'From ' lines. This cannot be done properly
1315 * since some servers pass them as 'From ' and others as
1316 * '>From '. Although one could identify the first kind of
1317 * server in principle, it is not possible to identify the
1318 * second as '>From ' may also come from a server of the
1319 * first type as actual data. So do what is absolutely
1320 * necessary only - mask 'From '.
1322 * If the line is the first line of the message header, it
1323 * is likely a real 'From ' line. In this case, it is just
1324 * ignored since it violates all standards.
1326 if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
1327 lp[3] == 'm' && lp[4] == ' ') {
1328 if (lines + headlines != 0) {
1329 fputc('>', mp->mb_otf);
1330 size++;
1331 } else
1332 goto skip;
1334 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1335 lp[linelen-2] == '\r')) {
1336 emptyline = linelen <= 2;
1337 if (linelen > 2) {
1338 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1339 size += linelen - 1;
1340 } else
1341 size++;
1342 fputc('\n', mp->mb_otf);
1343 } else {
1344 emptyline = 0;
1345 fwrite(lp, 1, linelen, mp->mb_otf);
1346 size += linelen;
1348 lines++;
1349 skip: if ((expected -= linelen) <= 0)
1350 break;
1352 if (!emptyline) {
1354 * This is very ugly; but some IMAP daemons don't end a
1355 * message with \r\n\r\n, and we need \n\n for mbox format.
1357 fputc('\n', mp->mb_otf);
1358 lines++;
1359 size++;
1361 fflush(mp->mb_otf);
1362 if (m != NULL) {
1363 m->m_size = size + headsize;
1364 m->m_lines = lines + headlines;
1365 m->m_block = mailx_blockof(offset);
1366 m->m_offset = mailx_offsetof(offset);
1367 switch (need) {
1368 case NEED_HEADER:
1369 m->m_have |= HAVE_HEADER;
1370 break;
1371 case NEED_BODY:
1372 m->m_have |= HAVE_HEADER|HAVE_BODY;
1373 m->m_xlines = m->m_lines;
1374 m->m_xsize = m->m_size;
1375 break;
1378 free(line);
1379 return excess;
1382 static void
1383 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1384 const char *head, size_t headsize, long headlines)
1386 off_t offset;
1387 size_t len;
1389 len = strlen(str);
1390 fseek(mp->mb_otf, 0L, SEEK_END);
1391 offset = ftell(mp->mb_otf);
1392 if (head)
1393 fwrite(head, 1, headsize, mp->mb_otf);
1394 if (len > 0) {
1395 fwrite(str, 1, len, mp->mb_otf);
1396 fputc('\n', mp->mb_otf);
1397 len++;
1399 fflush(mp->mb_otf);
1400 if (m != NULL) {
1401 m->m_size = headsize + len;
1402 m->m_lines = headlines + 1;
1403 m->m_block = mailx_blockof(offset);
1404 m->m_offset = mailx_offsetof(offset);
1405 m->m_have |= HAVE_HEADER|HAVE_BODY;
1406 m->m_xlines = m->m_lines;
1407 m->m_xsize = m->m_size;
1411 static enum okay
1412 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1414 sighandler_type saveint = SIG_IGN;
1415 sighandler_type savepipe = SIG_IGN;
1416 char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL;
1417 size_t expected, headsize = 0;
1418 int number = m - message + 1;
1419 enum okay ok = STOP;
1420 FILE *queuefp = NULL;
1421 char *head = NULL;
1422 long headlines = 0;
1423 struct message mt;
1424 long n = -1;
1425 unsigned long u = 0;
1427 (void)&saveint;
1428 (void)&savepipe;
1429 (void)&number;
1430 (void)&need;
1431 (void)&cp;
1432 (void)&loc;
1433 (void)&ok;
1434 (void)&headsize;
1435 (void)&head;
1436 (void)&headlines;
1437 (void)&item;
1438 (void)&resp;
1439 (void)&u;
1440 verbose = value("verbose") != NULL;
1441 if (getcache(mp, m, need) == OKAY)
1442 return OKAY;
1443 if (mp->mb_type == MB_CACHE) {
1444 fprintf(stderr, "Message %u not available.\n", number);
1445 return STOP;
1447 if (mp->mb_sock.s_fd < 0) {
1448 fprintf(stderr, "IMAP connection closed.\n");
1449 return STOP;
1451 switch (need) {
1452 case NEED_HEADER:
1453 resp = item = "RFC822.HEADER";
1454 break;
1455 case NEED_BODY:
1456 item = "BODY.PEEK[]";
1457 resp = "BODY[]";
1458 if (m->m_flag & HAVE_HEADER && m->m_size) {
1459 char *hdr = smalloc(m->m_size);
1460 fflush(mp->mb_otf);
1461 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1462 m->m_offset), SEEK_SET) < 0 ||
1463 fread(hdr, 1, m->m_size, mp->mb_itf)
1464 != m->m_size) {
1465 free(hdr);
1466 break;
1468 head = hdr;
1469 headsize = m->m_size;
1470 headlines = m->m_lines;
1471 item = "BODY.PEEK[TEXT]";
1472 resp = "BODY[TEXT]";
1474 break;
1475 case NEED_UNSPEC:
1476 return STOP;
1478 imaplock = 1;
1479 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1480 if (sigsetjmp(imapjmp, 1)) {
1481 safe_signal(SIGINT, saveint);
1482 safe_signal(SIGPIPE, savepipe);
1483 imaplock = 0;
1484 return STOP;
1486 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1487 safe_signal(SIGINT, maincatch);
1488 if (savepipe != SIG_IGN)
1489 safe_signal(SIGPIPE, imapcatch);
1490 if (m->m_uid)
1491 snprintf(o, sizeof o,
1492 "%s UID FETCH %lu (%s)\r\n",
1493 tag(1), m->m_uid, item);
1494 else {
1495 if (check_expunged() == STOP)
1496 goto out;
1497 snprintf(o, sizeof o,
1498 "%s FETCH %u (%s)\r\n",
1499 tag(1), number, item);
1501 IMAP_OUT(o, MB_COMD, goto out)
1502 for (;;) {
1503 ok = imap_answer(mp, 1);
1504 if (ok == STOP)
1505 break;
1506 if (response_status != RESPONSE_OTHER ||
1507 response_other != MESSAGE_DATA_FETCH)
1508 continue;
1509 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1510 continue;
1511 if (m->m_uid) {
1512 if ((cp = asccasestr(responded_other_text, "UID "))) {
1513 u = atol(&cp[4]);
1514 n = 0;
1515 } else {
1516 n = -1;
1517 u = 0;
1519 } else
1520 n = responded_other_number;
1521 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1522 if (m->m_uid ? m->m_uid != u : n != number)
1523 continue;
1524 if ((cp = strchr(loc, '"')) != NULL) {
1525 cp = imap_unquotestr(cp);
1526 imap_putstr(mp, m, cp,
1527 head, headsize, headlines);
1528 } else {
1529 m->m_have |= HAVE_HEADER|HAVE_BODY;
1530 m->m_xlines = m->m_lines;
1531 m->m_xsize = m->m_size;
1533 goto out;
1535 expected = atol(&cp[1]);
1536 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1537 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1538 continue;
1540 mt = *m;
1541 imap_fetchdata(mp, &mt, expected, need,
1542 head, headsize, headlines);
1543 if (n >= 0) {
1544 commitmsg(mp, m, mt, mt.m_have);
1545 break;
1547 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1548 &mp->mb_sock) > 0) {
1549 if (verbose)
1550 fputs(imapbuf, stderr);
1551 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1552 u = atol(&cp[4]);
1553 if (u == m->m_uid) {
1554 commitmsg(mp, m, mt, mt.m_have);
1555 break;
1560 out: while (mp->mb_active & MB_COMD)
1561 ok = imap_answer(mp, 1);
1562 if (saveint != SIG_IGN)
1563 safe_signal(SIGINT, saveint);
1564 if (savepipe != SIG_IGN)
1565 safe_signal(SIGPIPE, savepipe);
1566 imaplock--;
1567 if (ok == OKAY)
1568 putcache(mp, m);
1569 free(head);
1570 if (interrupts)
1571 onintr(0);
1572 return ok;
1575 enum okay
1576 imap_header(struct message *m)
1578 return imap_get(&mb, m, NEED_HEADER);
1582 enum okay
1583 imap_body(struct message *m)
1585 return imap_get(&mb, m, NEED_BODY);
1588 static void
1589 commitmsg(struct mailbox *mp, struct message *to,
1590 struct message from, enum havespec have)
1592 to->m_size = from.m_size;
1593 to->m_lines = from.m_lines;
1594 to->m_block = from.m_block;
1595 to->m_offset = from.m_offset;
1596 to->m_have = have;
1597 if (have & HAVE_BODY) {
1598 to->m_xlines = from.m_lines;
1599 to->m_xsize = from.m_size;
1601 putcache(mp, to);
1604 static enum okay
1605 imap_fetchheaders (
1606 struct mailbox *mp,
1607 struct message *m,
1608 int bot,
1609 int top /* bot > top */
1612 char o[LINESIZE], *cp;
1613 struct message mt;
1614 size_t expected;
1615 enum okay ok;
1616 int n = 0, u;
1617 FILE *queuefp = NULL;
1619 if (m[bot].m_uid)
1620 snprintf(o, sizeof o,
1621 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1622 tag(1), m[bot-1].m_uid, m[top-1].m_uid);
1623 else {
1624 if (check_expunged() == STOP)
1625 return STOP;
1626 snprintf(o, sizeof o,
1627 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1628 tag(1), bot, top);
1630 IMAP_OUT(o, MB_COMD, return STOP)
1631 for (;;) {
1632 ok = imap_answer(mp, 1);
1633 if (response_status != RESPONSE_OTHER)
1634 break;
1635 if (response_other != MESSAGE_DATA_FETCH)
1636 continue;
1637 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1638 return STOP;
1639 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1640 continue;
1641 expected = atol(&cp[1]);
1642 if (m[bot-1].m_uid) {
1643 if ((cp=asccasestr(responded_other_text, "UID "))) {
1644 u = atol(&cp[4]);
1645 for (n = bot; n <= top; n++)
1646 if (m[n-1].m_uid == u)
1647 break;
1648 if (n > top) {
1649 imap_fetchdata(mp, NULL, expected,
1650 NEED_HEADER,
1651 NULL, 0, 0);
1652 continue;
1654 } else
1655 n = -1;
1656 } else {
1657 n = responded_other_number;
1658 if (n <= 0 || n > msgCount) {
1659 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1660 NULL, 0, 0);
1661 continue;
1664 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1665 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1666 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1667 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1668 &mp->mb_sock) > 0) {
1669 if (verbose)
1670 fputs(imapbuf, stderr);
1671 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1672 u = atol(&cp[4]);
1673 for (n = bot; n <= top; n++)
1674 if (m[n-1].m_uid == u)
1675 break;
1676 if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
1677 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1678 n = 0;
1682 while (mp->mb_active & MB_COMD)
1683 ok = imap_answer(mp, 1);
1684 return ok;
1687 void
1688 imap_getheaders(int bot, int top)
1690 sighandler_type saveint, savepipe;
1691 enum okay ok = STOP;
1692 int i, chunk = 256;
1694 (void)&saveint;
1695 (void)&savepipe;
1696 (void)&ok;
1697 (void)&bot;
1698 (void)&top;
1699 verbose = value("verbose") != NULL;
1700 if (mb.mb_type == MB_CACHE)
1701 return;
1702 if (bot < 1)
1703 bot = 1;
1704 if (top > msgCount)
1705 top = msgCount;
1706 for (i = bot; i < top; i++) {
1707 if (message[i-1].m_have & HAVE_HEADER ||
1708 getcache(&mb, &message[i-1], NEED_HEADER)
1709 == OKAY)
1710 bot = i+1;
1711 else
1712 break;
1714 for (i = top; i > bot; i--) {
1715 if (message[i-1].m_have & HAVE_HEADER ||
1716 getcache(&mb, &message[i-1], NEED_HEADER)
1717 == OKAY)
1718 top = i-1;
1719 else
1720 break;
1722 if (bot >= top)
1723 return;
1724 imaplock = 1;
1725 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1726 safe_signal(SIGINT, maincatch);
1727 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1728 if (sigsetjmp(imapjmp, 1) == 0) {
1729 if (savepipe != SIG_IGN)
1730 safe_signal(SIGPIPE, imapcatch);
1731 for (i = bot; i <= top; i += chunk) {
1732 ok = imap_fetchheaders(&mb, message, i,
1733 i+chunk-1 < top ? i+chunk-1 : top);
1734 if (interrupts)
1735 onintr(0);
1738 safe_signal(SIGINT, saveint);
1739 safe_signal(SIGPIPE, savepipe);
1740 imaplock = 0;
1743 static enum okay
1744 imap_exit(struct mailbox *mp)
1746 char o[LINESIZE];
1747 FILE *queuefp = NULL;
1749 verbose = value("verbose") != NULL;
1750 mp->mb_active |= MB_BYE;
1751 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1752 IMAP_OUT(o, MB_COMD, return STOP)
1753 IMAP_ANSWER()
1754 return OKAY;
1757 static enum okay
1758 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1760 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1761 if (mp->mb_type == MB_IMAP)
1762 delcache(mp, m);
1763 return OKAY;
1766 static enum okay
1767 imap_close(struct mailbox *mp)
1769 char o[LINESIZE];
1770 FILE *queuefp = NULL;
1772 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1773 IMAP_OUT(o, MB_COMD, return STOP)
1774 IMAP_ANSWER()
1775 return OKAY;
1778 static enum okay
1779 imap_update(struct mailbox *mp)
1781 FILE *readstat = NULL;
1782 struct message *m;
1783 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1785 verbose = value("verbose") != NULL;
1786 if (Tflag != NULL) {
1787 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
1788 Tflag = NULL;
1790 if (!edit && mp->mb_perm != 0) {
1791 holdbits();
1792 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1793 if (m->m_flag & MBOX)
1794 c++;
1796 if (c > 0)
1797 if (makembox() == STOP)
1798 goto bypass;
1800 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1801 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
1802 char *id;
1804 if ((id = hfield("message-id", m)) != NULL ||
1805 (id = hfield("article-id", m)) != NULL)
1806 fprintf(readstat, "%s\n", id);
1808 if (mp->mb_perm == 0) {
1809 dodel = 0;
1810 } else if (edit) {
1811 dodel = m->m_flag & MDELETED;
1812 } else {
1813 dodel = !((m->m_flag&MPRESERVE) ||
1814 (m->m_flag&MTOUCH) == 0);
1817 * Fetch the result after around each 800 STORE commands
1818 * sent (approx. 32k data sent). Otherwise, servers will
1819 * try to flush the return queue at some point, leading
1820 * to a deadlock if we are still writing commands but not
1821 * reading their results.
1823 needstat = stored > 0 && stored % 800 == 0;
1825 * Even if this message has been deleted, continue
1826 * to set further flags. This is necessary to support
1827 * Gmail semantics, where "delete" actually means
1828 * "archive", and the flags are applied to the copy
1829 * in "All Mail".
1831 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1832 imap_store(mp, m, m-message+1,
1833 '+', "\\Seen", needstat);
1834 stored++;
1836 if (m->m_flag & MFLAG) {
1837 imap_store(mp, m, m-message+1,
1838 '+', "\\Flagged", needstat);
1839 stored++;
1841 if (m->m_flag & MUNFLAG) {
1842 imap_store(mp, m, m-message+1,
1843 '-', "\\Flagged", needstat);
1844 stored++;
1846 if (m->m_flag & MANSWER) {
1847 imap_store(mp, m, m-message+1,
1848 '+', "\\Answered", needstat);
1849 stored++;
1851 if (m->m_flag & MUNANSWER) {
1852 imap_store(mp, m, m-message+1,
1853 '-', "\\Answered", needstat);
1854 stored++;
1856 if (m->m_flag & MDRAFT) {
1857 imap_store(mp, m, m-message+1,
1858 '+', "\\Draft", needstat);
1859 stored++;
1861 if (m->m_flag & MUNDRAFT) {
1862 imap_store(mp, m, m-message+1,
1863 '-', "\\Draft", needstat);
1864 stored++;
1866 if (dodel) {
1867 imap_delete(mp, m-message+1, m, needstat);
1868 stored++;
1869 gotcha++;
1870 } else if (mp->mb_type != MB_CACHE ||
1871 !edit && (!(m->m_flag&(MBOXED|MSAVED|MDELETED))
1872 || (m->m_flag &
1873 (MBOXED|MPRESERVE|MTOUCH)) ==
1874 (MPRESERVE|MTOUCH)) ||
1875 edit && !(m->m_flag & MDELETED))
1876 held++;
1877 if (m->m_flag & MNEW) {
1878 m->m_flag &= ~MNEW;
1879 m->m_flag |= MSTATUS;
1882 bypass: if (readstat != NULL)
1883 Fclose(readstat);
1884 if (gotcha)
1885 imap_close(mp);
1886 for (m = &message[0]; m < &message[msgCount]; m++)
1887 if (!(m->m_flag&MUNLINKED) &&
1888 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1889 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1890 MDRAFT|MUNDRAFT)) {
1891 putcache(mp, m);
1892 modflags++;
1894 if ((gotcha || modflags) && edit) {
1895 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
1896 printf(value("bsdcompat") || value("bsdmsgs") ?
1897 catgets(catd, CATSET, 170, "complete\n") :
1898 catgets(catd, CATSET, 212, "updated.\n"));
1899 } else if (held && !edit && mp->mb_perm != 0) {
1900 if (held == 1)
1901 printf(catgets(catd, CATSET, 155,
1902 "Held 1 message in %s\n"), mailname);
1903 else if (held > 1)
1904 printf(catgets(catd, CATSET, 156,
1905 "Held %d messages in %s\n"), held, mailname);
1907 fflush(stdout);
1908 return OKAY;
1911 void
1912 imap_quit(void)
1914 sighandler_type saveint;
1915 sighandler_type savepipe;
1917 verbose = value("verbose") != NULL;
1918 if (mb.mb_type == MB_CACHE) {
1919 imap_update(&mb);
1920 return;
1922 if (mb.mb_sock.s_fd < 0) {
1923 fprintf(stderr, "IMAP connection closed.\n");
1924 return;
1926 imaplock = 1;
1927 saveint = safe_signal(SIGINT, SIG_IGN);
1928 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1929 if (sigsetjmp(imapjmp, 1)) {
1930 safe_signal(SIGINT, saveint);
1931 safe_signal(SIGPIPE, saveint);
1932 imaplock = 0;
1933 return;
1935 if (saveint != SIG_IGN)
1936 safe_signal(SIGINT, imapcatch);
1937 if (savepipe != SIG_IGN)
1938 safe_signal(SIGPIPE, imapcatch);
1939 imap_update(&mb);
1940 if (!same_imap_account) {
1941 imap_exit(&mb);
1942 sclose(&mb.mb_sock);
1944 safe_signal(SIGINT, saveint);
1945 safe_signal(SIGPIPE, savepipe);
1946 imaplock = 0;
1949 static enum okay
1950 imap_store(struct mailbox *mp, struct message *m, int n,
1951 int c, const char *sp, int needstat)
1953 char o[LINESIZE];
1954 FILE *queuefp = NULL;
1956 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1957 return STOP;
1958 if (m->m_uid)
1959 snprintf(o, sizeof o,
1960 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1961 tag(1), m->m_uid, c, sp);
1962 else {
1963 if (check_expunged() == STOP)
1964 return STOP;
1965 snprintf(o, sizeof o,
1966 "%s STORE %u %cFLAGS (%s)\r\n",
1967 tag(1), n, c, sp);
1969 IMAP_OUT(o, MB_COMD, return STOP)
1970 if (needstat)
1971 IMAP_ANSWER()
1972 else
1973 mb.mb_active &= ~MB_COMD;
1974 if (queuefp != NULL)
1975 Fclose(queuefp);
1976 return OKAY;
1979 enum okay
1980 imap_undelete(struct message *m, int n)
1982 return imap_unstore(m, n, "\\Deleted");
1985 enum okay
1986 imap_unread(struct message *m, int n)
1988 return imap_unstore(m, n, "\\Seen");
1991 static enum okay
1992 imap_unstore(struct message *m, int n, const char *flag)
1994 sighandler_type saveint, savepipe;
1995 enum okay ok = STOP;
1997 (void)&saveint;
1998 (void)&savepipe;
1999 (void)&ok;
2000 verbose = value("verbose") != NULL;
2001 imaplock = 1;
2002 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2003 safe_signal(SIGINT, maincatch);
2004 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2005 if (sigsetjmp(imapjmp, 1) == 0) {
2006 if (savepipe != SIG_IGN)
2007 safe_signal(SIGPIPE, imapcatch);
2008 ok = imap_store(&mb, m, n, '-', flag, 1);
2010 safe_signal(SIGINT, saveint);
2011 safe_signal(SIGPIPE, savepipe);
2012 imaplock = 0;
2013 if (interrupts)
2014 onintr(0);
2015 return ok;
2018 static const char *
2019 tag(int new)
2021 static char ts[20];
2022 static long n;
2024 if (new)
2025 n++;
2026 snprintf(ts, sizeof ts, "T%lu", n);
2027 return ts;
2030 int
2031 imap_imap(void *vp)
2033 sighandler_type saveint, savepipe;
2034 char o[LINESIZE];
2035 enum okay ok = STOP;
2036 struct mailbox *mp = &mb;
2037 FILE *queuefp = NULL;
2039 (void)&saveint;
2040 (void)&savepipe;
2041 (void)&ok;
2042 verbose = value("verbose") != NULL;
2043 if (mp->mb_type != MB_IMAP) {
2044 printf("Not operating on an IMAP mailbox.\n");
2045 return 1;
2047 imaplock = 1;
2048 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2049 safe_signal(SIGINT, maincatch);
2050 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2051 if (sigsetjmp(imapjmp, 1) == 0) {
2052 if (savepipe != SIG_IGN)
2053 safe_signal(SIGPIPE, imapcatch);
2054 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2055 IMAP_OUT(o, MB_COMD, goto out)
2056 while (mp->mb_active & MB_COMD) {
2057 ok = imap_answer(mp, 0);
2058 fputs(responded_text, stdout);
2061 out: safe_signal(SIGINT, saveint);
2062 safe_signal(SIGPIPE, savepipe);
2063 imaplock = 0;
2064 if (interrupts)
2065 onintr(0);
2066 return ok != OKAY;
2069 int
2070 imap_newmail(int autoinc)
2072 if (autoinc && had_exists < 0 && had_expunge < 0) {
2073 verbose = value("verbose") != NULL;
2074 imaplock = 1;
2075 imap_noop();
2076 imaplock = 0;
2078 if (had_exists == msgCount && had_expunge < 0)
2080 * Some servers always respond with EXISTS to NOOP. If
2081 * the mailbox has been changed but the number of messages
2082 * has not, an EXPUNGE must also had been sent; otherwise,
2083 * nothing has changed.
2085 had_exists = -1;
2086 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2089 static char *
2090 imap_putflags(int f)
2092 const char *cp;
2093 char *buf, *bp;
2095 bp = buf = salloc(100);
2096 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2097 *bp++ = '(';
2098 if (f & MREAD) {
2099 if (bp[-1] != '(')
2100 *bp++ = ' ';
2101 for (cp = "\\Seen"; *cp; cp++)
2102 *bp++ = *cp;
2104 if (f & MFLAGGED) {
2105 if (bp[-1] != '(')
2106 *bp++ = ' ';
2107 for (cp = "\\Flagged"; *cp; cp++)
2108 *bp++ = *cp;
2110 if (f & MANSWERED) {
2111 if (bp[-1] != '(')
2112 *bp++ = ' ';
2113 for (cp = "\\Answered"; *cp; cp++)
2114 *bp++ = *cp;
2116 if (f & MDRAFT) {
2117 if (bp[-1] != '(')
2118 *bp++ = ' ';
2119 for (cp = "\\Draft"; *cp; cp++)
2120 *bp++ = *cp;
2122 *bp++ = ')';
2123 *bp++ = ' ';
2125 *bp = '\0';
2126 return buf;
2129 static void
2130 imap_getflags(const char *cp, char **xp, enum mflag *f)
2132 while (*cp != ')') {
2133 if (*cp == '\\') {
2134 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2135 *f |= MREAD;
2136 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2137 *f |= MNEW;
2138 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2139 *f |= MDELETED;
2140 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2141 *f |= MFLAGGED;
2142 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2143 *f |= MANSWERED;
2144 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2145 *f |= MDRAFTED;
2147 cp++;
2149 if (xp)
2150 *xp = (char *)cp;
2153 static enum okay
2154 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2155 off_t off1, long xsize, enum mflag flag, time_t t)
2157 char o[LINESIZE];
2158 char *buf;
2159 size_t bufsize, buflen, count;
2160 enum okay ok = STOP;
2161 long size, lines, ysize;
2162 int twice = 0;
2163 FILE *queuefp = NULL;
2165 if (mp->mb_type == MB_CACHE) {
2166 queuefp = cache_queue(mp);
2167 if (queuefp == NULL)
2168 return STOP;
2169 ok = OKAY;
2171 buf = smalloc(bufsize = LINESIZE);
2172 buflen = 0;
2173 again: size = xsize;
2174 count = fsize(fp);
2175 fseek(fp, off1, SEEK_SET);
2176 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2177 tag(1), imap_quotestr(name),
2178 imap_putflags(flag),
2179 imap_make_date_time(t),
2180 size);
2181 IMAP_OUT(o, MB_COMD, goto out)
2182 while (mp->mb_active & MB_COMD) {
2183 ok = imap_answer(mp, twice);
2184 if (response_type == RESPONSE_CONT)
2185 break;
2187 if (mp->mb_type != MB_CACHE && ok == STOP) {
2188 if (twice == 0)
2189 goto trycreate;
2190 else
2191 goto out;
2193 lines = ysize = 0;
2194 while (size > 0) {
2195 fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2196 lines++;
2197 ysize += buflen;
2198 buf[buflen-1] = '\r';
2199 buf[buflen] = '\n';
2200 if (mp->mb_type != MB_CACHE)
2201 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2202 else if (queuefp)
2203 fwrite(buf, 1, buflen+1, queuefp);
2204 size -= buflen+1;
2206 if (mp->mb_type != MB_CACHE)
2207 swrite(&mp->mb_sock, "\r\n");
2208 else if (queuefp)
2209 fputs("\r\n", queuefp);
2210 while (mp->mb_active & MB_COMD) {
2211 ok = imap_answer(mp, 0);
2212 if (response_status == RESPONSE_NO /*&&
2213 ascncasecmp(responded_text,
2214 "[TRYCREATE] ", 12) == 0*/) {
2215 trycreate: if (twice++) {
2216 ok = STOP;
2217 goto out;
2219 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2220 tag(1),
2221 imap_quotestr(name));
2222 IMAP_OUT(o, MB_COMD, goto out);
2223 while (mp->mb_active & MB_COMD)
2224 ok = imap_answer(mp, 1);
2225 if (ok == STOP)
2226 goto out;
2227 imap_created_mailbox++;
2228 goto again;
2229 } else if (ok != OKAY)
2230 fprintf(stderr, "IMAP error: %s", responded_text);
2231 else if (response_status == RESPONSE_OK &&
2232 mp->mb_flags & MB_UIDPLUS)
2233 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2234 flag, name);
2236 out: if (queuefp != NULL)
2237 Fclose(queuefp);
2238 free(buf);
2239 return ok;
2242 static enum okay
2243 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2245 char *buf, *bp, *lp;
2246 size_t bufsize, buflen, count;
2247 off_t off1 = -1, offs;
2248 int inhead = 1;
2249 int flag = MNEW|MNEWEST;
2250 long size = 0;
2251 time_t tim;
2252 enum okay ok;
2254 buf = smalloc(bufsize = LINESIZE);
2255 buflen = 0;
2256 count = fsize(fp);
2257 offs = ftell(fp);
2258 time(&tim);
2259 do {
2260 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2261 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2262 if (off1 != (off_t)-1) {
2263 ok=imap_append1(mp, name, fp, off1,
2264 size, flag, tim);
2265 if (ok == STOP)
2266 return STOP;
2267 fseek(fp, offs+buflen, SEEK_SET);
2269 off1 = offs + buflen;
2270 size = 0;
2271 inhead = 1;
2272 flag = MNEW;
2273 if (bp != NULL)
2274 tim = unixtime(buf);
2275 } else
2276 size += buflen+1;
2277 offs += buflen;
2278 if (bp && buf[0] == '\n')
2279 inhead = 0;
2280 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2281 lp = &buf[6];
2282 while (whitechar(*lp&0377))
2283 lp++;
2284 if (*lp == ':')
2285 while (*++lp != '\0')
2286 switch (*lp) {
2287 case 'R':
2288 flag |= MREAD;
2289 break;
2290 case 'O':
2291 flag &= ~MNEW;
2292 break;
2294 } else if (bp && inhead &&
2295 ascncasecmp(buf, "x-status", 8) == 0) {
2296 lp = &buf[8];
2297 while (whitechar(*lp&0377))
2298 lp++;
2299 if (*lp == ':')
2300 while (*++lp != '\0')
2301 switch (*lp) {
2302 case 'F':
2303 flag |= MFLAGGED;
2304 break;
2305 case 'A':
2306 flag |= MANSWERED;
2307 break;
2308 case 'T':
2309 flag |= MDRAFTED;
2310 break;
2313 } while (bp != NULL);
2314 free(buf);
2315 return OKAY;
2318 enum okay
2319 imap_append(const char *xserver, FILE *fp)
2321 sighandler_type saveint, savepipe;
2322 char *server, *uhp, *mbx, *user;
2323 const char *sp, *cp, *pass;
2324 int use_ssl;
2325 enum okay ok = STOP;
2327 (void)&saveint;
2328 (void)&savepipe;
2329 (void)&ok;
2330 verbose = value("verbose") != NULL;
2331 server = savestr((char *)xserver);
2332 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2333 imaplock = 1;
2334 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2335 safe_signal(SIGINT, maincatch);
2336 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2337 if (sigsetjmp(imapjmp, 1))
2338 goto out;
2339 if (savepipe != SIG_IGN)
2340 safe_signal(SIGPIPE, imapcatch);
2341 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2342 mb.mb_imap_account &&
2343 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2344 ok = imap_append0(&mb, mbx, fp);
2346 else {
2347 struct mailbox mx;
2349 memset(&mx, 0, sizeof mx);
2350 if (disconnected(server) == 0) {
2351 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2352 use_ssl ? "imaps" : "imap",
2353 verbose) != OKAY)
2354 goto fail;
2355 mx.mb_sock.s_desc = "IMAP";
2356 mx.mb_type = MB_IMAP;
2357 mx.mb_imap_account = (char *)protbase(server);
2358 mx.mb_imap_mailbox = mbx;
2359 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2360 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2361 sclose(&mx.mb_sock);
2362 goto fail;
2364 ok = imap_append0(&mx, mbx, fp);
2365 imap_exit(&mx);
2366 sclose(&mx.mb_sock);
2367 } else {
2368 mx.mb_imap_account = (char *)protbase(server);
2369 mx.mb_imap_mailbox = mbx;
2370 mx.mb_type = MB_CACHE;
2371 ok = imap_append0(&mx, mbx, fp);
2373 fail:;
2375 out: safe_signal(SIGINT, saveint);
2376 safe_signal(SIGPIPE, savepipe);
2377 imaplock = 0;
2378 if (interrupts)
2379 onintr(0);
2380 return ok;
2383 static enum okay
2384 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2385 struct list_item **lend, int level)
2387 char o[LINESIZE];
2388 enum okay ok = STOP;
2389 char *cp;
2390 const char *bp;
2391 FILE *queuefp = NULL;
2392 struct list_item *lp;
2394 *list = *lend = NULL;
2395 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2396 tag(1), imap_quotestr(base));
2397 IMAP_OUT(o, MB_COMD, return STOP);
2398 while (mp->mb_active & MB_COMD) {
2399 ok = imap_answer(mp, 1);
2400 if (response_status == RESPONSE_OTHER &&
2401 response_other == MAILBOX_DATA_LIST &&
2402 imap_parse_list() == OKAY) {
2403 cp = imap_unquotestr(list_name);
2404 lp = csalloc(1, sizeof *lp);
2405 lp->l_name = cp;
2406 for (bp = base; *bp && *bp == *cp; bp++)
2407 cp++;
2408 lp->l_base = *cp ? cp : savestr(base);
2409 lp->l_attr = list_attributes;
2410 lp->l_level = level+1;
2411 lp->l_delim = list_hierarchy_delimiter;
2412 if (*list && *lend) {
2413 (*lend)->l_next = lp;
2414 *lend = lp;
2415 } else
2416 *list = *lend = lp;
2419 return ok;
2422 static enum okay
2423 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2425 struct list_item *list, *lend, *lp, *lx, *ly;
2426 int n;
2427 const char *bp;
2428 char *cp;
2429 int depth;
2431 verbose = value("verbose") != NULL;
2432 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2433 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2434 return STOP;
2435 if (list == NULL || lend == NULL)
2436 return OKAY;
2437 for (lp = list; lp; lp = lp->l_next)
2438 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2439 lp->l_level < depth &&
2440 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2441 cp = salloc((n = strlen(lp->l_name)) + 2);
2442 strcpy(cp, lp->l_name);
2443 cp[n] = lp->l_delim;
2444 cp[n+1] = '\0';
2445 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2446 lx && ly) {
2447 lp->l_has_children = 1;
2448 if (strcmp(cp, lx->l_name) == 0)
2449 lx = lx->l_next;
2450 if (lx) {
2451 lend->l_next = lx;
2452 lend = ly;
2456 for (lp = list; lp; lp = lp->l_next) {
2457 if (strip) {
2458 cp = lp->l_name;
2459 for (bp = base; *bp && *bp == *cp; bp++)
2460 cp++;
2461 } else
2462 cp = lp->l_name;
2463 if ((lp->l_attr&LIST_NOSELECT) == 0)
2464 fprintf(fp, "%s\n", *cp ? cp : base);
2465 else if (lp->l_has_children == 0)
2466 fprintf(fp, "%s%c\n", *cp ? cp : base,
2467 lp->l_delim != EOF ? lp->l_delim : '\n');
2469 return OKAY;
2472 void
2473 imap_folders(const char *name, int strip)
2475 sighandler_type saveint, savepipe;
2476 const char *fold, *cp, *sp;
2477 char *tempfn;
2478 FILE *fp;
2479 int columnize = is_a_tty[1];
2481 (void)&saveint;
2482 (void)&savepipe;
2483 (void)&fp;
2484 (void)&fold;
2485 cp = protbase(name);
2486 sp = mb.mb_imap_account;
2487 if (strcmp(cp, sp)) {
2488 fprintf(stderr, "Cannot list folders on other than the "
2489 "current IMAP account,\n\"%s\". "
2490 "Try \"folders @\".\n", sp);
2491 return;
2493 fold = protfile(name);
2494 if (columnize) {
2495 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2496 perror("tmpfile");
2497 return;
2499 rm(tempfn);
2500 Ftfree(&tempfn);
2501 } else
2502 fp = stdout;
2503 imaplock = 1;
2504 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2505 safe_signal(SIGINT, maincatch);
2506 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2507 if (sigsetjmp(imapjmp, 1))
2508 goto out;
2509 if (savepipe != SIG_IGN)
2510 safe_signal(SIGPIPE, imapcatch);
2511 if (mb.mb_type == MB_CACHE)
2512 cache_list(&mb, fold, strip, fp);
2513 else
2514 imap_list(&mb, fold, strip, fp);
2515 imaplock = 0;
2516 if (interrupts) {
2517 if (columnize)
2518 Fclose(fp);
2519 onintr(0);
2521 fflush(fp);
2522 if (columnize) {
2523 rewind(fp);
2524 if (fsize(fp) > 0)
2525 dopr(fp);
2526 else
2527 fprintf(stderr, "Folder not found.\n");
2529 out:
2530 safe_signal(SIGINT, saveint);
2531 safe_signal(SIGPIPE, savepipe);
2532 if (columnize)
2533 Fclose(fp);
2536 static void
2537 dopr(FILE *fp)
2539 char o[LINESIZE], *tempfn;
2540 int c;
2541 long n = 0, mx = 0, columns, width;
2542 FILE *out;
2544 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2545 perror("tmpfile");
2546 return;
2548 rm(tempfn);
2549 Ftfree(&tempfn);
2550 while ((c = getc(fp)) != EOF) {
2551 if (c == '\n') {
2552 if (n > mx)
2553 mx = n;
2554 n = 0;
2555 } else
2556 n++;
2558 rewind(fp);
2559 width = scrnwidth;
2560 if (mx < width / 2) {
2561 columns = width / (mx+2);
2562 snprintf(o, sizeof o,
2563 "sort | pr -%lu -w%lu -t",
2564 columns, width);
2565 } else
2566 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2567 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2568 try_pager(out);
2569 Fclose(out);
2572 static enum okay
2573 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2575 char o[LINESIZE];
2576 const char *qname;
2577 enum okay ok = STOP;
2578 int twice = 0;
2579 int stored = 0;
2580 FILE *queuefp = NULL;
2582 if (mp->mb_type == MB_CACHE) {
2583 if ((queuefp = cache_queue(mp)) == NULL)
2584 return STOP;
2585 ok = OKAY;
2587 qname = imap_quotestr(name = protfile(name));
2589 * Since it is not possible to set flags on the copy, recently
2590 * set flags must be set on the original to include it in the copy.
2592 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2593 imap_store(mp, m, n, '+', "\\Seen", 0);
2594 if (m->m_flag&MFLAG)
2595 imap_store(mp, m, n, '+', "\\Flagged", 0);
2596 if (m->m_flag&MUNFLAG)
2597 imap_store(mp, m, n, '-', "\\Flagged", 0);
2598 if (m->m_flag&MANSWER)
2599 imap_store(mp, m, n, '+', "\\Answered", 0);
2600 if (m->m_flag&MUNANSWER)
2601 imap_store(mp, m, n, '-', "\\Flagged", 0);
2602 if (m->m_flag&MDRAFT)
2603 imap_store(mp, m, n, '+', "\\Draft", 0);
2604 if (m->m_flag&MUNDRAFT)
2605 imap_store(mp, m, n, '-', "\\Draft", 0);
2606 again: if (m->m_uid)
2607 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2608 tag(1), m->m_uid, qname);
2609 else {
2610 if (check_expunged() == STOP)
2611 goto out;
2612 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2613 tag(1), n, qname);
2615 IMAP_OUT(o, MB_COMD, goto out)
2616 while (mp->mb_active & MB_COMD)
2617 ok = imap_answer(mp, twice);
2618 if (mp->mb_type == MB_IMAP &&
2619 mp->mb_flags & MB_UIDPLUS &&
2620 response_status == RESPONSE_OK)
2621 imap_copyuid(mp, m, name);
2622 if (response_status == RESPONSE_NO && twice++ == 0) {
2623 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2624 IMAP_OUT(o, MB_COMD, goto out)
2625 while (mp->mb_active & MB_COMD)
2626 ok = imap_answer(mp, 1);
2627 if (ok == OKAY) {
2628 imap_created_mailbox++;
2629 goto again;
2632 if (queuefp != NULL)
2633 Fclose(queuefp);
2635 * ... and reset the flag to its initial value so that
2636 * the 'exit' command still leaves the message unread.
2638 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2639 imap_store(mp, m, n, '-', "\\Seen", 0);
2640 stored++;
2642 if (m->m_flag&MFLAG) {
2643 imap_store(mp, m, n, '-', "\\Flagged", 0);
2644 stored++;
2646 if (m->m_flag&MUNFLAG) {
2647 imap_store(mp, m, n, '+', "\\Flagged", 0);
2648 stored++;
2650 if (m->m_flag&MANSWER) {
2651 imap_store(mp, m, n, '-', "\\Answered", 0);
2652 stored++;
2654 if (m->m_flag&MUNANSWER) {
2655 imap_store(mp, m, n, '+', "\\Answered", 0);
2656 stored++;
2658 if (m->m_flag&MDRAFT) {
2659 imap_store(mp, m, n, '-', "\\Draft", 0);
2660 stored++;
2662 if (m->m_flag&MUNDRAFT) {
2663 imap_store(mp, m, n, '+', "\\Draft", 0);
2664 stored++;
2666 if (stored) {
2667 mp->mb_active |= MB_COMD;
2668 imap_finish(mp);
2670 return ok;
2673 enum okay
2674 imap_copy(struct message *m, int n, const char *name)
2676 sighandler_type saveint, savepipe;
2677 enum okay ok = STOP;
2679 (void)&saveint;
2680 (void)&savepipe;
2681 (void)&ok;
2682 verbose = value("verbose") != NULL;
2683 imaplock = 1;
2684 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2685 safe_signal(SIGINT, maincatch);
2686 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2687 if (sigsetjmp(imapjmp, 1) == 0) {
2688 if (savepipe != SIG_IGN)
2689 safe_signal(SIGPIPE, imapcatch);
2690 ok = imap_copy1(&mb, m, n, name);
2692 safe_signal(SIGINT, saveint);
2693 safe_signal(SIGPIPE, savepipe);
2694 imaplock = 0;
2695 if (interrupts)
2696 onintr(0);
2697 return ok;
2700 static enum okay
2701 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2702 unsigned long *olduid, unsigned long *newuid)
2704 char *xp, *yp, *zp;
2706 *uidvalidity = strtoul(cp, &xp, 10);
2707 *olduid = strtoul(xp, &yp, 10);
2708 *newuid = strtoul(yp, &zp, 10);
2709 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2710 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2713 static enum okay
2714 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2715 unsigned long *uid)
2717 char *xp, *yp;
2719 *uidvalidity = strtoul(cp, &xp, 10);
2720 *uid = strtoul(xp, &yp, 10);
2721 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2722 yp > xp && *yp == ']';
2725 static enum okay
2726 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2728 const char *cp;
2729 unsigned long uidvalidity, olduid, newuid;
2730 struct mailbox xmb;
2731 struct message xm;
2733 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2734 imap_copyuid_parse(&cp[9], &uidvalidity,
2735 &olduid, &newuid) == STOP)
2736 return STOP;
2737 xmb = *mp;
2738 xmb.mb_cache_directory = NULL;
2739 xmb.mb_imap_mailbox = savestr((char *)name);
2740 xmb.mb_uidvalidity = uidvalidity;
2741 initcache(&xmb);
2742 if (m == NULL) {
2743 memset(&xm, 0, sizeof xm);
2744 xm.m_uid = olduid;
2745 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2746 return STOP;
2747 getcache(mp, &xm, NEED_HEADER);
2748 getcache(mp, &xm, NEED_BODY);
2749 } else {
2750 if ((m->m_flag & HAVE_HEADER) == 0)
2751 getcache(mp, m, NEED_HEADER);
2752 if ((m->m_flag & HAVE_BODY) == 0)
2753 getcache(mp, m, NEED_BODY);
2754 xm = *m;
2756 xm.m_uid = newuid;
2757 xm.m_flag &= ~MFULLYCACHED;
2758 putcache(&xmb, &xm);
2759 return OKAY;
2762 static enum okay
2763 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2764 long xsize, long size, long lines, int flag, const char *name)
2766 const char *cp;
2767 unsigned long uidvalidity, uid;
2768 struct mailbox xmb;
2769 struct message xm;
2771 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2772 imap_appenduid_parse(&cp[11], &uidvalidity,
2773 &uid) == STOP)
2774 return STOP;
2775 xmb = *mp;
2776 xmb.mb_cache_directory = NULL;
2777 xmb.mb_imap_mailbox = savestr((char *)name);
2778 xmb.mb_uidvalidity = uidvalidity;
2779 xmb.mb_otf = xmb.mb_itf = fp;
2780 initcache(&xmb);
2781 memset(&xm, 0, sizeof xm);
2782 xm.m_flag = flag&MREAD | MNEW;
2783 xm.m_time = t;
2784 xm.m_block = mailx_blockof(off1);
2785 xm.m_offset = mailx_offsetof(off1);
2786 xm.m_size = size;
2787 xm.m_xsize = xsize;
2788 xm.m_lines = xm.m_xlines = lines;
2789 xm.m_uid = uid;
2790 xm.m_have = HAVE_HEADER|HAVE_BODY;
2791 putcache(&xmb, &xm);
2792 return OKAY;
2795 static enum okay
2796 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2798 FILE *tp = NULL;
2799 time_t t;
2800 long size, xsize, ysize, lines;
2801 enum mflag flag = MNEW;
2802 char *name, *buf, *bp, *cp, *tempCopy;
2803 size_t bufsize, buflen, count;
2804 enum okay ok = STOP;
2806 buf = smalloc(bufsize = LINESIZE);
2807 buflen = 0;
2808 count = fsize(fp);
2809 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2810 goto stop;
2811 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2812 while (*bp == ' ')
2813 bp++;
2814 if ((cp = strrchr(bp, '{')) == NULL)
2815 goto stop;
2816 xsize = atol(&cp[1]) + 2;
2817 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2818 goto stop;
2819 while (*cp == ' ')
2820 cp++;
2821 if (*cp == '(') {
2822 imap_getflags(cp, &cp, &flag);
2823 while (*++cp == ' ');
2825 t = imap_read_date_time(cp);
2826 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2827 goto stop;
2828 rm(tempCopy);
2829 Ftfree(&tempCopy);
2830 size = xsize;
2831 ysize = lines = 0;
2832 while (size > 0) {
2833 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2834 goto stop;
2835 size -= buflen;
2836 buf[--buflen] = '\0';
2837 buf[buflen-1] = '\n';
2838 fwrite(buf, 1, buflen, tp);
2839 ysize += buflen;
2840 lines++;
2842 fflush(tp);
2843 rewind(tp);
2844 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2845 imap_unquotestr(name));
2846 ok = OKAY;
2847 stop: free(buf);
2848 if (tp)
2849 Fclose(tp);
2850 return ok;
2853 static enum okay
2854 imap_search2(struct mailbox *mp, struct message *m, int count,
2855 const char *spec, int f)
2857 char *o;
2858 size_t osize;
2859 FILE *queuefp = NULL;
2860 enum okay ok = STOP;
2861 int i;
2862 unsigned long n;
2863 const char *cp;
2864 char *xp, *cs, c;
2866 c = 0;
2867 for (cp = spec; *cp; cp++)
2868 c |= *cp;
2869 if (c & 0200) {
2870 cp = gettcharset();
2871 #ifdef HAVE_ICONV
2872 if (asccasecmp(cp, "utf-8")) {
2873 iconv_t it;
2874 char *sp, *nsp, *nspec;
2875 size_t sz, nsz;
2876 if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
2877 sz = strlen(spec) + 1;
2878 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2879 sp = (char *)spec;
2880 if (iconv(it, &sp, &sz, &nsp, &nsz)
2881 != (size_t)-1 && sz == 0) {
2882 spec = nspec;
2883 cp = "utf-8";
2885 iconv_close(it);
2888 #endif /* HAVE_ICONV */
2889 cp = imap_quotestr(cp);
2890 cs = salloc(n = strlen(cp) + 10);
2891 snprintf(cs, n, "CHARSET %s ", cp);
2892 } else
2893 cs = "";
2894 o = ac_alloc(osize = strlen(spec) + 60);
2895 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2896 IMAP_OUT(o, MB_COMD, goto out)
2897 while (mp->mb_active & MB_COMD) {
2898 ok = imap_answer(mp, 0);
2899 if (response_status == RESPONSE_OTHER &&
2900 response_other == MAILBOX_DATA_SEARCH) {
2901 xp = responded_other_text;
2902 while (*xp && *xp != '\r') {
2903 n = strtoul(xp, &xp, 10);
2904 for (i = 0; i < count; i++)
2905 if (m[i].m_uid == n &&
2906 (m[i].m_flag&MHIDDEN)
2907 == 0 &&
2908 (f == MDELETED ||
2909 (m[i].m_flag&MDELETED)
2910 == 0))
2911 mark(i+1, f);
2915 out: ac_free(o);
2916 return ok;
2919 enum okay
2920 imap_search1(const char *spec, int f)
2922 sighandler_type saveint, savepipe;
2923 enum okay ok = STOP;
2925 (void)&saveint;
2926 (void)&savepipe;
2927 (void)&ok;
2928 if (mb.mb_type != MB_IMAP)
2929 return STOP;
2930 verbose = value("verbose") != NULL;
2931 imaplock = 1;
2932 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2933 safe_signal(SIGINT, maincatch);
2934 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2935 if (sigsetjmp(imapjmp, 1) == 0) {
2936 if (savepipe != SIG_IGN)
2937 safe_signal(SIGPIPE, imapcatch);
2938 ok = imap_search2(&mb, message, msgCount, spec, f);
2940 safe_signal(SIGINT, saveint);
2941 safe_signal(SIGPIPE, savepipe);
2942 imaplock = 0;
2943 if (interrupts)
2944 onintr(0);
2945 return ok;
2948 int
2949 imap_thisaccount(const char *cp)
2951 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2952 return 0;
2953 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2954 mb.mb_imap_account == NULL)
2955 return 0;
2956 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2959 enum okay
2960 imap_remove(const char *name)
2962 sighandler_type saveint, savepipe;
2963 enum okay ok = STOP;
2965 (void)&saveint;
2966 (void)&savepipe;
2967 (void)&ok;
2968 verbose = value("verbose") != NULL;
2969 if (mb.mb_type != MB_IMAP) {
2970 fprintf(stderr, "Refusing to remove \"%s\" "
2971 "in disconnected mode.\n", name);
2972 return STOP;
2974 if (!imap_thisaccount(name)) {
2975 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2976 "server: \"%s\" not removed.\n", name);
2977 return STOP;
2979 imaplock = 1;
2980 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2981 safe_signal(SIGINT, maincatch);
2982 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2983 if (sigsetjmp(imapjmp, 1) == 0) {
2984 if (savepipe != SIG_IGN)
2985 safe_signal(SIGPIPE, imapcatch);
2986 ok = imap_remove1(&mb, protfile(name));
2988 safe_signal(SIGINT, saveint);
2989 safe_signal(SIGPIPE, savepipe);
2990 imaplock = 0;
2991 if (ok == OKAY)
2992 ok = cache_remove(name);
2993 if (interrupts)
2994 onintr(0);
2995 return ok;
2998 static enum okay
2999 imap_remove1(struct mailbox *mp, const char *name)
3001 FILE *queuefp = NULL;
3002 char *o;
3003 int os;
3004 enum okay ok = STOP;
3006 o = ac_alloc(os = 2*strlen(name) + 100);
3007 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3008 IMAP_OUT(o, MB_COMD, goto out)
3009 while (mp->mb_active & MB_COMD)
3010 ok = imap_answer(mp, 1);
3011 out: ac_free(o);
3012 return ok;
3015 enum okay
3016 imap_rename(const char *old, const char *new)
3018 sighandler_type saveint, savepipe;
3019 enum okay ok = STOP;
3021 (void)&saveint;
3022 (void)&savepipe;
3023 (void)&ok;
3024 verbose = value("verbose") != NULL;
3025 if (mb.mb_type != MB_IMAP) {
3026 fprintf(stderr, "Refusing to rename mailboxes "
3027 "in disconnected mode.\n");
3028 return STOP;
3030 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3031 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3032 "server: \"%s\" not renamed to \"%s\".\n",
3033 old, new);
3034 return STOP;
3036 imaplock = 1;
3037 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3038 safe_signal(SIGINT, maincatch);
3039 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3040 if (sigsetjmp(imapjmp, 1) == 0) {
3041 if (savepipe != SIG_IGN)
3042 safe_signal(SIGPIPE, imapcatch);
3043 ok = imap_rename1(&mb, protfile(old), protfile(new));
3045 safe_signal(SIGINT, saveint);
3046 safe_signal(SIGPIPE, savepipe);
3047 imaplock = 0;
3048 if (ok == OKAY)
3049 ok = cache_rename(old, new);
3050 if (interrupts)
3051 onintr(0);
3052 return ok;
3055 static enum okay
3056 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3058 FILE *queuefp = NULL;
3059 char *o;
3060 int os;
3061 enum okay ok = STOP;
3063 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3064 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3065 imap_quotestr(old), imap_quotestr(new));
3066 IMAP_OUT(o, MB_COMD, goto out)
3067 while (mp->mb_active & MB_COMD)
3068 ok = imap_answer(mp, 1);
3069 out: ac_free(o);
3070 return ok;
3073 enum okay
3074 imap_dequeue(struct mailbox *mp, FILE *fp)
3076 FILE *queuefp = NULL;
3077 char o[LINESIZE], *newname;
3078 char *buf, *bp, *cp, iob[4096];
3079 size_t bufsize, buflen, count;
3080 enum okay ok = OKAY, rok = OKAY;
3081 long offs, offs1, offs2, octets;
3082 int n, twice, gotcha = 0;
3084 buf = smalloc(bufsize = LINESIZE);
3085 buflen = 0;
3086 count = fsize(fp);
3087 while (offs1 = ftell(fp),
3088 fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
3089 != NULL) {
3090 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3091 while (*bp == ' ')
3092 bp++;
3093 twice = 0;
3094 offs = ftell(fp);
3095 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3096 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3097 cp = &bp[9];
3098 while (digitchar(*cp&0377))
3099 cp++;
3100 if (*cp != ' ')
3101 goto fail;
3102 while (*cp == ' ')
3103 cp++;
3104 if ((newname = imap_strex(cp, NULL)) == NULL)
3105 goto fail;
3106 IMAP_OUT(o, MB_COMD, continue)
3107 while (mp->mb_active & MB_COMD)
3108 ok = imap_answer(mp, twice);
3109 if (response_status == RESPONSE_NO && twice++ == 0)
3110 goto trycreate;
3111 if (response_status == RESPONSE_OK &&
3112 mp->mb_flags & MB_UIDPLUS) {
3113 imap_copyuid(mp, NULL,
3114 imap_unquotestr(newname));
3116 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3117 IMAP_OUT(o, MB_COMD, continue)
3118 while (mp->mb_active & MB_COMD)
3119 ok = imap_answer(mp, 1);
3120 if (ok == OKAY)
3121 gotcha++;
3122 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3123 if ((cp = strrchr(bp, '{')) == NULL)
3124 goto fail;
3125 octets = atol(&cp[1]) + 2;
3126 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3127 goto fail;
3128 IMAP_OUT(o, MB_COMD, continue)
3129 while (mp->mb_active & MB_COMD) {
3130 ok = imap_answer(mp, twice);
3131 if (response_type == RESPONSE_CONT)
3132 break;
3134 if (ok == STOP) {
3135 if (twice++ == 0) {
3136 fseek(fp, offs, SEEK_SET);
3137 goto trycreate;
3139 goto fail;
3141 while (octets > 0) {
3142 n = octets > sizeof iob ? sizeof iob : octets;
3143 octets -= n;
3144 if (fread(iob, 1, n, fp) != n)
3145 goto fail;
3146 swrite1(&mp->mb_sock, iob, n, 1);
3148 swrite(&mp->mb_sock, "");
3149 while (mp->mb_active & MB_COMD) {
3150 ok = imap_answer(mp, 0);
3151 if (response_status == RESPONSE_NO &&
3152 twice++ == 0) {
3153 fseek(fp, offs, SEEK_SET);
3154 goto trycreate;
3157 if (response_status == RESPONSE_OK &&
3158 mp->mb_flags & MB_UIDPLUS) {
3159 offs2 = ftell(fp);
3160 fseek(fp, offs1, SEEK_SET);
3161 if (imap_appenduid_cached(mp, fp) == STOP) {
3162 fseek(fp, offs2, SEEK_SET);
3163 goto fail;
3166 } else {
3167 fail: fprintf(stderr,
3168 "Invalid command in IMAP cache queue: \"%s\"\n",
3169 bp);
3170 rok = STOP;
3172 continue;
3173 trycreate:
3174 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3175 tag(1), newname);
3176 IMAP_OUT(o, MB_COMD, continue)
3177 while (mp->mb_active & MB_COMD)
3178 ok = imap_answer(mp, 1);
3179 if (ok == OKAY)
3180 goto again;
3182 fflush(fp);
3183 rewind(fp);
3184 ftruncate(fileno(fp), 0);
3185 if (gotcha)
3186 imap_close(mp);
3187 return rok;
3190 static char *
3191 imap_strex(const char *cp, char **xp)
3193 const char *cq;
3194 char *n;
3196 if (*cp != '"')
3197 return NULL;
3198 for (cq = &cp[1]; *cq; cq++) {
3199 if (*cq == '\\')
3200 cq++;
3201 else if (*cq == '"')
3202 break;
3204 if (*cq != '"')
3205 return NULL;
3206 n = salloc(cq - cp + 2);
3207 memcpy(n, cp, cq - cp + 1);
3208 n[cq - cp + 1] = '\0';
3209 if (xp)
3210 *xp = (char *)&cq[1];
3211 return n;
3214 static enum okay
3215 check_expunged(void)
3217 if (expunged_messages > 0) {
3218 fprintf(stderr,
3219 "Command not executed - messages have been expunged\n");
3220 return STOP;
3222 return OKAY;
3225 /*ARGSUSED*/
3226 int
3227 cconnect(void *vp)
3229 char *cp, *cq;
3230 int omsgCount = msgCount;
3232 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3233 fprintf(stderr, "Already connected.\n");
3234 return 1;
3236 unset_allow_undefined = 1;
3237 unset_internal("disconnected");
3238 cp = protbase(mailname);
3239 if (strncmp(cp, "imap://", 7) == 0)
3240 cp += 7;
3241 else if (strncmp(cp, "imaps://", 8) == 0)
3242 cp += 8;
3243 if ((cq = strchr(cp, ':')) != NULL)
3244 *cq = '\0';
3245 unset_internal(savecat("disconnected-", cp));
3246 unset_allow_undefined = 0;
3247 if (mb.mb_type == MB_CACHE) {
3248 imap_setfile1(mailname, 0, edit, 1);
3249 if (msgCount > omsgCount)
3250 newmailinfo(omsgCount);
3252 return 0;
3255 int
3256 cdisconnect(void *vp)
3258 int *msgvec = vp;
3260 if (mb.mb_type == MB_CACHE) {
3261 fprintf(stderr, "Not connected.\n");
3262 return 1;
3263 } else if (mb.mb_type == MB_IMAP) {
3264 if (cached_uidvalidity(&mb) == 0) {
3265 fprintf(stderr, "The current mailbox is not cached.\n");
3266 return 1;
3269 if (*msgvec)
3270 ccache(vp);
3271 assign("disconnected", "");
3272 if (mb.mb_type == MB_IMAP) {
3273 sclose(&mb.mb_sock);
3274 imap_setfile1(mailname, 0, edit, 1);
3276 return 0;
3278 int
3279 ccache(void *vp)
3281 int *msgvec = vp, *ip;
3282 struct message *mp;
3284 if (mb.mb_type != MB_IMAP) {
3285 fprintf(stderr, "Not connected to an IMAP server.\n");
3286 return 1;
3288 if (cached_uidvalidity(&mb) == 0) {
3289 fprintf(stderr, "The current mailbox is not cached.\n");
3290 return 1;
3292 for (ip = msgvec; *ip; ip++) {
3293 mp = &message[*ip-1];
3294 if (!(mp->m_have & HAVE_BODY))
3295 get_body(mp);
3297 return 0;
3299 #else /* !USE_IMAP */
3301 #include "extern.h"
3303 static void
3304 noimap(void)
3306 fprintf(stderr, catgets(catd, CATSET, 269,
3307 "No IMAP support compiled in.\n"));
3310 int
3311 imap_setfile(const char *server, int newmail, int isedit)
3313 noimap();
3314 return -1;
3317 enum okay
3318 imap_header(struct message *mp)
3320 noimap();
3321 return STOP;
3324 enum okay
3325 imap_body(struct message *mp)
3327 noimap();
3328 return STOP;
3331 void
3332 imap_getheaders(int bot, int top)
3336 void
3337 imap_quit(void)
3339 noimap();
3342 /*ARGSUSED*/
3343 int
3344 imap_imap(void *vp)
3346 noimap();
3347 return 1;
3350 /*ARGSUSED*/
3351 int
3352 imap_newmail(int dummy)
3354 return 0;
3357 /*ARGSUSED*/
3358 enum okay
3359 imap_undelete(struct message *m, int n)
3361 return STOP;
3364 /*ARGSUSED*/
3365 enum okay
3366 imap_unread(struct message *m, int n)
3368 return STOP;
3371 /*ARGSUSED*/
3372 enum okay
3373 imap_append(const char *server, FILE *fp)
3375 noimap();
3376 return STOP;
3379 /*ARGSUSED*/
3380 void
3381 imap_folders(const char *name, int strip)
3383 noimap();
3386 /*ARGSUSED*/
3387 enum okay
3388 imap_remove(const char *name)
3390 noimap();
3391 return STOP;
3394 /*ARGSUSED*/
3395 enum okay
3396 imap_rename(const char *old, const char *new)
3398 noimap();
3399 return STOP;
3402 enum okay
3403 imap_copy(struct message *m, int n, const char *name)
3405 noimap();
3406 return STOP;
3409 /*ARGSUSED*/
3410 enum okay
3411 imap_search1(const char *spec, int f)
3413 return STOP;
3416 int
3417 imap_thisaccount(const char *cp)
3419 return 0;
3422 enum okay
3423 imap_noop(void)
3425 noimap();
3426 return STOP;
3429 /*ARGSUSED*/
3430 int
3431 cconnect(void *vp)
3433 noimap();
3434 return 1;
3437 /*ARGSUSED*/
3438 int
3439 cdisconnect(void *vp)
3441 noimap();
3442 return 1;
3445 /*ARGSUSED*/
3446 int
3447 ccache(void *vp)
3449 noimap();
3450 return 1;
3452 #endif /* USE_IMAP */
3454 time_t
3455 imap_read_date_time(const char *cp)
3457 time_t t;
3458 int i, year, month, day, hour, minute, second;
3459 int sign = -1;
3460 char buf[3];
3463 * "25-Jul-2004 15:33:44 +0200"
3464 * | | | | | |
3465 * 0 5 10 15 20 25
3467 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3468 goto invalid;
3469 day = strtol(&cp[1], NULL, 10);
3470 for (i = 0; month_names[i]; i++)
3471 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3472 break;
3473 if (month_names[i] == NULL)
3474 goto invalid;
3475 month = i + 1;
3476 year = strtol(&cp[8], NULL, 10);
3477 hour = strtol(&cp[13], NULL, 10);
3478 minute = strtol(&cp[16], NULL, 10);
3479 second = strtol(&cp[19], NULL, 10);
3480 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3481 (time_t)-1)
3482 goto invalid;
3483 switch (cp[22]) {
3484 case '-':
3485 sign = 1;
3486 break;
3487 case '+':
3488 break;
3489 default:
3490 goto invalid;
3492 buf[2] = '\0';
3493 buf[0] = cp[23];
3494 buf[1] = cp[24];
3495 t += strtol(buf, NULL, 10) * sign * 3600;
3496 buf[0] = cp[25];
3497 buf[1] = cp[26];
3498 t += strtol(buf, NULL, 10) * sign * 60;
3499 return t;
3500 invalid:
3501 time(&t);
3502 return t;
3505 time_t
3506 imap_read_date(const char *cp)
3508 time_t t;
3509 int year, month, day, i, tzdiff;
3510 struct tm *tmptr;
3511 char *xp, *yp;
3513 if (*cp == '"')
3514 cp++;
3515 day = strtol(cp, &xp, 10);
3516 if (day <= 0 || day > 31 || *xp++ != '-')
3517 return -1;
3518 for (i = 0; month_names[i]; i++)
3519 if (ascncasecmp(xp, month_names[i], 3) == 0)
3520 break;
3521 if (month_names[i] == NULL)
3522 return -1;
3523 month = i+1;
3524 if (xp[3] != '-')
3525 return -1;
3526 year = strtol(&xp[4], &yp, 10);
3527 if (year < 1970 || year > 2037 || yp != &xp[8])
3528 return -1;
3529 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3530 return -1;
3531 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3532 return -1;
3533 tzdiff = t - mktime(gmtime(&t));
3534 tmptr = localtime(&t);
3535 if (tmptr->tm_isdst > 0)
3536 tzdiff += 3600;
3537 t -= tzdiff;
3538 return t;
3541 const char *
3542 imap_make_date_time(time_t t)
3544 static char s[30];
3545 struct tm *tmptr;
3546 int tzdiff, tzdiff_hour, tzdiff_min;
3548 tzdiff = t - mktime(gmtime(&t));
3549 tzdiff_hour = (int)(tzdiff / 60);
3550 tzdiff_min = tzdiff_hour % 60;
3551 tzdiff_hour /= 60;
3552 tmptr = localtime(&t);
3553 if (tmptr->tm_isdst > 0)
3554 tzdiff_hour++;
3555 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3556 tmptr->tm_mday,
3557 month_names[tmptr->tm_mon],
3558 tmptr->tm_year + 1900,
3559 tmptr->tm_hour,
3560 tmptr->tm_min,
3561 tmptr->tm_sec,
3562 tzdiff_hour,
3563 tzdiff_min);
3564 return s;
3567 char *
3568 imap_quotestr(const char *s)
3570 char *n, *np;
3572 np = n = salloc(2 * strlen(s) + 3);
3573 *np++ = '"';
3574 while (*s) {
3575 if (*s == '"' || *s == '\\')
3576 *np++ = '\\';
3577 *np++ = *s++;
3579 *np++ = '"';
3580 *np = '\0';
3581 return n;
3584 char *
3585 imap_unquotestr(const char *s)
3587 char *n, *np;
3589 if (*s != '"')
3590 return savestr(s);
3591 np = n = salloc(strlen(s) + 1);
3592 while (*++s) {
3593 if (*s == '\\')
3594 s++;
3595 else if (*s == '"')
3596 break;
3597 *np++ = *s;
3599 *np = '\0';
3600 return n;