nail.h: resort a bit
[s-mailx.git] / imap.c
blob374607bdd4128bdcf9ef281cb49851546804d11b
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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 "nail.h"
42 #ifdef HAVE_IMAP
43 # include <sys/socket.h>
45 # include <netdb.h>
47 # include <netinet/in.h>
49 # ifdef HAVE_ARPA_INET_H
50 # include <arpa/inet.h>
51 # endif
52 # ifdef HAVE_MD5
53 # include "md5.h"
54 # endif
55 #endif
57 #ifdef HAVE_IMAP
58 #define IMAP_ANSWER() { \
59 if (mp->mb_type != MB_CACHE) { \
60 enum okay ok = OKAY; \
61 while (mp->mb_active & MB_COMD) \
62 ok = imap_answer(mp, 1); \
63 if (ok == STOP) \
64 return STOP; \
65 } \
67 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
68 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
69 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
70 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
71 * TODO I.e., that should be a function, not a macro ... or so.
72 * TODO This entire module needs MASSIVE work! */
73 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
74 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL)\
75 do {\
76 if (mp->mb_type != MB_CACHE) {\
77 if (imap_finish(mp) == STOP) {\
78 ACTIONBAIL;\
80 if (options & OPT_VERBOSE)\
81 fprintf(stderr, ">>> %s", X);\
82 mp->mb_active |= Y;\
83 if (swrite(&mp->mb_sock, X) == STOP) {\
84 ACTIONERR;\
86 } else {\
87 if (queuefp != NULL)\
88 fputs(X, queuefp);\
90 } while (0);
92 static struct record {
93 struct record *rec_next;
94 unsigned long rec_count;
95 enum rec_type {
96 REC_EXISTS,
97 REC_EXPUNGE
98 } rec_type;
99 } *record, *recend;
101 static enum {
102 RESPONSE_TAGGED,
103 RESPONSE_DATA,
104 RESPONSE_FATAL,
105 RESPONSE_CONT,
106 RESPONSE_ILLEGAL
107 } response_type;
109 static enum {
110 RESPONSE_OK,
111 RESPONSE_NO,
112 RESPONSE_BAD,
113 RESPONSE_PREAUTH,
114 RESPONSE_BYE,
115 RESPONSE_OTHER,
116 RESPONSE_UNKNOWN
117 } response_status;
119 static char *responded_tag;
120 static char *responded_text;
121 static char *responded_other_text;
122 static long responded_other_number;
124 static enum {
125 MAILBOX_DATA_FLAGS,
126 MAILBOX_DATA_LIST,
127 MAILBOX_DATA_LSUB,
128 MAILBOX_DATA_MAILBOX,
129 MAILBOX_DATA_SEARCH,
130 MAILBOX_DATA_STATUS,
131 MAILBOX_DATA_EXISTS,
132 MAILBOX_DATA_RECENT,
133 MESSAGE_DATA_EXPUNGE,
134 MESSAGE_DATA_FETCH,
135 CAPABILITY_DATA,
136 RESPONSE_OTHER_UNKNOWN
137 } response_other;
139 static enum list_attributes {
140 LIST_NONE = 000,
141 LIST_NOINFERIORS = 001,
142 LIST_NOSELECT = 002,
143 LIST_MARKED = 004,
144 LIST_UNMARKED = 010
145 } list_attributes;
147 static int list_hierarchy_delimiter;
148 static char *list_name;
150 struct list_item {
151 struct list_item *l_next;
152 char *l_name;
153 char *l_base;
154 enum list_attributes l_attr;
155 int l_delim;
156 int l_level;
157 int l_has_children;
160 static char *imapbuf; /* TODO not static, use pool */
161 static size_t imapbufsize;
162 static sigjmp_buf imapjmp;
163 static sighandler_type savealrm;
164 static int imapkeepalive;
165 static long had_exists = -1;
166 static long had_expunge = -1;
167 static long expunged_messages;
168 static volatile int imaplock;
170 static int same_imap_account;
172 static void imap_other_get(char *pp);
173 static void imap_response_get(const char **cp);
174 static void imap_response_parse(void);
175 static enum okay imap_answer(struct mailbox *mp, int errprnt);
176 static enum okay imap_parse_list(void);
177 static enum okay imap_finish(struct mailbox *mp);
178 static void imap_timer_off(void);
179 static void imapcatch(int s);
180 static void maincatch(int s);
181 static enum okay imap_noop1(struct mailbox *mp);
182 static void rec_queue(enum rec_type type, unsigned long cnt);
183 static enum okay rec_dequeue(void);
184 static void rec_rmqueue(void);
185 static void imapalarm(int s);
186 static int imap_use_starttls(const char *uhp);
187 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
188 const char *uhp);
189 static enum okay imap_capability(struct mailbox *mp);
190 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
191 char *xuser, char *pass);
192 #ifdef HAVE_MD5
193 static enum okay imap_cram_md5(struct mailbox *mp,
194 char *xuser, char *xpass);
195 #endif
196 static enum okay imap_login(struct mailbox *mp, char *xuser, char *xpass);
197 #ifdef HAVE_GSSAPI
198 static enum okay imap_gss(struct mailbox *mp, char *user);
199 #endif
200 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
201 static void imap_init(struct mailbox *mp, int n);
202 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
203 int *prevcount);
204 static void imap_split(char **server, const char **sp, int *use_ssl,
205 const char **cp, char const **uhp, char const **mbx,
206 char **pass, char **user);
207 static int imap_setfile1(const char *xserver, int nmail, int isedit,
208 int transparent);
209 static int imap_fetchdata(struct mailbox *mp, struct message *m,
210 size_t expected, int need,
211 const char *head, size_t headsize, long headlines);
212 static void imap_putstr(struct mailbox *mp, struct message *m,
213 const char *str,
214 const char *head, size_t headsize, long headlines);
215 static enum okay imap_get(struct mailbox *mp, struct message *m,
216 enum needspec need);
217 static void commitmsg(struct mailbox *mp, struct message *to,
218 struct message *from, enum havespec have);
219 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
220 int bot, int top);
221 static enum okay imap_exit(struct mailbox *mp);
222 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
223 needstat);
224 static enum okay imap_close(struct mailbox *mp);
225 static enum okay imap_update(struct mailbox *mp);
226 static enum okay imap_store(struct mailbox *mp, struct message *m,
227 int n, int c, const char *sp, int needstat);
228 static enum okay imap_unstore(struct message *m, int n, const char *flag);
229 static const char *tag(int new);
230 static char *imap_putflags(int f);
231 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
232 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
233 off_t off1, long xsize, enum mflag flag, time_t t);
234 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
235 static enum okay imap_list1(struct mailbox *mp, const char *base,
236 struct list_item **list, struct list_item **lend, int level);
237 static enum okay imap_list(struct mailbox *mp, const char *base,
238 int strip, FILE *fp);
239 static void dopr(FILE *fp);
240 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
241 const char *name);
242 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
243 unsigned long *olduid, unsigned long *newuid);
244 static enum okay imap_appenduid_parse(const char *cp,
245 unsigned long *uidvalidity, unsigned long *uid);
246 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
247 const char *name);
248 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
249 long off1, long xsize, long size, long lines,
250 int flag, const char *name);
251 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
252 static enum okay imap_search2(struct mailbox *mp, struct message *m,
253 int cnt, const char *spec, int f);
254 static enum okay imap_remove1(struct mailbox *mp, const char *name);
255 static enum okay imap_rename1(struct mailbox *mp, const char *old,
256 const char *new);
257 static char *imap_strex(char const *cp, char const **xp);
258 static enum okay check_expunged(void);
260 static void
261 imap_other_get(char *pp)
263 char *xp;
265 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
266 pp += 6;
267 response_other = MAILBOX_DATA_FLAGS;
268 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
269 pp += 5;
270 response_other = MAILBOX_DATA_LIST;
271 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
272 pp += 5;
273 response_other = MAILBOX_DATA_LSUB;
274 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
275 pp += 8;
276 response_other = MAILBOX_DATA_MAILBOX;
277 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
278 pp += 7;
279 response_other = MAILBOX_DATA_SEARCH;
280 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
281 pp += 7;
282 response_other = MAILBOX_DATA_STATUS;
283 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
284 pp += 11;
285 response_other = CAPABILITY_DATA;
286 } else {
287 responded_other_number = strtol(pp, &xp, 10);
288 while (*xp == ' ')
289 xp++;
290 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
291 response_other = MAILBOX_DATA_EXISTS;
292 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
293 response_other = MAILBOX_DATA_RECENT;
294 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
295 response_other = MESSAGE_DATA_EXPUNGE;
296 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
297 pp = &xp[6];
298 response_other = MESSAGE_DATA_FETCH;
299 } else
300 response_other = RESPONSE_OTHER_UNKNOWN;
302 responded_other_text = pp;
305 static void
306 imap_response_get(const char **cp)
308 if (ascncasecmp(*cp, "OK ", 3) == 0) {
309 *cp += 3;
310 response_status = RESPONSE_OK;
311 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
312 *cp += 3;
313 response_status = RESPONSE_NO;
314 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
315 *cp += 4;
316 response_status = RESPONSE_BAD;
317 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
318 *cp += 8;
319 response_status = RESPONSE_PREAUTH;
320 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
321 *cp += 4;
322 response_status = RESPONSE_BYE;
323 } else
324 response_status = RESPONSE_OTHER;
327 static void
328 imap_response_parse(void)
330 static char *parsebuf; /* TODO Use pool */
331 static size_t parsebufsize;
332 const char *ip = imapbuf;
333 char *pp;
335 if (parsebufsize < imapbufsize)
336 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
337 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
338 pp = parsebuf;
339 switch (*ip) {
340 case '+':
341 response_type = RESPONSE_CONT;
342 ip++;
343 pp++;
344 while (*ip == ' ') {
345 ip++;
346 pp++;
348 break;
349 case '*':
350 ip++;
351 pp++;
352 while (*ip == ' ') {
353 ip++;
354 pp++;
356 imap_response_get(&ip);
357 pp = &parsebuf[ip - imapbuf];
358 switch (response_status) {
359 case RESPONSE_BYE:
360 response_type = RESPONSE_FATAL;
361 break;
362 default:
363 response_type = RESPONSE_DATA;
365 break;
366 default:
367 responded_tag = parsebuf;
368 while (*pp && *pp != ' ')
369 pp++;
370 if (*pp == '\0') {
371 response_type = RESPONSE_ILLEGAL;
372 break;
374 *pp++ = '\0';
375 while (*pp && *pp == ' ')
376 pp++;
377 if (*pp == '\0') {
378 response_type = RESPONSE_ILLEGAL;
379 break;
381 ip = &imapbuf[pp - parsebuf];
382 response_type = RESPONSE_TAGGED;
383 imap_response_get(&ip);
384 pp = &parsebuf[ip - imapbuf];
386 responded_text = pp;
387 if (response_type != RESPONSE_CONT &&
388 response_type != RESPONSE_ILLEGAL &&
389 response_status == RESPONSE_OTHER)
390 imap_other_get(pp);
393 static enum okay
394 imap_answer(struct mailbox *mp, int errprnt)
396 int i, complete;
397 enum okay ok = STOP;
399 if (mp->mb_type == MB_CACHE)
400 return OKAY;
401 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
402 if (options & OPT_VERBOSE)
403 fputs(imapbuf, stderr);
404 imap_response_parse();
405 if (response_type == RESPONSE_ILLEGAL)
406 goto again;
407 if (response_type == RESPONSE_CONT)
408 return OKAY;
409 if (response_status == RESPONSE_OTHER) {
410 if (response_other == MAILBOX_DATA_EXISTS) {
411 had_exists = responded_other_number;
412 rec_queue(REC_EXISTS, responded_other_number);
413 if (had_expunge > 0)
414 had_expunge = 0;
415 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
416 rec_queue(REC_EXPUNGE, responded_other_number);
417 if (had_expunge < 0)
418 had_expunge = 0;
419 had_expunge++;
420 expunged_messages++;
423 complete = 0;
424 if (response_type == RESPONSE_TAGGED) {
425 if (asccasecmp(responded_tag, tag(0)) == 0)
426 complete |= 1;
427 else
428 goto again;
430 switch (response_status) {
431 case RESPONSE_PREAUTH:
432 mp->mb_active &= ~MB_PREAUTH;
433 /*FALLTHRU*/
434 case RESPONSE_OK:
435 okay: ok = OKAY;
436 complete |= 2;
437 break;
438 case RESPONSE_NO:
439 case RESPONSE_BAD:
440 stop: ok = STOP;
441 complete |= 2;
442 if (errprnt)
443 fprintf(stderr, tr(270, "IMAP error: %s"),
444 responded_text);
445 break;
446 case RESPONSE_UNKNOWN: /* does not happen */
447 case RESPONSE_BYE:
448 i = mp->mb_active;
449 mp->mb_active = MB_NONE;
450 if (i & MB_BYE)
451 goto okay;
452 else
453 goto stop;
454 case RESPONSE_OTHER:
455 ok = OKAY;
457 if (response_status != RESPONSE_OTHER &&
458 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
459 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
460 if (complete == 3)
461 mp->mb_active &= ~MB_COMD;
462 } else {
463 ok = STOP;
464 mp->mb_active = MB_NONE;
466 return ok;
469 static enum okay
470 imap_parse_list(void)
472 char *cp;
474 cp = responded_other_text;
475 list_attributes = LIST_NONE;
476 if (*cp == '(') {
477 while (*cp && *cp != ')') {
478 if (*cp == '\\') {
479 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
480 == 0) {
481 list_attributes |= LIST_NOINFERIORS;
482 cp += 12;
483 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
484 == 0) {
485 list_attributes |= LIST_NOSELECT;
486 cp += 9;
487 } else if (ascncasecmp(&cp[1], "Marked ", 7)
488 == 0) {
489 list_attributes |= LIST_MARKED;
490 cp += 7;
491 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
492 == 0) {
493 list_attributes |= LIST_UNMARKED;
494 cp += 9;
497 cp++;
499 if (*++cp != ' ')
500 return STOP;
501 while (*cp == ' ')
502 cp++;
504 list_hierarchy_delimiter = EOF;
505 if (*cp == '"') {
506 if (*++cp == '\\')
507 cp++;
508 list_hierarchy_delimiter = *cp++ & 0377;
509 if (cp[0] != '"' || cp[1] != ' ')
510 return STOP;
511 cp++;
512 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
513 cp[3] == ' ') {
514 list_hierarchy_delimiter = EOF;
515 cp += 3;
517 while (*cp == ' ')
518 cp++;
519 list_name = cp;
520 while (*cp && *cp != '\r')
521 cp++;
522 *cp = '\0';
523 return OKAY;
526 static enum okay
527 imap_finish(struct mailbox *mp)
529 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
530 imap_answer(mp, 1);
531 return OKAY;
534 static void
535 imap_timer_off(void)
537 if (imapkeepalive > 0) {
538 alarm(0);
539 safe_signal(SIGALRM, savealrm);
543 /* TODO OOH MY GOOOOOOOOD WE SIGLONGJMP() FROM WITHIN SIGNAL HANDLERS!!! */
544 static void
545 imapcatch(int s)
547 termios_state_reset();
548 switch (s) {
549 case SIGINT:
550 fprintf(stderr, tr(102, "Interrupt\n"));
551 siglongjmp(imapjmp, 1);
552 /*NOTREACHED*/
553 case SIGPIPE:
554 fprintf(stderr, tr(98,
555 "Received SIGPIPE during IMAP operation\n"));
556 break;
560 /* TODO OOOOOHH MYYYY GOOOOOD WE DO ALL SORTS OF UNSAFE STUFF IN SIGHDLSS! */
561 static void
562 maincatch(int s)
564 (void)s;
565 if (interrupts++ == 0) {
566 fprintf(stderr, tr(102, "Interrupt\n"));
567 return;
569 onintr(0);
572 static enum okay
573 imap_noop1(struct mailbox *mp)
575 char o[LINESIZE];
576 FILE *queuefp = NULL;
578 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
579 IMAP_OUT(o, MB_COMD, return STOP)
580 IMAP_ANSWER()
581 return OKAY;
584 char const *
585 imap_fileof(char const *xcp)
587 char const *cp = xcp;
588 int state = 0;
590 while (*cp) {
591 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
592 cp += 3;
593 state = 1;
595 if (cp[0] == '/' && state == 1)
596 return (cp + 1);
597 if (cp[0] == '/')
598 return (xcp);
599 ++cp;
601 return (cp);
604 enum okay
605 imap_noop(void)
607 sighandler_type saveint, savepipe;
608 enum okay ok = STOP;
610 (void)&saveint;
611 (void)&savepipe;
612 (void)&ok;
613 if (mb.mb_type != MB_IMAP)
614 return STOP;
615 imaplock = 1;
616 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
617 safe_signal(SIGINT, maincatch);
618 savepipe = safe_signal(SIGPIPE, SIG_IGN);
619 if (sigsetjmp(imapjmp, 1) == 0) {
620 if (savepipe != SIG_IGN)
621 safe_signal(SIGPIPE, imapcatch);
622 ok = imap_noop1(&mb);
624 safe_signal(SIGINT, saveint);
625 safe_signal(SIGPIPE, savepipe);
626 imaplock = 0;
627 if (interrupts)
628 onintr(0);
629 return ok;
632 static void
633 rec_queue(enum rec_type rt, unsigned long cnt)
635 struct record *rp;
637 rp = scalloc(1, sizeof *rp);
638 rp->rec_type = rt;
639 rp->rec_count = cnt;
640 if (record && recend) {
641 recend->rec_next = rp;
642 recend = rp;
643 } else
644 record = recend = rp;
647 static enum okay
648 rec_dequeue(void)
650 struct message *omessage;
651 enum okay ok = OKAY;
652 struct record *rp = record, *rq = NULL;
653 unsigned long exists = 0, i;
655 if (record == NULL)
656 return STOP;
657 omessage = message;
658 message = smalloc((msgCount+1) * sizeof *message);
659 if (msgCount)
660 memcpy(message, omessage, msgCount * sizeof *message);
661 memset(&message[msgCount], 0, sizeof *message);
662 while (rp) {
663 switch (rp->rec_type) {
664 case REC_EXISTS:
665 exists = rp->rec_count;
666 break;
667 case REC_EXPUNGE:
668 if (rp->rec_count == 0) {
669 ok = STOP;
670 break;
672 if (rp->rec_count > (unsigned long)msgCount) {
673 if (exists == 0 || rp->rec_count > exists--)
674 ok = STOP;
675 break;
677 if (exists > 0)
678 exists--;
679 delcache(&mb, &message[rp->rec_count-1]);
680 memmove(&message[rp->rec_count-1],
681 &message[rp->rec_count],
682 (msgCount - rp->rec_count + 1) *
683 sizeof *message);
684 msgCount--;
686 * If the message was part of a collapsed thread,
687 * the m_collapsed field of one of its ancestors
688 * should be incremented. It seems hardly possible
689 * to do this with the current message structure,
690 * though. The result is that a '+' may be shown
691 * in the header summary even if no collapsed
692 * children exists.
694 break;
696 if (rq != NULL)
697 free(rq);
698 rq = rp;
699 rp = rp->rec_next;
701 if (rq != NULL)
702 free(rq);
703 record = recend = NULL;
704 if (ok == OKAY && exists > (unsigned long)msgCount) {
705 message = srealloc(message,
706 (exists + 1) * sizeof *message);
707 memset(&message[msgCount], 0,
708 (exists - msgCount + 1) * sizeof *message);
709 for (i = msgCount; i < exists; i++)
710 imap_init(&mb, i);
711 imap_flags(&mb, msgCount+1, exists);
712 msgCount = exists;
714 if (ok == STOP) {
715 free(message);
716 message = omessage;
718 return ok;
721 static void
722 rec_rmqueue(void)
724 struct record *rp;
726 for (rp = record; rp != NULL;) {
727 struct record *tmp = rp;
728 rp = rp->rec_next;
729 free(tmp);
731 record = recend = NULL;
734 /*ARGSUSED*/
735 static void
736 imapalarm(int s)
738 sighandler_type saveint;
739 sighandler_type savepipe;
740 (void)s;
742 if (imaplock++ == 0) {
743 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
744 safe_signal(SIGINT, maincatch);
745 savepipe = safe_signal(SIGPIPE, SIG_IGN);
746 if (sigsetjmp(imapjmp, 1)) {
747 safe_signal(SIGINT, saveint);
748 safe_signal(SIGPIPE, savepipe);
749 goto brk;
751 if (savepipe != SIG_IGN)
752 safe_signal(SIGPIPE, imapcatch);
753 if (imap_noop1(&mb) != OKAY) {
754 safe_signal(SIGINT, saveint);
755 safe_signal(SIGPIPE, savepipe);
756 goto out;
758 safe_signal(SIGINT, saveint);
759 safe_signal(SIGPIPE, savepipe);
761 brk: alarm(imapkeepalive);
762 out: imaplock--;
765 static int
766 imap_use_starttls(const char *uhp)
768 char *var;
770 if (value("imap-use-starttls"))
771 return 1;
772 var = savecat("imap-use-starttls-", uhp);
773 return value(var) != NULL;
776 static enum okay
777 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
779 char *cp;
781 mp->mb_active |= MB_PREAUTH;
782 imap_answer(mp, 1);
783 if ((cp = strchr(xserver, ':')) != NULL) {
784 char *x = salloc(cp - xserver + 1);
785 memcpy(x, xserver, cp - xserver);
786 x[cp - xserver] = '\0';
787 xserver = x;
789 #ifdef HAVE_SSL
790 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
791 FILE *queuefp = NULL;
792 char o[LINESIZE];
794 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
795 IMAP_OUT(o, MB_COMD, return STOP)
796 IMAP_ANSWER()
797 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
798 return STOP;
800 #else
801 if (imap_use_starttls(uhp)) {
802 fprintf(stderr, "No SSL support compiled in.\n");
803 return STOP;
805 #endif
806 imap_capability(mp);
807 return OKAY;
810 static enum okay
811 imap_capability(struct mailbox *mp)
813 char o[LINESIZE];
814 FILE *queuefp = NULL;
815 enum okay ok = STOP;
816 const char *cp;
818 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
819 IMAP_OUT(o, MB_COMD, return STOP)
820 while (mp->mb_active & MB_COMD) {
821 ok = imap_answer(mp, 0);
822 if (response_status == RESPONSE_OTHER &&
823 response_other == CAPABILITY_DATA) {
824 cp = responded_other_text;
825 while (*cp) {
826 while (spacechar(*cp&0377))
827 cp++;
828 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
829 spacechar(cp[7]&0377))
830 /* RFC 2359 */
831 mp->mb_flags |= MB_UIDPLUS;
832 while (*cp && !spacechar(*cp&0377))
833 cp++;
837 return ok;
840 static enum okay
841 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, char *pass)
843 char *var;
844 char *auth;
846 if (!(mp->mb_active & MB_PREAUTH))
847 return OKAY;
848 if ((auth = value("imap-auth")) == NULL) {
849 size_t i = strlen(uhp) + 1;
850 var = ac_alloc(i + 10);
851 memcpy(var, "imap-auth-", 10);
852 memcpy(var + 10, uhp, i);
853 auth = value(var);
854 ac_free(var);
856 if (auth == NULL || strcmp(auth, "login") == 0)
857 return imap_login(mp, xuser, pass);
858 if (strcmp(auth, "cram-md5") == 0) {
859 #ifdef HAVE_MD5
860 return imap_cram_md5(mp, xuser, pass);
861 #else
862 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
863 return (STOP);
864 #endif
866 if (strcmp(auth, "gssapi") == 0) {
867 #ifdef HAVE_GSSAPI
868 return imap_gss(mp, xuser);
869 #else
870 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
871 return STOP;
872 #endif
874 fprintf(stderr, tr(273, "Unknown IMAP authentication method: %s\n"),
875 auth);
876 return STOP;
880 * Implementation of RFC 2194.
882 #ifdef HAVE_MD5
883 static enum okay
884 imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass)
886 char o[LINESIZE], *user, *pass, *cp;
887 FILE *queuefp = NULL;
888 enum okay ok = STOP;
890 jretry:
891 user = xuser;
892 pass = xpass;
893 if (! getcredentials(&user, &pass))
894 goto jleave;
896 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
897 IMAP_XOUT(o, 0, goto jleave, goto jleave);
898 imap_answer(mp, 1);
899 if (response_type != RESPONSE_CONT)
900 goto jleave;
902 cp = cram_md5_string(user, pass, responded_text);
903 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
904 while (mp->mb_active & MB_COMD)
905 ok = imap_answer(mp, 1);
906 if (ok == STOP) {
907 xpass = NULL;
908 goto jretry;
910 jleave:
911 return ok;
913 #endif /* HAVE_MD5 */
915 static enum okay
916 imap_login(struct mailbox *mp, char *xuser, char *xpass)
918 char o[LINESIZE];
919 char *user, *pass;
920 FILE *queuefp = NULL;
921 enum okay ok = STOP;
923 jretry:
924 user = xuser;
925 pass = xpass;
926 if (! getcredentials(&user, &pass))
927 goto jleave;
929 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
930 tag(1), imap_quotestr(user), imap_quotestr(pass));
931 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
932 while (mp->mb_active & MB_COMD)
933 ok = imap_answer(mp, 1);
934 if (ok == STOP) {
935 xpass = NULL;
936 goto jretry;
938 jleave:
939 return ok;
942 #ifdef HAVE_GSSAPI
943 # include "imap_gssapi.h"
944 #endif
946 enum okay
947 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
949 enum okay ok = OKAY;
950 char const*cp;
951 char o[LINESIZE];
952 FILE *queuefp = NULL;
953 (void)size;
955 mp->mb_uidvalidity = 0;
956 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
957 IMAP_OUT(o, MB_COMD, return STOP)
958 while (mp->mb_active & MB_COMD) {
959 ok = imap_answer(mp, 1);
960 if (response_status != RESPONSE_OTHER &&
961 (cp = asccasestr(responded_text,
962 "[UIDVALIDITY ")) != NULL)
963 mp->mb_uidvalidity = atol(&cp[13]);
965 *cnt = had_exists > 0 ? had_exists : 0;
966 if (response_status != RESPONSE_OTHER &&
967 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
968 == 0)
969 mp->mb_perm = 0;
970 return ok;
973 static enum okay
974 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
976 char o[LINESIZE];
977 FILE *queuefp = NULL;
978 char const *cp;
979 struct message *m;
980 unsigned x = X, y = Y, n;
982 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
983 IMAP_OUT(o, MB_COMD, return STOP)
984 while (mp->mb_active & MB_COMD) {
985 imap_answer(mp, 1);
986 if (response_status == RESPONSE_OTHER &&
987 response_other == MESSAGE_DATA_FETCH) {
988 n = responded_other_number;
989 if (n < x || n > y)
990 continue;
991 m = &message[n-1];
992 m->m_xsize = 0;
993 } else
994 continue;
995 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
996 cp += 5;
997 while (*cp == ' ')
998 cp++;
999 if (*cp == '(')
1000 imap_getflags(cp, &cp, &m->m_flag);
1002 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1003 m->m_uid = strtoul(&cp[4], NULL, 10);
1004 getcache1(mp, m, NEED_UNSPEC, 1);
1005 m->m_flag &= ~MHIDDEN;
1007 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1008 x++;
1009 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1010 y--;
1011 if (x <= y) {
1012 snprintf(o, sizeof o,
1013 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1014 tag(1), x, y);
1015 IMAP_OUT(o, MB_COMD, return STOP)
1016 while (mp->mb_active & MB_COMD) {
1017 imap_answer(mp, 1);
1018 if (response_status == RESPONSE_OTHER &&
1019 response_other == MESSAGE_DATA_FETCH) {
1020 n = responded_other_number;
1021 if (n < x || n > y)
1022 continue;
1023 m = &message[n-1];
1024 } else
1025 continue;
1026 if ((cp = asccasestr(responded_other_text,
1027 "RFC822.SIZE ")) != NULL)
1028 m->m_xsize = strtol(&cp[12], NULL, 10);
1029 if ((cp = asccasestr(responded_other_text,
1030 "INTERNALDATE ")) != NULL)
1031 m->m_time = imap_read_date_time(&cp[13]);
1034 for (n = X; n <= Y; n++)
1035 putcache(mp, &message[n-1]);
1036 return OKAY;
1039 static void
1040 imap_init(struct mailbox *mp, int n)
1042 struct message *m = &message[n];
1043 (void)mp;
1045 m->m_flag = MUSED|MNOFROM;
1046 m->m_block = 0;
1047 m->m_offset = 0;
1050 static void
1051 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1053 struct message *omessage = 0;
1054 int i, omsgCount = 0;
1055 enum okay dequeued = STOP;
1057 if (nmail || transparent) {
1058 omessage = message;
1059 omsgCount = msgCount;
1061 if (nmail)
1062 dequeued = rec_dequeue();
1063 if (had_exists >= 0) {
1064 if (dequeued != OKAY)
1065 msgCount = had_exists;
1066 had_exists = -1;
1068 if (had_expunge >= 0) {
1069 if (dequeued != OKAY)
1070 msgCount -= had_expunge;
1071 had_expunge = -1;
1073 if (nmail && expunged_messages)
1074 printf("Expunged %ld message%s.\n",
1075 expunged_messages,
1076 expunged_messages != 1 ? "s" : "");
1077 *prevcount = omsgCount - expunged_messages;
1078 expunged_messages = 0;
1079 if (msgCount < 0) {
1080 fputs("IMAP error: Negative message count\n", stderr);
1081 msgCount = 0;
1083 if (dequeued != OKAY) {
1084 message = scalloc(msgCount + 1, sizeof *message);
1085 for (i = 0; i < msgCount; i++)
1086 imap_init(mp, i);
1087 if (!nmail && mp->mb_type == MB_IMAP)
1088 initcache(mp);
1089 if (msgCount > 0)
1090 imap_flags(mp, 1, msgCount);
1091 message[msgCount].m_size = 0;
1092 message[msgCount].m_lines = 0;
1093 rec_rmqueue();
1095 if (nmail || transparent)
1096 transflags(omessage, omsgCount, transparent);
1097 else
1098 setdot(message);
1101 static void
1102 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1103 char const **uhp, char const **mbx, char **pass, char **user)
1105 *sp = *server;
1106 if (strncmp(*sp, "imap://", 7) == 0) {
1107 *sp = &(*sp)[7];
1108 *use_ssl = 0;
1109 #ifdef HAVE_SSL
1110 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1111 *sp = &(*sp)[8];
1112 *use_ssl = 1;
1113 #endif
1115 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1116 char *x = savestr(*sp);
1117 x[*cp - *sp] = '\0';
1118 *uhp = x;
1119 *mbx = &(*cp)[1];
1120 } else {
1121 if (*cp)
1122 (*server)[*cp - *server] = '\0';
1123 *uhp = *sp;
1124 *mbx = "INBOX";
1126 *pass = lookup_password_for_token(*uhp);
1127 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1128 *user = salloc(*cp - *uhp + 1);
1129 memcpy(*user, *uhp, *cp - *uhp);
1130 (*user)[*cp - *uhp] = '\0';
1131 *sp = &(*cp)[1];
1132 *user = urlxdec(*user);
1133 } else {
1134 *user = NULL;
1135 *sp = *uhp;
1139 int
1140 imap_setfile(const char *xserver, int nmail, int isedit)
1142 return imap_setfile1(xserver, nmail, isedit, 0);
1145 static int
1146 imap_setfile1(const char *xserver, int nmail, int isedit,
1147 int volatile transparent)
1149 struct sock so;
1150 sighandler_type volatile saveint, savepipe;
1151 char *server, *user, *pass, *acc;
1152 char const *cp, *sp, *mbx, *uhp;
1153 int use_ssl = 0, prevcount = 0;
1154 enum mbflags same_flags;
1156 server = savestr(xserver);
1157 if (nmail) {
1158 saveint = safe_signal(SIGINT, SIG_IGN);
1159 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1160 if (saveint != SIG_IGN)
1161 safe_signal(SIGINT, imapcatch);
1162 if (savepipe != SIG_IGN)
1163 safe_signal(SIGPIPE, imapcatch);
1164 imaplock = 1;
1165 goto jnmail;
1167 same_flags = mb.mb_flags;
1168 same_imap_account = 0;
1169 sp = protbase(server);
1170 if (mb.mb_imap_account && mb.mb_type == MB_IMAP) {
1171 if (mb.mb_sock.s_fd > 0 &&
1172 strcmp(mb.mb_imap_account, sp) == 0 &&
1173 disconnected(mb.mb_imap_account) == 0)
1174 same_imap_account = 1;
1176 acc = sstrdup(sp);
1177 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1178 so.s_fd = -1;
1179 if (!same_imap_account) {
1180 if (!disconnected(acc) &&
1181 sopen(sp, &so, use_ssl, uhp,
1182 use_ssl ? "imaps" : "imap",
1183 (options & OPT_VERBOSE) != 0) != OKAY) {
1184 free(acc);
1185 return -1;
1187 } else
1188 so = mb.mb_sock;
1189 if (!transparent)
1190 quit();
1191 edit = (isedit != 0);
1192 if (mb.mb_imap_account != NULL)
1193 free(mb.mb_imap_account);
1194 mb.mb_imap_account = acc;
1195 if (!same_imap_account) {
1196 if (mb.mb_sock.s_fd >= 0)
1197 sclose(&mb.mb_sock);
1199 same_imap_account = 0;
1200 if (!transparent) {
1201 if (mb.mb_itf) {
1202 fclose(mb.mb_itf);
1203 mb.mb_itf = NULL;
1205 if (mb.mb_otf) {
1206 fclose(mb.mb_otf);
1207 mb.mb_otf = NULL;
1209 if (mb.mb_imap_mailbox != NULL)
1210 free(mb.mb_imap_mailbox);
1211 mb.mb_imap_mailbox = sstrdup(mbx);
1212 initbox(server);
1214 mb.mb_type = MB_VOID;
1215 mb.mb_active = MB_NONE;
1216 imaplock = 1;
1217 saveint = safe_signal(SIGINT, SIG_IGN);
1218 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1219 if (sigsetjmp(imapjmp, 1)) {
1220 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1221 sclose(&mb.mb_sock);
1222 safe_signal(SIGINT, saveint);
1223 safe_signal(SIGPIPE, savepipe);
1224 imaplock = 0;
1225 mb.mb_type = MB_VOID;
1226 mb.mb_active = MB_NONE;
1227 return -1;
1229 if (saveint != SIG_IGN)
1230 safe_signal(SIGINT, imapcatch);
1231 if (savepipe != SIG_IGN)
1232 safe_signal(SIGPIPE, imapcatch);
1233 if (mb.mb_sock.s_fd < 0) {
1234 if (disconnected(mb.mb_imap_account)) {
1235 if (cache_setptr(transparent) == STOP)
1236 fprintf(stderr,
1237 "Mailbox \"%s\" is not cached.\n",
1238 server);
1239 goto done;
1241 if ((cp = value("imap-keepalive")) != NULL) {
1242 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1243 savealrm = safe_signal(SIGALRM, imapalarm);
1244 alarm(imapkeepalive);
1247 mb.mb_sock = so;
1248 mb.mb_sock.s_desc = "IMAP";
1249 mb.mb_sock.s_onclose = imap_timer_off;
1250 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1251 imap_auth(&mb, uhp, user, pass) != OKAY) {
1252 sclose(&mb.mb_sock);
1253 imap_timer_off();
1254 safe_signal(SIGINT, saveint);
1255 safe_signal(SIGPIPE, savepipe);
1256 imaplock = 0;
1257 return -1;
1259 } else /* same account */
1260 mb.mb_flags |= same_flags;
1261 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1262 mb.mb_type = MB_IMAP;
1263 cache_dequeue(&mb);
1264 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1265 /*sclose(&mb.mb_sock);
1266 imap_timer_off();*/
1267 safe_signal(SIGINT, saveint);
1268 safe_signal(SIGPIPE, savepipe);
1269 imaplock = 0;
1270 mb.mb_type = MB_VOID;
1271 return -1;
1273 jnmail:
1274 imap_setptr(&mb, nmail, transparent, &prevcount);
1275 done: setmsize(msgCount);
1276 if (!nmail && !transparent)
1277 sawcom = FAL0;
1278 safe_signal(SIGINT, saveint);
1279 safe_signal(SIGPIPE, savepipe);
1280 imaplock = 0;
1281 if (!nmail && mb.mb_type == MB_IMAP)
1282 purgecache(&mb, message, msgCount);
1283 if ((nmail || transparent) && mb.mb_sorted) {
1284 mb.mb_threaded = 0;
1285 sort((void *)-1);
1287 if (!nmail && !edit && msgCount == 0) {
1288 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1289 value("emptystart") == NULL)
1290 fprintf(stderr, tr(258, "No mail at %s\n"), server);
1291 return 1;
1293 if (nmail)
1294 newmailinfo(prevcount);
1295 return 0;
1298 static int
1299 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1300 int need,
1301 const char *head, size_t headsize, long headlines)
1303 char *line = NULL, *lp;
1304 size_t linesize = 0, linelen, size = 0;
1305 int emptyline = 0, lines = 0, excess = 0;
1306 off_t offset;
1308 fseek(mp->mb_otf, 0L, SEEK_END);
1309 offset = ftell(mp->mb_otf);
1310 if (head)
1311 fwrite(head, 1, headsize, mp->mb_otf);
1312 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1313 lp = line;
1314 if (linelen > expected) {
1315 excess = linelen - expected;
1316 linelen = expected;
1318 /* TODO >>
1319 * Need to mask 'From ' lines. This cannot be done properly
1320 * since some servers pass them as 'From ' and others as
1321 * '>From '. Although one could identify the first kind of
1322 * server in principle, it is not possible to identify the
1323 * second as '>From ' may also come from a server of the
1324 * first type as actual data. So do what is absolutely
1325 * necessary only - mask 'From '.
1327 * If the line is the first line of the message header, it
1328 * is likely a real 'From ' line. In this case, it is just
1329 * ignored since it violates all standards.
1330 * TODO can the latter *really* happen??
1331 * TODO <<
1334 * Since we simply copy over data without doing any transfer
1335 * encoding reclassification/adjustment we *have* to perform
1336 * RFC 4155 compliant From_ quoting here
1338 if (is_head(lp, linelen)) {
1339 if (lines + headlines == 0)
1340 goto skip;
1341 fputc('>', mp->mb_otf);
1342 ++size;
1344 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1345 lp[linelen-2] == '\r')) {
1346 emptyline = linelen <= 2;
1347 if (linelen > 2) {
1348 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1349 size += linelen - 1;
1350 } else
1351 size++;
1352 fputc('\n', mp->mb_otf);
1353 } else {
1354 emptyline = 0;
1355 fwrite(lp, 1, linelen, mp->mb_otf);
1356 size += linelen;
1358 lines++;
1359 skip: if ((expected -= linelen) <= 0)
1360 break;
1362 if (!emptyline) {
1364 * This is very ugly; but some IMAP daemons don't end a
1365 * message with \r\n\r\n, and we need \n\n for mbox format.
1367 fputc('\n', mp->mb_otf);
1368 lines++;
1369 size++;
1371 fflush(mp->mb_otf);
1372 if (m != NULL) {
1373 m->m_size = size + headsize;
1374 m->m_lines = lines + headlines;
1375 m->m_block = mailx_blockof(offset);
1376 m->m_offset = mailx_offsetof(offset);
1377 switch (need) {
1378 case NEED_HEADER:
1379 m->m_have |= HAVE_HEADER;
1380 break;
1381 case NEED_BODY:
1382 m->m_have |= HAVE_HEADER|HAVE_BODY;
1383 m->m_xlines = m->m_lines;
1384 m->m_xsize = m->m_size;
1385 break;
1388 free(line);
1389 return excess;
1392 static void
1393 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1394 const char *head, size_t headsize, long headlines)
1396 off_t offset;
1397 size_t len;
1399 len = strlen(str);
1400 fseek(mp->mb_otf, 0L, SEEK_END);
1401 offset = ftell(mp->mb_otf);
1402 if (head)
1403 fwrite(head, 1, headsize, mp->mb_otf);
1404 if (len > 0) {
1405 fwrite(str, 1, len, mp->mb_otf);
1406 fputc('\n', mp->mb_otf);
1407 len++;
1409 fflush(mp->mb_otf);
1410 if (m != NULL) {
1411 m->m_size = headsize + len;
1412 m->m_lines = headlines + 1;
1413 m->m_block = mailx_blockof(offset);
1414 m->m_offset = mailx_offsetof(offset);
1415 m->m_have |= HAVE_HEADER|HAVE_BODY;
1416 m->m_xlines = m->m_lines;
1417 m->m_xsize = m->m_size;
1421 static enum okay
1422 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1424 char o[LINESIZE];
1425 struct message mt;
1426 sighandler_type saveint = SIG_IGN, savepipe = SIG_IGN;
1427 char *volatile head = NULL;
1428 char const *cp = NULL, *loc = NULL,
1429 *volatile item = NULL, *volatile resp = NULL;
1430 size_t expected;
1431 size_t volatile headsize = 0;
1432 int number = m - message + 1;
1433 enum okay ok = STOP;
1434 FILE *queuefp = NULL;
1435 long volatile headlines = 0;
1436 long n = -1;
1437 unsigned long u = 0;
1439 if (getcache(mp, m, need) == OKAY)
1440 return OKAY;
1441 if (mp->mb_type == MB_CACHE) {
1442 fprintf(stderr, "Message %u not available.\n", number);
1443 return STOP;
1445 if (mp->mb_sock.s_fd < 0) {
1446 fprintf(stderr, "IMAP connection closed.\n");
1447 return STOP;
1449 switch (need) {
1450 case NEED_HEADER:
1451 resp = item = "RFC822.HEADER";
1452 break;
1453 case NEED_BODY:
1454 item = "BODY.PEEK[]";
1455 resp = "BODY[]";
1456 if (m->m_flag & HAVE_HEADER && m->m_size) {
1457 char *hdr = smalloc(m->m_size);
1458 fflush(mp->mb_otf);
1459 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1460 m->m_offset), SEEK_SET) < 0 ||
1461 fread(hdr, 1, m->m_size, mp->mb_itf)
1462 != m->m_size) {
1463 free(hdr);
1464 break;
1466 head = hdr;
1467 headsize = m->m_size;
1468 headlines = m->m_lines;
1469 item = "BODY.PEEK[TEXT]";
1470 resp = "BODY[TEXT]";
1472 break;
1473 case NEED_UNSPEC:
1474 return STOP;
1476 imaplock = 1;
1477 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1478 if (sigsetjmp(imapjmp, 1)) {
1479 safe_signal(SIGINT, saveint);
1480 safe_signal(SIGPIPE, savepipe);
1481 imaplock = 0;
1482 return STOP;
1484 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1485 safe_signal(SIGINT, maincatch);
1486 if (savepipe != SIG_IGN)
1487 safe_signal(SIGPIPE, imapcatch);
1488 if (m->m_uid)
1489 snprintf(o, sizeof o,
1490 "%s UID FETCH %lu (%s)\r\n",
1491 tag(1), m->m_uid, item);
1492 else {
1493 if (check_expunged() == STOP)
1494 goto out;
1495 snprintf(o, sizeof o,
1496 "%s FETCH %u (%s)\r\n",
1497 tag(1), number, item);
1499 IMAP_OUT(o, MB_COMD, goto out)
1500 for (;;) {
1501 ok = imap_answer(mp, 1);
1502 if (ok == STOP)
1503 break;
1504 if (response_status != RESPONSE_OTHER ||
1505 response_other != MESSAGE_DATA_FETCH)
1506 continue;
1507 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1508 continue;
1509 if (m->m_uid) {
1510 if ((cp = asccasestr(responded_other_text, "UID "))) {
1511 u = atol(&cp[4]);
1512 n = 0;
1513 } else {
1514 n = -1;
1515 u = 0;
1517 } else
1518 n = responded_other_number;
1519 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1520 if (m->m_uid ? m->m_uid != u : n != number)
1521 continue;
1522 if ((cp = strchr(loc, '"')) != NULL) {
1523 cp = imap_unquotestr(cp);
1524 imap_putstr(mp, m, cp,
1525 head, headsize, headlines);
1526 } else {
1527 m->m_have |= HAVE_HEADER|HAVE_BODY;
1528 m->m_xlines = m->m_lines;
1529 m->m_xsize = m->m_size;
1531 goto out;
1533 expected = atol(&cp[1]);
1534 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1535 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1536 continue;
1538 mt = *m;
1539 imap_fetchdata(mp, &mt, expected, need,
1540 head, headsize, headlines);
1541 if (n >= 0) {
1542 commitmsg(mp, m, &mt, mt.m_have);
1543 break;
1545 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1546 &mp->mb_sock) > 0) {
1547 if (options & OPT_VERBOSE)
1548 fputs(imapbuf, stderr);
1549 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1550 u = atol(&cp[4]);
1551 if (u == m->m_uid) {
1552 commitmsg(mp, m, &mt, mt.m_have);
1553 break;
1558 out: while (mp->mb_active & MB_COMD)
1559 ok = imap_answer(mp, 1);
1560 if (saveint != SIG_IGN)
1561 safe_signal(SIGINT, saveint);
1562 if (savepipe != SIG_IGN)
1563 safe_signal(SIGPIPE, savepipe);
1564 imaplock--;
1565 if (ok == OKAY)
1566 putcache(mp, m);
1567 if (head != NULL)
1568 free(head);
1569 if (interrupts)
1570 onintr(0);
1571 return ok;
1574 enum okay
1575 imap_header(struct message *m)
1577 return imap_get(&mb, m, NEED_HEADER);
1581 enum okay
1582 imap_body(struct message *m)
1584 return imap_get(&mb, m, NEED_BODY);
1587 static void
1588 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1589 enum havespec have)
1591 tomp->m_size = frommp->m_size;
1592 tomp->m_lines = frommp->m_lines;
1593 tomp->m_block = frommp->m_block;
1594 tomp->m_offset = frommp->m_offset;
1595 tomp->m_have = have;
1596 if (have & HAVE_BODY) {
1597 tomp->m_xlines = frommp->m_lines;
1598 tomp->m_xsize = frommp->m_size;
1600 putcache(mp, tomp);
1603 static enum okay
1604 imap_fetchheaders (
1605 struct mailbox *mp,
1606 struct message *m,
1607 int bot,
1608 int topp /* bot > topp */
1611 char o[LINESIZE];
1612 char const *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[topp-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, topp);
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 = atoi(&cp[4]);
1645 for (n = bot; n <= topp; n++)
1646 if ((unsigned long)u == m[n-1].m_uid)
1647 break;
1648 if (n > topp) {
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 (options & OPT_VERBOSE)
1670 fputs(imapbuf, stderr);
1671 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1672 u = atoi(&cp[4]);
1673 for (n = bot; n <= topp; n++)
1674 if ((unsigned long)u == m[n-1].m_uid)
1675 break;
1676 if (n <= topp && !(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 volatile bot, int topp)
1690 sighandler_type saveint, savepipe;
1691 /* XXX enum okay ok = STOP;*/
1692 int i, chunk = 256;
1694 if (mb.mb_type == MB_CACHE)
1695 return;
1696 if (bot < 1)
1697 bot = 1;
1698 if (topp > msgCount)
1699 topp = msgCount;
1700 for (i = bot; i < topp; i++) {
1701 if (message[i-1].m_have & HAVE_HEADER ||
1702 getcache(&mb, &message[i-1], NEED_HEADER)
1703 == OKAY)
1704 bot = i+1;
1705 else
1706 break;
1708 for (i = topp; i > bot; i--) {
1709 if (message[i-1].m_have & HAVE_HEADER ||
1710 getcache(&mb, &message[i-1], NEED_HEADER)
1711 == OKAY)
1712 topp = i-1;
1713 else
1714 break;
1716 if (bot >= topp)
1717 return;
1718 imaplock = 1;
1719 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1720 safe_signal(SIGINT, maincatch);
1721 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1722 if (sigsetjmp(imapjmp, 1) == 0) {
1723 if (savepipe != SIG_IGN)
1724 safe_signal(SIGPIPE, imapcatch);
1725 for (i = bot; i <= topp; i += chunk) {
1726 /*ok = */imap_fetchheaders(&mb, message, i,
1727 i+chunk-1 < topp ? i+chunk-1 : topp);
1728 if (interrupts)
1729 onintr(0);
1732 safe_signal(SIGINT, saveint);
1733 safe_signal(SIGPIPE, savepipe);
1734 imaplock = 0;
1737 static enum okay
1738 __imap_exit(struct mailbox *mp)
1740 char o[LINESIZE];
1741 FILE *queuefp = NULL;
1743 mp->mb_active |= MB_BYE;
1744 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1745 IMAP_OUT(o, MB_COMD, return STOP)
1746 IMAP_ANSWER()
1747 return OKAY;
1750 static enum okay
1751 imap_exit(struct mailbox *mp)
1753 enum okay ret = __imap_exit(mp);
1754 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1755 free(mp->mb_imap_account);
1756 free(mp->mb_imap_mailbox);
1757 if (mp->mb_cache_directory != NULL)
1758 free(mp->mb_cache_directory);
1759 #ifndef HAVE_ASSERTS /* TODO ASSERT LEGACY */
1760 mp->mb_imap_account =
1761 mp->mb_imap_mailbox =
1762 mp->mb_cache_directory = "";
1763 #else
1764 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1765 mp->mb_imap_mailbox = NULL;
1766 mp->mb_cache_directory = NULL;
1767 #endif
1768 #endif
1769 sclose(&mp->mb_sock);
1770 return ret;
1773 static enum okay
1774 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1776 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1777 if (mp->mb_type == MB_IMAP)
1778 delcache(mp, m);
1779 return OKAY;
1782 static enum okay
1783 imap_close(struct mailbox *mp)
1785 char o[LINESIZE];
1786 FILE *queuefp = NULL;
1788 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1789 IMAP_OUT(o, MB_COMD, return STOP)
1790 IMAP_ANSWER()
1791 return OKAY;
1794 static enum okay
1795 imap_update(struct mailbox *mp)
1797 struct message *m;
1798 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1800 if (!edit && mp->mb_perm != 0) {
1801 holdbits();
1802 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1803 if (m->m_flag & MBOX)
1804 c++;
1806 if (c > 0)
1807 if (makembox() == STOP)
1808 goto bypass;
1810 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1811 if (mp->mb_perm == 0) {
1812 dodel = 0;
1813 } else if (edit) {
1814 dodel = m->m_flag & MDELETED;
1815 } else {
1816 dodel = !((m->m_flag&MPRESERVE) ||
1817 (m->m_flag&MTOUCH) == 0);
1820 * Fetch the result after around each 800 STORE commands
1821 * sent (approx. 32k data sent). Otherwise, servers will
1822 * try to flush the return queue at some point, leading
1823 * to a deadlock if we are still writing commands but not
1824 * reading their results.
1826 needstat = stored > 0 && stored % 800 == 0;
1828 * Even if this message has been deleted, continue
1829 * to set further flags. This is necessary to support
1830 * Gmail semantics, where "delete" actually means
1831 * "archive", and the flags are applied to the copy
1832 * in "All Mail".
1834 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1835 imap_store(mp, m, m-message+1,
1836 '+', "\\Seen", needstat);
1837 stored++;
1839 if (m->m_flag & MFLAG) {
1840 imap_store(mp, m, m-message+1,
1841 '+', "\\Flagged", needstat);
1842 stored++;
1844 if (m->m_flag & MUNFLAG) {
1845 imap_store(mp, m, m-message+1,
1846 '-', "\\Flagged", needstat);
1847 stored++;
1849 if (m->m_flag & MANSWER) {
1850 imap_store(mp, m, m-message+1,
1851 '+', "\\Answered", needstat);
1852 stored++;
1854 if (m->m_flag & MUNANSWER) {
1855 imap_store(mp, m, m-message+1,
1856 '-', "\\Answered", needstat);
1857 stored++;
1859 if (m->m_flag & MDRAFT) {
1860 imap_store(mp, m, m-message+1,
1861 '+', "\\Draft", needstat);
1862 stored++;
1864 if (m->m_flag & MUNDRAFT) {
1865 imap_store(mp, m, m-message+1,
1866 '-', "\\Draft", needstat);
1867 stored++;
1869 if (dodel) {
1870 imap_delete(mp, m-message+1, m, needstat);
1871 stored++;
1872 gotcha++;
1873 } else if (mp->mb_type != MB_CACHE ||
1874 (! edit && ! (m->m_flag&(MBOXED|MSAVED|MDELETED))) ||
1875 (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) ==
1876 (MPRESERVE|MTOUCH) ||
1877 (edit && ! (m->m_flag & MDELETED)))
1878 held++;
1879 if (m->m_flag & MNEW) {
1880 m->m_flag &= ~MNEW;
1881 m->m_flag |= MSTATUS;
1884 bypass:
1885 if (gotcha)
1886 imap_close(mp);
1887 for (m = &message[0]; m < &message[msgCount]; m++)
1888 if (!(m->m_flag&MUNLINKED) &&
1889 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1890 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1891 MDRAFT|MUNDRAFT)) {
1892 putcache(mp, m);
1893 modflags++;
1895 if ((gotcha || modflags) && edit) {
1896 printf(tr(168, "\"%s\" "), displayname);
1897 printf((value("bsdcompat") || value("bsdmsgs"))
1898 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1899 } else if (held && !edit && mp->mb_perm != 0) {
1900 if (held == 1)
1901 printf(tr(155, "Held 1 message in %s\n"), displayname);
1902 else if (held > 1)
1903 printf(tr(156, "Held %d messages in %s\n"), held,
1904 displayname);
1906 fflush(stdout);
1907 return OKAY;
1910 void
1911 imap_quit(void)
1913 sighandler_type saveint;
1914 sighandler_type savepipe;
1916 if (mb.mb_type == MB_CACHE) {
1917 imap_update(&mb);
1918 return;
1920 if (mb.mb_sock.s_fd < 0) {
1921 fprintf(stderr, "IMAP connection closed.\n");
1922 return;
1924 imaplock = 1;
1925 saveint = safe_signal(SIGINT, SIG_IGN);
1926 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1927 if (sigsetjmp(imapjmp, 1)) {
1928 safe_signal(SIGINT, saveint);
1929 safe_signal(SIGPIPE, saveint);
1930 imaplock = 0;
1931 return;
1933 if (saveint != SIG_IGN)
1934 safe_signal(SIGINT, imapcatch);
1935 if (savepipe != SIG_IGN)
1936 safe_signal(SIGPIPE, imapcatch);
1937 imap_update(&mb);
1938 if (!same_imap_account) {
1939 imap_exit(&mb);
1941 safe_signal(SIGINT, saveint);
1942 safe_signal(SIGPIPE, savepipe);
1943 imaplock = 0;
1946 static enum okay
1947 imap_store(struct mailbox *mp, struct message *m, int n,
1948 int c, const char *sp, int needstat)
1950 char o[LINESIZE];
1951 FILE *queuefp = NULL;
1953 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1954 return STOP;
1955 if (m->m_uid)
1956 snprintf(o, sizeof o,
1957 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1958 tag(1), m->m_uid, c, sp);
1959 else {
1960 if (check_expunged() == STOP)
1961 return STOP;
1962 snprintf(o, sizeof o,
1963 "%s STORE %u %cFLAGS (%s)\r\n",
1964 tag(1), n, c, sp);
1966 IMAP_OUT(o, MB_COMD, return STOP)
1967 if (needstat)
1968 IMAP_ANSWER()
1969 else
1970 mb.mb_active &= ~MB_COMD;
1971 if (queuefp != NULL)
1972 Fclose(queuefp);
1973 return OKAY;
1976 enum okay
1977 imap_undelete(struct message *m, int n)
1979 return imap_unstore(m, n, "\\Deleted");
1982 enum okay
1983 imap_unread(struct message *m, int n)
1985 return imap_unstore(m, n, "\\Seen");
1988 static enum okay
1989 imap_unstore(struct message *m, int n, const char *flag)
1991 sighandler_type saveint, savepipe;
1992 enum okay ok = STOP;
1994 (void)&saveint;
1995 (void)&savepipe;
1996 (void)&ok;
1997 imaplock = 1;
1998 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1999 safe_signal(SIGINT, maincatch);
2000 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2001 if (sigsetjmp(imapjmp, 1) == 0) {
2002 if (savepipe != SIG_IGN)
2003 safe_signal(SIGPIPE, imapcatch);
2004 ok = imap_store(&mb, m, n, '-', flag, 1);
2006 safe_signal(SIGINT, saveint);
2007 safe_signal(SIGPIPE, savepipe);
2008 imaplock = 0;
2009 if (interrupts)
2010 onintr(0);
2011 return ok;
2014 static const char *
2015 tag(int new)
2017 static char ts[20];
2018 static long n;
2020 if (new)
2021 n++;
2022 snprintf(ts, sizeof ts, "T%lu", n);
2023 return ts;
2026 int
2027 imap_imap(void *vp)
2029 sighandler_type saveint, savepipe;
2030 char o[LINESIZE];
2031 enum okay ok = STOP;
2032 struct mailbox *mp = &mb;
2033 FILE *queuefp = NULL;
2035 (void)&saveint;
2036 (void)&savepipe;
2037 (void)&ok;
2038 if (mp->mb_type != MB_IMAP) {
2039 printf("Not operating on an IMAP mailbox.\n");
2040 return 1;
2042 imaplock = 1;
2043 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2044 safe_signal(SIGINT, maincatch);
2045 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2046 if (sigsetjmp(imapjmp, 1) == 0) {
2047 if (savepipe != SIG_IGN)
2048 safe_signal(SIGPIPE, imapcatch);
2049 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2050 IMAP_OUT(o, MB_COMD, goto out)
2051 while (mp->mb_active & MB_COMD) {
2052 ok = imap_answer(mp, 0);
2053 fputs(responded_text, stdout);
2056 out: safe_signal(SIGINT, saveint);
2057 safe_signal(SIGPIPE, savepipe);
2058 imaplock = 0;
2059 if (interrupts)
2060 onintr(0);
2061 return ok != OKAY;
2064 int
2065 imap_newmail(int autoinc)
2067 if (autoinc && had_exists < 0 && had_expunge < 0) {
2068 imaplock = 1;
2069 imap_noop();
2070 imaplock = 0;
2072 if (had_exists == msgCount && had_expunge < 0)
2074 * Some servers always respond with EXISTS to NOOP. If
2075 * the mailbox has been changed but the number of messages
2076 * has not, an EXPUNGE must also had been sent; otherwise,
2077 * nothing has changed.
2079 had_exists = -1;
2080 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2083 static char *
2084 imap_putflags(int f)
2086 const char *cp;
2087 char *buf, *bp;
2089 bp = buf = salloc(100);
2090 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2091 *bp++ = '(';
2092 if (f & MREAD) {
2093 if (bp[-1] != '(')
2094 *bp++ = ' ';
2095 for (cp = "\\Seen"; *cp; cp++)
2096 *bp++ = *cp;
2098 if (f & MFLAGGED) {
2099 if (bp[-1] != '(')
2100 *bp++ = ' ';
2101 for (cp = "\\Flagged"; *cp; cp++)
2102 *bp++ = *cp;
2104 if (f & MANSWERED) {
2105 if (bp[-1] != '(')
2106 *bp++ = ' ';
2107 for (cp = "\\Answered"; *cp; cp++)
2108 *bp++ = *cp;
2110 if (f & MDRAFT) {
2111 if (bp[-1] != '(')
2112 *bp++ = ' ';
2113 for (cp = "\\Draft"; *cp; cp++)
2114 *bp++ = *cp;
2116 *bp++ = ')';
2117 *bp++ = ' ';
2119 *bp = '\0';
2120 return buf;
2123 static void
2124 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2126 while (*cp != ')') {
2127 if (*cp == '\\') {
2128 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2129 *f |= MREAD;
2130 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2131 *f |= MNEW;
2132 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2133 *f |= MDELETED;
2134 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2135 *f |= MFLAGGED;
2136 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2137 *f |= MANSWERED;
2138 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2139 *f |= MDRAFTED;
2141 cp++;
2143 if (xp)
2144 *xp = cp;
2147 static enum okay
2148 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2149 off_t off1, long xsize, enum mflag flag, time_t t)
2151 char o[LINESIZE];
2152 char *buf;
2153 size_t bufsize, buflen, cnt;
2154 enum okay ok = STOP;
2155 long size, lines, ysize;
2156 int twice = 0;
2157 FILE *queuefp = NULL;
2159 if (mp->mb_type == MB_CACHE) {
2160 queuefp = cache_queue(mp);
2161 if (queuefp == NULL)
2162 return STOP;
2163 ok = OKAY;
2165 buf = smalloc(bufsize = LINESIZE);
2166 buflen = 0;
2167 again: size = xsize;
2168 cnt = fsize(fp);
2169 if (fseek(fp, off1, SEEK_SET) < 0) {
2170 ok = STOP;
2171 goto out;
2173 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2174 tag(1), imap_quotestr(name),
2175 imap_putflags(flag),
2176 imap_make_date_time(t),
2177 size);
2178 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2179 while (mp->mb_active & MB_COMD) {
2180 ok = imap_answer(mp, twice);
2181 if (response_type == RESPONSE_CONT)
2182 break;
2184 if (mp->mb_type != MB_CACHE && ok == STOP) {
2185 if (twice == 0)
2186 goto trycreate;
2187 else
2188 goto out;
2190 lines = ysize = 0;
2191 while (size > 0) {
2192 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2193 lines++;
2194 ysize += buflen;
2195 buf[buflen-1] = '\r';
2196 buf[buflen] = '\n';
2197 if (mp->mb_type != MB_CACHE)
2198 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2199 else if (queuefp)
2200 fwrite(buf, 1, buflen+1, queuefp);
2201 size -= buflen+1;
2203 if (mp->mb_type != MB_CACHE)
2204 swrite(&mp->mb_sock, "\r\n");
2205 else if (queuefp)
2206 fputs("\r\n", queuefp);
2207 while (mp->mb_active & MB_COMD) {
2208 ok = imap_answer(mp, 0);
2209 if (response_status == RESPONSE_NO /*&&
2210 ascncasecmp(responded_text,
2211 "[TRYCREATE] ", 12) == 0*/) {
2212 trycreate: if (twice++) {
2213 ok = STOP;
2214 goto out;
2216 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2217 tag(1),
2218 imap_quotestr(name));
2219 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2220 while (mp->mb_active & MB_COMD)
2221 ok = imap_answer(mp, 1);
2222 if (ok == STOP)
2223 goto out;
2224 imap_created_mailbox++;
2225 goto again;
2226 } else if (ok != OKAY)
2227 fprintf(stderr, tr(270, "IMAP error: %s"),
2228 responded_text);
2229 else if (response_status == RESPONSE_OK &&
2230 mp->mb_flags & MB_UIDPLUS)
2231 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2232 flag, name);
2234 out: if (queuefp != NULL)
2235 Fclose(queuefp);
2236 free(buf);
2237 return ok;
2240 static enum okay
2241 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2243 char *buf, *bp, *lp;
2244 size_t bufsize, buflen, cnt;
2245 off_t off1 = -1, offs;
2246 int inhead = 1;
2247 int flag = MNEW|MNEWEST;
2248 long size = 0;
2249 time_t tim;
2250 enum okay ok;
2252 buf = smalloc(bufsize = LINESIZE);
2253 buflen = 0;
2254 cnt = fsize(fp);
2255 offs = ftell(fp);
2256 time(&tim);
2257 do {
2258 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2259 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2260 if (off1 != (off_t)-1) {
2261 ok=imap_append1(mp, name, fp, off1,
2262 size, flag, tim);
2263 if (ok == STOP)
2264 return STOP;
2265 fseek(fp, offs+buflen, SEEK_SET);
2267 off1 = offs + buflen;
2268 size = 0;
2269 inhead = 1;
2270 flag = MNEW;
2271 if (bp != NULL)
2272 tim = unixtime(buf);
2273 } else
2274 size += buflen+1;
2275 offs += buflen;
2276 if (bp && buf[0] == '\n')
2277 inhead = 0;
2278 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2279 lp = &buf[6];
2280 while (whitechar(*lp&0377))
2281 lp++;
2282 if (*lp == ':')
2283 while (*++lp != '\0')
2284 switch (*lp) {
2285 case 'R':
2286 flag |= MREAD;
2287 break;
2288 case 'O':
2289 flag &= ~MNEW;
2290 break;
2292 } else if (bp && inhead &&
2293 ascncasecmp(buf, "x-status", 8) == 0) {
2294 lp = &buf[8];
2295 while (whitechar(*lp&0377))
2296 lp++;
2297 if (*lp == ':')
2298 while (*++lp != '\0')
2299 switch (*lp) {
2300 case 'F':
2301 flag |= MFLAGGED;
2302 break;
2303 case 'A':
2304 flag |= MANSWERED;
2305 break;
2306 case 'T':
2307 flag |= MDRAFTED;
2308 break;
2311 } while (bp != NULL);
2312 free(buf);
2313 return OKAY;
2316 enum okay
2317 imap_append(const char *xserver, FILE *fp)
2319 sighandler_type saveint, savepipe;
2320 char *server, *user, *pass;
2321 char const *sp, *cp, *mbx, *uhp;
2322 int use_ssl;
2323 enum okay ok = STOP;
2325 server = savestr(xserver);
2326 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2327 imaplock = 1;
2328 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2329 safe_signal(SIGINT, maincatch);
2330 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2331 if (sigsetjmp(imapjmp, 1))
2332 goto out;
2333 if (savepipe != SIG_IGN)
2334 safe_signal(SIGPIPE, imapcatch);
2335 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2336 mb.mb_imap_account &&
2337 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2338 ok = imap_append0(&mb, mbx, fp);
2340 else {
2341 struct mailbox mx;
2343 memset(&mx, 0, sizeof mx);
2344 if (disconnected(server) == 0) {
2345 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2346 use_ssl ? "imaps" : "imap",
2347 (options & OPT_VERBOSE) != 0) != OKAY)
2348 goto fail;
2349 mx.mb_sock.s_desc = "IMAP";
2350 mx.mb_type = MB_IMAP;
2351 mx.mb_imap_account = (char *)protbase(server);
2352 /* TODO the code now did
2353 * TODO mx.mb_imap_mailbox = mbx;
2354 * TODO though imap_mailbox is sfree()d and mbx
2355 * TODO is possibly even a constant
2356 * TODO i changed this to sstrdup() sofar, as is used
2357 * TODO somewhere else in this file for this! */
2358 mx.mb_imap_mailbox = sstrdup(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 } else {
2367 mx.mb_imap_account = (char *)protbase(server);
2368 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2369 mx.mb_type = MB_CACHE;
2370 ok = imap_append0(&mx, mbx, fp);
2372 fail:;
2374 out: safe_signal(SIGINT, saveint);
2375 safe_signal(SIGPIPE, savepipe);
2376 imaplock = 0;
2377 if (interrupts)
2378 onintr(0);
2379 return ok;
2382 static enum okay
2383 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2384 struct list_item **lend, int level)
2386 char o[LINESIZE];
2387 enum okay ok = STOP;
2388 char *cp;
2389 const char *bp;
2390 FILE *queuefp = NULL;
2391 struct list_item *lp;
2393 *list = *lend = NULL;
2394 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2395 tag(1), imap_quotestr(base));
2396 IMAP_OUT(o, MB_COMD, return STOP)
2397 while (mp->mb_active & MB_COMD) {
2398 ok = imap_answer(mp, 1);
2399 if (response_status == RESPONSE_OTHER &&
2400 response_other == MAILBOX_DATA_LIST &&
2401 imap_parse_list() == OKAY) {
2402 cp = imap_unquotestr(list_name);
2403 lp = csalloc(1, sizeof *lp);
2404 lp->l_name = cp;
2405 for (bp = base; *bp && *bp == *cp; bp++)
2406 cp++;
2407 lp->l_base = *cp ? cp : savestr(base);
2408 lp->l_attr = list_attributes;
2409 lp->l_level = level+1;
2410 lp->l_delim = list_hierarchy_delimiter;
2411 if (*list && *lend) {
2412 (*lend)->l_next = lp;
2413 *lend = lp;
2414 } else
2415 *list = *lend = lp;
2418 return ok;
2421 static enum okay
2422 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2424 struct list_item *list, *lend, *lp, *lx, *ly;
2425 int n;
2426 const char *bp;
2427 char *cp;
2428 int depth;
2430 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2431 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2432 return STOP;
2433 if (list == NULL || lend == NULL)
2434 return OKAY;
2435 for (lp = list; lp; lp = lp->l_next)
2436 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2437 lp->l_level < depth &&
2438 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2439 cp = salloc((n = strlen(lp->l_name)) + 2);
2440 memcpy(cp, lp->l_name, n);
2441 cp[n] = lp->l_delim;
2442 cp[n+1] = '\0';
2443 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2444 lx && ly) {
2445 lp->l_has_children = 1;
2446 if (strcmp(cp, lx->l_name) == 0)
2447 lx = lx->l_next;
2448 if (lx) {
2449 lend->l_next = lx;
2450 lend = ly;
2454 for (lp = list; lp; lp = lp->l_next) {
2455 if (strip) {
2456 cp = lp->l_name;
2457 for (bp = base; *bp && *bp == *cp; bp++)
2458 cp++;
2459 } else
2460 cp = lp->l_name;
2461 if ((lp->l_attr&LIST_NOSELECT) == 0)
2462 fprintf(fp, "%s\n", *cp ? cp : base);
2463 else if (lp->l_has_children == 0)
2464 fprintf(fp, "%s%c\n", *cp ? cp : base,
2465 lp->l_delim != EOF ? lp->l_delim : '\n');
2467 return OKAY;
2470 void
2471 imap_folders(const char *name, int strip)
2473 sighandler_type saveint, savepipe;
2474 const char *fold, *cp, *sp;
2475 char *tempfn;
2476 FILE *volatile fp;
2478 cp = protbase(name);
2479 sp = mb.mb_imap_account;
2480 if (sp == NULL || strcmp(cp, sp)) {
2481 fprintf(stderr, tr(502,
2482 "Cannot perform `folders' but when on the very IMAP "
2483 "account; the current one is\n `%s' -- "
2484 "try `folders @'.\n"),
2485 (sp != NULL) ? sp : tr(503, "[NONE]"));
2486 return;
2488 fold = imap_fileof(name);
2489 if (options & OPT_TTYOUT) {
2490 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2491 perror("tmpfile");
2492 return;
2494 rm(tempfn);
2495 Ftfree(&tempfn);
2496 } else
2497 fp = stdout;
2498 imaplock = 1;
2499 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2500 safe_signal(SIGINT, maincatch);
2501 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2502 if (sigsetjmp(imapjmp, 1))
2503 goto out;
2504 if (savepipe != SIG_IGN)
2505 safe_signal(SIGPIPE, imapcatch);
2506 if (mb.mb_type == MB_CACHE)
2507 cache_list(&mb, fold, strip, fp);
2508 else
2509 imap_list(&mb, fold, strip, fp);
2510 imaplock = 0;
2511 if (interrupts) {
2512 if (options & OPT_TTYOUT)
2513 Fclose(fp);
2514 onintr(0);
2516 fflush(fp);
2517 if (options & OPT_TTYOUT) {
2518 rewind(fp);
2519 if (fsize(fp) > 0)
2520 dopr(fp);
2521 else
2522 fprintf(stderr, "Folder not found.\n");
2524 out:
2525 safe_signal(SIGINT, saveint);
2526 safe_signal(SIGPIPE, savepipe);
2527 if (options & OPT_TTYOUT)
2528 Fclose(fp);
2531 static void
2532 dopr(FILE *fp)
2534 char o[LINESIZE], *tempfn;
2535 int c;
2536 long n = 0, mx = 0, columns, width;
2537 FILE *out;
2539 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2540 perror("tmpfile");
2541 return;
2543 rm(tempfn);
2544 Ftfree(&tempfn);
2545 while ((c = getc(fp)) != EOF) {
2546 if (c == '\n') {
2547 if (n > mx)
2548 mx = n;
2549 n = 0;
2550 } else
2551 n++;
2553 rewind(fp);
2554 width = scrnwidth;
2555 if (mx < width / 2) {
2556 columns = width / (mx+2);
2557 snprintf(o, sizeof o,
2558 "sort | pr -%lu -w%lu -t",
2559 columns, width);
2560 } else
2561 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2562 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2563 try_pager(out);
2564 Fclose(out);
2567 static enum okay
2568 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2570 char o[LINESIZE];
2571 const char *qname;
2572 enum okay ok = STOP;
2573 int twice = 0;
2574 int stored = 0;
2575 FILE *queuefp = NULL;
2577 if (mp->mb_type == MB_CACHE) {
2578 if ((queuefp = cache_queue(mp)) == NULL)
2579 return STOP;
2580 ok = OKAY;
2582 qname = imap_quotestr(name = imap_fileof(name));
2584 * Since it is not possible to set flags on the copy, recently
2585 * set flags must be set on the original to include it in the copy.
2587 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2588 imap_store(mp, m, n, '+', "\\Seen", 0);
2589 if (m->m_flag&MFLAG)
2590 imap_store(mp, m, n, '+', "\\Flagged", 0);
2591 if (m->m_flag&MUNFLAG)
2592 imap_store(mp, m, n, '-', "\\Flagged", 0);
2593 if (m->m_flag&MANSWER)
2594 imap_store(mp, m, n, '+', "\\Answered", 0);
2595 if (m->m_flag&MUNANSWER)
2596 imap_store(mp, m, n, '-', "\\Flagged", 0);
2597 if (m->m_flag&MDRAFT)
2598 imap_store(mp, m, n, '+', "\\Draft", 0);
2599 if (m->m_flag&MUNDRAFT)
2600 imap_store(mp, m, n, '-', "\\Draft", 0);
2601 again: if (m->m_uid)
2602 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2603 tag(1), m->m_uid, qname);
2604 else {
2605 if (check_expunged() == STOP)
2606 goto out;
2607 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2608 tag(1), n, qname);
2610 IMAP_OUT(o, MB_COMD, goto out)
2611 while (mp->mb_active & MB_COMD)
2612 ok = imap_answer(mp, twice);
2613 if (mp->mb_type == MB_IMAP &&
2614 mp->mb_flags & MB_UIDPLUS &&
2615 response_status == RESPONSE_OK)
2616 imap_copyuid(mp, m, name);
2617 if (response_status == RESPONSE_NO && twice++ == 0) {
2618 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2619 IMAP_OUT(o, MB_COMD, goto out)
2620 while (mp->mb_active & MB_COMD)
2621 ok = imap_answer(mp, 1);
2622 if (ok == OKAY) {
2623 imap_created_mailbox++;
2624 goto again;
2627 if (queuefp != NULL)
2628 Fclose(queuefp);
2630 * ... and reset the flag to its initial value so that
2631 * the 'exit' command still leaves the message unread.
2633 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2634 imap_store(mp, m, n, '-', "\\Seen", 0);
2635 stored++;
2637 if (m->m_flag&MFLAG) {
2638 imap_store(mp, m, n, '-', "\\Flagged", 0);
2639 stored++;
2641 if (m->m_flag&MUNFLAG) {
2642 imap_store(mp, m, n, '+', "\\Flagged", 0);
2643 stored++;
2645 if (m->m_flag&MANSWER) {
2646 imap_store(mp, m, n, '-', "\\Answered", 0);
2647 stored++;
2649 if (m->m_flag&MUNANSWER) {
2650 imap_store(mp, m, n, '+', "\\Answered", 0);
2651 stored++;
2653 if (m->m_flag&MDRAFT) {
2654 imap_store(mp, m, n, '-', "\\Draft", 0);
2655 stored++;
2657 if (m->m_flag&MUNDRAFT) {
2658 imap_store(mp, m, n, '+', "\\Draft", 0);
2659 stored++;
2661 if (stored) {
2662 mp->mb_active |= MB_COMD;
2663 (void)imap_finish(mp);
2665 return ok;
2668 enum okay
2669 imap_copy(struct message *m, int n, const char *name)
2671 sighandler_type saveint, savepipe;
2672 enum okay ok = STOP;
2674 (void)&saveint;
2675 (void)&savepipe;
2676 (void)&ok;
2677 imaplock = 1;
2678 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2679 safe_signal(SIGINT, maincatch);
2680 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2681 if (sigsetjmp(imapjmp, 1) == 0) {
2682 if (savepipe != SIG_IGN)
2683 safe_signal(SIGPIPE, imapcatch);
2684 ok = imap_copy1(&mb, m, n, name);
2686 safe_signal(SIGINT, saveint);
2687 safe_signal(SIGPIPE, savepipe);
2688 imaplock = 0;
2689 if (interrupts)
2690 onintr(0);
2691 return ok;
2694 static enum okay
2695 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2696 unsigned long *olduid, unsigned long *newuid)
2698 char *xp, *yp, *zp;
2700 *uidvalidity = strtoul(cp, &xp, 10);
2701 *olduid = strtoul(xp, &yp, 10);
2702 *newuid = strtoul(yp, &zp, 10);
2703 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2704 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2707 static enum okay
2708 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2709 unsigned long *uid)
2711 char *xp, *yp;
2713 *uidvalidity = strtoul(cp, &xp, 10);
2714 *uid = strtoul(xp, &yp, 10);
2715 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2716 yp > xp && *yp == ']';
2719 static enum okay
2720 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2722 const char *cp;
2723 unsigned long uidvalidity, olduid, newuid;
2724 struct mailbox xmb;
2725 struct message xm;
2727 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2728 imap_copyuid_parse(&cp[9], &uidvalidity,
2729 &olduid, &newuid) == STOP)
2730 return STOP;
2731 xmb = *mp;
2732 xmb.mb_cache_directory = NULL;
2733 xmb.mb_imap_mailbox = savestr(name);
2734 xmb.mb_uidvalidity = uidvalidity;
2735 initcache(&xmb);
2736 if (m == NULL) {
2737 memset(&xm, 0, sizeof xm);
2738 xm.m_uid = olduid;
2739 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2740 return STOP;
2741 getcache(mp, &xm, NEED_HEADER);
2742 getcache(mp, &xm, NEED_BODY);
2743 } else {
2744 if ((m->m_flag & HAVE_HEADER) == 0)
2745 getcache(mp, m, NEED_HEADER);
2746 if ((m->m_flag & HAVE_BODY) == 0)
2747 getcache(mp, m, NEED_BODY);
2748 xm = *m;
2750 xm.m_uid = newuid;
2751 xm.m_flag &= ~MFULLYCACHED;
2752 putcache(&xmb, &xm);
2753 return OKAY;
2756 static enum okay
2757 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2758 long xsize, long size, long lines, int flag, const char *name)
2760 const char *cp;
2761 unsigned long uidvalidity, uid;
2762 struct mailbox xmb;
2763 struct message xm;
2765 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2766 imap_appenduid_parse(&cp[11], &uidvalidity,
2767 &uid) == STOP)
2768 return STOP;
2769 xmb = *mp;
2770 xmb.mb_cache_directory = NULL;
2771 xmb.mb_imap_mailbox = savestr(name);
2772 xmb.mb_uidvalidity = uidvalidity;
2773 xmb.mb_otf = xmb.mb_itf = fp;
2774 initcache(&xmb);
2775 memset(&xm, 0, sizeof xm);
2776 xm.m_flag = (flag & MREAD) | MNEW;
2777 xm.m_time = t;
2778 xm.m_block = mailx_blockof(off1);
2779 xm.m_offset = mailx_offsetof(off1);
2780 xm.m_size = size;
2781 xm.m_xsize = xsize;
2782 xm.m_lines = xm.m_xlines = lines;
2783 xm.m_uid = uid;
2784 xm.m_have = HAVE_HEADER|HAVE_BODY;
2785 putcache(&xmb, &xm);
2786 return OKAY;
2789 static enum okay
2790 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2792 FILE *tp = NULL;
2793 time_t t;
2794 long size, xsize, ysize, lines;
2795 enum mflag flag = MNEW;
2796 char *name, *buf, *bp, *tempCopy;
2797 char const *cp;
2798 size_t bufsize, buflen, cnt;
2799 enum okay ok = STOP;
2801 buf = smalloc(bufsize = LINESIZE);
2802 buflen = 0;
2803 cnt = fsize(fp);
2804 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2805 goto stop;
2806 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2807 while (*bp == ' ')
2808 bp++;
2809 if ((cp = strrchr(bp, '{')) == NULL)
2810 goto stop;
2811 xsize = atol(&cp[1]) + 2;
2812 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2813 goto stop;
2814 while (*cp == ' ')
2815 cp++;
2816 if (*cp == '(') {
2817 imap_getflags(cp, &cp, &flag);
2818 while (*++cp == ' ');
2820 t = imap_read_date_time(cp);
2821 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2822 goto stop;
2823 rm(tempCopy);
2824 Ftfree(&tempCopy);
2825 size = xsize;
2826 ysize = lines = 0;
2827 while (size > 0) {
2828 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2829 goto stop;
2830 size -= buflen;
2831 buf[--buflen] = '\0';
2832 buf[buflen-1] = '\n';
2833 fwrite(buf, 1, buflen, tp);
2834 ysize += buflen;
2835 lines++;
2837 fflush(tp);
2838 rewind(tp);
2839 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2840 imap_unquotestr(name));
2841 ok = OKAY;
2842 stop: free(buf);
2843 if (tp)
2844 Fclose(tp);
2845 return ok;
2848 static enum okay
2849 imap_search2(struct mailbox *mp, struct message *m, int cnt,
2850 const char *spec, int f)
2852 char *o;
2853 size_t osize;
2854 FILE *queuefp = NULL;
2855 enum okay ok = STOP;
2856 int i;
2857 unsigned long n;
2858 const char *cp;
2859 char *xp, *cs, c;
2861 c = 0;
2862 for (cp = spec; *cp; cp++)
2863 c |= *cp;
2864 if (c & 0200) {
2865 cp = charset_get_lc();
2866 #ifdef HAVE_ICONV
2867 if (asccasecmp(cp, "utf-8")) {
2868 iconv_t it;
2869 char *nsp, *nspec;
2870 size_t sz, nsz;
2871 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
2872 sz = strlen(spec) + 1;
2873 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2874 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz,
2875 FAL0) == 0 && sz == 0) {
2876 spec = nspec;
2877 cp = "utf-8";
2879 n_iconv_close(it);
2882 #endif
2883 cp = imap_quotestr(cp);
2884 cs = salloc(n = strlen(cp) + 10);
2885 snprintf(cs, n, "CHARSET %s ", cp);
2886 } else
2887 cs = UNCONST("");
2888 o = ac_alloc(osize = strlen(spec) + 60);
2889 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2890 IMAP_OUT(o, MB_COMD, goto out)
2891 while (mp->mb_active & MB_COMD) {
2892 ok = imap_answer(mp, 0);
2893 if (response_status == RESPONSE_OTHER &&
2894 response_other == MAILBOX_DATA_SEARCH) {
2895 xp = responded_other_text;
2896 while (*xp && *xp != '\r') {
2897 n = strtoul(xp, &xp, 10);
2898 for (i = 0; i < cnt; i++)
2899 if (m[i].m_uid == n &&
2900 (m[i].m_flag&MHIDDEN)
2901 == 0 &&
2902 (f == MDELETED ||
2903 (m[i].m_flag&MDELETED)
2904 == 0))
2905 mark(i+1, f);
2909 out: ac_free(o);
2910 return ok;
2913 enum okay
2914 imap_search1(const char *volatile spec, int f)
2916 sighandler_type saveint, savepipe;
2917 enum okay ok = STOP;
2919 (void)&saveint;
2920 (void)&savepipe;
2921 (void)&ok;
2922 if (mb.mb_type != MB_IMAP)
2923 return STOP;
2924 imaplock = 1;
2925 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2926 safe_signal(SIGINT, maincatch);
2927 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2928 if (sigsetjmp(imapjmp, 1) == 0) {
2929 if (savepipe != SIG_IGN)
2930 safe_signal(SIGPIPE, imapcatch);
2931 ok = imap_search2(&mb, message, msgCount, spec, f);
2933 safe_signal(SIGINT, saveint);
2934 safe_signal(SIGPIPE, savepipe);
2935 imaplock = 0;
2936 if (interrupts)
2937 onintr(0);
2938 return ok;
2941 int
2942 imap_thisaccount(const char *cp)
2944 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2945 return 0;
2946 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2947 mb.mb_imap_account == NULL)
2948 return 0;
2949 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2952 enum okay
2953 imap_remove(const char *name)
2955 sighandler_type saveint, savepipe;
2956 enum okay ok = STOP;
2958 (void)&saveint;
2959 (void)&savepipe;
2960 (void)&ok;
2961 if (mb.mb_type != MB_IMAP) {
2962 fprintf(stderr, "Refusing to remove \"%s\" "
2963 "in disconnected mode.\n", name);
2964 return STOP;
2966 if (!imap_thisaccount(name)) {
2967 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2968 "server: \"%s\" not removed.\n", name);
2969 return STOP;
2971 imaplock = 1;
2972 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2973 safe_signal(SIGINT, maincatch);
2974 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2975 if (sigsetjmp(imapjmp, 1) == 0) {
2976 if (savepipe != SIG_IGN)
2977 safe_signal(SIGPIPE, imapcatch);
2978 ok = imap_remove1(&mb, imap_fileof(name));
2980 safe_signal(SIGINT, saveint);
2981 safe_signal(SIGPIPE, savepipe);
2982 imaplock = 0;
2983 if (ok == OKAY)
2984 ok = cache_remove(name);
2985 if (interrupts)
2986 onintr(0);
2987 return ok;
2990 static enum okay
2991 imap_remove1(struct mailbox *mp, const char *name)
2993 FILE *queuefp = NULL;
2994 char *o;
2995 int os;
2996 enum okay ok = STOP;
2998 o = ac_alloc(os = 2*strlen(name) + 100);
2999 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3000 IMAP_OUT(o, MB_COMD, goto out)
3001 while (mp->mb_active & MB_COMD)
3002 ok = imap_answer(mp, 1);
3003 out: ac_free(o);
3004 return ok;
3007 enum okay
3008 imap_rename(const char *old, const char *new)
3010 sighandler_type saveint, savepipe;
3011 enum okay ok = STOP;
3013 (void)&saveint;
3014 (void)&savepipe;
3015 (void)&ok;
3016 if (mb.mb_type != MB_IMAP) {
3017 fprintf(stderr, "Refusing to rename mailboxes "
3018 "in disconnected mode.\n");
3019 return STOP;
3021 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3022 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3023 "server: \"%s\" not renamed to \"%s\".\n",
3024 old, new);
3025 return STOP;
3027 imaplock = 1;
3028 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3029 safe_signal(SIGINT, maincatch);
3030 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3031 if (sigsetjmp(imapjmp, 1) == 0) {
3032 if (savepipe != SIG_IGN)
3033 safe_signal(SIGPIPE, imapcatch);
3034 ok = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3036 safe_signal(SIGINT, saveint);
3037 safe_signal(SIGPIPE, savepipe);
3038 imaplock = 0;
3039 if (ok == OKAY)
3040 ok = cache_rename(old, new);
3041 if (interrupts)
3042 onintr(0);
3043 return ok;
3046 static enum okay
3047 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3049 FILE *queuefp = NULL;
3050 char *o;
3051 int os;
3052 enum okay ok = STOP;
3054 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3055 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3056 imap_quotestr(old), imap_quotestr(new));
3057 IMAP_OUT(o, MB_COMD, goto out)
3058 while (mp->mb_active & MB_COMD)
3059 ok = imap_answer(mp, 1);
3060 out: ac_free(o);
3061 return ok;
3064 enum okay
3065 imap_dequeue(struct mailbox *mp, FILE *fp)
3067 FILE *queuefp = NULL;
3068 char o[LINESIZE], *newname;
3069 char *buf, *bp, *cp, iob[4096];
3070 size_t bufsize, buflen, cnt;
3071 enum okay ok = OKAY, rok = OKAY;
3072 long offs, offs1, offs2, octets;
3073 int twice, gotcha = 0;
3075 buf = smalloc(bufsize = LINESIZE);
3076 buflen = 0;
3077 cnt = fsize(fp);
3078 while ((offs1 = ftell(fp)) >= 0 &&
3079 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0)
3080 != NULL) {
3081 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3082 while (*bp == ' ')
3083 bp++;
3084 twice = 0;
3085 if ((offs = ftell(fp)) < 0)
3086 goto fail;
3087 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3088 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3089 cp = &bp[9];
3090 while (digitchar(*cp&0377))
3091 cp++;
3092 if (*cp != ' ')
3093 goto fail;
3094 while (*cp == ' ')
3095 cp++;
3096 if ((newname = imap_strex(cp, NULL)) == NULL)
3097 goto fail;
3098 IMAP_OUT(o, MB_COMD, continue)
3099 while (mp->mb_active & MB_COMD)
3100 ok = imap_answer(mp, twice);
3101 if (response_status == RESPONSE_NO && twice++ == 0)
3102 goto trycreate;
3103 if (response_status == RESPONSE_OK &&
3104 mp->mb_flags & MB_UIDPLUS) {
3105 imap_copyuid(mp, NULL,
3106 imap_unquotestr(newname));
3108 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3109 IMAP_OUT(o, MB_COMD, continue)
3110 while (mp->mb_active & MB_COMD)
3111 ok = imap_answer(mp, 1);
3112 if (ok == OKAY)
3113 gotcha++;
3114 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3115 if ((cp = strrchr(bp, '{')) == NULL)
3116 goto fail;
3117 octets = atol(&cp[1]) + 2;
3118 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3119 goto fail;
3120 IMAP_OUT(o, MB_COMD, continue)
3121 while (mp->mb_active & MB_COMD) {
3122 ok = imap_answer(mp, twice);
3123 if (response_type == RESPONSE_CONT)
3124 break;
3126 if (ok == STOP) {
3127 if (twice++ == 0 &&
3128 fseek(fp, offs, SEEK_SET) >= 0)
3129 goto trycreate;
3130 goto fail;
3132 while (octets > 0) {
3133 size_t n = (size_t)octets > sizeof iob
3134 ? sizeof iob : (size_t)octets;
3135 octets -= n;
3136 if (n != fread(iob, 1, n, fp))
3137 goto fail;
3138 swrite1(&mp->mb_sock, iob, n, 1);
3140 swrite(&mp->mb_sock, "");
3141 while (mp->mb_active & MB_COMD) {
3142 ok = imap_answer(mp, 0);
3143 if (response_status == RESPONSE_NO &&
3144 twice++ == 0) {
3145 if (fseek(fp, offs, SEEK_SET) < 0)
3146 goto fail;
3147 goto trycreate;
3150 if (response_status == RESPONSE_OK &&
3151 mp->mb_flags & MB_UIDPLUS) {
3152 if ((offs2 = ftell(fp)) < 0)
3153 goto fail;
3154 fseek(fp, offs1, SEEK_SET);
3155 if (imap_appenduid_cached(mp, fp) == STOP) {
3156 (void)fseek(fp, offs2, SEEK_SET);
3157 goto fail;
3160 } else {
3161 fail: fprintf(stderr,
3162 "Invalid command in IMAP cache queue: \"%s\"\n",
3163 bp);
3164 rok = STOP;
3166 continue;
3167 trycreate:
3168 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3169 tag(1), newname);
3170 IMAP_OUT(o, MB_COMD, continue)
3171 while (mp->mb_active & MB_COMD)
3172 ok = imap_answer(mp, 1);
3173 if (ok == OKAY)
3174 goto again;
3176 fflush(fp);
3177 rewind(fp);
3178 ftruncate(fileno(fp), 0);
3179 if (gotcha)
3180 imap_close(mp);
3181 free(buf);
3182 return rok;
3185 static char *
3186 imap_strex(char const *cp, char const **xp)
3188 char const *cq;
3189 char *n;
3191 if (*cp != '"')
3192 return NULL;
3193 for (cq = &cp[1]; *cq; cq++) {
3194 if (*cq == '\\')
3195 cq++;
3196 else if (*cq == '"')
3197 break;
3199 if (*cq != '"')
3200 return NULL;
3201 n = salloc(cq - cp + 2);
3202 memcpy(n, cp, cq - cp + 1);
3203 n[cq - cp + 1] = '\0';
3204 if (xp)
3205 *xp = &cq[1];
3206 return n;
3209 static enum okay
3210 check_expunged(void)
3212 if (expunged_messages > 0) {
3213 fprintf(stderr,
3214 "Command not executed - messages have been expunged\n");
3215 return STOP;
3217 return OKAY;
3220 /*ARGSUSED*/
3221 int
3222 cconnect(void *vp)
3224 char *cp, *cq;
3225 int omsgCount = msgCount;
3226 (void)vp;
3228 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3229 fprintf(stderr, "Already connected.\n");
3230 return 1;
3232 unset_allow_undefined = TRU1;
3233 unset_internal("disconnected");
3234 cp = protbase(mailname);
3235 if (strncmp(cp, "imap://", 7) == 0)
3236 cp += 7;
3237 else if (strncmp(cp, "imaps://", 8) == 0)
3238 cp += 8;
3239 if ((cq = strchr(cp, ':')) != NULL)
3240 *cq = '\0';
3241 unset_internal(savecat("disconnected-", cp));
3242 unset_allow_undefined = FAL0;
3243 if (mb.mb_type == MB_CACHE) {
3244 imap_setfile1(mailname, 0, edit, 1);
3245 if (msgCount > omsgCount)
3246 newmailinfo(omsgCount);
3248 return 0;
3251 int
3252 cdisconnect(void *vp)
3254 int *msgvec = vp;
3256 if (mb.mb_type == MB_CACHE) {
3257 fprintf(stderr, "Not connected.\n");
3258 return 1;
3259 } else if (mb.mb_type == MB_IMAP) {
3260 if (cached_uidvalidity(&mb) == 0) {
3261 fprintf(stderr, "The current mailbox is not cached.\n");
3262 return 1;
3265 if (*msgvec)
3266 ccache(vp);
3267 assign("disconnected", "");
3268 if (mb.mb_type == MB_IMAP) {
3269 sclose(&mb.mb_sock);
3270 imap_setfile1(mailname, 0, edit, 1);
3272 return 0;
3275 int
3276 ccache(void *vp)
3278 int *msgvec = vp, *ip;
3279 struct message *mp;
3281 if (mb.mb_type != MB_IMAP) {
3282 fprintf(stderr, "Not connected to an IMAP server.\n");
3283 return 1;
3285 if (cached_uidvalidity(&mb) == 0) {
3286 fprintf(stderr, "The current mailbox is not cached.\n");
3287 return 1;
3289 for (ip = msgvec; *ip; ip++) {
3290 mp = &message[*ip-1];
3291 if (!(mp->m_have & HAVE_BODY))
3292 get_body(mp);
3294 return 0;
3298 disconnected(const char *file)
3300 char *cp, *cq, *vp;
3301 int vs, r;
3303 if (value("disconnected"))
3304 return 1;
3305 cp = protbase(file);
3306 if (strncmp(cp, "imap://", 7) == 0)
3307 cp += 7;
3308 else if (strncmp(cp, "imaps://", 8) == 0)
3309 cp += 8;
3310 else
3311 return 0;
3312 if ((cq = strchr(cp, ':')) != NULL)
3313 *cq = '\0';
3314 vp = ac_alloc(vs = strlen(cp) + 14);
3315 snprintf(vp, vs, "disconnected-%s", cp);
3316 r = value(vp) != NULL;
3317 ac_free(vp);
3318 return r;
3321 void
3322 transflags(struct message *omessage, long omsgCount, int transparent)
3324 struct message *omp, *nmp, *newdot, *newprevdot;
3325 int hf;
3327 omp = omessage;
3328 nmp = message;
3329 newdot = message;
3330 newprevdot = NULL;
3331 while (omp < &omessage[omsgCount] &&
3332 nmp < &message[msgCount]) {
3333 if (dot && nmp->m_uid == dot->m_uid)
3334 newdot = nmp;
3335 if (prevdot && nmp->m_uid == prevdot->m_uid)
3336 newprevdot = nmp;
3337 if (omp->m_uid == nmp->m_uid) {
3338 hf = nmp->m_flag & MHIDDEN;
3339 if (transparent && mb.mb_type == MB_IMAP)
3340 omp->m_flag &= ~MHIDDEN;
3341 *nmp++ = *omp++;
3342 if (transparent && mb.mb_type == MB_CACHE)
3343 nmp[-1].m_flag |= hf;
3344 } else if (omp->m_uid < nmp->m_uid)
3345 omp++;
3346 else
3347 nmp++;
3349 dot = newdot;
3350 setdot(newdot);
3351 prevdot = newprevdot;
3352 free(omessage);
3354 #endif /* HAVE_IMAP */
3356 time_t
3357 imap_read_date_time(const char *cp)
3359 time_t t;
3360 int i, year, month, day, hour, minute, second;
3361 int sign = -1;
3362 char buf[3];
3365 * "25-Jul-2004 15:33:44 +0200"
3366 * | | | | | |
3367 * 0 5 10 15 20 25
3369 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3370 goto invalid;
3371 day = strtol(&cp[1], NULL, 10);
3372 for (i = 0; month_names[i]; i++)
3373 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3374 break;
3375 if (month_names[i] == NULL)
3376 goto invalid;
3377 month = i + 1;
3378 year = strtol(&cp[8], NULL, 10);
3379 hour = strtol(&cp[13], NULL, 10);
3380 minute = strtol(&cp[16], NULL, 10);
3381 second = strtol(&cp[19], NULL, 10);
3382 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3383 (time_t)-1)
3384 goto invalid;
3385 switch (cp[22]) {
3386 case '-':
3387 sign = 1;
3388 break;
3389 case '+':
3390 break;
3391 default:
3392 goto invalid;
3394 buf[2] = '\0';
3395 buf[0] = cp[23];
3396 buf[1] = cp[24];
3397 t += strtol(buf, NULL, 10) * sign * 3600;
3398 buf[0] = cp[25];
3399 buf[1] = cp[26];
3400 t += strtol(buf, NULL, 10) * sign * 60;
3401 return t;
3402 invalid:
3403 time(&t);
3404 return t;
3407 time_t
3408 imap_read_date(const char *cp)
3410 time_t t;
3411 int year, month, day, i, tzdiff;
3412 struct tm *tmptr;
3413 char *xp, *yp;
3415 if (*cp == '"')
3416 cp++;
3417 day = strtol(cp, &xp, 10);
3418 if (day <= 0 || day > 31 || *xp++ != '-')
3419 return -1;
3420 for (i = 0; month_names[i]; i++)
3421 if (ascncasecmp(xp, month_names[i], 3) == 0)
3422 break;
3423 if (month_names[i] == NULL)
3424 return -1;
3425 month = i+1;
3426 if (xp[3] != '-')
3427 return -1;
3428 year = strtol(&xp[4], &yp, 10);
3429 if (year < 1970 || year > 2037 || yp != &xp[8])
3430 return -1;
3431 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3432 return -1;
3433 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3434 return -1;
3435 tzdiff = t - mktime(gmtime(&t));
3436 tmptr = localtime(&t);
3437 if (tmptr->tm_isdst > 0)
3438 tzdiff += 3600;
3439 t -= tzdiff;
3440 return t;
3443 const char *
3444 imap_make_date_time(time_t t)
3446 static char s[30];
3447 struct tm *tmptr;
3448 int tzdiff, tzdiff_hour, tzdiff_min;
3450 tzdiff = t - mktime(gmtime(&t));
3451 tzdiff_hour = (int)(tzdiff / 60);
3452 tzdiff_min = tzdiff_hour % 60;
3453 tzdiff_hour /= 60;
3454 tmptr = localtime(&t);
3455 if (tmptr->tm_isdst > 0)
3456 tzdiff_hour++;
3457 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3458 tmptr->tm_mday,
3459 month_names[tmptr->tm_mon],
3460 tmptr->tm_year + 1900,
3461 tmptr->tm_hour,
3462 tmptr->tm_min,
3463 tmptr->tm_sec,
3464 tzdiff_hour,
3465 tzdiff_min);
3466 return s;
3469 char *
3470 imap_quotestr(const char *s)
3472 char *n, *np;
3474 np = n = salloc(2 * strlen(s) + 3);
3475 *np++ = '"';
3476 while (*s) {
3477 if (*s == '"' || *s == '\\')
3478 *np++ = '\\';
3479 *np++ = *s++;
3481 *np++ = '"';
3482 *np = '\0';
3483 return n;
3486 char *
3487 imap_unquotestr(const char *s)
3489 char *n, *np;
3491 if (*s != '"')
3492 return savestr(s);
3493 np = n = salloc(strlen(s) + 1);
3494 while (*++s) {
3495 if (*s == '\\')
3496 s++;
3497 else if (*s == '"')
3498 break;
3499 *np++ = *s;
3501 *np = '\0';
3502 return n;