Fix -u, change meaning of $USER to be EQ -u etc..
[s-mailx.git] / imap.c
blobeb82abf6417794940863e45d72ef37cae401c093
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 int j = i + chunk - 1;
1727 /*ok = */imap_fetchheaders(&mb, message, i,
1728 j < topp ? j : topp);
1729 if (interrupts)
1730 onintr(0);
1733 safe_signal(SIGINT, saveint);
1734 safe_signal(SIGPIPE, savepipe);
1735 imaplock = 0;
1738 static enum okay
1739 __imap_exit(struct mailbox *mp)
1741 char o[LINESIZE];
1742 FILE *queuefp = NULL;
1744 mp->mb_active |= MB_BYE;
1745 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1746 IMAP_OUT(o, MB_COMD, return STOP)
1747 IMAP_ANSWER()
1748 return OKAY;
1751 static enum okay
1752 imap_exit(struct mailbox *mp)
1754 enum okay ret = __imap_exit(mp);
1755 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1756 free(mp->mb_imap_account);
1757 free(mp->mb_imap_mailbox);
1758 if (mp->mb_cache_directory != NULL)
1759 free(mp->mb_cache_directory);
1760 #ifndef HAVE_ASSERTS /* TODO ASSERT LEGACY */
1761 mp->mb_imap_account =
1762 mp->mb_imap_mailbox =
1763 mp->mb_cache_directory = "";
1764 #else
1765 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1766 mp->mb_imap_mailbox = NULL;
1767 mp->mb_cache_directory = NULL;
1768 #endif
1769 #endif
1770 sclose(&mp->mb_sock);
1771 return ret;
1774 static enum okay
1775 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1777 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1778 if (mp->mb_type == MB_IMAP)
1779 delcache(mp, m);
1780 return OKAY;
1783 static enum okay
1784 imap_close(struct mailbox *mp)
1786 char o[LINESIZE];
1787 FILE *queuefp = NULL;
1789 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1790 IMAP_OUT(o, MB_COMD, return STOP)
1791 IMAP_ANSWER()
1792 return OKAY;
1795 static enum okay
1796 imap_update(struct mailbox *mp)
1798 struct message *m;
1799 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1801 if (!edit && mp->mb_perm != 0) {
1802 holdbits();
1803 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1804 if (m->m_flag & MBOX)
1805 c++;
1807 if (c > 0)
1808 if (makembox() == STOP)
1809 goto bypass;
1811 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1812 if (mp->mb_perm == 0) {
1813 dodel = 0;
1814 } else if (edit) {
1815 dodel = m->m_flag & MDELETED;
1816 } else {
1817 dodel = !((m->m_flag&MPRESERVE) ||
1818 (m->m_flag&MTOUCH) == 0);
1821 * Fetch the result after around each 800 STORE commands
1822 * sent (approx. 32k data sent). Otherwise, servers will
1823 * try to flush the return queue at some point, leading
1824 * to a deadlock if we are still writing commands but not
1825 * reading their results.
1827 needstat = stored > 0 && stored % 800 == 0;
1829 * Even if this message has been deleted, continue
1830 * to set further flags. This is necessary to support
1831 * Gmail semantics, where "delete" actually means
1832 * "archive", and the flags are applied to the copy
1833 * in "All Mail".
1835 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1836 imap_store(mp, m, m-message+1,
1837 '+', "\\Seen", needstat);
1838 stored++;
1840 if (m->m_flag & MFLAG) {
1841 imap_store(mp, m, m-message+1,
1842 '+', "\\Flagged", needstat);
1843 stored++;
1845 if (m->m_flag & MUNFLAG) {
1846 imap_store(mp, m, m-message+1,
1847 '-', "\\Flagged", needstat);
1848 stored++;
1850 if (m->m_flag & MANSWER) {
1851 imap_store(mp, m, m-message+1,
1852 '+', "\\Answered", needstat);
1853 stored++;
1855 if (m->m_flag & MUNANSWER) {
1856 imap_store(mp, m, m-message+1,
1857 '-', "\\Answered", needstat);
1858 stored++;
1860 if (m->m_flag & MDRAFT) {
1861 imap_store(mp, m, m-message+1,
1862 '+', "\\Draft", needstat);
1863 stored++;
1865 if (m->m_flag & MUNDRAFT) {
1866 imap_store(mp, m, m-message+1,
1867 '-', "\\Draft", needstat);
1868 stored++;
1870 if (dodel) {
1871 imap_delete(mp, m-message+1, m, needstat);
1872 stored++;
1873 gotcha++;
1874 } else if (mp->mb_type != MB_CACHE ||
1875 (! edit && ! (m->m_flag&(MBOXED|MSAVED|MDELETED))) ||
1876 (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) ==
1877 (MPRESERVE|MTOUCH) ||
1878 (edit && ! (m->m_flag & MDELETED)))
1879 held++;
1880 if (m->m_flag & MNEW) {
1881 m->m_flag &= ~MNEW;
1882 m->m_flag |= MSTATUS;
1885 bypass:
1886 if (gotcha)
1887 imap_close(mp);
1888 for (m = &message[0]; m < &message[msgCount]; m++)
1889 if (!(m->m_flag&MUNLINKED) &&
1890 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1891 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1892 MDRAFT|MUNDRAFT)) {
1893 putcache(mp, m);
1894 modflags++;
1896 if ((gotcha || modflags) && edit) {
1897 printf(tr(168, "\"%s\" "), displayname);
1898 printf((value("bsdcompat") || value("bsdmsgs"))
1899 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1900 } else if (held && !edit && mp->mb_perm != 0) {
1901 if (held == 1)
1902 printf(tr(155, "Held 1 message in %s\n"), displayname);
1903 else
1904 printf(tr(156, "Held %d messages in %s\n"), held,
1905 displayname);
1907 fflush(stdout);
1908 return OKAY;
1911 void
1912 imap_quit(void)
1914 sighandler_type saveint;
1915 sighandler_type savepipe;
1917 if (mb.mb_type == MB_CACHE) {
1918 imap_update(&mb);
1919 return;
1921 if (mb.mb_sock.s_fd < 0) {
1922 fprintf(stderr, "IMAP connection closed.\n");
1923 return;
1925 imaplock = 1;
1926 saveint = safe_signal(SIGINT, SIG_IGN);
1927 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1928 if (sigsetjmp(imapjmp, 1)) {
1929 safe_signal(SIGINT, saveint);
1930 safe_signal(SIGPIPE, saveint);
1931 imaplock = 0;
1932 return;
1934 if (saveint != SIG_IGN)
1935 safe_signal(SIGINT, imapcatch);
1936 if (savepipe != SIG_IGN)
1937 safe_signal(SIGPIPE, imapcatch);
1938 imap_update(&mb);
1939 if (!same_imap_account) {
1940 imap_exit(&mb);
1942 safe_signal(SIGINT, saveint);
1943 safe_signal(SIGPIPE, savepipe);
1944 imaplock = 0;
1947 static enum okay
1948 imap_store(struct mailbox *mp, struct message *m, int n,
1949 int c, const char *sp, int needstat)
1951 char o[LINESIZE];
1952 FILE *queuefp = NULL;
1954 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1955 return STOP;
1956 if (m->m_uid)
1957 snprintf(o, sizeof o,
1958 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1959 tag(1), m->m_uid, c, sp);
1960 else {
1961 if (check_expunged() == STOP)
1962 return STOP;
1963 snprintf(o, sizeof o,
1964 "%s STORE %u %cFLAGS (%s)\r\n",
1965 tag(1), n, c, sp);
1967 IMAP_OUT(o, MB_COMD, return STOP)
1968 if (needstat)
1969 IMAP_ANSWER()
1970 else
1971 mb.mb_active &= ~MB_COMD;
1972 if (queuefp != NULL)
1973 Fclose(queuefp);
1974 return OKAY;
1977 enum okay
1978 imap_undelete(struct message *m, int n)
1980 return imap_unstore(m, n, "\\Deleted");
1983 enum okay
1984 imap_unread(struct message *m, int n)
1986 return imap_unstore(m, n, "\\Seen");
1989 static enum okay
1990 imap_unstore(struct message *m, int n, const char *flag)
1992 sighandler_type saveint, savepipe;
1993 enum okay ok = STOP;
1995 (void)&saveint;
1996 (void)&savepipe;
1997 (void)&ok;
1998 imaplock = 1;
1999 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2000 safe_signal(SIGINT, maincatch);
2001 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2002 if (sigsetjmp(imapjmp, 1) == 0) {
2003 if (savepipe != SIG_IGN)
2004 safe_signal(SIGPIPE, imapcatch);
2005 ok = imap_store(&mb, m, n, '-', flag, 1);
2007 safe_signal(SIGINT, saveint);
2008 safe_signal(SIGPIPE, savepipe);
2009 imaplock = 0;
2010 if (interrupts)
2011 onintr(0);
2012 return ok;
2015 static const char *
2016 tag(int new)
2018 static char ts[20];
2019 static long n;
2021 if (new)
2022 n++;
2023 snprintf(ts, sizeof ts, "T%lu", n);
2024 return ts;
2027 int
2028 imap_imap(void *vp)
2030 sighandler_type saveint, savepipe;
2031 char o[LINESIZE];
2032 enum okay ok = STOP;
2033 struct mailbox *mp = &mb;
2034 FILE *queuefp = NULL;
2036 (void)&saveint;
2037 (void)&savepipe;
2038 (void)&ok;
2039 if (mp->mb_type != MB_IMAP) {
2040 printf("Not operating on an IMAP mailbox.\n");
2041 return 1;
2043 imaplock = 1;
2044 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2045 safe_signal(SIGINT, maincatch);
2046 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2047 if (sigsetjmp(imapjmp, 1) == 0) {
2048 if (savepipe != SIG_IGN)
2049 safe_signal(SIGPIPE, imapcatch);
2050 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2051 IMAP_OUT(o, MB_COMD, goto out)
2052 while (mp->mb_active & MB_COMD) {
2053 ok = imap_answer(mp, 0);
2054 fputs(responded_text, stdout);
2057 out: safe_signal(SIGINT, saveint);
2058 safe_signal(SIGPIPE, savepipe);
2059 imaplock = 0;
2060 if (interrupts)
2061 onintr(0);
2062 return ok != OKAY;
2065 int
2066 imap_newmail(int autoinc)
2068 if (autoinc && had_exists < 0 && had_expunge < 0) {
2069 imaplock = 1;
2070 imap_noop();
2071 imaplock = 0;
2073 if (had_exists == msgCount && had_expunge < 0)
2075 * Some servers always respond with EXISTS to NOOP. If
2076 * the mailbox has been changed but the number of messages
2077 * has not, an EXPUNGE must also had been sent; otherwise,
2078 * nothing has changed.
2080 had_exists = -1;
2081 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2084 static char *
2085 imap_putflags(int f)
2087 const char *cp;
2088 char *buf, *bp;
2090 bp = buf = salloc(100);
2091 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2092 *bp++ = '(';
2093 if (f & MREAD) {
2094 if (bp[-1] != '(')
2095 *bp++ = ' ';
2096 for (cp = "\\Seen"; *cp; cp++)
2097 *bp++ = *cp;
2099 if (f & MFLAGGED) {
2100 if (bp[-1] != '(')
2101 *bp++ = ' ';
2102 for (cp = "\\Flagged"; *cp; cp++)
2103 *bp++ = *cp;
2105 if (f & MANSWERED) {
2106 if (bp[-1] != '(')
2107 *bp++ = ' ';
2108 for (cp = "\\Answered"; *cp; cp++)
2109 *bp++ = *cp;
2111 if (f & MDRAFT) {
2112 if (bp[-1] != '(')
2113 *bp++ = ' ';
2114 for (cp = "\\Draft"; *cp; cp++)
2115 *bp++ = *cp;
2117 *bp++ = ')';
2118 *bp++ = ' ';
2120 *bp = '\0';
2121 return buf;
2124 static void
2125 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2127 while (*cp != ')') {
2128 if (*cp == '\\') {
2129 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2130 *f |= MREAD;
2131 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2132 *f |= MNEW;
2133 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2134 *f |= MDELETED;
2135 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2136 *f |= MFLAGGED;
2137 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2138 *f |= MANSWERED;
2139 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2140 *f |= MDRAFTED;
2142 cp++;
2144 if (xp)
2145 *xp = cp;
2148 static enum okay
2149 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2150 off_t off1, long xsize, enum mflag flag, time_t t)
2152 char o[LINESIZE];
2153 char *buf;
2154 size_t bufsize, buflen, cnt;
2155 enum okay ok = STOP;
2156 long size, lines, ysize;
2157 int twice = 0;
2158 FILE *queuefp = NULL;
2160 if (mp->mb_type == MB_CACHE) {
2161 queuefp = cache_queue(mp);
2162 if (queuefp == NULL)
2163 return STOP;
2164 ok = OKAY;
2166 buf = smalloc(bufsize = LINESIZE);
2167 buflen = 0;
2168 again: size = xsize;
2169 cnt = fsize(fp);
2170 if (fseek(fp, off1, SEEK_SET) < 0) {
2171 ok = STOP;
2172 goto out;
2174 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2175 tag(1), imap_quotestr(name),
2176 imap_putflags(flag),
2177 imap_make_date_time(t),
2178 size);
2179 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2180 while (mp->mb_active & MB_COMD) {
2181 ok = imap_answer(mp, twice);
2182 if (response_type == RESPONSE_CONT)
2183 break;
2185 if (mp->mb_type != MB_CACHE && ok == STOP) {
2186 if (twice == 0)
2187 goto trycreate;
2188 else
2189 goto out;
2191 lines = ysize = 0;
2192 while (size > 0) {
2193 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2194 lines++;
2195 ysize += buflen;
2196 buf[buflen-1] = '\r';
2197 buf[buflen] = '\n';
2198 if (mp->mb_type != MB_CACHE)
2199 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2200 else if (queuefp)
2201 fwrite(buf, 1, buflen+1, queuefp);
2202 size -= buflen+1;
2204 if (mp->mb_type != MB_CACHE)
2205 swrite(&mp->mb_sock, "\r\n");
2206 else if (queuefp)
2207 fputs("\r\n", queuefp);
2208 while (mp->mb_active & MB_COMD) {
2209 ok = imap_answer(mp, 0);
2210 if (response_status == RESPONSE_NO /*&&
2211 ascncasecmp(responded_text,
2212 "[TRYCREATE] ", 12) == 0*/) {
2213 trycreate: if (twice++) {
2214 ok = STOP;
2215 goto out;
2217 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2218 tag(1),
2219 imap_quotestr(name));
2220 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2221 while (mp->mb_active & MB_COMD)
2222 ok = imap_answer(mp, 1);
2223 if (ok == STOP)
2224 goto out;
2225 imap_created_mailbox++;
2226 goto again;
2227 } else if (ok != OKAY)
2228 fprintf(stderr, tr(270, "IMAP error: %s"),
2229 responded_text);
2230 else if (response_status == RESPONSE_OK &&
2231 mp->mb_flags & MB_UIDPLUS)
2232 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2233 flag, name);
2235 out: if (queuefp != NULL)
2236 Fclose(queuefp);
2237 free(buf);
2238 return ok;
2241 static enum okay
2242 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2244 char *buf, *bp, *lp;
2245 size_t bufsize, buflen, cnt;
2246 off_t off1 = -1, offs;
2247 int inhead = 1;
2248 int flag = MNEW|MNEWEST;
2249 long size = 0;
2250 time_t tim;
2251 enum okay ok;
2253 buf = smalloc(bufsize = LINESIZE);
2254 buflen = 0;
2255 cnt = fsize(fp);
2256 offs = ftell(fp);
2257 time(&tim);
2258 do {
2259 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2260 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2261 if (off1 != (off_t)-1) {
2262 ok=imap_append1(mp, name, fp, off1,
2263 size, flag, tim);
2264 if (ok == STOP)
2265 return STOP;
2266 fseek(fp, offs+buflen, SEEK_SET);
2268 off1 = offs + buflen;
2269 size = 0;
2270 inhead = 1;
2271 flag = MNEW;
2272 if (bp != NULL)
2273 tim = unixtime(buf);
2274 } else
2275 size += buflen+1;
2276 offs += buflen;
2277 if (bp && buf[0] == '\n')
2278 inhead = 0;
2279 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2280 lp = &buf[6];
2281 while (whitechar(*lp&0377))
2282 lp++;
2283 if (*lp == ':')
2284 while (*++lp != '\0')
2285 switch (*lp) {
2286 case 'R':
2287 flag |= MREAD;
2288 break;
2289 case 'O':
2290 flag &= ~MNEW;
2291 break;
2293 } else if (bp && inhead &&
2294 ascncasecmp(buf, "x-status", 8) == 0) {
2295 lp = &buf[8];
2296 while (whitechar(*lp&0377))
2297 lp++;
2298 if (*lp == ':')
2299 while (*++lp != '\0')
2300 switch (*lp) {
2301 case 'F':
2302 flag |= MFLAGGED;
2303 break;
2304 case 'A':
2305 flag |= MANSWERED;
2306 break;
2307 case 'T':
2308 flag |= MDRAFTED;
2309 break;
2312 } while (bp != NULL);
2313 free(buf);
2314 return OKAY;
2317 enum okay
2318 imap_append(const char *xserver, FILE *fp)
2320 sighandler_type saveint, savepipe;
2321 char *server, *user, *pass;
2322 char const *sp, *cp, *mbx, *uhp;
2323 int use_ssl;
2324 enum okay ok = STOP;
2326 server = savestr(xserver);
2327 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2328 imaplock = 1;
2329 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2330 safe_signal(SIGINT, maincatch);
2331 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2332 if (sigsetjmp(imapjmp, 1))
2333 goto out;
2334 if (savepipe != SIG_IGN)
2335 safe_signal(SIGPIPE, imapcatch);
2336 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2337 mb.mb_imap_account &&
2338 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2339 ok = imap_append0(&mb, mbx, fp);
2341 else {
2342 struct mailbox mx;
2344 memset(&mx, 0, sizeof mx);
2345 if (disconnected(server) == 0) {
2346 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2347 use_ssl ? "imaps" : "imap",
2348 (options & OPT_VERBOSE) != 0) != OKAY)
2349 goto fail;
2350 mx.mb_sock.s_desc = "IMAP";
2351 mx.mb_type = MB_IMAP;
2352 mx.mb_imap_account = (char *)protbase(server);
2353 /* TODO the code now did
2354 * TODO mx.mb_imap_mailbox = mbx;
2355 * TODO though imap_mailbox is sfree()d and mbx
2356 * TODO is possibly even a constant
2357 * TODO i changed this to sstrdup() sofar, as is used
2358 * TODO somewhere else in this file for this! */
2359 mx.mb_imap_mailbox = sstrdup(mbx);
2360 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2361 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2362 sclose(&mx.mb_sock);
2363 goto fail;
2365 ok = imap_append0(&mx, mbx, fp);
2366 imap_exit(&mx);
2367 } else {
2368 mx.mb_imap_account = (char *)protbase(server);
2369 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
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 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2432 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2433 return STOP;
2434 if (list == NULL || lend == NULL)
2435 return OKAY;
2436 for (lp = list; lp; lp = lp->l_next)
2437 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2438 lp->l_level < depth &&
2439 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2440 cp = salloc((n = strlen(lp->l_name)) + 2);
2441 memcpy(cp, lp->l_name, n);
2442 cp[n] = lp->l_delim;
2443 cp[n+1] = '\0';
2444 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2445 lx && ly) {
2446 lp->l_has_children = 1;
2447 if (strcmp(cp, lx->l_name) == 0)
2448 lx = lx->l_next;
2449 if (lx) {
2450 lend->l_next = lx;
2451 lend = ly;
2455 for (lp = list; lp; lp = lp->l_next) {
2456 if (strip) {
2457 cp = lp->l_name;
2458 for (bp = base; *bp && *bp == *cp; bp++)
2459 cp++;
2460 } else
2461 cp = lp->l_name;
2462 if ((lp->l_attr&LIST_NOSELECT) == 0)
2463 fprintf(fp, "%s\n", *cp ? cp : base);
2464 else if (lp->l_has_children == 0)
2465 fprintf(fp, "%s%c\n", *cp ? cp : base,
2466 lp->l_delim != EOF ? lp->l_delim : '\n');
2468 return OKAY;
2471 void
2472 imap_folders(const char *name, int strip)
2474 sighandler_type saveint, savepipe;
2475 const char *fold, *cp, *sp;
2476 char *tempfn;
2477 FILE *volatile fp;
2479 cp = protbase(name);
2480 sp = mb.mb_imap_account;
2481 if (sp == NULL || strcmp(cp, sp)) {
2482 fprintf(stderr, tr(502,
2483 "Cannot perform `folders' but when on the very IMAP "
2484 "account; the current one is\n `%s' -- "
2485 "try `folders @'.\n"),
2486 (sp != NULL) ? sp : tr(503, "[NONE]"));
2487 return;
2489 fold = imap_fileof(name);
2490 if (options & OPT_TTYOUT) {
2491 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2492 perror("tmpfile");
2493 return;
2495 rm(tempfn);
2496 Ftfree(&tempfn);
2497 } else
2498 fp = stdout;
2499 imaplock = 1;
2500 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2501 safe_signal(SIGINT, maincatch);
2502 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2503 if (sigsetjmp(imapjmp, 1))
2504 goto out;
2505 if (savepipe != SIG_IGN)
2506 safe_signal(SIGPIPE, imapcatch);
2507 if (mb.mb_type == MB_CACHE)
2508 cache_list(&mb, fold, strip, fp);
2509 else
2510 imap_list(&mb, fold, strip, fp);
2511 imaplock = 0;
2512 if (interrupts) {
2513 if (options & OPT_TTYOUT)
2514 Fclose(fp);
2515 onintr(0);
2517 fflush(fp);
2518 if (options & OPT_TTYOUT) {
2519 rewind(fp);
2520 if (fsize(fp) > 0)
2521 dopr(fp);
2522 else
2523 fprintf(stderr, "Folder not found.\n");
2525 out:
2526 safe_signal(SIGINT, saveint);
2527 safe_signal(SIGPIPE, savepipe);
2528 if (options & OPT_TTYOUT)
2529 Fclose(fp);
2532 static void
2533 dopr(FILE *fp)
2535 char o[LINESIZE], *tempfn;
2536 int c;
2537 long n = 0, mx = 0, columns, width;
2538 FILE *out;
2540 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2541 perror("tmpfile");
2542 return;
2544 rm(tempfn);
2545 Ftfree(&tempfn);
2546 while ((c = getc(fp)) != EOF) {
2547 if (c == '\n') {
2548 if (n > mx)
2549 mx = n;
2550 n = 0;
2551 } else
2552 n++;
2554 rewind(fp);
2555 width = scrnwidth;
2556 if (mx < width / 2) {
2557 columns = width / (mx+2);
2558 snprintf(o, sizeof o,
2559 "sort | pr -%lu -w%lu -t",
2560 columns, width);
2561 } else
2562 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2563 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2564 try_pager(out);
2565 Fclose(out);
2568 static enum okay
2569 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2571 char o[LINESIZE];
2572 const char *qname;
2573 enum okay ok = STOP;
2574 int twice = 0;
2575 int stored = 0;
2576 FILE *queuefp = NULL;
2578 if (mp->mb_type == MB_CACHE) {
2579 if ((queuefp = cache_queue(mp)) == NULL)
2580 return STOP;
2581 ok = OKAY;
2583 qname = imap_quotestr(name = imap_fileof(name));
2585 * Since it is not possible to set flags on the copy, recently
2586 * set flags must be set on the original to include it in the copy.
2588 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2589 imap_store(mp, m, n, '+', "\\Seen", 0);
2590 if (m->m_flag&MFLAG)
2591 imap_store(mp, m, n, '+', "\\Flagged", 0);
2592 if (m->m_flag&MUNFLAG)
2593 imap_store(mp, m, n, '-', "\\Flagged", 0);
2594 if (m->m_flag&MANSWER)
2595 imap_store(mp, m, n, '+', "\\Answered", 0);
2596 if (m->m_flag&MUNANSWER)
2597 imap_store(mp, m, n, '-', "\\Flagged", 0);
2598 if (m->m_flag&MDRAFT)
2599 imap_store(mp, m, n, '+', "\\Draft", 0);
2600 if (m->m_flag&MUNDRAFT)
2601 imap_store(mp, m, n, '-', "\\Draft", 0);
2602 again: if (m->m_uid)
2603 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2604 tag(1), m->m_uid, qname);
2605 else {
2606 if (check_expunged() == STOP)
2607 goto out;
2608 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2609 tag(1), n, qname);
2611 IMAP_OUT(o, MB_COMD, goto out)
2612 while (mp->mb_active & MB_COMD)
2613 ok = imap_answer(mp, twice);
2614 if (mp->mb_type == MB_IMAP &&
2615 mp->mb_flags & MB_UIDPLUS &&
2616 response_status == RESPONSE_OK)
2617 imap_copyuid(mp, m, name);
2618 if (response_status == RESPONSE_NO && twice++ == 0) {
2619 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2620 IMAP_OUT(o, MB_COMD, goto out)
2621 while (mp->mb_active & MB_COMD)
2622 ok = imap_answer(mp, 1);
2623 if (ok == OKAY) {
2624 imap_created_mailbox++;
2625 goto again;
2628 if (queuefp != NULL)
2629 Fclose(queuefp);
2631 * ... and reset the flag to its initial value so that
2632 * the 'exit' command still leaves the message unread.
2634 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2635 imap_store(mp, m, n, '-', "\\Seen", 0);
2636 stored++;
2638 if (m->m_flag&MFLAG) {
2639 imap_store(mp, m, n, '-', "\\Flagged", 0);
2640 stored++;
2642 if (m->m_flag&MUNFLAG) {
2643 imap_store(mp, m, n, '+', "\\Flagged", 0);
2644 stored++;
2646 if (m->m_flag&MANSWER) {
2647 imap_store(mp, m, n, '-', "\\Answered", 0);
2648 stored++;
2650 if (m->m_flag&MUNANSWER) {
2651 imap_store(mp, m, n, '+', "\\Answered", 0);
2652 stored++;
2654 if (m->m_flag&MDRAFT) {
2655 imap_store(mp, m, n, '-', "\\Draft", 0);
2656 stored++;
2658 if (m->m_flag&MUNDRAFT) {
2659 imap_store(mp, m, n, '+', "\\Draft", 0);
2660 stored++;
2662 if (stored) {
2663 mp->mb_active |= MB_COMD;
2664 (void)imap_finish(mp);
2666 return ok;
2669 enum okay
2670 imap_copy(struct message *m, int n, const char *name)
2672 sighandler_type saveint, savepipe;
2673 enum okay ok = STOP;
2675 (void)&saveint;
2676 (void)&savepipe;
2677 (void)&ok;
2678 imaplock = 1;
2679 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2680 safe_signal(SIGINT, maincatch);
2681 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2682 if (sigsetjmp(imapjmp, 1) == 0) {
2683 if (savepipe != SIG_IGN)
2684 safe_signal(SIGPIPE, imapcatch);
2685 ok = imap_copy1(&mb, m, n, name);
2687 safe_signal(SIGINT, saveint);
2688 safe_signal(SIGPIPE, savepipe);
2689 imaplock = 0;
2690 if (interrupts)
2691 onintr(0);
2692 return ok;
2695 static enum okay
2696 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2697 unsigned long *olduid, unsigned long *newuid)
2699 char *xp, *yp, *zp;
2701 *uidvalidity = strtoul(cp, &xp, 10);
2702 *olduid = strtoul(xp, &yp, 10);
2703 *newuid = strtoul(yp, &zp, 10);
2704 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2705 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2708 static enum okay
2709 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2710 unsigned long *uid)
2712 char *xp, *yp;
2714 *uidvalidity = strtoul(cp, &xp, 10);
2715 *uid = strtoul(xp, &yp, 10);
2716 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2717 yp > xp && *yp == ']';
2720 static enum okay
2721 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2723 const char *cp;
2724 unsigned long uidvalidity, olduid, newuid;
2725 struct mailbox xmb;
2726 struct message xm;
2728 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2729 imap_copyuid_parse(&cp[9], &uidvalidity,
2730 &olduid, &newuid) == STOP)
2731 return STOP;
2732 xmb = *mp;
2733 xmb.mb_cache_directory = NULL;
2734 xmb.mb_imap_mailbox = savestr(name);
2735 xmb.mb_uidvalidity = uidvalidity;
2736 initcache(&xmb);
2737 if (m == NULL) {
2738 memset(&xm, 0, sizeof xm);
2739 xm.m_uid = olduid;
2740 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2741 return STOP;
2742 getcache(mp, &xm, NEED_HEADER);
2743 getcache(mp, &xm, NEED_BODY);
2744 } else {
2745 if ((m->m_flag & HAVE_HEADER) == 0)
2746 getcache(mp, m, NEED_HEADER);
2747 if ((m->m_flag & HAVE_BODY) == 0)
2748 getcache(mp, m, NEED_BODY);
2749 xm = *m;
2751 xm.m_uid = newuid;
2752 xm.m_flag &= ~MFULLYCACHED;
2753 putcache(&xmb, &xm);
2754 return OKAY;
2757 static enum okay
2758 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2759 long xsize, long size, long lines, int flag, const char *name)
2761 const char *cp;
2762 unsigned long uidvalidity, uid;
2763 struct mailbox xmb;
2764 struct message xm;
2766 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2767 imap_appenduid_parse(&cp[11], &uidvalidity,
2768 &uid) == STOP)
2769 return STOP;
2770 xmb = *mp;
2771 xmb.mb_cache_directory = NULL;
2772 xmb.mb_imap_mailbox = savestr(name);
2773 xmb.mb_uidvalidity = uidvalidity;
2774 xmb.mb_otf = xmb.mb_itf = fp;
2775 initcache(&xmb);
2776 memset(&xm, 0, sizeof xm);
2777 xm.m_flag = (flag & MREAD) | MNEW;
2778 xm.m_time = t;
2779 xm.m_block = mailx_blockof(off1);
2780 xm.m_offset = mailx_offsetof(off1);
2781 xm.m_size = size;
2782 xm.m_xsize = xsize;
2783 xm.m_lines = xm.m_xlines = lines;
2784 xm.m_uid = uid;
2785 xm.m_have = HAVE_HEADER|HAVE_BODY;
2786 putcache(&xmb, &xm);
2787 return OKAY;
2790 static enum okay
2791 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2793 FILE *tp = NULL;
2794 time_t t;
2795 long size, xsize, ysize, lines;
2796 enum mflag flag = MNEW;
2797 char *name, *buf, *bp, *tempCopy;
2798 char const *cp;
2799 size_t bufsize, buflen, cnt;
2800 enum okay ok = STOP;
2802 buf = smalloc(bufsize = LINESIZE);
2803 buflen = 0;
2804 cnt = fsize(fp);
2805 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2806 goto stop;
2807 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2808 while (*bp == ' ')
2809 bp++;
2810 if ((cp = strrchr(bp, '{')) == NULL)
2811 goto stop;
2812 xsize = atol(&cp[1]) + 2;
2813 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2814 goto stop;
2815 while (*cp == ' ')
2816 cp++;
2817 if (*cp == '(') {
2818 imap_getflags(cp, &cp, &flag);
2819 while (*++cp == ' ');
2821 t = imap_read_date_time(cp);
2822 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2823 goto stop;
2824 rm(tempCopy);
2825 Ftfree(&tempCopy);
2826 size = xsize;
2827 ysize = lines = 0;
2828 while (size > 0) {
2829 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2830 goto stop;
2831 size -= buflen;
2832 buf[--buflen] = '\0';
2833 buf[buflen-1] = '\n';
2834 fwrite(buf, 1, buflen, tp);
2835 ysize += buflen;
2836 lines++;
2838 fflush(tp);
2839 rewind(tp);
2840 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2841 imap_unquotestr(name));
2842 ok = OKAY;
2843 stop: free(buf);
2844 if (tp)
2845 Fclose(tp);
2846 return ok;
2849 static enum okay
2850 imap_search2(struct mailbox *mp, struct message *m, int cnt,
2851 const char *spec, int f)
2853 char *o;
2854 size_t osize;
2855 FILE *queuefp = NULL;
2856 enum okay ok = STOP;
2857 int i;
2858 unsigned long n;
2859 const char *cp;
2860 char *xp, *cs, c;
2862 c = 0;
2863 for (cp = spec; *cp; cp++)
2864 c |= *cp;
2865 if (c & 0200) {
2866 cp = charset_get_lc();
2867 #ifdef HAVE_ICONV
2868 if (asccasecmp(cp, "utf-8")) {
2869 iconv_t it;
2870 char *nsp, *nspec;
2871 size_t sz, nsz;
2872 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
2873 sz = strlen(spec) + 1;
2874 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2875 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz,
2876 FAL0) == 0 && sz == 0) {
2877 spec = nspec;
2878 cp = "utf-8";
2880 n_iconv_close(it);
2883 #endif
2884 cp = imap_quotestr(cp);
2885 cs = salloc(n = strlen(cp) + 10);
2886 snprintf(cs, n, "CHARSET %s ", cp);
2887 } else
2888 cs = UNCONST("");
2889 o = ac_alloc(osize = strlen(spec) + 60);
2890 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2891 IMAP_OUT(o, MB_COMD, goto out)
2892 while (mp->mb_active & MB_COMD) {
2893 ok = imap_answer(mp, 0);
2894 if (response_status == RESPONSE_OTHER &&
2895 response_other == MAILBOX_DATA_SEARCH) {
2896 xp = responded_other_text;
2897 while (*xp && *xp != '\r') {
2898 n = strtoul(xp, &xp, 10);
2899 for (i = 0; i < cnt; i++)
2900 if (m[i].m_uid == n &&
2901 (m[i].m_flag&MHIDDEN)
2902 == 0 &&
2903 (f == MDELETED ||
2904 (m[i].m_flag&MDELETED)
2905 == 0))
2906 mark(i+1, f);
2910 out: ac_free(o);
2911 return ok;
2914 enum okay
2915 imap_search1(const char *volatile spec, int f)
2917 sighandler_type saveint, savepipe;
2918 enum okay ok = STOP;
2920 (void)&saveint;
2921 (void)&savepipe;
2922 (void)&ok;
2923 if (mb.mb_type != MB_IMAP)
2924 return STOP;
2925 imaplock = 1;
2926 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2927 safe_signal(SIGINT, maincatch);
2928 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2929 if (sigsetjmp(imapjmp, 1) == 0) {
2930 if (savepipe != SIG_IGN)
2931 safe_signal(SIGPIPE, imapcatch);
2932 ok = imap_search2(&mb, message, msgCount, spec, f);
2934 safe_signal(SIGINT, saveint);
2935 safe_signal(SIGPIPE, savepipe);
2936 imaplock = 0;
2937 if (interrupts)
2938 onintr(0);
2939 return ok;
2942 int
2943 imap_thisaccount(const char *cp)
2945 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2946 return 0;
2947 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2948 mb.mb_imap_account == NULL)
2949 return 0;
2950 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2953 enum okay
2954 imap_remove(const char *name)
2956 sighandler_type saveint, savepipe;
2957 enum okay ok = STOP;
2959 (void)&saveint;
2960 (void)&savepipe;
2961 (void)&ok;
2962 if (mb.mb_type != MB_IMAP) {
2963 fprintf(stderr, "Refusing to remove \"%s\" "
2964 "in disconnected mode.\n", name);
2965 return STOP;
2967 if (!imap_thisaccount(name)) {
2968 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2969 "server: \"%s\" not removed.\n", name);
2970 return STOP;
2972 imaplock = 1;
2973 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2974 safe_signal(SIGINT, maincatch);
2975 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2976 if (sigsetjmp(imapjmp, 1) == 0) {
2977 if (savepipe != SIG_IGN)
2978 safe_signal(SIGPIPE, imapcatch);
2979 ok = imap_remove1(&mb, imap_fileof(name));
2981 safe_signal(SIGINT, saveint);
2982 safe_signal(SIGPIPE, savepipe);
2983 imaplock = 0;
2984 if (ok == OKAY)
2985 ok = cache_remove(name);
2986 if (interrupts)
2987 onintr(0);
2988 return ok;
2991 static enum okay
2992 imap_remove1(struct mailbox *mp, const char *name)
2994 FILE *queuefp = NULL;
2995 char *o;
2996 int os;
2997 enum okay ok = STOP;
2999 o = ac_alloc(os = 2*strlen(name) + 100);
3000 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3001 IMAP_OUT(o, MB_COMD, goto out)
3002 while (mp->mb_active & MB_COMD)
3003 ok = imap_answer(mp, 1);
3004 out: ac_free(o);
3005 return ok;
3008 enum okay
3009 imap_rename(const char *old, const char *new)
3011 sighandler_type saveint, savepipe;
3012 enum okay ok = STOP;
3014 (void)&saveint;
3015 (void)&savepipe;
3016 (void)&ok;
3017 if (mb.mb_type != MB_IMAP) {
3018 fprintf(stderr, "Refusing to rename mailboxes "
3019 "in disconnected mode.\n");
3020 return STOP;
3022 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3023 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3024 "server: \"%s\" not renamed to \"%s\".\n",
3025 old, new);
3026 return STOP;
3028 imaplock = 1;
3029 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3030 safe_signal(SIGINT, maincatch);
3031 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3032 if (sigsetjmp(imapjmp, 1) == 0) {
3033 if (savepipe != SIG_IGN)
3034 safe_signal(SIGPIPE, imapcatch);
3035 ok = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3037 safe_signal(SIGINT, saveint);
3038 safe_signal(SIGPIPE, savepipe);
3039 imaplock = 0;
3040 if (ok == OKAY)
3041 ok = cache_rename(old, new);
3042 if (interrupts)
3043 onintr(0);
3044 return ok;
3047 static enum okay
3048 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3050 FILE *queuefp = NULL;
3051 char *o;
3052 int os;
3053 enum okay ok = STOP;
3055 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3056 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3057 imap_quotestr(old), imap_quotestr(new));
3058 IMAP_OUT(o, MB_COMD, goto out)
3059 while (mp->mb_active & MB_COMD)
3060 ok = imap_answer(mp, 1);
3061 out: ac_free(o);
3062 return ok;
3065 enum okay
3066 imap_dequeue(struct mailbox *mp, FILE *fp)
3068 FILE *queuefp = NULL;
3069 char o[LINESIZE], *newname;
3070 char *buf, *bp, *cp, iob[4096];
3071 size_t bufsize, buflen, cnt;
3072 enum okay ok = OKAY, rok = OKAY;
3073 long offs, offs1, offs2, octets;
3074 int twice, gotcha = 0;
3076 buf = smalloc(bufsize = LINESIZE);
3077 buflen = 0;
3078 cnt = fsize(fp);
3079 while ((offs1 = ftell(fp)) >= 0 &&
3080 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0)
3081 != NULL) {
3082 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3083 while (*bp == ' ')
3084 bp++;
3085 twice = 0;
3086 if ((offs = ftell(fp)) < 0)
3087 goto fail;
3088 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3089 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3090 cp = &bp[9];
3091 while (digitchar(*cp&0377))
3092 cp++;
3093 if (*cp != ' ')
3094 goto fail;
3095 while (*cp == ' ')
3096 cp++;
3097 if ((newname = imap_strex(cp, NULL)) == NULL)
3098 goto fail;
3099 IMAP_OUT(o, MB_COMD, continue)
3100 while (mp->mb_active & MB_COMD)
3101 ok = imap_answer(mp, twice);
3102 if (response_status == RESPONSE_NO && twice++ == 0)
3103 goto trycreate;
3104 if (response_status == RESPONSE_OK &&
3105 mp->mb_flags & MB_UIDPLUS) {
3106 imap_copyuid(mp, NULL,
3107 imap_unquotestr(newname));
3109 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3110 IMAP_OUT(o, MB_COMD, continue)
3111 while (mp->mb_active & MB_COMD)
3112 ok = imap_answer(mp, 1);
3113 if (ok == OKAY)
3114 gotcha++;
3115 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3116 if ((cp = strrchr(bp, '{')) == NULL)
3117 goto fail;
3118 octets = atol(&cp[1]) + 2;
3119 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3120 goto fail;
3121 IMAP_OUT(o, MB_COMD, continue)
3122 while (mp->mb_active & MB_COMD) {
3123 ok = imap_answer(mp, twice);
3124 if (response_type == RESPONSE_CONT)
3125 break;
3127 if (ok == STOP) {
3128 if (twice++ == 0 &&
3129 fseek(fp, offs, SEEK_SET) >= 0)
3130 goto trycreate;
3131 goto fail;
3133 while (octets > 0) {
3134 size_t n = (size_t)octets > sizeof iob
3135 ? sizeof iob : (size_t)octets;
3136 octets -= n;
3137 if (n != fread(iob, 1, n, fp))
3138 goto fail;
3139 swrite1(&mp->mb_sock, iob, n, 1);
3141 swrite(&mp->mb_sock, "");
3142 while (mp->mb_active & MB_COMD) {
3143 ok = imap_answer(mp, 0);
3144 if (response_status == RESPONSE_NO &&
3145 twice++ == 0) {
3146 if (fseek(fp, offs, SEEK_SET) < 0)
3147 goto fail;
3148 goto trycreate;
3151 if (response_status == RESPONSE_OK &&
3152 mp->mb_flags & MB_UIDPLUS) {
3153 if ((offs2 = ftell(fp)) < 0)
3154 goto fail;
3155 fseek(fp, offs1, SEEK_SET);
3156 if (imap_appenduid_cached(mp, fp) == STOP) {
3157 (void)fseek(fp, offs2, SEEK_SET);
3158 goto fail;
3161 } else {
3162 fail: fprintf(stderr,
3163 "Invalid command in IMAP cache queue: \"%s\"\n",
3164 bp);
3165 rok = STOP;
3167 continue;
3168 trycreate:
3169 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3170 tag(1), newname);
3171 IMAP_OUT(o, MB_COMD, continue)
3172 while (mp->mb_active & MB_COMD)
3173 ok = imap_answer(mp, 1);
3174 if (ok == OKAY)
3175 goto again;
3177 fflush(fp);
3178 rewind(fp);
3179 ftruncate(fileno(fp), 0);
3180 if (gotcha)
3181 imap_close(mp);
3182 free(buf);
3183 return rok;
3186 static char *
3187 imap_strex(char const *cp, char const **xp)
3189 char const *cq;
3190 char *n;
3192 if (*cp != '"')
3193 return NULL;
3194 for (cq = &cp[1]; *cq; cq++) {
3195 if (*cq == '\\')
3196 cq++;
3197 else if (*cq == '"')
3198 break;
3200 if (*cq != '"')
3201 return NULL;
3202 n = salloc(cq - cp + 2);
3203 memcpy(n, cp, cq - cp + 1);
3204 n[cq - cp + 1] = '\0';
3205 if (xp)
3206 *xp = &cq[1];
3207 return n;
3210 static enum okay
3211 check_expunged(void)
3213 if (expunged_messages > 0) {
3214 fprintf(stderr,
3215 "Command not executed - messages have been expunged\n");
3216 return STOP;
3218 return OKAY;
3221 /*ARGSUSED*/
3222 int
3223 cconnect(void *vp)
3225 char *cp, *cq;
3226 int omsgCount = msgCount;
3227 (void)vp;
3229 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3230 fprintf(stderr, "Already connected.\n");
3231 return 1;
3233 unset_allow_undefined = TRU1;
3234 unset_internal("disconnected");
3235 cp = protbase(mailname);
3236 if (strncmp(cp, "imap://", 7) == 0)
3237 cp += 7;
3238 else if (strncmp(cp, "imaps://", 8) == 0)
3239 cp += 8;
3240 if ((cq = strchr(cp, ':')) != NULL)
3241 *cq = '\0';
3242 unset_internal(savecat("disconnected-", cp));
3243 unset_allow_undefined = FAL0;
3244 if (mb.mb_type == MB_CACHE) {
3245 imap_setfile1(mailname, 0, edit, 1);
3246 if (msgCount > omsgCount)
3247 newmailinfo(omsgCount);
3249 return 0;
3252 int
3253 cdisconnect(void *vp)
3255 int *msgvec = vp;
3257 if (mb.mb_type == MB_CACHE) {
3258 fprintf(stderr, "Not connected.\n");
3259 return 1;
3260 } else if (mb.mb_type == MB_IMAP) {
3261 if (cached_uidvalidity(&mb) == 0) {
3262 fprintf(stderr, "The current mailbox is not cached.\n");
3263 return 1;
3266 if (*msgvec)
3267 ccache(vp);
3268 assign("disconnected", "");
3269 if (mb.mb_type == MB_IMAP) {
3270 sclose(&mb.mb_sock);
3271 imap_setfile1(mailname, 0, edit, 1);
3273 return 0;
3276 int
3277 ccache(void *vp)
3279 int *msgvec = vp, *ip;
3280 struct message *mp;
3282 if (mb.mb_type != MB_IMAP) {
3283 fprintf(stderr, "Not connected to an IMAP server.\n");
3284 return 1;
3286 if (cached_uidvalidity(&mb) == 0) {
3287 fprintf(stderr, "The current mailbox is not cached.\n");
3288 return 1;
3290 for (ip = msgvec; *ip; ip++) {
3291 mp = &message[*ip-1];
3292 if (!(mp->m_have & HAVE_BODY))
3293 get_body(mp);
3295 return 0;
3299 disconnected(const char *file)
3301 char *cp, *cq, *vp;
3302 int vs, r;
3304 if (value("disconnected"))
3305 return 1;
3306 cp = protbase(file);
3307 if (strncmp(cp, "imap://", 7) == 0)
3308 cp += 7;
3309 else if (strncmp(cp, "imaps://", 8) == 0)
3310 cp += 8;
3311 else
3312 return 0;
3313 if ((cq = strchr(cp, ':')) != NULL)
3314 *cq = '\0';
3315 vp = ac_alloc(vs = strlen(cp) + 14);
3316 snprintf(vp, vs, "disconnected-%s", cp);
3317 r = value(vp) != NULL;
3318 ac_free(vp);
3319 return r;
3322 void
3323 transflags(struct message *omessage, long omsgCount, int transparent)
3325 struct message *omp, *nmp, *newdot, *newprevdot;
3326 int hf;
3328 omp = omessage;
3329 nmp = message;
3330 newdot = message;
3331 newprevdot = NULL;
3332 while (omp < &omessage[omsgCount] &&
3333 nmp < &message[msgCount]) {
3334 if (dot && nmp->m_uid == dot->m_uid)
3335 newdot = nmp;
3336 if (prevdot && nmp->m_uid == prevdot->m_uid)
3337 newprevdot = nmp;
3338 if (omp->m_uid == nmp->m_uid) {
3339 hf = nmp->m_flag & MHIDDEN;
3340 if (transparent && mb.mb_type == MB_IMAP)
3341 omp->m_flag &= ~MHIDDEN;
3342 *nmp++ = *omp++;
3343 if (transparent && mb.mb_type == MB_CACHE)
3344 nmp[-1].m_flag |= hf;
3345 } else if (omp->m_uid < nmp->m_uid)
3346 omp++;
3347 else
3348 nmp++;
3350 dot = newdot;
3351 setdot(newdot);
3352 prevdot = newprevdot;
3353 free(omessage);
3355 #endif /* HAVE_IMAP */
3357 time_t
3358 imap_read_date_time(const char *cp)
3360 time_t t;
3361 int i, year, month, day, hour, minute, second;
3362 int sign = -1;
3363 char buf[3];
3366 * "25-Jul-2004 15:33:44 +0200"
3367 * | | | | | |
3368 * 0 5 10 15 20 25
3370 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3371 goto invalid;
3372 day = strtol(&cp[1], NULL, 10);
3373 for (i = 0;;) {
3374 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3375 break;
3376 if (month_names[++i][0] == '\0')
3377 goto invalid;
3379 month = i + 1;
3380 year = strtol(&cp[8], NULL, 10);
3381 hour = strtol(&cp[13], NULL, 10);
3382 minute = strtol(&cp[16], NULL, 10);
3383 second = strtol(&cp[19], NULL, 10);
3384 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3385 (time_t)-1)
3386 goto invalid;
3387 switch (cp[22]) {
3388 case '-':
3389 sign = 1;
3390 break;
3391 case '+':
3392 break;
3393 default:
3394 goto invalid;
3396 buf[2] = '\0';
3397 buf[0] = cp[23];
3398 buf[1] = cp[24];
3399 t += strtol(buf, NULL, 10) * sign * 3600;
3400 buf[0] = cp[25];
3401 buf[1] = cp[26];
3402 t += strtol(buf, NULL, 10) * sign * 60;
3403 return t;
3404 invalid:
3405 time(&t);
3406 return t;
3409 time_t
3410 imap_read_date(const char *cp)
3412 time_t t;
3413 int year, month, day, i, tzdiff;
3414 struct tm *tmptr;
3415 char *xp, *yp;
3417 if (*cp == '"')
3418 cp++;
3419 day = strtol(cp, &xp, 10);
3420 if (day <= 0 || day > 31 || *xp++ != '-')
3421 return -1;
3422 for (i = 0;;) {
3423 if (ascncasecmp(xp, month_names[i], 3) == 0)
3424 break;
3425 if (month_names[++i][0] == '\0')
3426 return -1;
3428 month = i+1;
3429 if (xp[3] != '-')
3430 return -1;
3431 year = strtol(&xp[4], &yp, 10);
3432 if (year < 1970 || year > 2037 || yp != &xp[8])
3433 return -1;
3434 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3435 return -1;
3436 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3437 return -1;
3438 tzdiff = t - mktime(gmtime(&t));
3439 tmptr = localtime(&t);
3440 if (tmptr->tm_isdst > 0)
3441 tzdiff += 3600;
3442 t -= tzdiff;
3443 return t;
3446 const char *
3447 imap_make_date_time(time_t t)
3449 static char s[30];
3450 struct tm *tmptr;
3451 int tzdiff, tzdiff_hour, tzdiff_min;
3453 tzdiff = t - mktime(gmtime(&t));
3454 tzdiff_hour = (int)(tzdiff / 60);
3455 tzdiff_min = tzdiff_hour % 60;
3456 tzdiff_hour /= 60;
3457 tmptr = localtime(&t);
3458 if (tmptr->tm_isdst > 0)
3459 tzdiff_hour++;
3460 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3461 tmptr->tm_mday,
3462 month_names[tmptr->tm_mon],
3463 tmptr->tm_year + 1900,
3464 tmptr->tm_hour,
3465 tmptr->tm_min,
3466 tmptr->tm_sec,
3467 tzdiff_hour,
3468 tzdiff_min);
3469 return s;
3472 char *
3473 imap_quotestr(const char *s)
3475 char *n, *np;
3477 np = n = salloc(2 * strlen(s) + 3);
3478 *np++ = '"';
3479 while (*s) {
3480 if (*s == '"' || *s == '\\')
3481 *np++ = '\\';
3482 *np++ = *s++;
3484 *np++ = '"';
3485 *np = '\0';
3486 return n;
3489 char *
3490 imap_unquotestr(const char *s)
3492 char *n, *np;
3494 if (*s != '"')
3495 return savestr(s);
3496 np = n = salloc(strlen(s) + 1);
3497 while (*++s) {
3498 if (*s == '\\')
3499 s++;
3500 else if (*s == '"')
3501 break;
3502 *np++ = *s;
3504 *np = '\0';
3505 return n;