imap.c: fix compiler warnings..
[s-mailx.git] / imap.c
blobe74302d0c06b08e41db1b719cb7abc8a63579461
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)imap.c 1.222 (gritter) 3/13/09";
43 #endif
44 #endif /* not lint */
46 #include "config.h"
49 * Mail -- a mail program
51 * IMAP v4r1 client following RFC 2060.
54 #include "rcv.h"
55 #include <errno.h>
56 #include <sys/stat.h>
57 #include <unistd.h>
58 #include <time.h>
60 #ifdef USE_IMAP
62 #include "md5.h"
64 #include <sys/socket.h>
65 #include <netdb.h>
66 #include <netinet/in.h>
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif /* HAVE_ARPA_INET_H */
71 #include "extern.h"
73 static int verbose;
75 #define IMAP_ANSWER() { \
76 if (mp->mb_type != MB_CACHE) { \
77 enum okay ok = OKAY; \
78 while (mp->mb_active & MB_COMD) \
79 ok = imap_answer(mp, 1); \
80 if (ok == STOP) \
81 return STOP; \
82 } \
84 #define IMAP_OUT(x, y, action) \
85 { \
86 if (mp->mb_type != MB_CACHE) { \
87 if (imap_finish(mp) == STOP) \
88 return STOP; \
89 if (verbose) \
90 fprintf(stderr, ">>> %s", x); \
91 mp->mb_active |= (y); \
92 if (swrite(&mp->mb_sock, x) == STOP) \
93 action; \
94 } else { \
95 if (queuefp != NULL) \
96 fputs(x, queuefp); \
97 } \
100 static struct record {
101 struct record *rec_next;
102 unsigned long rec_count;
103 enum rec_type {
104 REC_EXISTS,
105 REC_EXPUNGE
106 } rec_type;
107 } *record, *recend;
109 static enum {
110 RESPONSE_TAGGED,
111 RESPONSE_DATA,
112 RESPONSE_FATAL,
113 RESPONSE_CONT,
114 RESPONSE_ILLEGAL
115 } response_type;
117 static enum {
118 RESPONSE_OK,
119 RESPONSE_NO,
120 RESPONSE_BAD,
121 RESPONSE_PREAUTH,
122 RESPONSE_BYE,
123 RESPONSE_OTHER,
124 RESPONSE_UNKNOWN
125 } response_status;
127 static char *responded_tag;
128 static char *responded_text;
129 static char *responded_other_text;
130 static long responded_other_number;
132 static enum {
133 MAILBOX_DATA_FLAGS,
134 MAILBOX_DATA_LIST,
135 MAILBOX_DATA_LSUB,
136 MAILBOX_DATA_MAILBOX,
137 MAILBOX_DATA_SEARCH,
138 MAILBOX_DATA_STATUS,
139 MAILBOX_DATA_EXISTS,
140 MAILBOX_DATA_RECENT,
141 MESSAGE_DATA_EXPUNGE,
142 MESSAGE_DATA_FETCH,
143 CAPABILITY_DATA,
144 RESPONSE_OTHER_UNKNOWN
145 } response_other;
147 static enum list_attributes {
148 LIST_NONE = 000,
149 LIST_NOINFERIORS = 001,
150 LIST_NOSELECT = 002,
151 LIST_MARKED = 004,
152 LIST_UNMARKED = 010
153 } list_attributes;
155 static int list_hierarchy_delimiter;
156 static char *list_name;
158 struct list_item {
159 struct list_item *l_next;
160 char *l_name;
161 char *l_base;
162 enum list_attributes l_attr;
163 int l_delim;
164 int l_level;
165 int l_has_children;
168 static char *imapbuf;
169 static size_t imapbufsize;
170 static sigjmp_buf imapjmp;
171 static sighandler_type savealrm;
172 static int reset_tio;
173 static struct termios otio;
174 static int imapkeepalive;
175 static long had_exists = -1;
176 static long had_expunge = -1;
177 static long expunged_messages;
178 static volatile int imaplock;
180 static int same_imap_account;
182 static void imap_other_get(char *pp);
183 static void imap_response_get(const char **cp);
184 static void imap_response_parse(void);
185 static enum okay imap_answer(struct mailbox *mp, int errprnt);
186 static enum okay imap_parse_list(void);
187 static enum okay imap_finish(struct mailbox *mp);
188 static void imap_timer_off(void);
189 static void imapcatch(int s);
190 static void maincatch(int s);
191 static enum okay imap_noop1(struct mailbox *mp);
192 static void rec_queue(enum rec_type type, unsigned long count);
193 static enum okay rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s);
196 static int imap_use_starttls(const char *uhp);
197 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
198 const char *uhp);
199 static enum okay imap_capability(struct mailbox *mp);
200 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
201 char *xuser, const char *pass);
202 static enum okay imap_cram_md5(struct mailbox *mp,
203 char *xuser, const char *xpass);
204 static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass);
205 #ifdef USE_GSSAPI
206 static enum okay imap_gss(struct mailbox *mp, char *user);
207 #endif /* USE_GSSAPI */
208 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
209 static void imap_init(struct mailbox *mp, int n);
210 static void imap_setptr(struct mailbox *mp, int newmail, int transparent,
211 int *prevcount);
212 static char *imap_have_password(const char *server);
213 static void imap_split(char **server, const char **sp, int *use_ssl,
214 const char **cp, char **uhp, char **mbx,
215 const char **pass, char **user);
216 static int imap_setfile1(const char *xserver, int newmail, int isedit,
217 int transparent);
218 static int imap_fetchdata(struct mailbox *mp, struct message *m,
219 size_t expected, int need,
220 const char *head, size_t headsize, long headlines);
221 static void imap_putstr(struct mailbox *mp, struct message *m,
222 const char *str,
223 const char *head, size_t headsize, long headlines);
224 static enum okay imap_get(struct mailbox *mp, struct message *m,
225 enum needspec need);
226 static void commitmsg(struct mailbox *mp, struct message *to,
227 struct message from, enum havespec have);
228 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
229 int bot, int top);
230 static enum okay imap_exit(struct mailbox *mp);
231 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
232 needstat);
233 static enum okay imap_close(struct mailbox *mp);
234 static enum okay imap_update(struct mailbox *mp);
235 static enum okay imap_store(struct mailbox *mp, struct message *m,
236 int n, int c, const char *sp, int needstat);
237 static enum okay imap_unstore(struct message *m, int n, const char *flag);
238 static const char *tag(int new);
239 static char *imap_putflags(int f);
240 static void imap_getflags(const char *cp, char **xp, enum mflag *f);
241 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
242 off_t off1, long xsize, enum mflag flag, time_t t);
243 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
244 static enum okay imap_list1(struct mailbox *mp, const char *base,
245 struct list_item **list, struct list_item **lend, int level);
246 static enum okay imap_list(struct mailbox *mp, const char *base,
247 int strip, FILE *fp);
248 static void dopr(FILE *fp);
249 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
250 const char *name);
251 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
252 unsigned long *olduid, unsigned long *newuid);
253 static enum okay imap_appenduid_parse(const char *cp,
254 unsigned long *uidvalidity, unsigned long *uid);
255 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
256 const char *name);
257 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
258 long off1, long xsize, long size, long lines,
259 int flag, const char *name);
260 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
261 static enum okay imap_search2(struct mailbox *mp, struct message *m,
262 int count, const char *spec, int f);
263 static enum okay imap_remove1(struct mailbox *mp, const char *name);
264 static enum okay imap_rename1(struct mailbox *mp, const char *old,
265 const char *new);
266 static char *imap_strex(const char *cp, char **xp);
267 static enum okay check_expunged(void);
269 static void
270 imap_other_get(char *pp)
272 char *xp;
274 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
275 pp += 6;
276 response_other = MAILBOX_DATA_FLAGS;
277 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
278 pp += 5;
279 response_other = MAILBOX_DATA_LIST;
280 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
281 pp += 5;
282 response_other = MAILBOX_DATA_LSUB;
283 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
284 pp += 8;
285 response_other = MAILBOX_DATA_MAILBOX;
286 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
287 pp += 7;
288 response_other = MAILBOX_DATA_SEARCH;
289 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
290 pp += 7;
291 response_other = MAILBOX_DATA_STATUS;
292 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
293 pp += 11;
294 response_other = CAPABILITY_DATA;
295 } else {
296 responded_other_number = strtol(pp, &xp, 10);
297 while (*xp == ' ')
298 xp++;
299 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
300 response_other = MAILBOX_DATA_EXISTS;
301 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
302 response_other = MAILBOX_DATA_RECENT;
303 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
304 response_other = MESSAGE_DATA_EXPUNGE;
305 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
306 pp = &xp[6];
307 response_other = MESSAGE_DATA_FETCH;
308 } else
309 response_other = RESPONSE_OTHER_UNKNOWN;
311 responded_other_text = pp;
314 static void
315 imap_response_get(const char **cp)
317 if (ascncasecmp(*cp, "OK ", 3) == 0) {
318 *cp += 3;
319 response_status = RESPONSE_OK;
320 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
321 *cp += 3;
322 response_status = RESPONSE_NO;
323 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
324 *cp += 4;
325 response_status = RESPONSE_BAD;
326 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
327 *cp += 8;
328 response_status = RESPONSE_PREAUTH;
329 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
330 *cp += 4;
331 response_status = RESPONSE_BYE;
332 } else
333 response_status = RESPONSE_OTHER;
336 static void
337 imap_response_parse(void)
339 static char *parsebuf;
340 static size_t parsebufsize;
341 const char *ip = imapbuf;
342 char *pp;
344 if (parsebufsize < imapbufsize) {
345 free(parsebuf);
346 parsebuf = smalloc(parsebufsize = imapbufsize);
348 strcpy(parsebuf, imapbuf);
349 pp = parsebuf;
350 switch (*ip) {
351 case '+':
352 response_type = RESPONSE_CONT;
353 ip++;
354 pp++;
355 while (*ip == ' ') {
356 ip++;
357 pp++;
359 break;
360 case '*':
361 ip++;
362 pp++;
363 while (*ip == ' ') {
364 ip++;
365 pp++;
367 imap_response_get(&ip);
368 pp = &parsebuf[ip - imapbuf];
369 switch (response_status) {
370 case RESPONSE_BYE:
371 response_type = RESPONSE_FATAL;
372 break;
373 default:
374 response_type = RESPONSE_DATA;
376 break;
377 default:
378 responded_tag = parsebuf;
379 while (*pp && *pp != ' ')
380 pp++;
381 if (*pp == '\0') {
382 response_type = RESPONSE_ILLEGAL;
383 break;
385 *pp++ = '\0';
386 while (*pp && *pp == ' ')
387 pp++;
388 if (*pp == '\0') {
389 response_type = RESPONSE_ILLEGAL;
390 break;
392 ip = &imapbuf[pp - parsebuf];
393 response_type = RESPONSE_TAGGED;
394 imap_response_get(&ip);
395 pp = &parsebuf[ip - imapbuf];
397 responded_text = pp;
398 if (response_type != RESPONSE_CONT &&
399 response_type != RESPONSE_ILLEGAL &&
400 response_status == RESPONSE_OTHER)
401 imap_other_get(pp);
404 static enum okay
405 imap_answer(struct mailbox *mp, int errprnt)
407 int i, complete;
408 enum okay ok = STOP;
410 if (mp->mb_type == MB_CACHE)
411 return OKAY;
412 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
413 if (verbose)
414 fputs(imapbuf, stderr);
415 imap_response_parse();
416 if (response_type == RESPONSE_ILLEGAL)
417 goto again;
418 if (response_type == RESPONSE_CONT)
419 return OKAY;
420 if (response_status == RESPONSE_OTHER) {
421 if (response_other == MAILBOX_DATA_EXISTS) {
422 had_exists = responded_other_number;
423 rec_queue(REC_EXISTS, responded_other_number);
424 if (had_expunge > 0)
425 had_expunge = 0;
426 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
427 rec_queue(REC_EXPUNGE, responded_other_number);
428 if (had_expunge < 0)
429 had_expunge = 0;
430 had_expunge++;
431 expunged_messages++;
434 complete = 0;
435 if (response_type == RESPONSE_TAGGED) {
436 if (asccasecmp(responded_tag, tag(0)) == 0)
437 complete |= 1;
438 else
439 goto again;
441 switch (response_status) {
442 case RESPONSE_PREAUTH:
443 mp->mb_active &= ~MB_PREAUTH;
444 /*FALLTHRU*/
445 case RESPONSE_OK:
446 okay: ok = OKAY;
447 complete |= 2;
448 break;
449 case RESPONSE_NO:
450 case RESPONSE_BAD:
451 stop: ok = STOP;
452 complete |= 2;
453 if (errprnt)
454 fprintf(stderr, catgets(catd, CATSET, 218,
455 "IMAP error: %s"), responded_text);
456 break;
457 case RESPONSE_UNKNOWN: /* does not happen */
458 case RESPONSE_BYE:
459 i = mp->mb_active;
460 mp->mb_active = MB_NONE;
461 if (i & MB_BYE)
462 goto okay;
463 else
464 goto stop;
465 case RESPONSE_OTHER:
466 ok = OKAY;
468 if (response_status != RESPONSE_OTHER &&
469 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
470 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
471 if (complete == 3)
472 mp->mb_active &= ~MB_COMD;
473 } else {
474 ok = STOP;
475 mp->mb_active = MB_NONE;
477 return ok;
480 static enum okay
481 imap_parse_list(void)
483 char *cp;
485 cp = responded_other_text;
486 list_attributes = LIST_NONE;
487 if (*cp == '(') {
488 while (*cp && *cp != ')') {
489 if (*cp == '\\') {
490 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
491 == 0) {
492 list_attributes |= LIST_NOINFERIORS;
493 cp += 12;
494 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
495 == 0) {
496 list_attributes |= LIST_NOSELECT;
497 cp += 9;
498 } else if (ascncasecmp(&cp[1], "Marked ", 7)
499 == 0) {
500 list_attributes |= LIST_MARKED;
501 cp += 7;
502 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
503 == 0) {
504 list_attributes |= LIST_UNMARKED;
505 cp += 9;
508 cp++;
510 if (*++cp != ' ')
511 return STOP;
512 while (*cp == ' ')
513 cp++;
515 list_hierarchy_delimiter = EOF;
516 if (*cp == '"') {
517 if (*++cp == '\\')
518 cp++;
519 list_hierarchy_delimiter = *cp++ & 0377;
520 if (cp[0] != '"' || cp[1] != ' ')
521 return STOP;
522 cp++;
523 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
524 cp[3] == ' ') {
525 list_hierarchy_delimiter = EOF;
526 cp += 3;
528 while (*cp == ' ')
529 cp++;
530 list_name = cp;
531 while (*cp && *cp != '\r')
532 cp++;
533 *cp = '\0';
534 return OKAY;
537 static enum okay
538 imap_finish(struct mailbox *mp)
540 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
541 imap_answer(mp, 1);
542 return OKAY;
545 static void
546 imap_timer_off(void)
548 if (imapkeepalive > 0) {
549 alarm(0);
550 safe_signal(SIGALRM, savealrm);
554 static void
555 imapcatch(int s)
557 if (reset_tio)
558 tcsetattr(0, TCSADRAIN, &otio);
559 switch (s) {
560 case SIGINT:
561 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
562 siglongjmp(imapjmp, 1);
563 /*NOTREACHED*/
564 case SIGPIPE:
565 fprintf(stderr, "Received SIGPIPE during IMAP operation\n");
566 break;
570 static void
571 maincatch(int s)
573 (void)s;
574 if (interrupts++ == 0) {
575 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
576 return;
578 onintr(0);
581 static enum okay
582 imap_noop1(struct mailbox *mp)
584 char o[LINESIZE];
585 FILE *queuefp = NULL;
587 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
588 IMAP_OUT(o, MB_COMD, return STOP)
589 IMAP_ANSWER()
590 return OKAY;
593 enum okay
594 imap_noop(void)
596 sighandler_type saveint, savepipe;
597 enum okay ok = STOP;
599 (void)&saveint;
600 (void)&savepipe;
601 (void)&ok;
602 if (mb.mb_type != MB_IMAP)
603 return STOP;
604 verbose = value("verbose") != NULL;
605 imaplock = 1;
606 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
607 safe_signal(SIGINT, maincatch);
608 savepipe = safe_signal(SIGPIPE, SIG_IGN);
609 if (sigsetjmp(imapjmp, 1) == 0) {
610 if (savepipe != SIG_IGN)
611 safe_signal(SIGPIPE, imapcatch);
612 ok = imap_noop1(&mb);
614 safe_signal(SIGINT, saveint);
615 safe_signal(SIGPIPE, savepipe);
616 imaplock = 0;
617 if (interrupts)
618 onintr(0);
619 return ok;
622 static void
623 rec_queue(enum rec_type type, unsigned long count)
625 struct record *rp;
627 rp = scalloc(1, sizeof *rp);
628 rp->rec_type = type;
629 rp->rec_count = count;
630 if (record && recend) {
631 recend->rec_next = rp;
632 recend = rp;
633 } else
634 record = recend = rp;
637 static enum okay
638 rec_dequeue(void)
640 struct message *omessage;
641 enum okay ok = OKAY;
642 struct record *rp = record, *rq = NULL;
643 unsigned long exists = 0, i;
645 if (record == NULL)
646 return STOP;
647 omessage = message;
648 message = smalloc((msgCount+1) * sizeof *message);
649 if (msgCount)
650 memcpy(message, omessage, msgCount * sizeof *message);
651 memset(&message[msgCount], 0, sizeof *message);
652 while (rp) {
653 switch (rp->rec_type) {
654 case REC_EXISTS:
655 exists = rp->rec_count;
656 break;
657 case REC_EXPUNGE:
658 if (rp->rec_count == 0) {
659 ok = STOP;
660 break;
662 if (rp->rec_count > (unsigned long)msgCount) {
663 if (exists == 0 || rp->rec_count > exists--)
664 ok = STOP;
665 break;
667 if (exists > 0)
668 exists--;
669 delcache(&mb, &message[rp->rec_count-1]);
670 memmove(&message[rp->rec_count-1],
671 &message[rp->rec_count],
672 (msgCount - rp->rec_count + 1) *
673 sizeof *message);
674 msgCount--;
676 * If the message was part of a collapsed thread,
677 * the m_collapsed field of one of its ancestors
678 * should be incremented. It seems hardly possible
679 * to do this with the current message structure,
680 * though. The result is that a '+' may be shown
681 * in the header summary even if no collapsed
682 * children exists.
684 break;
686 free(rq);
687 rq = rp;
688 rp = rp->rec_next;
690 free(rq);
691 record = recend = NULL;
692 if (ok == OKAY && exists > (unsigned long)msgCount) {
693 message = srealloc(message,
694 (exists + 1) * sizeof *message);
695 memset(&message[msgCount], 0,
696 (exists - msgCount + 1) * sizeof *message);
697 for (i = msgCount; i < exists; i++)
698 imap_init(&mb, i);
699 imap_flags(&mb, msgCount+1, exists);
700 msgCount = exists;
702 if (ok == STOP) {
703 free(message);
704 message = omessage;
706 return ok;
709 static void
710 rec_rmqueue(void)
712 struct record *rp, *rq = NULL;
714 for (rp = record; rp; rp = rp->rec_next) {
715 free(rq);
716 rq = rp;
718 free(rq);
719 record = recend = NULL;
722 /*ARGSUSED*/
723 static void
724 imapalarm(int s)
726 sighandler_type saveint;
727 sighandler_type savepipe;
728 (void)s;
730 if (imaplock++ == 0) {
731 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
732 safe_signal(SIGINT, maincatch);
733 savepipe = safe_signal(SIGPIPE, SIG_IGN);
734 if (sigsetjmp(imapjmp, 1)) {
735 safe_signal(SIGINT, saveint);
736 safe_signal(SIGPIPE, savepipe);
737 goto brk;
739 if (savepipe != SIG_IGN)
740 safe_signal(SIGPIPE, imapcatch);
741 if (imap_noop1(&mb) != OKAY) {
742 safe_signal(SIGINT, saveint);
743 safe_signal(SIGPIPE, savepipe);
744 goto out;
746 safe_signal(SIGINT, saveint);
747 safe_signal(SIGPIPE, savepipe);
749 brk: alarm(imapkeepalive);
750 out: imaplock--;
753 static int
754 imap_use_starttls(const char *uhp)
756 char *var;
758 if (value("imap-use-starttls"))
759 return 1;
760 var = savecat("imap-use-starttls-", uhp);
761 return value(var) != NULL;
764 static enum okay
765 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
767 char *server, *cp;
769 mp->mb_active |= MB_PREAUTH;
770 imap_answer(mp, 1);
771 if ((cp = strchr(xserver, ':')) != NULL) {
772 server = salloc(cp - xserver + 1);
773 memcpy(server, xserver, cp - xserver);
774 server[cp - xserver] = '\0';
775 } else
776 server = (char *)xserver;
777 #ifdef USE_SSL
778 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
779 FILE *queuefp = NULL;
780 char o[LINESIZE];
782 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
783 IMAP_OUT(o, MB_COMD, return STOP);
784 IMAP_ANSWER()
785 if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
786 return STOP;
788 #else /* !USE_SSL */
789 if (imap_use_starttls(uhp)) {
790 fprintf(stderr, "No SSL support compiled in.\n");
791 return STOP;
793 #endif /* !USE_SSL */
794 imap_capability(mp);
795 return OKAY;
798 static enum okay
799 imap_capability(struct mailbox *mp)
801 char o[LINESIZE];
802 FILE *queuefp = NULL;
803 enum okay ok = STOP;
804 const char *cp;
806 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
807 IMAP_OUT(o, MB_COMD, return STOP)
808 while (mp->mb_active & MB_COMD) {
809 ok = imap_answer(mp, 0);
810 if (response_status == RESPONSE_OTHER &&
811 response_other == CAPABILITY_DATA) {
812 cp = responded_other_text;
813 while (*cp) {
814 while (spacechar(*cp&0377))
815 cp++;
816 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
817 spacechar(cp[7]&0377))
818 /* RFC 2359 */
819 mp->mb_flags |= MB_UIDPLUS;
820 while (*cp && !spacechar(*cp&0377))
821 cp++;
825 return ok;
828 static enum okay
829 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass)
831 char *var;
832 char *auth;
834 if (!(mp->mb_active & MB_PREAUTH))
835 return OKAY;
836 if ((auth = value("imap-auth")) == NULL) {
837 var = ac_alloc(strlen(uhp) + 11);
838 strcpy(var, "imap-auth-");
839 strcpy(&var[10], uhp);
840 auth = value(var);
841 ac_free(var);
843 if (auth == NULL || strcmp(auth, "login") == 0)
844 return imap_login(mp, xuser, pass);
845 if (strcmp(auth, "cram-md5") == 0)
846 return imap_cram_md5(mp, xuser, pass);
847 if (strcmp(auth, "gssapi") == 0) {
848 #ifdef USE_GSSAPI
849 return imap_gss(mp, xuser);
850 #else /* !USE_GSSAPI */
851 fprintf(stderr, "No GSSAPI support compiled in.\n");
852 return STOP;
853 #endif /* !USE_GSSAPI */
855 fprintf(stderr, "Unknown IMAP authentication method: \"%s\"\n", auth);
856 return STOP;
860 * Implementation of RFC 2194.
862 static enum okay
863 imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
865 char o[LINESIZE];
866 const char *user, *pass;
867 char *cp;
868 FILE *queuefp = NULL;
869 enum okay ok = STOP;
871 retry: if (xuser == NULL) {
872 if ((user = getuser()) == NULL)
873 return STOP;
874 } else
875 user = xuser;
876 if (xpass == NULL) {
877 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
878 return STOP;
879 } else
880 pass = xpass;
881 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
882 IMAP_OUT(o, 0, return STOP)
883 imap_answer(mp, 1);
884 if (response_type != RESPONSE_CONT)
885 return STOP;
886 cp = cram_md5_string(user, pass, responded_text);
887 IMAP_OUT(cp, MB_COMD, return STOP)
888 while (mp->mb_active & MB_COMD)
889 ok = imap_answer(mp, 1);
890 if (ok == STOP) {
891 xpass = NULL;
892 goto retry;
894 return ok;
897 static enum okay
898 imap_login(struct mailbox *mp, char *xuser, const char *xpass)
900 char o[LINESIZE];
901 const char *user, *pass;
902 FILE *queuefp = NULL;
903 enum okay ok = STOP;
905 retry: if (xuser == NULL) {
906 if ((user = getuser()) == NULL)
907 return STOP;
908 } else
909 user = xuser;
910 if (xpass == NULL) {
911 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
912 return STOP;
913 } else
914 pass = xpass;
915 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
916 tag(1), imap_quotestr(user), imap_quotestr(pass));
917 IMAP_OUT(o, MB_COMD, return STOP)
918 while (mp->mb_active & MB_COMD)
919 ok = imap_answer(mp, 1);
920 if (ok == STOP) {
921 xpass = NULL;
922 goto retry;
924 return OKAY;
927 #ifdef USE_GSSAPI
928 #include "imap_gssapi.c"
929 #endif /* USE_GSSAPI */
931 enum okay
932 imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
934 enum okay ok = OKAY;
935 char *cp;
936 char o[LINESIZE];
937 FILE *queuefp = NULL;
938 (void)size;
940 mp->mb_uidvalidity = 0;
941 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
942 IMAP_OUT(o, MB_COMD, return STOP)
943 while (mp->mb_active & MB_COMD) {
944 ok = imap_answer(mp, 1);
945 if (response_status != RESPONSE_OTHER &&
946 (cp = asccasestr(responded_text,
947 "[UIDVALIDITY ")) != NULL)
948 mp->mb_uidvalidity = atol(&cp[13]);
950 *count = had_exists > 0 ? had_exists : 0;
951 if (response_status != RESPONSE_OTHER &&
952 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
953 == 0)
954 mp->mb_perm = 0;
955 return ok;
958 static enum okay
959 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
961 char o[LINESIZE];
962 FILE *queuefp = NULL;
963 char *cp;
964 struct message *m;
965 unsigned x = X, y = Y, n;
967 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
968 IMAP_OUT(o, MB_COMD, return STOP)
969 while (mp->mb_active & MB_COMD) {
970 imap_answer(mp, 1);
971 if (response_status == RESPONSE_OTHER &&
972 response_other == MESSAGE_DATA_FETCH) {
973 n = responded_other_number;
974 if (n < x || n > y)
975 continue;
976 m = &message[n-1];
977 m->m_xsize = 0;
978 } else
979 continue;
980 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
981 cp += 5;
982 while (*cp == ' ')
983 cp++;
984 if (*cp == '(')
985 imap_getflags(cp, &cp, &m->m_flag);
987 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
988 m->m_uid = strtoul(&cp[4], NULL, 10);
989 getcache1(mp, m, NEED_UNSPEC, 1);
990 m->m_flag &= ~MHIDDEN;
992 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
993 x++;
994 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
995 y--;
996 if (x <= y) {
997 snprintf(o, sizeof o,
998 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
999 tag(1), x, y);
1000 IMAP_OUT(o, MB_COMD, return STOP)
1001 while (mp->mb_active & MB_COMD) {
1002 imap_answer(mp, 1);
1003 if (response_status == RESPONSE_OTHER &&
1004 response_other == MESSAGE_DATA_FETCH) {
1005 n = responded_other_number;
1006 if (n < x || n > y)
1007 continue;
1008 m = &message[n-1];
1009 } else
1010 continue;
1011 if ((cp = asccasestr(responded_other_text,
1012 "RFC822.SIZE ")) != NULL)
1013 m->m_xsize = strtol(&cp[12], NULL, 10);
1014 if ((cp = asccasestr(responded_other_text,
1015 "INTERNALDATE ")) != NULL)
1016 m->m_time = imap_read_date_time(&cp[13]);
1019 for (n = X; n <= Y; n++)
1020 putcache(mp, &message[n-1]);
1021 return OKAY;
1024 static void
1025 imap_init(struct mailbox *mp, int n)
1027 struct message *m = &message[n];
1028 (void)mp;
1030 m->m_flag = MUSED|MNOFROM;
1031 m->m_block = 0;
1032 m->m_offset = 0;
1035 static void
1036 imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
1038 struct message *omessage = 0;
1039 int i, omsgCount = 0;
1040 enum okay dequeued = STOP;
1042 if (newmail || transparent) {
1043 omessage = message;
1044 omsgCount = msgCount;
1046 if (newmail)
1047 dequeued = rec_dequeue();
1048 if (had_exists >= 0) {
1049 if (dequeued != OKAY)
1050 msgCount = had_exists;
1051 had_exists = -1;
1053 if (had_expunge >= 0) {
1054 if (dequeued != OKAY)
1055 msgCount -= had_expunge;
1056 had_expunge = -1;
1058 if (newmail && expunged_messages)
1059 printf("Expunged %ld message%s.\n",
1060 expunged_messages,
1061 expunged_messages != 1 ? "s" : "");
1062 *prevcount = omsgCount - expunged_messages;
1063 expunged_messages = 0;
1064 if (msgCount < 0) {
1065 fputs("IMAP error: Negative message count\n", stderr);
1066 msgCount = 0;
1068 if (dequeued != OKAY) {
1069 message = scalloc(msgCount + 1, sizeof *message);
1070 for (i = 0; i < msgCount; i++)
1071 imap_init(mp, i);
1072 if (!newmail && mp->mb_type == MB_IMAP)
1073 initcache(mp);
1074 if (msgCount > 0)
1075 imap_flags(mp, 1, msgCount);
1076 message[msgCount].m_size = 0;
1077 message[msgCount].m_lines = 0;
1078 rec_rmqueue();
1080 if (newmail || transparent)
1081 transflags(omessage, omsgCount, transparent);
1082 else
1083 setdot(message);
1086 static char *
1087 imap_have_password(const char *server)
1089 char *var, *cp;
1091 var = ac_alloc(strlen(server) + 10);
1092 strcpy(var, "password-");
1093 strcpy(&var[9], server);
1094 if ((cp = value(var)) != NULL)
1095 cp = savestr(cp);
1096 ac_free(var);
1097 return cp;
1100 static void
1101 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1102 char **uhp, char **mbx, const char **pass, char **user)
1104 *sp = *server;
1105 if (strncmp(*sp, "imap://", 7) == 0) {
1106 *sp = &(*sp)[7];
1107 *use_ssl = 0;
1108 #ifdef USE_SSL
1109 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1110 *sp = &(*sp)[8];
1111 *use_ssl = 1;
1112 #endif /* USE_SSL */
1114 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1115 *uhp = savestr((char *)(*sp));
1116 (*uhp)[*cp - *sp] = '\0';
1117 *mbx = (char *)&(*cp)[1];
1118 } else {
1119 if (*cp)
1120 (*server)[*cp - *server] = '\0';
1121 *uhp = (char *)(*sp);
1122 *mbx = "INBOX";
1124 *pass = imap_have_password(*uhp);
1125 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1126 *user = salloc(*cp - *uhp + 1);
1127 memcpy(*user, *uhp, *cp - *uhp);
1128 (*user)[*cp - *uhp] = '\0';
1129 *sp = &(*cp)[1];
1130 *user = strdec(*user);
1131 } else {
1132 *user = NULL;
1133 *sp = *uhp;
1137 int
1138 imap_setfile(const char *xserver, int newmail, int isedit)
1140 return imap_setfile1(xserver, newmail, isedit, 0);
1143 static int
1144 imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
1146 struct sock so;
1147 sighandler_type saveint;
1148 sighandler_type savepipe;
1149 char *server, *user, *account;
1150 const char *cp, *sp, *pass;
1151 char *uhp, *mbx;
1152 int use_ssl = 0;
1153 enum mbflags same_flags;
1154 int prevcount = 0;
1156 (void)&sp;
1157 (void)&use_ssl;
1158 (void)&saveint;
1159 (void)&savepipe;
1160 server = savestr((char *)xserver);
1161 verbose = value("verbose") != NULL;
1162 if (newmail) {
1163 saveint = safe_signal(SIGINT, SIG_IGN);
1164 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1165 if (saveint != SIG_IGN)
1166 safe_signal(SIGINT, imapcatch);
1167 if (savepipe != SIG_IGN)
1168 safe_signal(SIGPIPE, imapcatch);
1169 imaplock = 1;
1170 goto newmail;
1172 same_flags = mb.mb_flags;
1173 same_imap_account = 0;
1174 sp = protbase(server);
1175 if (mb.mb_imap_account) {
1176 if (mb.mb_sock.s_fd > 0 &&
1177 strcmp(mb.mb_imap_account, sp) == 0 &&
1178 disconnected(mb.mb_imap_account) == 0)
1179 same_imap_account = 1;
1181 account = sstrdup(sp);
1182 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1183 so.s_fd = -1;
1184 if (!same_imap_account) {
1185 if (!disconnected(account) &&
1186 sopen(sp, &so, use_ssl, uhp,
1187 use_ssl ? "imaps" : "imap", verbose) != OKAY)
1188 return -1;
1189 } else
1190 so = mb.mb_sock;
1191 if (!transparent)
1192 quit();
1193 edit = isedit;
1194 free(mb.mb_imap_account);
1195 mb.mb_imap_account = account;
1196 if (!same_imap_account) {
1197 if (mb.mb_sock.s_fd >= 0)
1198 sclose(&mb.mb_sock);
1200 same_imap_account = 0;
1201 if (!transparent) {
1202 if (mb.mb_itf) {
1203 fclose(mb.mb_itf);
1204 mb.mb_itf = NULL;
1206 if (mb.mb_otf) {
1207 fclose(mb.mb_otf);
1208 mb.mb_otf = 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 sclose(&so);
1221 safe_signal(SIGINT, saveint);
1222 safe_signal(SIGPIPE, savepipe);
1223 imaplock = 0;
1224 return -1;
1226 if (saveint != SIG_IGN)
1227 safe_signal(SIGINT, imapcatch);
1228 if (savepipe != SIG_IGN)
1229 safe_signal(SIGPIPE, imapcatch);
1230 if (mb.mb_sock.s_fd < 0) {
1231 if (disconnected(mb.mb_imap_account)) {
1232 if (cache_setptr(transparent) == STOP)
1233 fprintf(stderr,
1234 "Mailbox \"%s\" is not cached.\n",
1235 server);
1236 goto done;
1238 if ((cp = value("imap-keepalive")) != NULL) {
1239 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1240 savealrm = safe_signal(SIGALRM, imapalarm);
1241 alarm(imapkeepalive);
1244 mb.mb_sock = so;
1245 mb.mb_sock.s_desc = "IMAP";
1246 mb.mb_sock.s_onclose = imap_timer_off;
1247 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1248 imap_auth(&mb, uhp, user, pass) != OKAY) {
1249 sclose(&mb.mb_sock);
1250 imap_timer_off();
1251 safe_signal(SIGINT, saveint);
1252 safe_signal(SIGPIPE, savepipe);
1253 imaplock = 0;
1254 return -1;
1256 } else /* same account */
1257 mb.mb_flags |= same_flags;
1258 mb.mb_perm = Rflag ? 0 : MB_DELE;
1259 mb.mb_type = MB_IMAP;
1260 cache_dequeue(&mb);
1261 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1262 /*sclose(&mb.mb_sock);
1263 imap_timer_off();*/
1264 safe_signal(SIGINT, saveint);
1265 safe_signal(SIGPIPE, savepipe);
1266 imaplock = 0;
1267 mb.mb_type = MB_VOID;
1268 return -1;
1270 newmail:
1271 imap_setptr(&mb, newmail, transparent, &prevcount);
1272 done: setmsize(msgCount);
1273 if (!newmail && !transparent)
1274 sawcom = 0;
1275 safe_signal(SIGINT, saveint);
1276 safe_signal(SIGPIPE, savepipe);
1277 imaplock = 0;
1278 if (!newmail && mb.mb_type == MB_IMAP)
1279 purgecache(&mb, message, msgCount);
1280 if ((newmail || transparent) && mb.mb_sorted) {
1281 mb.mb_threaded = 0;
1282 sort((void *)-1);
1284 if (!newmail && !edit && msgCount == 0) {
1285 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1286 value("emptystart") == NULL)
1287 fprintf(stderr, catgets(catd, CATSET, 258,
1288 "No mail at %s\n"), server);
1289 return 1;
1291 if (newmail)
1292 newmailinfo(prevcount);
1293 return 0;
1296 static int
1297 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1298 int need,
1299 const char *head, size_t headsize, long headlines)
1301 char *line = NULL, *lp;
1302 size_t linesize = 0, linelen, size = 0;
1303 int emptyline = 0, lines = 0, excess = 0;
1304 off_t offset;
1306 fseek(mp->mb_otf, 0L, SEEK_END);
1307 offset = ftell(mp->mb_otf);
1308 if (head)
1309 fwrite(head, 1, headsize, mp->mb_otf);
1310 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1311 lp = line;
1312 if (linelen > expected) {
1313 excess = linelen - expected;
1314 linelen = expected;
1317 * Need to mask 'From ' lines. This cannot be done properly
1318 * since some servers pass them as 'From ' and others as
1319 * '>From '. Although one could identify the first kind of
1320 * server in principle, it is not possible to identify the
1321 * second as '>From ' may also come from a server of the
1322 * first type as actual data. So do what is absolutely
1323 * necessary only - mask 'From '.
1325 * If the line is the first line of the message header, it
1326 * is likely a real 'From ' line. In this case, it is just
1327 * ignored since it violates all standards.
1329 if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
1330 lp[3] == 'm' && lp[4] == ' ') {
1331 if (lines + headlines != 0) {
1332 fputc('>', mp->mb_otf);
1333 size++;
1334 } else
1335 goto skip;
1337 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1338 lp[linelen-2] == '\r')) {
1339 emptyline = linelen <= 2;
1340 if (linelen > 2) {
1341 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1342 size += linelen - 1;
1343 } else
1344 size++;
1345 fputc('\n', mp->mb_otf);
1346 } else {
1347 emptyline = 0;
1348 fwrite(lp, 1, linelen, mp->mb_otf);
1349 size += linelen;
1351 lines++;
1352 skip: if ((expected -= linelen) <= 0)
1353 break;
1355 if (!emptyline) {
1357 * This is very ugly; but some IMAP daemons don't end a
1358 * message with \r\n\r\n, and we need \n\n for mbox format.
1360 fputc('\n', mp->mb_otf);
1361 lines++;
1362 size++;
1364 fflush(mp->mb_otf);
1365 if (m != NULL) {
1366 m->m_size = size + headsize;
1367 m->m_lines = lines + headlines;
1368 m->m_block = mailx_blockof(offset);
1369 m->m_offset = mailx_offsetof(offset);
1370 switch (need) {
1371 case NEED_HEADER:
1372 m->m_have |= HAVE_HEADER;
1373 break;
1374 case NEED_BODY:
1375 m->m_have |= HAVE_HEADER|HAVE_BODY;
1376 m->m_xlines = m->m_lines;
1377 m->m_xsize = m->m_size;
1378 break;
1381 free(line);
1382 return excess;
1385 static void
1386 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1387 const char *head, size_t headsize, long headlines)
1389 off_t offset;
1390 size_t len;
1392 len = strlen(str);
1393 fseek(mp->mb_otf, 0L, SEEK_END);
1394 offset = ftell(mp->mb_otf);
1395 if (head)
1396 fwrite(head, 1, headsize, mp->mb_otf);
1397 if (len > 0) {
1398 fwrite(str, 1, len, mp->mb_otf);
1399 fputc('\n', mp->mb_otf);
1400 len++;
1402 fflush(mp->mb_otf);
1403 if (m != NULL) {
1404 m->m_size = headsize + len;
1405 m->m_lines = headlines + 1;
1406 m->m_block = mailx_blockof(offset);
1407 m->m_offset = mailx_offsetof(offset);
1408 m->m_have |= HAVE_HEADER|HAVE_BODY;
1409 m->m_xlines = m->m_lines;
1410 m->m_xsize = m->m_size;
1414 static enum okay
1415 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1417 sighandler_type saveint = SIG_IGN;
1418 sighandler_type savepipe = SIG_IGN;
1419 char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL;
1420 size_t expected, headsize = 0;
1421 int number = m - message + 1;
1422 enum okay ok = STOP;
1423 FILE *queuefp = NULL;
1424 char *head = NULL;
1425 long headlines = 0;
1426 struct message mt;
1427 long n = -1;
1428 unsigned long u = 0;
1430 (void)&saveint;
1431 (void)&savepipe;
1432 (void)&number;
1433 (void)&need;
1434 (void)&cp;
1435 (void)&loc;
1436 (void)&ok;
1437 (void)&headsize;
1438 (void)&head;
1439 (void)&headlines;
1440 (void)&item;
1441 (void)&resp;
1442 (void)&u;
1443 verbose = value("verbose") != NULL;
1444 if (getcache(mp, m, need) == OKAY)
1445 return OKAY;
1446 if (mp->mb_type == MB_CACHE) {
1447 fprintf(stderr, "Message %u not available.\n", number);
1448 return STOP;
1450 if (mp->mb_sock.s_fd < 0) {
1451 fprintf(stderr, "IMAP connection closed.\n");
1452 return STOP;
1454 switch (need) {
1455 case NEED_HEADER:
1456 resp = item = "RFC822.HEADER";
1457 break;
1458 case NEED_BODY:
1459 item = "BODY.PEEK[]";
1460 resp = "BODY[]";
1461 if (m->m_flag & HAVE_HEADER && m->m_size) {
1462 char *hdr = smalloc(m->m_size);
1463 fflush(mp->mb_otf);
1464 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1465 m->m_offset), SEEK_SET) < 0 ||
1466 fread(hdr, 1, m->m_size, mp->mb_itf)
1467 != m->m_size) {
1468 free(hdr);
1469 break;
1471 head = hdr;
1472 headsize = m->m_size;
1473 headlines = m->m_lines;
1474 item = "BODY.PEEK[TEXT]";
1475 resp = "BODY[TEXT]";
1477 break;
1478 case NEED_UNSPEC:
1479 return STOP;
1481 imaplock = 1;
1482 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1483 if (sigsetjmp(imapjmp, 1)) {
1484 safe_signal(SIGINT, saveint);
1485 safe_signal(SIGPIPE, savepipe);
1486 imaplock = 0;
1487 return STOP;
1489 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1490 safe_signal(SIGINT, maincatch);
1491 if (savepipe != SIG_IGN)
1492 safe_signal(SIGPIPE, imapcatch);
1493 if (m->m_uid)
1494 snprintf(o, sizeof o,
1495 "%s UID FETCH %lu (%s)\r\n",
1496 tag(1), m->m_uid, item);
1497 else {
1498 if (check_expunged() == STOP)
1499 goto out;
1500 snprintf(o, sizeof o,
1501 "%s FETCH %u (%s)\r\n",
1502 tag(1), number, item);
1504 IMAP_OUT(o, MB_COMD, goto out)
1505 for (;;) {
1506 ok = imap_answer(mp, 1);
1507 if (ok == STOP)
1508 break;
1509 if (response_status != RESPONSE_OTHER ||
1510 response_other != MESSAGE_DATA_FETCH)
1511 continue;
1512 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1513 continue;
1514 if (m->m_uid) {
1515 if ((cp = asccasestr(responded_other_text, "UID "))) {
1516 u = atol(&cp[4]);
1517 n = 0;
1518 } else {
1519 n = -1;
1520 u = 0;
1522 } else
1523 n = responded_other_number;
1524 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1525 if (m->m_uid ? m->m_uid != u : n != number)
1526 continue;
1527 if ((cp = strchr(loc, '"')) != NULL) {
1528 cp = imap_unquotestr(cp);
1529 imap_putstr(mp, m, cp,
1530 head, headsize, headlines);
1531 } else {
1532 m->m_have |= HAVE_HEADER|HAVE_BODY;
1533 m->m_xlines = m->m_lines;
1534 m->m_xsize = m->m_size;
1536 goto out;
1538 expected = atol(&cp[1]);
1539 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1540 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1541 continue;
1543 mt = *m;
1544 imap_fetchdata(mp, &mt, expected, need,
1545 head, headsize, headlines);
1546 if (n >= 0) {
1547 commitmsg(mp, m, mt, mt.m_have);
1548 break;
1550 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1551 &mp->mb_sock) > 0) {
1552 if (verbose)
1553 fputs(imapbuf, stderr);
1554 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1555 u = atol(&cp[4]);
1556 if (u == m->m_uid) {
1557 commitmsg(mp, m, mt, mt.m_have);
1558 break;
1563 out: while (mp->mb_active & MB_COMD)
1564 ok = imap_answer(mp, 1);
1565 if (saveint != SIG_IGN)
1566 safe_signal(SIGINT, saveint);
1567 if (savepipe != SIG_IGN)
1568 safe_signal(SIGPIPE, savepipe);
1569 imaplock--;
1570 if (ok == OKAY)
1571 putcache(mp, m);
1572 free(head);
1573 if (interrupts)
1574 onintr(0);
1575 return ok;
1578 enum okay
1579 imap_header(struct message *m)
1581 return imap_get(&mb, m, NEED_HEADER);
1585 enum okay
1586 imap_body(struct message *m)
1588 return imap_get(&mb, m, NEED_BODY);
1591 static void
1592 commitmsg(struct mailbox *mp, struct message *to,
1593 struct message from, enum havespec have)
1595 to->m_size = from.m_size;
1596 to->m_lines = from.m_lines;
1597 to->m_block = from.m_block;
1598 to->m_offset = from.m_offset;
1599 to->m_have = have;
1600 if (have & HAVE_BODY) {
1601 to->m_xlines = from.m_lines;
1602 to->m_xsize = from.m_size;
1604 putcache(mp, to);
1607 static enum okay
1608 imap_fetchheaders (
1609 struct mailbox *mp,
1610 struct message *m,
1611 int bot,
1612 int top /* bot > top */
1615 char o[LINESIZE], *cp;
1616 struct message mt;
1617 size_t expected;
1618 enum okay ok;
1619 int n = 0, u;
1620 FILE *queuefp = NULL;
1622 if (m[bot].m_uid)
1623 snprintf(o, sizeof o,
1624 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1625 tag(1), m[bot-1].m_uid, m[top-1].m_uid);
1626 else {
1627 if (check_expunged() == STOP)
1628 return STOP;
1629 snprintf(o, sizeof o,
1630 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1631 tag(1), bot, top);
1633 IMAP_OUT(o, MB_COMD, return STOP)
1634 for (;;) {
1635 ok = imap_answer(mp, 1);
1636 if (response_status != RESPONSE_OTHER)
1637 break;
1638 if (response_other != MESSAGE_DATA_FETCH)
1639 continue;
1640 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1641 return STOP;
1642 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1643 continue;
1644 expected = atol(&cp[1]);
1645 if (m[bot-1].m_uid) {
1646 if ((cp=asccasestr(responded_other_text, "UID "))) {
1647 u = atoi(&cp[4]);
1648 for (n = bot; n <= top; n++)
1649 if ((unsigned long)u == m[n-1].m_uid)
1650 break;
1651 if (n > top) {
1652 imap_fetchdata(mp, NULL, expected,
1653 NEED_HEADER,
1654 NULL, 0, 0);
1655 continue;
1657 } else
1658 n = -1;
1659 } else {
1660 n = responded_other_number;
1661 if (n <= 0 || n > msgCount) {
1662 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1663 NULL, 0, 0);
1664 continue;
1667 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1668 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1669 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1670 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1671 &mp->mb_sock) > 0) {
1672 if (verbose)
1673 fputs(imapbuf, stderr);
1674 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1675 u = atoi(&cp[4]);
1676 for (n = bot; n <= top; n++)
1677 if ((unsigned long)u == m[n-1].m_uid)
1678 break;
1679 if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
1680 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1681 n = 0;
1685 while (mp->mb_active & MB_COMD)
1686 ok = imap_answer(mp, 1);
1687 return ok;
1690 void
1691 imap_getheaders(int bot, int top)
1693 sighandler_type saveint, savepipe;
1694 enum okay ok = STOP;
1695 int i, chunk = 256;
1697 (void)&saveint;
1698 (void)&savepipe;
1699 (void)&ok;
1700 (void)&bot;
1701 (void)&top;
1702 verbose = value("verbose") != NULL;
1703 if (mb.mb_type == MB_CACHE)
1704 return;
1705 if (bot < 1)
1706 bot = 1;
1707 if (top > msgCount)
1708 top = msgCount;
1709 for (i = bot; i < top; i++) {
1710 if (message[i-1].m_have & HAVE_HEADER ||
1711 getcache(&mb, &message[i-1], NEED_HEADER)
1712 == OKAY)
1713 bot = i+1;
1714 else
1715 break;
1717 for (i = top; i > bot; i--) {
1718 if (message[i-1].m_have & HAVE_HEADER ||
1719 getcache(&mb, &message[i-1], NEED_HEADER)
1720 == OKAY)
1721 top = i-1;
1722 else
1723 break;
1725 if (bot >= top)
1726 return;
1727 imaplock = 1;
1728 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1729 safe_signal(SIGINT, maincatch);
1730 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1731 if (sigsetjmp(imapjmp, 1) == 0) {
1732 if (savepipe != SIG_IGN)
1733 safe_signal(SIGPIPE, imapcatch);
1734 for (i = bot; i <= top; i += chunk) {
1735 ok = imap_fetchheaders(&mb, message, i,
1736 i+chunk-1 < top ? i+chunk-1 : top);
1737 if (interrupts)
1738 onintr(0);
1741 safe_signal(SIGINT, saveint);
1742 safe_signal(SIGPIPE, savepipe);
1743 imaplock = 0;
1746 static enum okay
1747 imap_exit(struct mailbox *mp)
1749 char o[LINESIZE];
1750 FILE *queuefp = NULL;
1752 verbose = value("verbose") != NULL;
1753 mp->mb_active |= MB_BYE;
1754 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1755 IMAP_OUT(o, MB_COMD, return STOP)
1756 IMAP_ANSWER()
1757 return OKAY;
1760 static enum okay
1761 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1763 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1764 if (mp->mb_type == MB_IMAP)
1765 delcache(mp, m);
1766 return OKAY;
1769 static enum okay
1770 imap_close(struct mailbox *mp)
1772 char o[LINESIZE];
1773 FILE *queuefp = NULL;
1775 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1776 IMAP_OUT(o, MB_COMD, return STOP)
1777 IMAP_ANSWER()
1778 return OKAY;
1781 static enum okay
1782 imap_update(struct mailbox *mp)
1784 FILE *readstat = NULL;
1785 struct message *m;
1786 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1788 verbose = value("verbose") != NULL;
1789 if (Tflag != NULL) {
1790 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
1791 Tflag = NULL;
1793 if (!edit && mp->mb_perm != 0) {
1794 holdbits();
1795 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1796 if (m->m_flag & MBOX)
1797 c++;
1799 if (c > 0)
1800 if (makembox() == STOP)
1801 goto bypass;
1803 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1804 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
1805 char *id;
1807 if ((id = hfield("message-id", m)) != NULL ||
1808 (id = hfield("article-id", m)) != NULL)
1809 fprintf(readstat, "%s\n", id);
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: if (readstat != NULL)
1885 Fclose(readstat);
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(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
1898 printf(value("bsdcompat") || value("bsdmsgs") ?
1899 catgets(catd, CATSET, 170, "complete\n") :
1900 catgets(catd, CATSET, 212, "updated.\n"));
1901 } else if (held && !edit && mp->mb_perm != 0) {
1902 if (held == 1)
1903 printf(catgets(catd, CATSET, 155,
1904 "Held 1 message in %s\n"), mailname);
1905 else if (held > 1)
1906 printf(catgets(catd, CATSET, 156,
1907 "Held %d messages in %s\n"), held, mailname);
1909 fflush(stdout);
1910 return OKAY;
1913 void
1914 imap_quit(void)
1916 sighandler_type saveint;
1917 sighandler_type savepipe;
1919 verbose = value("verbose") != NULL;
1920 if (mb.mb_type == MB_CACHE) {
1921 imap_update(&mb);
1922 return;
1924 if (mb.mb_sock.s_fd < 0) {
1925 fprintf(stderr, "IMAP connection closed.\n");
1926 return;
1928 imaplock = 1;
1929 saveint = safe_signal(SIGINT, SIG_IGN);
1930 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1931 if (sigsetjmp(imapjmp, 1)) {
1932 safe_signal(SIGINT, saveint);
1933 safe_signal(SIGPIPE, saveint);
1934 imaplock = 0;
1935 return;
1937 if (saveint != SIG_IGN)
1938 safe_signal(SIGINT, imapcatch);
1939 if (savepipe != SIG_IGN)
1940 safe_signal(SIGPIPE, imapcatch);
1941 imap_update(&mb);
1942 if (!same_imap_account) {
1943 imap_exit(&mb);
1944 sclose(&mb.mb_sock);
1946 safe_signal(SIGINT, saveint);
1947 safe_signal(SIGPIPE, savepipe);
1948 imaplock = 0;
1951 static enum okay
1952 imap_store(struct mailbox *mp, struct message *m, int n,
1953 int c, const char *sp, int needstat)
1955 char o[LINESIZE];
1956 FILE *queuefp = NULL;
1958 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1959 return STOP;
1960 if (m->m_uid)
1961 snprintf(o, sizeof o,
1962 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1963 tag(1), m->m_uid, c, sp);
1964 else {
1965 if (check_expunged() == STOP)
1966 return STOP;
1967 snprintf(o, sizeof o,
1968 "%s STORE %u %cFLAGS (%s)\r\n",
1969 tag(1), n, c, sp);
1971 IMAP_OUT(o, MB_COMD, return STOP)
1972 if (needstat)
1973 IMAP_ANSWER()
1974 else
1975 mb.mb_active &= ~MB_COMD;
1976 if (queuefp != NULL)
1977 Fclose(queuefp);
1978 return OKAY;
1981 enum okay
1982 imap_undelete(struct message *m, int n)
1984 return imap_unstore(m, n, "\\Deleted");
1987 enum okay
1988 imap_unread(struct message *m, int n)
1990 return imap_unstore(m, n, "\\Seen");
1993 static enum okay
1994 imap_unstore(struct message *m, int n, const char *flag)
1996 sighandler_type saveint, savepipe;
1997 enum okay ok = STOP;
1999 (void)&saveint;
2000 (void)&savepipe;
2001 (void)&ok;
2002 verbose = value("verbose") != NULL;
2003 imaplock = 1;
2004 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2005 safe_signal(SIGINT, maincatch);
2006 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2007 if (sigsetjmp(imapjmp, 1) == 0) {
2008 if (savepipe != SIG_IGN)
2009 safe_signal(SIGPIPE, imapcatch);
2010 ok = imap_store(&mb, m, n, '-', flag, 1);
2012 safe_signal(SIGINT, saveint);
2013 safe_signal(SIGPIPE, savepipe);
2014 imaplock = 0;
2015 if (interrupts)
2016 onintr(0);
2017 return ok;
2020 static const char *
2021 tag(int new)
2023 static char ts[20];
2024 static long n;
2026 if (new)
2027 n++;
2028 snprintf(ts, sizeof ts, "T%lu", n);
2029 return ts;
2032 int
2033 imap_imap(void *vp)
2035 sighandler_type saveint, savepipe;
2036 char o[LINESIZE];
2037 enum okay ok = STOP;
2038 struct mailbox *mp = &mb;
2039 FILE *queuefp = NULL;
2041 (void)&saveint;
2042 (void)&savepipe;
2043 (void)&ok;
2044 verbose = value("verbose") != NULL;
2045 if (mp->mb_type != MB_IMAP) {
2046 printf("Not operating on an IMAP mailbox.\n");
2047 return 1;
2049 imaplock = 1;
2050 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2051 safe_signal(SIGINT, maincatch);
2052 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2053 if (sigsetjmp(imapjmp, 1) == 0) {
2054 if (savepipe != SIG_IGN)
2055 safe_signal(SIGPIPE, imapcatch);
2056 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2057 IMAP_OUT(o, MB_COMD, goto out)
2058 while (mp->mb_active & MB_COMD) {
2059 ok = imap_answer(mp, 0);
2060 fputs(responded_text, stdout);
2063 out: safe_signal(SIGINT, saveint);
2064 safe_signal(SIGPIPE, savepipe);
2065 imaplock = 0;
2066 if (interrupts)
2067 onintr(0);
2068 return ok != OKAY;
2071 int
2072 imap_newmail(int autoinc)
2074 if (autoinc && had_exists < 0 && had_expunge < 0) {
2075 verbose = value("verbose") != NULL;
2076 imaplock = 1;
2077 imap_noop();
2078 imaplock = 0;
2080 if (had_exists == msgCount && had_expunge < 0)
2082 * Some servers always respond with EXISTS to NOOP. If
2083 * the mailbox has been changed but the number of messages
2084 * has not, an EXPUNGE must also had been sent; otherwise,
2085 * nothing has changed.
2087 had_exists = -1;
2088 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2091 static char *
2092 imap_putflags(int f)
2094 const char *cp;
2095 char *buf, *bp;
2097 bp = buf = salloc(100);
2098 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2099 *bp++ = '(';
2100 if (f & MREAD) {
2101 if (bp[-1] != '(')
2102 *bp++ = ' ';
2103 for (cp = "\\Seen"; *cp; cp++)
2104 *bp++ = *cp;
2106 if (f & MFLAGGED) {
2107 if (bp[-1] != '(')
2108 *bp++ = ' ';
2109 for (cp = "\\Flagged"; *cp; cp++)
2110 *bp++ = *cp;
2112 if (f & MANSWERED) {
2113 if (bp[-1] != '(')
2114 *bp++ = ' ';
2115 for (cp = "\\Answered"; *cp; cp++)
2116 *bp++ = *cp;
2118 if (f & MDRAFT) {
2119 if (bp[-1] != '(')
2120 *bp++ = ' ';
2121 for (cp = "\\Draft"; *cp; cp++)
2122 *bp++ = *cp;
2124 *bp++ = ')';
2125 *bp++ = ' ';
2127 *bp = '\0';
2128 return buf;
2131 static void
2132 imap_getflags(const char *cp, char **xp, enum mflag *f)
2134 while (*cp != ')') {
2135 if (*cp == '\\') {
2136 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2137 *f |= MREAD;
2138 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2139 *f |= MNEW;
2140 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2141 *f |= MDELETED;
2142 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2143 *f |= MFLAGGED;
2144 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2145 *f |= MANSWERED;
2146 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2147 *f |= MDRAFTED;
2149 cp++;
2151 if (xp)
2152 *xp = (char *)cp;
2155 static enum okay
2156 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2157 off_t off1, long xsize, enum mflag flag, time_t t)
2159 char o[LINESIZE];
2160 char *buf;
2161 size_t bufsize, buflen, count;
2162 enum okay ok = STOP;
2163 long size, lines, ysize;
2164 int twice = 0;
2165 FILE *queuefp = NULL;
2167 if (mp->mb_type == MB_CACHE) {
2168 queuefp = cache_queue(mp);
2169 if (queuefp == NULL)
2170 return STOP;
2171 ok = OKAY;
2173 buf = smalloc(bufsize = LINESIZE);
2174 buflen = 0;
2175 again: size = xsize;
2176 count = fsize(fp);
2177 fseek(fp, off1, SEEK_SET);
2178 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2179 tag(1), imap_quotestr(name),
2180 imap_putflags(flag),
2181 imap_make_date_time(t),
2182 size);
2183 IMAP_OUT(o, MB_COMD, goto out)
2184 while (mp->mb_active & MB_COMD) {
2185 ok = imap_answer(mp, twice);
2186 if (response_type == RESPONSE_CONT)
2187 break;
2189 if (mp->mb_type != MB_CACHE && ok == STOP) {
2190 if (twice == 0)
2191 goto trycreate;
2192 else
2193 goto out;
2195 lines = ysize = 0;
2196 while (size > 0) {
2197 fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2198 lines++;
2199 ysize += buflen;
2200 buf[buflen-1] = '\r';
2201 buf[buflen] = '\n';
2202 if (mp->mb_type != MB_CACHE)
2203 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2204 else if (queuefp)
2205 fwrite(buf, 1, buflen+1, queuefp);
2206 size -= buflen+1;
2208 if (mp->mb_type != MB_CACHE)
2209 swrite(&mp->mb_sock, "\r\n");
2210 else if (queuefp)
2211 fputs("\r\n", queuefp);
2212 while (mp->mb_active & MB_COMD) {
2213 ok = imap_answer(mp, 0);
2214 if (response_status == RESPONSE_NO /*&&
2215 ascncasecmp(responded_text,
2216 "[TRYCREATE] ", 12) == 0*/) {
2217 trycreate: if (twice++) {
2218 ok = STOP;
2219 goto out;
2221 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2222 tag(1),
2223 imap_quotestr(name));
2224 IMAP_OUT(o, MB_COMD, goto out);
2225 while (mp->mb_active & MB_COMD)
2226 ok = imap_answer(mp, 1);
2227 if (ok == STOP)
2228 goto out;
2229 imap_created_mailbox++;
2230 goto again;
2231 } else if (ok != OKAY)
2232 fprintf(stderr, "IMAP error: %s", responded_text);
2233 else if (response_status == RESPONSE_OK &&
2234 mp->mb_flags & MB_UIDPLUS)
2235 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2236 flag, name);
2238 out: if (queuefp != NULL)
2239 Fclose(queuefp);
2240 free(buf);
2241 return ok;
2244 static enum okay
2245 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2247 char *buf, *bp, *lp;
2248 size_t bufsize, buflen, count;
2249 off_t off1 = -1, offs;
2250 int inhead = 1;
2251 int flag = MNEW|MNEWEST;
2252 long size = 0;
2253 time_t tim;
2254 enum okay ok;
2256 buf = smalloc(bufsize = LINESIZE);
2257 buflen = 0;
2258 count = fsize(fp);
2259 offs = ftell(fp);
2260 time(&tim);
2261 do {
2262 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2263 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2264 if (off1 != (off_t)-1) {
2265 ok=imap_append1(mp, name, fp, off1,
2266 size, flag, tim);
2267 if (ok == STOP)
2268 return STOP;
2269 fseek(fp, offs+buflen, SEEK_SET);
2271 off1 = offs + buflen;
2272 size = 0;
2273 inhead = 1;
2274 flag = MNEW;
2275 if (bp != NULL)
2276 tim = unixtime(buf);
2277 } else
2278 size += buflen+1;
2279 offs += buflen;
2280 if (bp && buf[0] == '\n')
2281 inhead = 0;
2282 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2283 lp = &buf[6];
2284 while (whitechar(*lp&0377))
2285 lp++;
2286 if (*lp == ':')
2287 while (*++lp != '\0')
2288 switch (*lp) {
2289 case 'R':
2290 flag |= MREAD;
2291 break;
2292 case 'O':
2293 flag &= ~MNEW;
2294 break;
2296 } else if (bp && inhead &&
2297 ascncasecmp(buf, "x-status", 8) == 0) {
2298 lp = &buf[8];
2299 while (whitechar(*lp&0377))
2300 lp++;
2301 if (*lp == ':')
2302 while (*++lp != '\0')
2303 switch (*lp) {
2304 case 'F':
2305 flag |= MFLAGGED;
2306 break;
2307 case 'A':
2308 flag |= MANSWERED;
2309 break;
2310 case 'T':
2311 flag |= MDRAFTED;
2312 break;
2315 } while (bp != NULL);
2316 free(buf);
2317 return OKAY;
2320 enum okay
2321 imap_append(const char *xserver, FILE *fp)
2323 sighandler_type saveint, savepipe;
2324 char *server, *uhp, *mbx, *user;
2325 const char *sp, *cp, *pass;
2326 int use_ssl;
2327 enum okay ok = STOP;
2329 (void)&saveint;
2330 (void)&savepipe;
2331 (void)&ok;
2332 verbose = value("verbose") != NULL;
2333 server = savestr((char *)xserver);
2334 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2335 imaplock = 1;
2336 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2337 safe_signal(SIGINT, maincatch);
2338 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2339 if (sigsetjmp(imapjmp, 1))
2340 goto out;
2341 if (savepipe != SIG_IGN)
2342 safe_signal(SIGPIPE, imapcatch);
2343 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2344 mb.mb_imap_account &&
2345 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2346 ok = imap_append0(&mb, mbx, fp);
2348 else {
2349 struct mailbox mx;
2351 memset(&mx, 0, sizeof mx);
2352 if (disconnected(server) == 0) {
2353 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2354 use_ssl ? "imaps" : "imap",
2355 verbose) != OKAY)
2356 goto fail;
2357 mx.mb_sock.s_desc = "IMAP";
2358 mx.mb_type = MB_IMAP;
2359 mx.mb_imap_account = (char *)protbase(server);
2360 mx.mb_imap_mailbox = mbx;
2361 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2362 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2363 sclose(&mx.mb_sock);
2364 goto fail;
2366 ok = imap_append0(&mx, mbx, fp);
2367 imap_exit(&mx);
2368 sclose(&mx.mb_sock);
2369 } else {
2370 mx.mb_imap_account = (char *)protbase(server);
2371 mx.mb_imap_mailbox = mbx;
2372 mx.mb_type = MB_CACHE;
2373 ok = imap_append0(&mx, mbx, fp);
2375 fail:;
2377 out: safe_signal(SIGINT, saveint);
2378 safe_signal(SIGPIPE, savepipe);
2379 imaplock = 0;
2380 if (interrupts)
2381 onintr(0);
2382 return ok;
2385 static enum okay
2386 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2387 struct list_item **lend, int level)
2389 char o[LINESIZE];
2390 enum okay ok = STOP;
2391 char *cp;
2392 const char *bp;
2393 FILE *queuefp = NULL;
2394 struct list_item *lp;
2396 *list = *lend = NULL;
2397 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2398 tag(1), imap_quotestr(base));
2399 IMAP_OUT(o, MB_COMD, return STOP);
2400 while (mp->mb_active & MB_COMD) {
2401 ok = imap_answer(mp, 1);
2402 if (response_status == RESPONSE_OTHER &&
2403 response_other == MAILBOX_DATA_LIST &&
2404 imap_parse_list() == OKAY) {
2405 cp = imap_unquotestr(list_name);
2406 lp = csalloc(1, sizeof *lp);
2407 lp->l_name = cp;
2408 for (bp = base; *bp && *bp == *cp; bp++)
2409 cp++;
2410 lp->l_base = *cp ? cp : savestr(base);
2411 lp->l_attr = list_attributes;
2412 lp->l_level = level+1;
2413 lp->l_delim = list_hierarchy_delimiter;
2414 if (*list && *lend) {
2415 (*lend)->l_next = lp;
2416 *lend = lp;
2417 } else
2418 *list = *lend = lp;
2421 return ok;
2424 static enum okay
2425 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2427 struct list_item *list, *lend, *lp, *lx, *ly;
2428 int n;
2429 const char *bp;
2430 char *cp;
2431 int depth;
2433 verbose = value("verbose") != NULL;
2434 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2435 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2436 return STOP;
2437 if (list == NULL || lend == NULL)
2438 return OKAY;
2439 for (lp = list; lp; lp = lp->l_next)
2440 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2441 lp->l_level < depth &&
2442 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2443 cp = salloc((n = strlen(lp->l_name)) + 2);
2444 strcpy(cp, lp->l_name);
2445 cp[n] = lp->l_delim;
2446 cp[n+1] = '\0';
2447 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2448 lx && ly) {
2449 lp->l_has_children = 1;
2450 if (strcmp(cp, lx->l_name) == 0)
2451 lx = lx->l_next;
2452 if (lx) {
2453 lend->l_next = lx;
2454 lend = ly;
2458 for (lp = list; lp; lp = lp->l_next) {
2459 if (strip) {
2460 cp = lp->l_name;
2461 for (bp = base; *bp && *bp == *cp; bp++)
2462 cp++;
2463 } else
2464 cp = lp->l_name;
2465 if ((lp->l_attr&LIST_NOSELECT) == 0)
2466 fprintf(fp, "%s\n", *cp ? cp : base);
2467 else if (lp->l_has_children == 0)
2468 fprintf(fp, "%s%c\n", *cp ? cp : base,
2469 lp->l_delim != EOF ? lp->l_delim : '\n');
2471 return OKAY;
2474 void
2475 imap_folders(const char *name, int strip)
2477 sighandler_type saveint, savepipe;
2478 const char *fold, *cp, *sp;
2479 char *tempfn;
2480 FILE *fp;
2481 int columnize = is_a_tty[1];
2483 (void)&saveint;
2484 (void)&savepipe;
2485 (void)&fp;
2486 (void)&fold;
2487 cp = protbase(name);
2488 sp = mb.mb_imap_account;
2489 if (strcmp(cp, sp)) {
2490 fprintf(stderr, "Cannot list folders on other than the "
2491 "current IMAP account,\n\"%s\". "
2492 "Try \"folders @\".\n", sp);
2493 return;
2495 fold = protfile(name);
2496 if (columnize) {
2497 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2498 perror("tmpfile");
2499 return;
2501 rm(tempfn);
2502 Ftfree(&tempfn);
2503 } else
2504 fp = stdout;
2505 imaplock = 1;
2506 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2507 safe_signal(SIGINT, maincatch);
2508 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2509 if (sigsetjmp(imapjmp, 1))
2510 goto out;
2511 if (savepipe != SIG_IGN)
2512 safe_signal(SIGPIPE, imapcatch);
2513 if (mb.mb_type == MB_CACHE)
2514 cache_list(&mb, fold, strip, fp);
2515 else
2516 imap_list(&mb, fold, strip, fp);
2517 imaplock = 0;
2518 if (interrupts) {
2519 if (columnize)
2520 Fclose(fp);
2521 onintr(0);
2523 fflush(fp);
2524 if (columnize) {
2525 rewind(fp);
2526 if (fsize(fp) > 0)
2527 dopr(fp);
2528 else
2529 fprintf(stderr, "Folder not found.\n");
2531 out:
2532 safe_signal(SIGINT, saveint);
2533 safe_signal(SIGPIPE, savepipe);
2534 if (columnize)
2535 Fclose(fp);
2538 static void
2539 dopr(FILE *fp)
2541 char o[LINESIZE], *tempfn;
2542 int c;
2543 long n = 0, mx = 0, columns, width;
2544 FILE *out;
2546 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2547 perror("tmpfile");
2548 return;
2550 rm(tempfn);
2551 Ftfree(&tempfn);
2552 while ((c = getc(fp)) != EOF) {
2553 if (c == '\n') {
2554 if (n > mx)
2555 mx = n;
2556 n = 0;
2557 } else
2558 n++;
2560 rewind(fp);
2561 width = scrnwidth;
2562 if (mx < width / 2) {
2563 columns = width / (mx+2);
2564 snprintf(o, sizeof o,
2565 "sort | pr -%lu -w%lu -t",
2566 columns, width);
2567 } else
2568 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2569 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2570 try_pager(out);
2571 Fclose(out);
2574 static enum okay
2575 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2577 char o[LINESIZE];
2578 const char *qname;
2579 enum okay ok = STOP;
2580 int twice = 0;
2581 int stored = 0;
2582 FILE *queuefp = NULL;
2584 if (mp->mb_type == MB_CACHE) {
2585 if ((queuefp = cache_queue(mp)) == NULL)
2586 return STOP;
2587 ok = OKAY;
2589 qname = imap_quotestr(name = protfile(name));
2591 * Since it is not possible to set flags on the copy, recently
2592 * set flags must be set on the original to include it in the copy.
2594 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2595 imap_store(mp, m, n, '+', "\\Seen", 0);
2596 if (m->m_flag&MFLAG)
2597 imap_store(mp, m, n, '+', "\\Flagged", 0);
2598 if (m->m_flag&MUNFLAG)
2599 imap_store(mp, m, n, '-', "\\Flagged", 0);
2600 if (m->m_flag&MANSWER)
2601 imap_store(mp, m, n, '+', "\\Answered", 0);
2602 if (m->m_flag&MUNANSWER)
2603 imap_store(mp, m, n, '-', "\\Flagged", 0);
2604 if (m->m_flag&MDRAFT)
2605 imap_store(mp, m, n, '+', "\\Draft", 0);
2606 if (m->m_flag&MUNDRAFT)
2607 imap_store(mp, m, n, '-', "\\Draft", 0);
2608 again: if (m->m_uid)
2609 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2610 tag(1), m->m_uid, qname);
2611 else {
2612 if (check_expunged() == STOP)
2613 goto out;
2614 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2615 tag(1), n, qname);
2617 IMAP_OUT(o, MB_COMD, goto out)
2618 while (mp->mb_active & MB_COMD)
2619 ok = imap_answer(mp, twice);
2620 if (mp->mb_type == MB_IMAP &&
2621 mp->mb_flags & MB_UIDPLUS &&
2622 response_status == RESPONSE_OK)
2623 imap_copyuid(mp, m, name);
2624 if (response_status == RESPONSE_NO && twice++ == 0) {
2625 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2626 IMAP_OUT(o, MB_COMD, goto out)
2627 while (mp->mb_active & MB_COMD)
2628 ok = imap_answer(mp, 1);
2629 if (ok == OKAY) {
2630 imap_created_mailbox++;
2631 goto again;
2634 if (queuefp != NULL)
2635 Fclose(queuefp);
2637 * ... and reset the flag to its initial value so that
2638 * the 'exit' command still leaves the message unread.
2640 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2641 imap_store(mp, m, n, '-', "\\Seen", 0);
2642 stored++;
2644 if (m->m_flag&MFLAG) {
2645 imap_store(mp, m, n, '-', "\\Flagged", 0);
2646 stored++;
2648 if (m->m_flag&MUNFLAG) {
2649 imap_store(mp, m, n, '+', "\\Flagged", 0);
2650 stored++;
2652 if (m->m_flag&MANSWER) {
2653 imap_store(mp, m, n, '-', "\\Answered", 0);
2654 stored++;
2656 if (m->m_flag&MUNANSWER) {
2657 imap_store(mp, m, n, '+', "\\Answered", 0);
2658 stored++;
2660 if (m->m_flag&MDRAFT) {
2661 imap_store(mp, m, n, '-', "\\Draft", 0);
2662 stored++;
2664 if (m->m_flag&MUNDRAFT) {
2665 imap_store(mp, m, n, '+', "\\Draft", 0);
2666 stored++;
2668 if (stored) {
2669 mp->mb_active |= MB_COMD;
2670 imap_finish(mp);
2672 return ok;
2675 enum okay
2676 imap_copy(struct message *m, int n, const char *name)
2678 sighandler_type saveint, savepipe;
2679 enum okay ok = STOP;
2681 (void)&saveint;
2682 (void)&savepipe;
2683 (void)&ok;
2684 verbose = value("verbose") != NULL;
2685 imaplock = 1;
2686 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2687 safe_signal(SIGINT, maincatch);
2688 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2689 if (sigsetjmp(imapjmp, 1) == 0) {
2690 if (savepipe != SIG_IGN)
2691 safe_signal(SIGPIPE, imapcatch);
2692 ok = imap_copy1(&mb, m, n, name);
2694 safe_signal(SIGINT, saveint);
2695 safe_signal(SIGPIPE, savepipe);
2696 imaplock = 0;
2697 if (interrupts)
2698 onintr(0);
2699 return ok;
2702 static enum okay
2703 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2704 unsigned long *olduid, unsigned long *newuid)
2706 char *xp, *yp, *zp;
2708 *uidvalidity = strtoul(cp, &xp, 10);
2709 *olduid = strtoul(xp, &yp, 10);
2710 *newuid = strtoul(yp, &zp, 10);
2711 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2712 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2715 static enum okay
2716 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2717 unsigned long *uid)
2719 char *xp, *yp;
2721 *uidvalidity = strtoul(cp, &xp, 10);
2722 *uid = strtoul(xp, &yp, 10);
2723 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2724 yp > xp && *yp == ']';
2727 static enum okay
2728 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2730 const char *cp;
2731 unsigned long uidvalidity, olduid, newuid;
2732 struct mailbox xmb;
2733 struct message xm;
2735 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2736 imap_copyuid_parse(&cp[9], &uidvalidity,
2737 &olduid, &newuid) == STOP)
2738 return STOP;
2739 xmb = *mp;
2740 xmb.mb_cache_directory = NULL;
2741 xmb.mb_imap_mailbox = savestr((char *)name);
2742 xmb.mb_uidvalidity = uidvalidity;
2743 initcache(&xmb);
2744 if (m == NULL) {
2745 memset(&xm, 0, sizeof xm);
2746 xm.m_uid = olduid;
2747 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2748 return STOP;
2749 getcache(mp, &xm, NEED_HEADER);
2750 getcache(mp, &xm, NEED_BODY);
2751 } else {
2752 if ((m->m_flag & HAVE_HEADER) == 0)
2753 getcache(mp, m, NEED_HEADER);
2754 if ((m->m_flag & HAVE_BODY) == 0)
2755 getcache(mp, m, NEED_BODY);
2756 xm = *m;
2758 xm.m_uid = newuid;
2759 xm.m_flag &= ~MFULLYCACHED;
2760 putcache(&xmb, &xm);
2761 return OKAY;
2764 static enum okay
2765 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2766 long xsize, long size, long lines, int flag, const char *name)
2768 const char *cp;
2769 unsigned long uidvalidity, uid;
2770 struct mailbox xmb;
2771 struct message xm;
2773 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2774 imap_appenduid_parse(&cp[11], &uidvalidity,
2775 &uid) == STOP)
2776 return STOP;
2777 xmb = *mp;
2778 xmb.mb_cache_directory = NULL;
2779 xmb.mb_imap_mailbox = savestr((char *)name);
2780 xmb.mb_uidvalidity = uidvalidity;
2781 xmb.mb_otf = xmb.mb_itf = fp;
2782 initcache(&xmb);
2783 memset(&xm, 0, sizeof xm);
2784 xm.m_flag = (flag & MREAD) | MNEW;
2785 xm.m_time = t;
2786 xm.m_block = mailx_blockof(off1);
2787 xm.m_offset = mailx_offsetof(off1);
2788 xm.m_size = size;
2789 xm.m_xsize = xsize;
2790 xm.m_lines = xm.m_xlines = lines;
2791 xm.m_uid = uid;
2792 xm.m_have = HAVE_HEADER|HAVE_BODY;
2793 putcache(&xmb, &xm);
2794 return OKAY;
2797 static enum okay
2798 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2800 FILE *tp = NULL;
2801 time_t t;
2802 long size, xsize, ysize, lines;
2803 enum mflag flag = MNEW;
2804 char *name, *buf, *bp, *cp, *tempCopy;
2805 size_t bufsize, buflen, count;
2806 enum okay ok = STOP;
2808 buf = smalloc(bufsize = LINESIZE);
2809 buflen = 0;
2810 count = fsize(fp);
2811 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2812 goto stop;
2813 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2814 while (*bp == ' ')
2815 bp++;
2816 if ((cp = strrchr(bp, '{')) == NULL)
2817 goto stop;
2818 xsize = atol(&cp[1]) + 2;
2819 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2820 goto stop;
2821 while (*cp == ' ')
2822 cp++;
2823 if (*cp == '(') {
2824 imap_getflags(cp, &cp, &flag);
2825 while (*++cp == ' ');
2827 t = imap_read_date_time(cp);
2828 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2829 goto stop;
2830 rm(tempCopy);
2831 Ftfree(&tempCopy);
2832 size = xsize;
2833 ysize = lines = 0;
2834 while (size > 0) {
2835 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2836 goto stop;
2837 size -= buflen;
2838 buf[--buflen] = '\0';
2839 buf[buflen-1] = '\n';
2840 fwrite(buf, 1, buflen, tp);
2841 ysize += buflen;
2842 lines++;
2844 fflush(tp);
2845 rewind(tp);
2846 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2847 imap_unquotestr(name));
2848 ok = OKAY;
2849 stop: free(buf);
2850 if (tp)
2851 Fclose(tp);
2852 return ok;
2855 static enum okay
2856 imap_search2(struct mailbox *mp, struct message *m, int count,
2857 const char *spec, int f)
2859 char *o;
2860 size_t osize;
2861 FILE *queuefp = NULL;
2862 enum okay ok = STOP;
2863 int i;
2864 unsigned long n;
2865 const char *cp;
2866 char *xp, *cs, c;
2868 c = 0;
2869 for (cp = spec; *cp; cp++)
2870 c |= *cp;
2871 if (c & 0200) {
2872 cp = gettcharset();
2873 #ifdef HAVE_ICONV
2874 if (asccasecmp(cp, "utf-8")) {
2875 iconv_t it;
2876 char *sp, *nsp, *nspec;
2877 size_t sz, nsz;
2878 if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
2879 sz = strlen(spec) + 1;
2880 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2881 sp = (char *)spec;
2882 if (iconv(it, &sp, &sz, &nsp, &nsz)
2883 != (size_t)-1 && sz == 0) {
2884 spec = nspec;
2885 cp = "utf-8";
2887 iconv_close(it);
2890 #endif /* HAVE_ICONV */
2891 cp = imap_quotestr(cp);
2892 cs = salloc(n = strlen(cp) + 10);
2893 snprintf(cs, n, "CHARSET %s ", cp);
2894 } else
2895 cs = "";
2896 o = ac_alloc(osize = strlen(spec) + 60);
2897 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2898 IMAP_OUT(o, MB_COMD, goto out)
2899 while (mp->mb_active & MB_COMD) {
2900 ok = imap_answer(mp, 0);
2901 if (response_status == RESPONSE_OTHER &&
2902 response_other == MAILBOX_DATA_SEARCH) {
2903 xp = responded_other_text;
2904 while (*xp && *xp != '\r') {
2905 n = strtoul(xp, &xp, 10);
2906 for (i = 0; i < count; i++)
2907 if (m[i].m_uid == n &&
2908 (m[i].m_flag&MHIDDEN)
2909 == 0 &&
2910 (f == MDELETED ||
2911 (m[i].m_flag&MDELETED)
2912 == 0))
2913 mark(i+1, f);
2917 out: ac_free(o);
2918 return ok;
2921 enum okay
2922 imap_search1(const char *spec, int f)
2924 sighandler_type saveint, savepipe;
2925 enum okay ok = STOP;
2927 (void)&saveint;
2928 (void)&savepipe;
2929 (void)&ok;
2930 if (mb.mb_type != MB_IMAP)
2931 return STOP;
2932 verbose = value("verbose") != NULL;
2933 imaplock = 1;
2934 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2935 safe_signal(SIGINT, maincatch);
2936 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2937 if (sigsetjmp(imapjmp, 1) == 0) {
2938 if (savepipe != SIG_IGN)
2939 safe_signal(SIGPIPE, imapcatch);
2940 ok = imap_search2(&mb, message, msgCount, spec, f);
2942 safe_signal(SIGINT, saveint);
2943 safe_signal(SIGPIPE, savepipe);
2944 imaplock = 0;
2945 if (interrupts)
2946 onintr(0);
2947 return ok;
2950 int
2951 imap_thisaccount(const char *cp)
2953 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2954 return 0;
2955 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2956 mb.mb_imap_account == NULL)
2957 return 0;
2958 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2961 enum okay
2962 imap_remove(const char *name)
2964 sighandler_type saveint, savepipe;
2965 enum okay ok = STOP;
2967 (void)&saveint;
2968 (void)&savepipe;
2969 (void)&ok;
2970 verbose = value("verbose") != NULL;
2971 if (mb.mb_type != MB_IMAP) {
2972 fprintf(stderr, "Refusing to remove \"%s\" "
2973 "in disconnected mode.\n", name);
2974 return STOP;
2976 if (!imap_thisaccount(name)) {
2977 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2978 "server: \"%s\" not removed.\n", name);
2979 return STOP;
2981 imaplock = 1;
2982 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2983 safe_signal(SIGINT, maincatch);
2984 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2985 if (sigsetjmp(imapjmp, 1) == 0) {
2986 if (savepipe != SIG_IGN)
2987 safe_signal(SIGPIPE, imapcatch);
2988 ok = imap_remove1(&mb, protfile(name));
2990 safe_signal(SIGINT, saveint);
2991 safe_signal(SIGPIPE, savepipe);
2992 imaplock = 0;
2993 if (ok == OKAY)
2994 ok = cache_remove(name);
2995 if (interrupts)
2996 onintr(0);
2997 return ok;
3000 static enum okay
3001 imap_remove1(struct mailbox *mp, const char *name)
3003 FILE *queuefp = NULL;
3004 char *o;
3005 int os;
3006 enum okay ok = STOP;
3008 o = ac_alloc(os = 2*strlen(name) + 100);
3009 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3010 IMAP_OUT(o, MB_COMD, goto out)
3011 while (mp->mb_active & MB_COMD)
3012 ok = imap_answer(mp, 1);
3013 out: ac_free(o);
3014 return ok;
3017 enum okay
3018 imap_rename(const char *old, const char *new)
3020 sighandler_type saveint, savepipe;
3021 enum okay ok = STOP;
3023 (void)&saveint;
3024 (void)&savepipe;
3025 (void)&ok;
3026 verbose = value("verbose") != NULL;
3027 if (mb.mb_type != MB_IMAP) {
3028 fprintf(stderr, "Refusing to rename mailboxes "
3029 "in disconnected mode.\n");
3030 return STOP;
3032 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3033 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3034 "server: \"%s\" not renamed to \"%s\".\n",
3035 old, new);
3036 return STOP;
3038 imaplock = 1;
3039 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3040 safe_signal(SIGINT, maincatch);
3041 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3042 if (sigsetjmp(imapjmp, 1) == 0) {
3043 if (savepipe != SIG_IGN)
3044 safe_signal(SIGPIPE, imapcatch);
3045 ok = imap_rename1(&mb, protfile(old), protfile(new));
3047 safe_signal(SIGINT, saveint);
3048 safe_signal(SIGPIPE, savepipe);
3049 imaplock = 0;
3050 if (ok == OKAY)
3051 ok = cache_rename(old, new);
3052 if (interrupts)
3053 onintr(0);
3054 return ok;
3057 static enum okay
3058 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3060 FILE *queuefp = NULL;
3061 char *o;
3062 int os;
3063 enum okay ok = STOP;
3065 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3066 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3067 imap_quotestr(old), imap_quotestr(new));
3068 IMAP_OUT(o, MB_COMD, goto out)
3069 while (mp->mb_active & MB_COMD)
3070 ok = imap_answer(mp, 1);
3071 out: ac_free(o);
3072 return ok;
3075 enum okay
3076 imap_dequeue(struct mailbox *mp, FILE *fp)
3078 FILE *queuefp = NULL;
3079 char o[LINESIZE], *newname;
3080 char *buf, *bp, *cp, iob[4096];
3081 size_t bufsize, buflen, count;
3082 enum okay ok = OKAY, rok = OKAY;
3083 long offs, offs1, offs2, octets;
3084 int twice, gotcha = 0;
3086 buf = smalloc(bufsize = LINESIZE);
3087 buflen = 0;
3088 count = fsize(fp);
3089 while (offs1 = ftell(fp),
3090 fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
3091 != NULL) {
3092 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3093 while (*bp == ' ')
3094 bp++;
3095 twice = 0;
3096 offs = ftell(fp);
3097 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3098 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3099 cp = &bp[9];
3100 while (digitchar(*cp&0377))
3101 cp++;
3102 if (*cp != ' ')
3103 goto fail;
3104 while (*cp == ' ')
3105 cp++;
3106 if ((newname = imap_strex(cp, NULL)) == NULL)
3107 goto fail;
3108 IMAP_OUT(o, MB_COMD, continue)
3109 while (mp->mb_active & MB_COMD)
3110 ok = imap_answer(mp, twice);
3111 if (response_status == RESPONSE_NO && twice++ == 0)
3112 goto trycreate;
3113 if (response_status == RESPONSE_OK &&
3114 mp->mb_flags & MB_UIDPLUS) {
3115 imap_copyuid(mp, NULL,
3116 imap_unquotestr(newname));
3118 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3119 IMAP_OUT(o, MB_COMD, continue)
3120 while (mp->mb_active & MB_COMD)
3121 ok = imap_answer(mp, 1);
3122 if (ok == OKAY)
3123 gotcha++;
3124 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3125 if ((cp = strrchr(bp, '{')) == NULL)
3126 goto fail;
3127 octets = atol(&cp[1]) + 2;
3128 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3129 goto fail;
3130 IMAP_OUT(o, MB_COMD, continue)
3131 while (mp->mb_active & MB_COMD) {
3132 ok = imap_answer(mp, twice);
3133 if (response_type == RESPONSE_CONT)
3134 break;
3136 if (ok == STOP) {
3137 if (twice++ == 0) {
3138 fseek(fp, offs, SEEK_SET);
3139 goto trycreate;
3141 goto fail;
3143 while (octets > 0) {
3144 size_t n = (size_t)octets > sizeof iob
3145 ? sizeof iob : (size_t)octets;
3146 octets -= n;
3147 if (n != fread(iob, 1, n, fp))
3148 goto fail;
3149 swrite1(&mp->mb_sock, iob, n, 1);
3151 swrite(&mp->mb_sock, "");
3152 while (mp->mb_active & MB_COMD) {
3153 ok = imap_answer(mp, 0);
3154 if (response_status == RESPONSE_NO &&
3155 twice++ == 0) {
3156 fseek(fp, offs, SEEK_SET);
3157 goto trycreate;
3160 if (response_status == RESPONSE_OK &&
3161 mp->mb_flags & MB_UIDPLUS) {
3162 offs2 = ftell(fp);
3163 fseek(fp, offs1, SEEK_SET);
3164 if (imap_appenduid_cached(mp, fp) == STOP) {
3165 fseek(fp, offs2, SEEK_SET);
3166 goto fail;
3169 } else {
3170 fail: fprintf(stderr,
3171 "Invalid command in IMAP cache queue: \"%s\"\n",
3172 bp);
3173 rok = STOP;
3175 continue;
3176 trycreate:
3177 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3178 tag(1), newname);
3179 IMAP_OUT(o, MB_COMD, continue)
3180 while (mp->mb_active & MB_COMD)
3181 ok = imap_answer(mp, 1);
3182 if (ok == OKAY)
3183 goto again;
3185 fflush(fp);
3186 rewind(fp);
3187 ftruncate(fileno(fp), 0);
3188 if (gotcha)
3189 imap_close(mp);
3190 return rok;
3193 static char *
3194 imap_strex(const char *cp, char **xp)
3196 const char *cq;
3197 char *n;
3199 if (*cp != '"')
3200 return NULL;
3201 for (cq = &cp[1]; *cq; cq++) {
3202 if (*cq == '\\')
3203 cq++;
3204 else if (*cq == '"')
3205 break;
3207 if (*cq != '"')
3208 return NULL;
3209 n = salloc(cq - cp + 2);
3210 memcpy(n, cp, cq - cp + 1);
3211 n[cq - cp + 1] = '\0';
3212 if (xp)
3213 *xp = (char *)&cq[1];
3214 return n;
3217 static enum okay
3218 check_expunged(void)
3220 if (expunged_messages > 0) {
3221 fprintf(stderr,
3222 "Command not executed - messages have been expunged\n");
3223 return STOP;
3225 return OKAY;
3228 /*ARGSUSED*/
3229 int
3230 cconnect(void *vp)
3232 char *cp, *cq;
3233 int omsgCount = msgCount;
3234 (void)vp;
3236 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3237 fprintf(stderr, "Already connected.\n");
3238 return 1;
3240 unset_allow_undefined = 1;
3241 unset_internal("disconnected");
3242 cp = protbase(mailname);
3243 if (strncmp(cp, "imap://", 7) == 0)
3244 cp += 7;
3245 else if (strncmp(cp, "imaps://", 8) == 0)
3246 cp += 8;
3247 if ((cq = strchr(cp, ':')) != NULL)
3248 *cq = '\0';
3249 unset_internal(savecat("disconnected-", cp));
3250 unset_allow_undefined = 0;
3251 if (mb.mb_type == MB_CACHE) {
3252 imap_setfile1(mailname, 0, edit, 1);
3253 if (msgCount > omsgCount)
3254 newmailinfo(omsgCount);
3256 return 0;
3259 int
3260 cdisconnect(void *vp)
3262 int *msgvec = vp;
3264 if (mb.mb_type == MB_CACHE) {
3265 fprintf(stderr, "Not connected.\n");
3266 return 1;
3267 } else if (mb.mb_type == MB_IMAP) {
3268 if (cached_uidvalidity(&mb) == 0) {
3269 fprintf(stderr, "The current mailbox is not cached.\n");
3270 return 1;
3273 if (*msgvec)
3274 ccache(vp);
3275 assign("disconnected", "");
3276 if (mb.mb_type == MB_IMAP) {
3277 sclose(&mb.mb_sock);
3278 imap_setfile1(mailname, 0, edit, 1);
3280 return 0;
3282 int
3283 ccache(void *vp)
3285 int *msgvec = vp, *ip;
3286 struct message *mp;
3288 if (mb.mb_type != MB_IMAP) {
3289 fprintf(stderr, "Not connected to an IMAP server.\n");
3290 return 1;
3292 if (cached_uidvalidity(&mb) == 0) {
3293 fprintf(stderr, "The current mailbox is not cached.\n");
3294 return 1;
3296 for (ip = msgvec; *ip; ip++) {
3297 mp = &message[*ip-1];
3298 if (!(mp->m_have & HAVE_BODY))
3299 get_body(mp);
3301 return 0;
3303 #else /* !USE_IMAP */
3305 #include "extern.h"
3307 static void
3308 noimap(void)
3310 fprintf(stderr, catgets(catd, CATSET, 269,
3311 "No IMAP support compiled in.\n"));
3314 int
3315 imap_setfile(const char *server, int newmail, int isedit)
3317 (void)server;
3318 (void)newmail;
3319 (void)isedit;
3320 noimap();
3321 return -1;
3324 enum okay
3325 imap_header(struct message *mp)
3327 (void)mp;
3328 noimap();
3329 return STOP;
3332 enum okay
3333 imap_body(struct message *mp)
3335 (void)mp;
3336 noimap();
3337 return STOP;
3340 void
3341 imap_getheaders(int bot, int top)
3343 (void)bot;
3344 (void)top;
3347 void
3348 imap_quit(void)
3350 noimap();
3353 /*ARGSUSED*/
3354 int
3355 imap_imap(void *vp)
3357 (void)vp;
3358 noimap();
3359 return 1;
3362 /*ARGSUSED*/
3363 int
3364 imap_newmail(int dummy)
3366 (void)dummy;
3367 return 0;
3370 /*ARGSUSED*/
3371 enum okay
3372 imap_undelete(struct message *m, int n)
3374 (void)m;
3375 (void)n;
3376 return STOP;
3379 /*ARGSUSED*/
3380 enum okay
3381 imap_unread(struct message *m, int n)
3383 (void)m;
3384 (void)n;
3385 return STOP;
3388 /*ARGSUSED*/
3389 enum okay
3390 imap_append(const char *server, FILE *fp)
3392 (void)server;
3393 (void)fp;
3394 noimap();
3395 return STOP;
3398 /*ARGSUSED*/
3399 void
3400 imap_folders(const char *name, int strip)
3402 (void)name;
3403 (void)strip;
3404 noimap();
3407 /*ARGSUSED*/
3408 enum okay
3409 imap_remove(const char *name)
3411 (void)name;
3412 noimap();
3413 return STOP;
3416 /*ARGSUSED*/
3417 enum okay
3418 imap_rename(const char *old, const char *new)
3420 (void)old;
3421 (void)new;
3422 noimap();
3423 return STOP;
3426 enum okay
3427 imap_copy(struct message *m, int n, const char *name)
3429 (void)m;
3430 (void)n;
3431 (void)name;
3432 noimap();
3433 return STOP;
3436 /*ARGSUSED*/
3437 enum okay
3438 imap_search1(const char *spec, int f)
3440 (void)spec;
3441 (void)f;
3442 return STOP;
3445 int
3446 imap_thisaccount(const char *cp)
3448 (void)cp;
3449 return 0;
3452 enum okay
3453 imap_noop(void)
3455 noimap();
3456 return STOP;
3459 /*ARGSUSED*/
3460 int
3461 cconnect(void *vp)
3463 (void)vp;
3464 noimap();
3465 return 1;
3468 /*ARGSUSED*/
3469 int
3470 cdisconnect(void *vp)
3472 (void)vp;
3473 noimap();
3474 return 1;
3477 /*ARGSUSED*/
3478 int
3479 ccache(void *vp)
3481 (void)vp;
3482 noimap();
3483 return 1;
3485 #endif /* USE_IMAP */
3487 time_t
3488 imap_read_date_time(const char *cp)
3490 time_t t;
3491 int i, year, month, day, hour, minute, second;
3492 int sign = -1;
3493 char buf[3];
3496 * "25-Jul-2004 15:33:44 +0200"
3497 * | | | | | |
3498 * 0 5 10 15 20 25
3500 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3501 goto invalid;
3502 day = strtol(&cp[1], NULL, 10);
3503 for (i = 0; month_names[i]; i++)
3504 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3505 break;
3506 if (month_names[i] == NULL)
3507 goto invalid;
3508 month = i + 1;
3509 year = strtol(&cp[8], NULL, 10);
3510 hour = strtol(&cp[13], NULL, 10);
3511 minute = strtol(&cp[16], NULL, 10);
3512 second = strtol(&cp[19], NULL, 10);
3513 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3514 (time_t)-1)
3515 goto invalid;
3516 switch (cp[22]) {
3517 case '-':
3518 sign = 1;
3519 break;
3520 case '+':
3521 break;
3522 default:
3523 goto invalid;
3525 buf[2] = '\0';
3526 buf[0] = cp[23];
3527 buf[1] = cp[24];
3528 t += strtol(buf, NULL, 10) * sign * 3600;
3529 buf[0] = cp[25];
3530 buf[1] = cp[26];
3531 t += strtol(buf, NULL, 10) * sign * 60;
3532 return t;
3533 invalid:
3534 time(&t);
3535 return t;
3538 time_t
3539 imap_read_date(const char *cp)
3541 time_t t;
3542 int year, month, day, i, tzdiff;
3543 struct tm *tmptr;
3544 char *xp, *yp;
3546 if (*cp == '"')
3547 cp++;
3548 day = strtol(cp, &xp, 10);
3549 if (day <= 0 || day > 31 || *xp++ != '-')
3550 return -1;
3551 for (i = 0; month_names[i]; i++)
3552 if (ascncasecmp(xp, month_names[i], 3) == 0)
3553 break;
3554 if (month_names[i] == NULL)
3555 return -1;
3556 month = i+1;
3557 if (xp[3] != '-')
3558 return -1;
3559 year = strtol(&xp[4], &yp, 10);
3560 if (year < 1970 || year > 2037 || yp != &xp[8])
3561 return -1;
3562 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3563 return -1;
3564 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3565 return -1;
3566 tzdiff = t - mktime(gmtime(&t));
3567 tmptr = localtime(&t);
3568 if (tmptr->tm_isdst > 0)
3569 tzdiff += 3600;
3570 t -= tzdiff;
3571 return t;
3574 const char *
3575 imap_make_date_time(time_t t)
3577 static char s[30];
3578 struct tm *tmptr;
3579 int tzdiff, tzdiff_hour, tzdiff_min;
3581 tzdiff = t - mktime(gmtime(&t));
3582 tzdiff_hour = (int)(tzdiff / 60);
3583 tzdiff_min = tzdiff_hour % 60;
3584 tzdiff_hour /= 60;
3585 tmptr = localtime(&t);
3586 if (tmptr->tm_isdst > 0)
3587 tzdiff_hour++;
3588 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3589 tmptr->tm_mday,
3590 month_names[tmptr->tm_mon],
3591 tmptr->tm_year + 1900,
3592 tmptr->tm_hour,
3593 tmptr->tm_min,
3594 tmptr->tm_sec,
3595 tzdiff_hour,
3596 tzdiff_min);
3597 return s;
3600 char *
3601 imap_quotestr(const char *s)
3603 char *n, *np;
3605 np = n = salloc(2 * strlen(s) + 3);
3606 *np++ = '"';
3607 while (*s) {
3608 if (*s == '"' || *s == '\\')
3609 *np++ = '\\';
3610 *np++ = *s++;
3612 *np++ = '"';
3613 *np = '\0';
3614 return n;
3617 char *
3618 imap_unquotestr(const char *s)
3620 char *n, *np;
3622 if (*s != '"')
3623 return savestr(s);
3624 np = n = salloc(strlen(s) + 1);
3625 while (*++s) {
3626 if (*s == '\\')
3627 s++;
3628 else if (*s == '"')
3629 break;
3630 *np++ = *s;
3632 *np = '\0';
3633 return n;