IMAP: add some missing translation hooks
[s-mailx.git] / imap.c
blob830e8f44dfe38439e0dc30e371bcb696ec4487e1
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, tr(272, "No GSSAPI support compiled in.\n"));
852 return STOP;
853 #endif /* !USE_GSSAPI */
855 fprintf(stderr, tr(273, "Unknown IMAP authentication method: %s\n"),
856 auth);
857 return STOP;
861 * Implementation of RFC 2194.
863 static enum okay
864 imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
866 char o[LINESIZE];
867 const char *user, *pass;
868 char *cp;
869 FILE *queuefp = NULL;
870 enum okay ok = STOP;
872 retry: if (xuser == NULL) {
873 if ((user = getuser()) == NULL)
874 return STOP;
875 } else
876 user = xuser;
877 if (xpass == NULL) {
878 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
879 return STOP;
880 } else
881 pass = xpass;
882 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
883 IMAP_OUT(o, 0, return STOP)
884 imap_answer(mp, 1);
885 if (response_type != RESPONSE_CONT)
886 return STOP;
887 cp = cram_md5_string(user, pass, responded_text);
888 IMAP_OUT(cp, MB_COMD, return STOP)
889 while (mp->mb_active & MB_COMD)
890 ok = imap_answer(mp, 1);
891 if (ok == STOP) {
892 xpass = NULL;
893 goto retry;
895 return ok;
898 static enum okay
899 imap_login(struct mailbox *mp, char *xuser, const char *xpass)
901 char o[LINESIZE];
902 const char *user, *pass;
903 FILE *queuefp = NULL;
904 enum okay ok = STOP;
906 retry: if (xuser == NULL) {
907 if ((user = getuser()) == NULL)
908 return STOP;
909 } else
910 user = xuser;
911 if (xpass == NULL) {
912 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
913 return STOP;
914 } else
915 pass = xpass;
916 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
917 tag(1), imap_quotestr(user), imap_quotestr(pass));
918 IMAP_OUT(o, MB_COMD, return STOP)
919 while (mp->mb_active & MB_COMD)
920 ok = imap_answer(mp, 1);
921 if (ok == STOP) {
922 xpass = NULL;
923 goto retry;
925 return OKAY;
928 #ifdef USE_GSSAPI
929 #include "imap_gssapi.c"
930 #endif /* USE_GSSAPI */
932 enum okay
933 imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
935 enum okay ok = OKAY;
936 char *cp;
937 char o[LINESIZE];
938 FILE *queuefp = NULL;
939 (void)size;
941 mp->mb_uidvalidity = 0;
942 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
943 IMAP_OUT(o, MB_COMD, return STOP)
944 while (mp->mb_active & MB_COMD) {
945 ok = imap_answer(mp, 1);
946 if (response_status != RESPONSE_OTHER &&
947 (cp = asccasestr(responded_text,
948 "[UIDVALIDITY ")) != NULL)
949 mp->mb_uidvalidity = atol(&cp[13]);
951 *count = had_exists > 0 ? had_exists : 0;
952 if (response_status != RESPONSE_OTHER &&
953 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
954 == 0)
955 mp->mb_perm = 0;
956 return ok;
959 static enum okay
960 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
962 char o[LINESIZE];
963 FILE *queuefp = NULL;
964 char *cp;
965 struct message *m;
966 unsigned x = X, y = Y, n;
968 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
969 IMAP_OUT(o, MB_COMD, return STOP)
970 while (mp->mb_active & MB_COMD) {
971 imap_answer(mp, 1);
972 if (response_status == RESPONSE_OTHER &&
973 response_other == MESSAGE_DATA_FETCH) {
974 n = responded_other_number;
975 if (n < x || n > y)
976 continue;
977 m = &message[n-1];
978 m->m_xsize = 0;
979 } else
980 continue;
981 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
982 cp += 5;
983 while (*cp == ' ')
984 cp++;
985 if (*cp == '(')
986 imap_getflags(cp, &cp, &m->m_flag);
988 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
989 m->m_uid = strtoul(&cp[4], NULL, 10);
990 getcache1(mp, m, NEED_UNSPEC, 1);
991 m->m_flag &= ~MHIDDEN;
993 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
994 x++;
995 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
996 y--;
997 if (x <= y) {
998 snprintf(o, sizeof o,
999 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1000 tag(1), x, y);
1001 IMAP_OUT(o, MB_COMD, return STOP)
1002 while (mp->mb_active & MB_COMD) {
1003 imap_answer(mp, 1);
1004 if (response_status == RESPONSE_OTHER &&
1005 response_other == MESSAGE_DATA_FETCH) {
1006 n = responded_other_number;
1007 if (n < x || n > y)
1008 continue;
1009 m = &message[n-1];
1010 } else
1011 continue;
1012 if ((cp = asccasestr(responded_other_text,
1013 "RFC822.SIZE ")) != NULL)
1014 m->m_xsize = strtol(&cp[12], NULL, 10);
1015 if ((cp = asccasestr(responded_other_text,
1016 "INTERNALDATE ")) != NULL)
1017 m->m_time = imap_read_date_time(&cp[13]);
1020 for (n = X; n <= Y; n++)
1021 putcache(mp, &message[n-1]);
1022 return OKAY;
1025 static void
1026 imap_init(struct mailbox *mp, int n)
1028 struct message *m = &message[n];
1029 (void)mp;
1031 m->m_flag = MUSED|MNOFROM;
1032 m->m_block = 0;
1033 m->m_offset = 0;
1036 static void
1037 imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
1039 struct message *omessage = 0;
1040 int i, omsgCount = 0;
1041 enum okay dequeued = STOP;
1043 if (newmail || transparent) {
1044 omessage = message;
1045 omsgCount = msgCount;
1047 if (newmail)
1048 dequeued = rec_dequeue();
1049 if (had_exists >= 0) {
1050 if (dequeued != OKAY)
1051 msgCount = had_exists;
1052 had_exists = -1;
1054 if (had_expunge >= 0) {
1055 if (dequeued != OKAY)
1056 msgCount -= had_expunge;
1057 had_expunge = -1;
1059 if (newmail && expunged_messages)
1060 printf("Expunged %ld message%s.\n",
1061 expunged_messages,
1062 expunged_messages != 1 ? "s" : "");
1063 *prevcount = omsgCount - expunged_messages;
1064 expunged_messages = 0;
1065 if (msgCount < 0) {
1066 fputs("IMAP error: Negative message count\n", stderr);
1067 msgCount = 0;
1069 if (dequeued != OKAY) {
1070 message = scalloc(msgCount + 1, sizeof *message);
1071 for (i = 0; i < msgCount; i++)
1072 imap_init(mp, i);
1073 if (!newmail && mp->mb_type == MB_IMAP)
1074 initcache(mp);
1075 if (msgCount > 0)
1076 imap_flags(mp, 1, msgCount);
1077 message[msgCount].m_size = 0;
1078 message[msgCount].m_lines = 0;
1079 rec_rmqueue();
1081 if (newmail || transparent)
1082 transflags(omessage, omsgCount, transparent);
1083 else
1084 setdot(message);
1087 static char *
1088 imap_have_password(const char *server)
1090 char *var, *cp;
1092 var = ac_alloc(strlen(server) + 10);
1093 strcpy(var, "password-");
1094 strcpy(&var[9], server);
1095 if ((cp = value(var)) != NULL)
1096 cp = savestr(cp);
1097 ac_free(var);
1098 return cp;
1101 static void
1102 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1103 char **uhp, char **mbx, const char **pass, char **user)
1105 *sp = *server;
1106 if (strncmp(*sp, "imap://", 7) == 0) {
1107 *sp = &(*sp)[7];
1108 *use_ssl = 0;
1109 #ifdef USE_SSL
1110 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1111 *sp = &(*sp)[8];
1112 *use_ssl = 1;
1113 #endif /* USE_SSL */
1115 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1116 *uhp = savestr((char *)(*sp));
1117 (*uhp)[*cp - *sp] = '\0';
1118 *mbx = (char *)&(*cp)[1];
1119 } else {
1120 if (*cp)
1121 (*server)[*cp - *server] = '\0';
1122 *uhp = (char *)(*sp);
1123 *mbx = "INBOX";
1125 *pass = imap_have_password(*uhp);
1126 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1127 *user = salloc(*cp - *uhp + 1);
1128 memcpy(*user, *uhp, *cp - *uhp);
1129 (*user)[*cp - *uhp] = '\0';
1130 *sp = &(*cp)[1];
1131 *user = strdec(*user);
1132 } else {
1133 *user = NULL;
1134 *sp = *uhp;
1138 int
1139 imap_setfile(const char *xserver, int newmail, int isedit)
1141 return imap_setfile1(xserver, newmail, isedit, 0);
1144 static int
1145 imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
1147 struct sock so;
1148 sighandler_type saveint;
1149 sighandler_type savepipe;
1150 char *server, *user, *account;
1151 const char *cp, *sp, *pass;
1152 char *uhp, *mbx;
1153 int use_ssl = 0;
1154 enum mbflags same_flags;
1155 int prevcount = 0;
1157 (void)&sp;
1158 (void)&use_ssl;
1159 (void)&saveint;
1160 (void)&savepipe;
1161 server = savestr((char *)xserver);
1162 verbose = value("verbose") != NULL;
1163 if (newmail) {
1164 saveint = safe_signal(SIGINT, SIG_IGN);
1165 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1166 if (saveint != SIG_IGN)
1167 safe_signal(SIGINT, imapcatch);
1168 if (savepipe != SIG_IGN)
1169 safe_signal(SIGPIPE, imapcatch);
1170 imaplock = 1;
1171 goto newmail;
1173 same_flags = mb.mb_flags;
1174 same_imap_account = 0;
1175 sp = protbase(server);
1176 if (mb.mb_imap_account) {
1177 if (mb.mb_sock.s_fd > 0 &&
1178 strcmp(mb.mb_imap_account, sp) == 0 &&
1179 disconnected(mb.mb_imap_account) == 0)
1180 same_imap_account = 1;
1182 account = sstrdup(sp);
1183 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1184 so.s_fd = -1;
1185 if (!same_imap_account) {
1186 if (!disconnected(account) &&
1187 sopen(sp, &so, use_ssl, uhp,
1188 use_ssl ? "imaps" : "imap", verbose) != OKAY)
1189 return -1;
1190 } else
1191 so = mb.mb_sock;
1192 if (!transparent)
1193 quit();
1194 edit = isedit;
1195 free(mb.mb_imap_account);
1196 mb.mb_imap_account = account;
1197 if (!same_imap_account) {
1198 if (mb.mb_sock.s_fd >= 0)
1199 sclose(&mb.mb_sock);
1201 same_imap_account = 0;
1202 if (!transparent) {
1203 if (mb.mb_itf) {
1204 fclose(mb.mb_itf);
1205 mb.mb_itf = NULL;
1207 if (mb.mb_otf) {
1208 fclose(mb.mb_otf);
1209 mb.mb_otf = NULL;
1211 free(mb.mb_imap_mailbox);
1212 mb.mb_imap_mailbox = sstrdup(mbx);
1213 initbox(server);
1215 mb.mb_type = MB_VOID;
1216 mb.mb_active = MB_NONE;;
1217 imaplock = 1;
1218 saveint = safe_signal(SIGINT, SIG_IGN);
1219 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1220 if (sigsetjmp(imapjmp, 1)) {
1221 sclose(&so);
1222 safe_signal(SIGINT, saveint);
1223 safe_signal(SIGPIPE, savepipe);
1224 imaplock = 0;
1225 return -1;
1227 if (saveint != SIG_IGN)
1228 safe_signal(SIGINT, imapcatch);
1229 if (savepipe != SIG_IGN)
1230 safe_signal(SIGPIPE, imapcatch);
1231 if (mb.mb_sock.s_fd < 0) {
1232 if (disconnected(mb.mb_imap_account)) {
1233 if (cache_setptr(transparent) == STOP)
1234 fprintf(stderr,
1235 "Mailbox \"%s\" is not cached.\n",
1236 server);
1237 goto done;
1239 if ((cp = value("imap-keepalive")) != NULL) {
1240 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1241 savealrm = safe_signal(SIGALRM, imapalarm);
1242 alarm(imapkeepalive);
1245 mb.mb_sock = so;
1246 mb.mb_sock.s_desc = "IMAP";
1247 mb.mb_sock.s_onclose = imap_timer_off;
1248 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1249 imap_auth(&mb, uhp, user, pass) != OKAY) {
1250 sclose(&mb.mb_sock);
1251 imap_timer_off();
1252 safe_signal(SIGINT, saveint);
1253 safe_signal(SIGPIPE, savepipe);
1254 imaplock = 0;
1255 return -1;
1257 } else /* same account */
1258 mb.mb_flags |= same_flags;
1259 mb.mb_perm = Rflag ? 0 : MB_DELE;
1260 mb.mb_type = MB_IMAP;
1261 cache_dequeue(&mb);
1262 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1263 /*sclose(&mb.mb_sock);
1264 imap_timer_off();*/
1265 safe_signal(SIGINT, saveint);
1266 safe_signal(SIGPIPE, savepipe);
1267 imaplock = 0;
1268 mb.mb_type = MB_VOID;
1269 return -1;
1271 newmail:
1272 imap_setptr(&mb, newmail, transparent, &prevcount);
1273 done: setmsize(msgCount);
1274 if (!newmail && !transparent)
1275 sawcom = 0;
1276 safe_signal(SIGINT, saveint);
1277 safe_signal(SIGPIPE, savepipe);
1278 imaplock = 0;
1279 if (!newmail && mb.mb_type == MB_IMAP)
1280 purgecache(&mb, message, msgCount);
1281 if ((newmail || transparent) && mb.mb_sorted) {
1282 mb.mb_threaded = 0;
1283 sort((void *)-1);
1285 if (!newmail && !edit && msgCount == 0) {
1286 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1287 value("emptystart") == NULL)
1288 fprintf(stderr, catgets(catd, CATSET, 258,
1289 "No mail at %s\n"), server);
1290 return 1;
1292 if (newmail)
1293 newmailinfo(prevcount);
1294 return 0;
1297 static int
1298 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1299 int need,
1300 const char *head, size_t headsize, long headlines)
1302 char *line = NULL, *lp;
1303 size_t linesize = 0, linelen, size = 0;
1304 int emptyline = 0, lines = 0, excess = 0;
1305 off_t offset;
1307 fseek(mp->mb_otf, 0L, SEEK_END);
1308 offset = ftell(mp->mb_otf);
1309 if (head)
1310 fwrite(head, 1, headsize, mp->mb_otf);
1311 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1312 lp = line;
1313 if (linelen > expected) {
1314 excess = linelen - expected;
1315 linelen = expected;
1318 * Need to mask 'From ' lines. This cannot be done properly
1319 * since some servers pass them as 'From ' and others as
1320 * '>From '. Although one could identify the first kind of
1321 * server in principle, it is not possible to identify the
1322 * second as '>From ' may also come from a server of the
1323 * first type as actual data. So do what is absolutely
1324 * necessary only - mask 'From '.
1326 * If the line is the first line of the message header, it
1327 * is likely a real 'From ' line. In this case, it is just
1328 * ignored since it violates all standards.
1330 if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
1331 lp[3] == 'm' && lp[4] == ' ') {
1332 if (lines + headlines != 0) {
1333 fputc('>', mp->mb_otf);
1334 size++;
1335 } else
1336 goto skip;
1338 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1339 lp[linelen-2] == '\r')) {
1340 emptyline = linelen <= 2;
1341 if (linelen > 2) {
1342 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1343 size += linelen - 1;
1344 } else
1345 size++;
1346 fputc('\n', mp->mb_otf);
1347 } else {
1348 emptyline = 0;
1349 fwrite(lp, 1, linelen, mp->mb_otf);
1350 size += linelen;
1352 lines++;
1353 skip: if ((expected -= linelen) <= 0)
1354 break;
1356 if (!emptyline) {
1358 * This is very ugly; but some IMAP daemons don't end a
1359 * message with \r\n\r\n, and we need \n\n for mbox format.
1361 fputc('\n', mp->mb_otf);
1362 lines++;
1363 size++;
1365 fflush(mp->mb_otf);
1366 if (m != NULL) {
1367 m->m_size = size + headsize;
1368 m->m_lines = lines + headlines;
1369 m->m_block = mailx_blockof(offset);
1370 m->m_offset = mailx_offsetof(offset);
1371 switch (need) {
1372 case NEED_HEADER:
1373 m->m_have |= HAVE_HEADER;
1374 break;
1375 case NEED_BODY:
1376 m->m_have |= HAVE_HEADER|HAVE_BODY;
1377 m->m_xlines = m->m_lines;
1378 m->m_xsize = m->m_size;
1379 break;
1382 free(line);
1383 return excess;
1386 static void
1387 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1388 const char *head, size_t headsize, long headlines)
1390 off_t offset;
1391 size_t len;
1393 len = strlen(str);
1394 fseek(mp->mb_otf, 0L, SEEK_END);
1395 offset = ftell(mp->mb_otf);
1396 if (head)
1397 fwrite(head, 1, headsize, mp->mb_otf);
1398 if (len > 0) {
1399 fwrite(str, 1, len, mp->mb_otf);
1400 fputc('\n', mp->mb_otf);
1401 len++;
1403 fflush(mp->mb_otf);
1404 if (m != NULL) {
1405 m->m_size = headsize + len;
1406 m->m_lines = headlines + 1;
1407 m->m_block = mailx_blockof(offset);
1408 m->m_offset = mailx_offsetof(offset);
1409 m->m_have |= HAVE_HEADER|HAVE_BODY;
1410 m->m_xlines = m->m_lines;
1411 m->m_xsize = m->m_size;
1415 static enum okay
1416 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1418 sighandler_type saveint = SIG_IGN;
1419 sighandler_type savepipe = SIG_IGN;
1420 char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL;
1421 size_t expected, headsize = 0;
1422 int number = m - message + 1;
1423 enum okay ok = STOP;
1424 FILE *queuefp = NULL;
1425 char *head = NULL;
1426 long headlines = 0;
1427 struct message mt;
1428 long n = -1;
1429 unsigned long u = 0;
1431 (void)&saveint;
1432 (void)&savepipe;
1433 (void)&number;
1434 (void)&need;
1435 (void)&cp;
1436 (void)&loc;
1437 (void)&ok;
1438 (void)&headsize;
1439 (void)&head;
1440 (void)&headlines;
1441 (void)&item;
1442 (void)&resp;
1443 (void)&u;
1444 verbose = value("verbose") != NULL;
1445 if (getcache(mp, m, need) == OKAY)
1446 return OKAY;
1447 if (mp->mb_type == MB_CACHE) {
1448 fprintf(stderr, "Message %u not available.\n", number);
1449 return STOP;
1451 if (mp->mb_sock.s_fd < 0) {
1452 fprintf(stderr, "IMAP connection closed.\n");
1453 return STOP;
1455 switch (need) {
1456 case NEED_HEADER:
1457 resp = item = "RFC822.HEADER";
1458 break;
1459 case NEED_BODY:
1460 item = "BODY.PEEK[]";
1461 resp = "BODY[]";
1462 if (m->m_flag & HAVE_HEADER && m->m_size) {
1463 char *hdr = smalloc(m->m_size);
1464 fflush(mp->mb_otf);
1465 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1466 m->m_offset), SEEK_SET) < 0 ||
1467 fread(hdr, 1, m->m_size, mp->mb_itf)
1468 != m->m_size) {
1469 free(hdr);
1470 break;
1472 head = hdr;
1473 headsize = m->m_size;
1474 headlines = m->m_lines;
1475 item = "BODY.PEEK[TEXT]";
1476 resp = "BODY[TEXT]";
1478 break;
1479 case NEED_UNSPEC:
1480 return STOP;
1482 imaplock = 1;
1483 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1484 if (sigsetjmp(imapjmp, 1)) {
1485 safe_signal(SIGINT, saveint);
1486 safe_signal(SIGPIPE, savepipe);
1487 imaplock = 0;
1488 return STOP;
1490 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1491 safe_signal(SIGINT, maincatch);
1492 if (savepipe != SIG_IGN)
1493 safe_signal(SIGPIPE, imapcatch);
1494 if (m->m_uid)
1495 snprintf(o, sizeof o,
1496 "%s UID FETCH %lu (%s)\r\n",
1497 tag(1), m->m_uid, item);
1498 else {
1499 if (check_expunged() == STOP)
1500 goto out;
1501 snprintf(o, sizeof o,
1502 "%s FETCH %u (%s)\r\n",
1503 tag(1), number, item);
1505 IMAP_OUT(o, MB_COMD, goto out)
1506 for (;;) {
1507 ok = imap_answer(mp, 1);
1508 if (ok == STOP)
1509 break;
1510 if (response_status != RESPONSE_OTHER ||
1511 response_other != MESSAGE_DATA_FETCH)
1512 continue;
1513 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1514 continue;
1515 if (m->m_uid) {
1516 if ((cp = asccasestr(responded_other_text, "UID "))) {
1517 u = atol(&cp[4]);
1518 n = 0;
1519 } else {
1520 n = -1;
1521 u = 0;
1523 } else
1524 n = responded_other_number;
1525 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1526 if (m->m_uid ? m->m_uid != u : n != number)
1527 continue;
1528 if ((cp = strchr(loc, '"')) != NULL) {
1529 cp = imap_unquotestr(cp);
1530 imap_putstr(mp, m, cp,
1531 head, headsize, headlines);
1532 } else {
1533 m->m_have |= HAVE_HEADER|HAVE_BODY;
1534 m->m_xlines = m->m_lines;
1535 m->m_xsize = m->m_size;
1537 goto out;
1539 expected = atol(&cp[1]);
1540 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1541 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1542 continue;
1544 mt = *m;
1545 imap_fetchdata(mp, &mt, expected, need,
1546 head, headsize, headlines);
1547 if (n >= 0) {
1548 commitmsg(mp, m, mt, mt.m_have);
1549 break;
1551 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1552 &mp->mb_sock) > 0) {
1553 if (verbose)
1554 fputs(imapbuf, stderr);
1555 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1556 u = atol(&cp[4]);
1557 if (u == m->m_uid) {
1558 commitmsg(mp, m, mt, mt.m_have);
1559 break;
1564 out: while (mp->mb_active & MB_COMD)
1565 ok = imap_answer(mp, 1);
1566 if (saveint != SIG_IGN)
1567 safe_signal(SIGINT, saveint);
1568 if (savepipe != SIG_IGN)
1569 safe_signal(SIGPIPE, savepipe);
1570 imaplock--;
1571 if (ok == OKAY)
1572 putcache(mp, m);
1573 free(head);
1574 if (interrupts)
1575 onintr(0);
1576 return ok;
1579 enum okay
1580 imap_header(struct message *m)
1582 return imap_get(&mb, m, NEED_HEADER);
1586 enum okay
1587 imap_body(struct message *m)
1589 return imap_get(&mb, m, NEED_BODY);
1592 static void
1593 commitmsg(struct mailbox *mp, struct message *to,
1594 struct message from, enum havespec have)
1596 to->m_size = from.m_size;
1597 to->m_lines = from.m_lines;
1598 to->m_block = from.m_block;
1599 to->m_offset = from.m_offset;
1600 to->m_have = have;
1601 if (have & HAVE_BODY) {
1602 to->m_xlines = from.m_lines;
1603 to->m_xsize = from.m_size;
1605 putcache(mp, to);
1608 static enum okay
1609 imap_fetchheaders (
1610 struct mailbox *mp,
1611 struct message *m,
1612 int bot,
1613 int top /* bot > top */
1616 char o[LINESIZE], *cp;
1617 struct message mt;
1618 size_t expected;
1619 enum okay ok;
1620 int n = 0, u;
1621 FILE *queuefp = NULL;
1623 if (m[bot].m_uid)
1624 snprintf(o, sizeof o,
1625 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1626 tag(1), m[bot-1].m_uid, m[top-1].m_uid);
1627 else {
1628 if (check_expunged() == STOP)
1629 return STOP;
1630 snprintf(o, sizeof o,
1631 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1632 tag(1), bot, top);
1634 IMAP_OUT(o, MB_COMD, return STOP)
1635 for (;;) {
1636 ok = imap_answer(mp, 1);
1637 if (response_status != RESPONSE_OTHER)
1638 break;
1639 if (response_other != MESSAGE_DATA_FETCH)
1640 continue;
1641 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1642 return STOP;
1643 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1644 continue;
1645 expected = atol(&cp[1]);
1646 if (m[bot-1].m_uid) {
1647 if ((cp=asccasestr(responded_other_text, "UID "))) {
1648 u = atoi(&cp[4]);
1649 for (n = bot; n <= top; n++)
1650 if ((unsigned long)u == m[n-1].m_uid)
1651 break;
1652 if (n > top) {
1653 imap_fetchdata(mp, NULL, expected,
1654 NEED_HEADER,
1655 NULL, 0, 0);
1656 continue;
1658 } else
1659 n = -1;
1660 } else {
1661 n = responded_other_number;
1662 if (n <= 0 || n > msgCount) {
1663 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1664 NULL, 0, 0);
1665 continue;
1668 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1669 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1670 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1671 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1672 &mp->mb_sock) > 0) {
1673 if (verbose)
1674 fputs(imapbuf, stderr);
1675 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1676 u = atoi(&cp[4]);
1677 for (n = bot; n <= top; n++)
1678 if ((unsigned long)u == m[n-1].m_uid)
1679 break;
1680 if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
1681 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1682 n = 0;
1686 while (mp->mb_active & MB_COMD)
1687 ok = imap_answer(mp, 1);
1688 return ok;
1691 void
1692 imap_getheaders(int bot, int top)
1694 sighandler_type saveint, savepipe;
1695 enum okay ok = STOP;
1696 int i, chunk = 256;
1698 (void)&saveint;
1699 (void)&savepipe;
1700 (void)&ok;
1701 (void)&bot;
1702 (void)&top;
1703 verbose = value("verbose") != NULL;
1704 if (mb.mb_type == MB_CACHE)
1705 return;
1706 if (bot < 1)
1707 bot = 1;
1708 if (top > msgCount)
1709 top = msgCount;
1710 for (i = bot; i < top; i++) {
1711 if (message[i-1].m_have & HAVE_HEADER ||
1712 getcache(&mb, &message[i-1], NEED_HEADER)
1713 == OKAY)
1714 bot = i+1;
1715 else
1716 break;
1718 for (i = top; i > bot; i--) {
1719 if (message[i-1].m_have & HAVE_HEADER ||
1720 getcache(&mb, &message[i-1], NEED_HEADER)
1721 == OKAY)
1722 top = i-1;
1723 else
1724 break;
1726 if (bot >= top)
1727 return;
1728 imaplock = 1;
1729 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1730 safe_signal(SIGINT, maincatch);
1731 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1732 if (sigsetjmp(imapjmp, 1) == 0) {
1733 if (savepipe != SIG_IGN)
1734 safe_signal(SIGPIPE, imapcatch);
1735 for (i = bot; i <= top; i += chunk) {
1736 ok = imap_fetchheaders(&mb, message, i,
1737 i+chunk-1 < top ? i+chunk-1 : top);
1738 if (interrupts)
1739 onintr(0);
1742 safe_signal(SIGINT, saveint);
1743 safe_signal(SIGPIPE, savepipe);
1744 imaplock = 0;
1747 static enum okay
1748 imap_exit(struct mailbox *mp)
1750 char o[LINESIZE];
1751 FILE *queuefp = NULL;
1753 verbose = value("verbose") != NULL;
1754 mp->mb_active |= MB_BYE;
1755 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1756 IMAP_OUT(o, MB_COMD, return STOP)
1757 IMAP_ANSWER()
1758 return OKAY;
1761 static enum okay
1762 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1764 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1765 if (mp->mb_type == MB_IMAP)
1766 delcache(mp, m);
1767 return OKAY;
1770 static enum okay
1771 imap_close(struct mailbox *mp)
1773 char o[LINESIZE];
1774 FILE *queuefp = NULL;
1776 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1777 IMAP_OUT(o, MB_COMD, return STOP)
1778 IMAP_ANSWER()
1779 return OKAY;
1782 static enum okay
1783 imap_update(struct mailbox *mp)
1785 FILE *readstat = NULL;
1786 struct message *m;
1787 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1789 verbose = value("verbose") != NULL;
1790 if (Tflag != NULL) {
1791 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
1792 Tflag = NULL;
1794 if (!edit && mp->mb_perm != 0) {
1795 holdbits();
1796 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1797 if (m->m_flag & MBOX)
1798 c++;
1800 if (c > 0)
1801 if (makembox() == STOP)
1802 goto bypass;
1804 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1805 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
1806 char *id;
1808 if ((id = hfield("message-id", m)) != NULL ||
1809 (id = hfield("article-id", m)) != NULL)
1810 fprintf(readstat, "%s\n", id);
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: if (readstat != NULL)
1886 Fclose(readstat);
1887 if (gotcha)
1888 imap_close(mp);
1889 for (m = &message[0]; m < &message[msgCount]; m++)
1890 if (!(m->m_flag&MUNLINKED) &&
1891 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1892 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1893 MDRAFT|MUNDRAFT)) {
1894 putcache(mp, m);
1895 modflags++;
1897 if ((gotcha || modflags) && edit) {
1898 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
1899 printf(value("bsdcompat") || value("bsdmsgs") ?
1900 catgets(catd, CATSET, 170, "complete\n") :
1901 catgets(catd, CATSET, 212, "updated.\n"));
1902 } else if (held && !edit && mp->mb_perm != 0) {
1903 if (held == 1)
1904 printf(catgets(catd, CATSET, 155,
1905 "Held 1 message in %s\n"), mailname);
1906 else if (held > 1)
1907 printf(catgets(catd, CATSET, 156,
1908 "Held %d messages in %s\n"), held, mailname);
1910 fflush(stdout);
1911 return OKAY;
1914 void
1915 imap_quit(void)
1917 sighandler_type saveint;
1918 sighandler_type savepipe;
1920 verbose = value("verbose") != NULL;
1921 if (mb.mb_type == MB_CACHE) {
1922 imap_update(&mb);
1923 return;
1925 if (mb.mb_sock.s_fd < 0) {
1926 fprintf(stderr, "IMAP connection closed.\n");
1927 return;
1929 imaplock = 1;
1930 saveint = safe_signal(SIGINT, SIG_IGN);
1931 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1932 if (sigsetjmp(imapjmp, 1)) {
1933 safe_signal(SIGINT, saveint);
1934 safe_signal(SIGPIPE, saveint);
1935 imaplock = 0;
1936 return;
1938 if (saveint != SIG_IGN)
1939 safe_signal(SIGINT, imapcatch);
1940 if (savepipe != SIG_IGN)
1941 safe_signal(SIGPIPE, imapcatch);
1942 imap_update(&mb);
1943 if (!same_imap_account) {
1944 imap_exit(&mb);
1945 sclose(&mb.mb_sock);
1947 safe_signal(SIGINT, saveint);
1948 safe_signal(SIGPIPE, savepipe);
1949 imaplock = 0;
1952 static enum okay
1953 imap_store(struct mailbox *mp, struct message *m, int n,
1954 int c, const char *sp, int needstat)
1956 char o[LINESIZE];
1957 FILE *queuefp = NULL;
1959 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1960 return STOP;
1961 if (m->m_uid)
1962 snprintf(o, sizeof o,
1963 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1964 tag(1), m->m_uid, c, sp);
1965 else {
1966 if (check_expunged() == STOP)
1967 return STOP;
1968 snprintf(o, sizeof o,
1969 "%s STORE %u %cFLAGS (%s)\r\n",
1970 tag(1), n, c, sp);
1972 IMAP_OUT(o, MB_COMD, return STOP)
1973 if (needstat)
1974 IMAP_ANSWER()
1975 else
1976 mb.mb_active &= ~MB_COMD;
1977 if (queuefp != NULL)
1978 Fclose(queuefp);
1979 return OKAY;
1982 enum okay
1983 imap_undelete(struct message *m, int n)
1985 return imap_unstore(m, n, "\\Deleted");
1988 enum okay
1989 imap_unread(struct message *m, int n)
1991 return imap_unstore(m, n, "\\Seen");
1994 static enum okay
1995 imap_unstore(struct message *m, int n, const char *flag)
1997 sighandler_type saveint, savepipe;
1998 enum okay ok = STOP;
2000 (void)&saveint;
2001 (void)&savepipe;
2002 (void)&ok;
2003 verbose = value("verbose") != NULL;
2004 imaplock = 1;
2005 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2006 safe_signal(SIGINT, maincatch);
2007 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2008 if (sigsetjmp(imapjmp, 1) == 0) {
2009 if (savepipe != SIG_IGN)
2010 safe_signal(SIGPIPE, imapcatch);
2011 ok = imap_store(&mb, m, n, '-', flag, 1);
2013 safe_signal(SIGINT, saveint);
2014 safe_signal(SIGPIPE, savepipe);
2015 imaplock = 0;
2016 if (interrupts)
2017 onintr(0);
2018 return ok;
2021 static const char *
2022 tag(int new)
2024 static char ts[20];
2025 static long n;
2027 if (new)
2028 n++;
2029 snprintf(ts, sizeof ts, "T%lu", n);
2030 return ts;
2033 int
2034 imap_imap(void *vp)
2036 sighandler_type saveint, savepipe;
2037 char o[LINESIZE];
2038 enum okay ok = STOP;
2039 struct mailbox *mp = &mb;
2040 FILE *queuefp = NULL;
2042 (void)&saveint;
2043 (void)&savepipe;
2044 (void)&ok;
2045 verbose = value("verbose") != NULL;
2046 if (mp->mb_type != MB_IMAP) {
2047 printf("Not operating on an IMAP mailbox.\n");
2048 return 1;
2050 imaplock = 1;
2051 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2052 safe_signal(SIGINT, maincatch);
2053 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2054 if (sigsetjmp(imapjmp, 1) == 0) {
2055 if (savepipe != SIG_IGN)
2056 safe_signal(SIGPIPE, imapcatch);
2057 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2058 IMAP_OUT(o, MB_COMD, goto out)
2059 while (mp->mb_active & MB_COMD) {
2060 ok = imap_answer(mp, 0);
2061 fputs(responded_text, stdout);
2064 out: safe_signal(SIGINT, saveint);
2065 safe_signal(SIGPIPE, savepipe);
2066 imaplock = 0;
2067 if (interrupts)
2068 onintr(0);
2069 return ok != OKAY;
2072 int
2073 imap_newmail(int autoinc)
2075 if (autoinc && had_exists < 0 && had_expunge < 0) {
2076 verbose = value("verbose") != NULL;
2077 imaplock = 1;
2078 imap_noop();
2079 imaplock = 0;
2081 if (had_exists == msgCount && had_expunge < 0)
2083 * Some servers always respond with EXISTS to NOOP. If
2084 * the mailbox has been changed but the number of messages
2085 * has not, an EXPUNGE must also had been sent; otherwise,
2086 * nothing has changed.
2088 had_exists = -1;
2089 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2092 static char *
2093 imap_putflags(int f)
2095 const char *cp;
2096 char *buf, *bp;
2098 bp = buf = salloc(100);
2099 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2100 *bp++ = '(';
2101 if (f & MREAD) {
2102 if (bp[-1] != '(')
2103 *bp++ = ' ';
2104 for (cp = "\\Seen"; *cp; cp++)
2105 *bp++ = *cp;
2107 if (f & MFLAGGED) {
2108 if (bp[-1] != '(')
2109 *bp++ = ' ';
2110 for (cp = "\\Flagged"; *cp; cp++)
2111 *bp++ = *cp;
2113 if (f & MANSWERED) {
2114 if (bp[-1] != '(')
2115 *bp++ = ' ';
2116 for (cp = "\\Answered"; *cp; cp++)
2117 *bp++ = *cp;
2119 if (f & MDRAFT) {
2120 if (bp[-1] != '(')
2121 *bp++ = ' ';
2122 for (cp = "\\Draft"; *cp; cp++)
2123 *bp++ = *cp;
2125 *bp++ = ')';
2126 *bp++ = ' ';
2128 *bp = '\0';
2129 return buf;
2132 static void
2133 imap_getflags(const char *cp, char **xp, enum mflag *f)
2135 while (*cp != ')') {
2136 if (*cp == '\\') {
2137 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2138 *f |= MREAD;
2139 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2140 *f |= MNEW;
2141 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2142 *f |= MDELETED;
2143 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2144 *f |= MFLAGGED;
2145 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2146 *f |= MANSWERED;
2147 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2148 *f |= MDRAFTED;
2150 cp++;
2152 if (xp)
2153 *xp = (char *)cp;
2156 static enum okay
2157 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2158 off_t off1, long xsize, enum mflag flag, time_t t)
2160 char o[LINESIZE];
2161 char *buf;
2162 size_t bufsize, buflen, count;
2163 enum okay ok = STOP;
2164 long size, lines, ysize;
2165 int twice = 0;
2166 FILE *queuefp = NULL;
2168 if (mp->mb_type == MB_CACHE) {
2169 queuefp = cache_queue(mp);
2170 if (queuefp == NULL)
2171 return STOP;
2172 ok = OKAY;
2174 buf = smalloc(bufsize = LINESIZE);
2175 buflen = 0;
2176 again: size = xsize;
2177 count = fsize(fp);
2178 fseek(fp, off1, SEEK_SET);
2179 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2180 tag(1), imap_quotestr(name),
2181 imap_putflags(flag),
2182 imap_make_date_time(t),
2183 size);
2184 IMAP_OUT(o, MB_COMD, goto out)
2185 while (mp->mb_active & MB_COMD) {
2186 ok = imap_answer(mp, twice);
2187 if (response_type == RESPONSE_CONT)
2188 break;
2190 if (mp->mb_type != MB_CACHE && ok == STOP) {
2191 if (twice == 0)
2192 goto trycreate;
2193 else
2194 goto out;
2196 lines = ysize = 0;
2197 while (size > 0) {
2198 fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2199 lines++;
2200 ysize += buflen;
2201 buf[buflen-1] = '\r';
2202 buf[buflen] = '\n';
2203 if (mp->mb_type != MB_CACHE)
2204 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2205 else if (queuefp)
2206 fwrite(buf, 1, buflen+1, queuefp);
2207 size -= buflen+1;
2209 if (mp->mb_type != MB_CACHE)
2210 swrite(&mp->mb_sock, "\r\n");
2211 else if (queuefp)
2212 fputs("\r\n", queuefp);
2213 while (mp->mb_active & MB_COMD) {
2214 ok = imap_answer(mp, 0);
2215 if (response_status == RESPONSE_NO /*&&
2216 ascncasecmp(responded_text,
2217 "[TRYCREATE] ", 12) == 0*/) {
2218 trycreate: if (twice++) {
2219 ok = STOP;
2220 goto out;
2222 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2223 tag(1),
2224 imap_quotestr(name));
2225 IMAP_OUT(o, MB_COMD, goto out);
2226 while (mp->mb_active & MB_COMD)
2227 ok = imap_answer(mp, 1);
2228 if (ok == STOP)
2229 goto out;
2230 imap_created_mailbox++;
2231 goto again;
2232 } else if (ok != OKAY)
2233 fprintf(stderr, "IMAP error: %s", responded_text);
2234 else if (response_status == RESPONSE_OK &&
2235 mp->mb_flags & MB_UIDPLUS)
2236 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2237 flag, name);
2239 out: if (queuefp != NULL)
2240 Fclose(queuefp);
2241 free(buf);
2242 return ok;
2245 static enum okay
2246 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2248 char *buf, *bp, *lp;
2249 size_t bufsize, buflen, count;
2250 off_t off1 = -1, offs;
2251 int inhead = 1;
2252 int flag = MNEW|MNEWEST;
2253 long size = 0;
2254 time_t tim;
2255 enum okay ok;
2257 buf = smalloc(bufsize = LINESIZE);
2258 buflen = 0;
2259 count = fsize(fp);
2260 offs = ftell(fp);
2261 time(&tim);
2262 do {
2263 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2264 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2265 if (off1 != (off_t)-1) {
2266 ok=imap_append1(mp, name, fp, off1,
2267 size, flag, tim);
2268 if (ok == STOP)
2269 return STOP;
2270 fseek(fp, offs+buflen, SEEK_SET);
2272 off1 = offs + buflen;
2273 size = 0;
2274 inhead = 1;
2275 flag = MNEW;
2276 if (bp != NULL)
2277 tim = unixtime(buf);
2278 } else
2279 size += buflen+1;
2280 offs += buflen;
2281 if (bp && buf[0] == '\n')
2282 inhead = 0;
2283 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2284 lp = &buf[6];
2285 while (whitechar(*lp&0377))
2286 lp++;
2287 if (*lp == ':')
2288 while (*++lp != '\0')
2289 switch (*lp) {
2290 case 'R':
2291 flag |= MREAD;
2292 break;
2293 case 'O':
2294 flag &= ~MNEW;
2295 break;
2297 } else if (bp && inhead &&
2298 ascncasecmp(buf, "x-status", 8) == 0) {
2299 lp = &buf[8];
2300 while (whitechar(*lp&0377))
2301 lp++;
2302 if (*lp == ':')
2303 while (*++lp != '\0')
2304 switch (*lp) {
2305 case 'F':
2306 flag |= MFLAGGED;
2307 break;
2308 case 'A':
2309 flag |= MANSWERED;
2310 break;
2311 case 'T':
2312 flag |= MDRAFTED;
2313 break;
2316 } while (bp != NULL);
2317 free(buf);
2318 return OKAY;
2321 enum okay
2322 imap_append(const char *xserver, FILE *fp)
2324 sighandler_type saveint, savepipe;
2325 char *server, *uhp, *mbx, *user;
2326 const char *sp, *cp, *pass;
2327 int use_ssl;
2328 enum okay ok = STOP;
2330 (void)&saveint;
2331 (void)&savepipe;
2332 (void)&ok;
2333 verbose = value("verbose") != NULL;
2334 server = savestr((char *)xserver);
2335 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2336 imaplock = 1;
2337 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2338 safe_signal(SIGINT, maincatch);
2339 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2340 if (sigsetjmp(imapjmp, 1))
2341 goto out;
2342 if (savepipe != SIG_IGN)
2343 safe_signal(SIGPIPE, imapcatch);
2344 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2345 mb.mb_imap_account &&
2346 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2347 ok = imap_append0(&mb, mbx, fp);
2349 else {
2350 struct mailbox mx;
2352 memset(&mx, 0, sizeof mx);
2353 if (disconnected(server) == 0) {
2354 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2355 use_ssl ? "imaps" : "imap",
2356 verbose) != OKAY)
2357 goto fail;
2358 mx.mb_sock.s_desc = "IMAP";
2359 mx.mb_type = MB_IMAP;
2360 mx.mb_imap_account = (char *)protbase(server);
2361 mx.mb_imap_mailbox = mbx;
2362 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2363 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2364 sclose(&mx.mb_sock);
2365 goto fail;
2367 ok = imap_append0(&mx, mbx, fp);
2368 imap_exit(&mx);
2369 sclose(&mx.mb_sock);
2370 } else {
2371 mx.mb_imap_account = (char *)protbase(server);
2372 mx.mb_imap_mailbox = mbx;
2373 mx.mb_type = MB_CACHE;
2374 ok = imap_append0(&mx, mbx, fp);
2376 fail:;
2378 out: safe_signal(SIGINT, saveint);
2379 safe_signal(SIGPIPE, savepipe);
2380 imaplock = 0;
2381 if (interrupts)
2382 onintr(0);
2383 return ok;
2386 static enum okay
2387 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2388 struct list_item **lend, int level)
2390 char o[LINESIZE];
2391 enum okay ok = STOP;
2392 char *cp;
2393 const char *bp;
2394 FILE *queuefp = NULL;
2395 struct list_item *lp;
2397 *list = *lend = NULL;
2398 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2399 tag(1), imap_quotestr(base));
2400 IMAP_OUT(o, MB_COMD, return STOP);
2401 while (mp->mb_active & MB_COMD) {
2402 ok = imap_answer(mp, 1);
2403 if (response_status == RESPONSE_OTHER &&
2404 response_other == MAILBOX_DATA_LIST &&
2405 imap_parse_list() == OKAY) {
2406 cp = imap_unquotestr(list_name);
2407 lp = csalloc(1, sizeof *lp);
2408 lp->l_name = cp;
2409 for (bp = base; *bp && *bp == *cp; bp++)
2410 cp++;
2411 lp->l_base = *cp ? cp : savestr(base);
2412 lp->l_attr = list_attributes;
2413 lp->l_level = level+1;
2414 lp->l_delim = list_hierarchy_delimiter;
2415 if (*list && *lend) {
2416 (*lend)->l_next = lp;
2417 *lend = lp;
2418 } else
2419 *list = *lend = lp;
2422 return ok;
2425 static enum okay
2426 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2428 struct list_item *list, *lend, *lp, *lx, *ly;
2429 int n;
2430 const char *bp;
2431 char *cp;
2432 int depth;
2434 verbose = value("verbose") != NULL;
2435 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2436 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2437 return STOP;
2438 if (list == NULL || lend == NULL)
2439 return OKAY;
2440 for (lp = list; lp; lp = lp->l_next)
2441 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2442 lp->l_level < depth &&
2443 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2444 cp = salloc((n = strlen(lp->l_name)) + 2);
2445 strcpy(cp, lp->l_name);
2446 cp[n] = lp->l_delim;
2447 cp[n+1] = '\0';
2448 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2449 lx && ly) {
2450 lp->l_has_children = 1;
2451 if (strcmp(cp, lx->l_name) == 0)
2452 lx = lx->l_next;
2453 if (lx) {
2454 lend->l_next = lx;
2455 lend = ly;
2459 for (lp = list; lp; lp = lp->l_next) {
2460 if (strip) {
2461 cp = lp->l_name;
2462 for (bp = base; *bp && *bp == *cp; bp++)
2463 cp++;
2464 } else
2465 cp = lp->l_name;
2466 if ((lp->l_attr&LIST_NOSELECT) == 0)
2467 fprintf(fp, "%s\n", *cp ? cp : base);
2468 else if (lp->l_has_children == 0)
2469 fprintf(fp, "%s%c\n", *cp ? cp : base,
2470 lp->l_delim != EOF ? lp->l_delim : '\n');
2472 return OKAY;
2475 void
2476 imap_folders(const char *name, int strip)
2478 sighandler_type saveint, savepipe;
2479 const char *fold, *cp, *sp;
2480 char *tempfn;
2481 FILE *fp;
2482 int columnize = is_a_tty[1];
2484 (void)&saveint;
2485 (void)&savepipe;
2486 (void)&fp;
2487 (void)&fold;
2488 cp = protbase(name);
2489 sp = mb.mb_imap_account;
2490 if (strcmp(cp, sp)) {
2491 fprintf(stderr, "Cannot list folders on other than the "
2492 "current IMAP account,\n\"%s\". "
2493 "Try \"folders @\".\n", sp);
2494 return;
2496 fold = protfile(name);
2497 if (columnize) {
2498 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2499 perror("tmpfile");
2500 return;
2502 rm(tempfn);
2503 Ftfree(&tempfn);
2504 } else
2505 fp = stdout;
2506 imaplock = 1;
2507 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2508 safe_signal(SIGINT, maincatch);
2509 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2510 if (sigsetjmp(imapjmp, 1))
2511 goto out;
2512 if (savepipe != SIG_IGN)
2513 safe_signal(SIGPIPE, imapcatch);
2514 if (mb.mb_type == MB_CACHE)
2515 cache_list(&mb, fold, strip, fp);
2516 else
2517 imap_list(&mb, fold, strip, fp);
2518 imaplock = 0;
2519 if (interrupts) {
2520 if (columnize)
2521 Fclose(fp);
2522 onintr(0);
2524 fflush(fp);
2525 if (columnize) {
2526 rewind(fp);
2527 if (fsize(fp) > 0)
2528 dopr(fp);
2529 else
2530 fprintf(stderr, "Folder not found.\n");
2532 out:
2533 safe_signal(SIGINT, saveint);
2534 safe_signal(SIGPIPE, savepipe);
2535 if (columnize)
2536 Fclose(fp);
2539 static void
2540 dopr(FILE *fp)
2542 char o[LINESIZE], *tempfn;
2543 int c;
2544 long n = 0, mx = 0, columns, width;
2545 FILE *out;
2547 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2548 perror("tmpfile");
2549 return;
2551 rm(tempfn);
2552 Ftfree(&tempfn);
2553 while ((c = getc(fp)) != EOF) {
2554 if (c == '\n') {
2555 if (n > mx)
2556 mx = n;
2557 n = 0;
2558 } else
2559 n++;
2561 rewind(fp);
2562 width = scrnwidth;
2563 if (mx < width / 2) {
2564 columns = width / (mx+2);
2565 snprintf(o, sizeof o,
2566 "sort | pr -%lu -w%lu -t",
2567 columns, width);
2568 } else
2569 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2570 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2571 try_pager(out);
2572 Fclose(out);
2575 static enum okay
2576 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2578 char o[LINESIZE];
2579 const char *qname;
2580 enum okay ok = STOP;
2581 int twice = 0;
2582 int stored = 0;
2583 FILE *queuefp = NULL;
2585 if (mp->mb_type == MB_CACHE) {
2586 if ((queuefp = cache_queue(mp)) == NULL)
2587 return STOP;
2588 ok = OKAY;
2590 qname = imap_quotestr(name = protfile(name));
2592 * Since it is not possible to set flags on the copy, recently
2593 * set flags must be set on the original to include it in the copy.
2595 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2596 imap_store(mp, m, n, '+', "\\Seen", 0);
2597 if (m->m_flag&MFLAG)
2598 imap_store(mp, m, n, '+', "\\Flagged", 0);
2599 if (m->m_flag&MUNFLAG)
2600 imap_store(mp, m, n, '-', "\\Flagged", 0);
2601 if (m->m_flag&MANSWER)
2602 imap_store(mp, m, n, '+', "\\Answered", 0);
2603 if (m->m_flag&MUNANSWER)
2604 imap_store(mp, m, n, '-', "\\Flagged", 0);
2605 if (m->m_flag&MDRAFT)
2606 imap_store(mp, m, n, '+', "\\Draft", 0);
2607 if (m->m_flag&MUNDRAFT)
2608 imap_store(mp, m, n, '-', "\\Draft", 0);
2609 again: if (m->m_uid)
2610 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2611 tag(1), m->m_uid, qname);
2612 else {
2613 if (check_expunged() == STOP)
2614 goto out;
2615 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2616 tag(1), n, qname);
2618 IMAP_OUT(o, MB_COMD, goto out)
2619 while (mp->mb_active & MB_COMD)
2620 ok = imap_answer(mp, twice);
2621 if (mp->mb_type == MB_IMAP &&
2622 mp->mb_flags & MB_UIDPLUS &&
2623 response_status == RESPONSE_OK)
2624 imap_copyuid(mp, m, name);
2625 if (response_status == RESPONSE_NO && twice++ == 0) {
2626 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2627 IMAP_OUT(o, MB_COMD, goto out)
2628 while (mp->mb_active & MB_COMD)
2629 ok = imap_answer(mp, 1);
2630 if (ok == OKAY) {
2631 imap_created_mailbox++;
2632 goto again;
2635 if (queuefp != NULL)
2636 Fclose(queuefp);
2638 * ... and reset the flag to its initial value so that
2639 * the 'exit' command still leaves the message unread.
2641 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2642 imap_store(mp, m, n, '-', "\\Seen", 0);
2643 stored++;
2645 if (m->m_flag&MFLAG) {
2646 imap_store(mp, m, n, '-', "\\Flagged", 0);
2647 stored++;
2649 if (m->m_flag&MUNFLAG) {
2650 imap_store(mp, m, n, '+', "\\Flagged", 0);
2651 stored++;
2653 if (m->m_flag&MANSWER) {
2654 imap_store(mp, m, n, '-', "\\Answered", 0);
2655 stored++;
2657 if (m->m_flag&MUNANSWER) {
2658 imap_store(mp, m, n, '+', "\\Answered", 0);
2659 stored++;
2661 if (m->m_flag&MDRAFT) {
2662 imap_store(mp, m, n, '-', "\\Draft", 0);
2663 stored++;
2665 if (m->m_flag&MUNDRAFT) {
2666 imap_store(mp, m, n, '+', "\\Draft", 0);
2667 stored++;
2669 if (stored) {
2670 mp->mb_active |= MB_COMD;
2671 imap_finish(mp);
2673 return ok;
2676 enum okay
2677 imap_copy(struct message *m, int n, const char *name)
2679 sighandler_type saveint, savepipe;
2680 enum okay ok = STOP;
2682 (void)&saveint;
2683 (void)&savepipe;
2684 (void)&ok;
2685 verbose = value("verbose") != NULL;
2686 imaplock = 1;
2687 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2688 safe_signal(SIGINT, maincatch);
2689 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2690 if (sigsetjmp(imapjmp, 1) == 0) {
2691 if (savepipe != SIG_IGN)
2692 safe_signal(SIGPIPE, imapcatch);
2693 ok = imap_copy1(&mb, m, n, name);
2695 safe_signal(SIGINT, saveint);
2696 safe_signal(SIGPIPE, savepipe);
2697 imaplock = 0;
2698 if (interrupts)
2699 onintr(0);
2700 return ok;
2703 static enum okay
2704 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2705 unsigned long *olduid, unsigned long *newuid)
2707 char *xp, *yp, *zp;
2709 *uidvalidity = strtoul(cp, &xp, 10);
2710 *olduid = strtoul(xp, &yp, 10);
2711 *newuid = strtoul(yp, &zp, 10);
2712 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2713 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2716 static enum okay
2717 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2718 unsigned long *uid)
2720 char *xp, *yp;
2722 *uidvalidity = strtoul(cp, &xp, 10);
2723 *uid = strtoul(xp, &yp, 10);
2724 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2725 yp > xp && *yp == ']';
2728 static enum okay
2729 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2731 const char *cp;
2732 unsigned long uidvalidity, olduid, newuid;
2733 struct mailbox xmb;
2734 struct message xm;
2736 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2737 imap_copyuid_parse(&cp[9], &uidvalidity,
2738 &olduid, &newuid) == STOP)
2739 return STOP;
2740 xmb = *mp;
2741 xmb.mb_cache_directory = NULL;
2742 xmb.mb_imap_mailbox = savestr((char *)name);
2743 xmb.mb_uidvalidity = uidvalidity;
2744 initcache(&xmb);
2745 if (m == NULL) {
2746 memset(&xm, 0, sizeof xm);
2747 xm.m_uid = olduid;
2748 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2749 return STOP;
2750 getcache(mp, &xm, NEED_HEADER);
2751 getcache(mp, &xm, NEED_BODY);
2752 } else {
2753 if ((m->m_flag & HAVE_HEADER) == 0)
2754 getcache(mp, m, NEED_HEADER);
2755 if ((m->m_flag & HAVE_BODY) == 0)
2756 getcache(mp, m, NEED_BODY);
2757 xm = *m;
2759 xm.m_uid = newuid;
2760 xm.m_flag &= ~MFULLYCACHED;
2761 putcache(&xmb, &xm);
2762 return OKAY;
2765 static enum okay
2766 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2767 long xsize, long size, long lines, int flag, const char *name)
2769 const char *cp;
2770 unsigned long uidvalidity, uid;
2771 struct mailbox xmb;
2772 struct message xm;
2774 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2775 imap_appenduid_parse(&cp[11], &uidvalidity,
2776 &uid) == STOP)
2777 return STOP;
2778 xmb = *mp;
2779 xmb.mb_cache_directory = NULL;
2780 xmb.mb_imap_mailbox = savestr((char *)name);
2781 xmb.mb_uidvalidity = uidvalidity;
2782 xmb.mb_otf = xmb.mb_itf = fp;
2783 initcache(&xmb);
2784 memset(&xm, 0, sizeof xm);
2785 xm.m_flag = (flag & MREAD) | MNEW;
2786 xm.m_time = t;
2787 xm.m_block = mailx_blockof(off1);
2788 xm.m_offset = mailx_offsetof(off1);
2789 xm.m_size = size;
2790 xm.m_xsize = xsize;
2791 xm.m_lines = xm.m_xlines = lines;
2792 xm.m_uid = uid;
2793 xm.m_have = HAVE_HEADER|HAVE_BODY;
2794 putcache(&xmb, &xm);
2795 return OKAY;
2798 static enum okay
2799 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2801 FILE *tp = NULL;
2802 time_t t;
2803 long size, xsize, ysize, lines;
2804 enum mflag flag = MNEW;
2805 char *name, *buf, *bp, *cp, *tempCopy;
2806 size_t bufsize, buflen, count;
2807 enum okay ok = STOP;
2809 buf = smalloc(bufsize = LINESIZE);
2810 buflen = 0;
2811 count = fsize(fp);
2812 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2813 goto stop;
2814 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2815 while (*bp == ' ')
2816 bp++;
2817 if ((cp = strrchr(bp, '{')) == NULL)
2818 goto stop;
2819 xsize = atol(&cp[1]) + 2;
2820 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2821 goto stop;
2822 while (*cp == ' ')
2823 cp++;
2824 if (*cp == '(') {
2825 imap_getflags(cp, &cp, &flag);
2826 while (*++cp == ' ');
2828 t = imap_read_date_time(cp);
2829 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2830 goto stop;
2831 rm(tempCopy);
2832 Ftfree(&tempCopy);
2833 size = xsize;
2834 ysize = lines = 0;
2835 while (size > 0) {
2836 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2837 goto stop;
2838 size -= buflen;
2839 buf[--buflen] = '\0';
2840 buf[buflen-1] = '\n';
2841 fwrite(buf, 1, buflen, tp);
2842 ysize += buflen;
2843 lines++;
2845 fflush(tp);
2846 rewind(tp);
2847 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2848 imap_unquotestr(name));
2849 ok = OKAY;
2850 stop: free(buf);
2851 if (tp)
2852 Fclose(tp);
2853 return ok;
2856 static enum okay
2857 imap_search2(struct mailbox *mp, struct message *m, int count,
2858 const char *spec, int f)
2860 char *o;
2861 size_t osize;
2862 FILE *queuefp = NULL;
2863 enum okay ok = STOP;
2864 int i;
2865 unsigned long n;
2866 const char *cp;
2867 char *xp, *cs, c;
2869 c = 0;
2870 for (cp = spec; *cp; cp++)
2871 c |= *cp;
2872 if (c & 0200) {
2873 cp = gettcharset();
2874 #ifdef HAVE_ICONV
2875 if (asccasecmp(cp, "utf-8")) {
2876 iconv_t it;
2877 char *sp, *nsp, *nspec;
2878 size_t sz, nsz;
2879 if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
2880 sz = strlen(spec) + 1;
2881 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2882 sp = (char *)spec;
2883 if (iconv_ft(it, &sp, &sz, &nsp, &nsz, 0)
2884 != (size_t)-1 && sz == 0) {
2885 spec = nspec;
2886 cp = "utf-8";
2888 iconv_close(it);
2891 #endif /* HAVE_ICONV */
2892 cp = imap_quotestr(cp);
2893 cs = salloc(n = strlen(cp) + 10);
2894 snprintf(cs, n, "CHARSET %s ", cp);
2895 } else
2896 cs = "";
2897 o = ac_alloc(osize = strlen(spec) + 60);
2898 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2899 IMAP_OUT(o, MB_COMD, goto out)
2900 while (mp->mb_active & MB_COMD) {
2901 ok = imap_answer(mp, 0);
2902 if (response_status == RESPONSE_OTHER &&
2903 response_other == MAILBOX_DATA_SEARCH) {
2904 xp = responded_other_text;
2905 while (*xp && *xp != '\r') {
2906 n = strtoul(xp, &xp, 10);
2907 for (i = 0; i < count; i++)
2908 if (m[i].m_uid == n &&
2909 (m[i].m_flag&MHIDDEN)
2910 == 0 &&
2911 (f == MDELETED ||
2912 (m[i].m_flag&MDELETED)
2913 == 0))
2914 mark(i+1, f);
2918 out: ac_free(o);
2919 return ok;
2922 enum okay
2923 imap_search1(const char *spec, int f)
2925 sighandler_type saveint, savepipe;
2926 enum okay ok = STOP;
2928 (void)&saveint;
2929 (void)&savepipe;
2930 (void)&ok;
2931 if (mb.mb_type != MB_IMAP)
2932 return STOP;
2933 verbose = value("verbose") != NULL;
2934 imaplock = 1;
2935 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2936 safe_signal(SIGINT, maincatch);
2937 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2938 if (sigsetjmp(imapjmp, 1) == 0) {
2939 if (savepipe != SIG_IGN)
2940 safe_signal(SIGPIPE, imapcatch);
2941 ok = imap_search2(&mb, message, msgCount, spec, f);
2943 safe_signal(SIGINT, saveint);
2944 safe_signal(SIGPIPE, savepipe);
2945 imaplock = 0;
2946 if (interrupts)
2947 onintr(0);
2948 return ok;
2951 int
2952 imap_thisaccount(const char *cp)
2954 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2955 return 0;
2956 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2957 mb.mb_imap_account == NULL)
2958 return 0;
2959 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2962 enum okay
2963 imap_remove(const char *name)
2965 sighandler_type saveint, savepipe;
2966 enum okay ok = STOP;
2968 (void)&saveint;
2969 (void)&savepipe;
2970 (void)&ok;
2971 verbose = value("verbose") != NULL;
2972 if (mb.mb_type != MB_IMAP) {
2973 fprintf(stderr, "Refusing to remove \"%s\" "
2974 "in disconnected mode.\n", name);
2975 return STOP;
2977 if (!imap_thisaccount(name)) {
2978 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2979 "server: \"%s\" not removed.\n", name);
2980 return STOP;
2982 imaplock = 1;
2983 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2984 safe_signal(SIGINT, maincatch);
2985 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2986 if (sigsetjmp(imapjmp, 1) == 0) {
2987 if (savepipe != SIG_IGN)
2988 safe_signal(SIGPIPE, imapcatch);
2989 ok = imap_remove1(&mb, protfile(name));
2991 safe_signal(SIGINT, saveint);
2992 safe_signal(SIGPIPE, savepipe);
2993 imaplock = 0;
2994 if (ok == OKAY)
2995 ok = cache_remove(name);
2996 if (interrupts)
2997 onintr(0);
2998 return ok;
3001 static enum okay
3002 imap_remove1(struct mailbox *mp, const char *name)
3004 FILE *queuefp = NULL;
3005 char *o;
3006 int os;
3007 enum okay ok = STOP;
3009 o = ac_alloc(os = 2*strlen(name) + 100);
3010 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3011 IMAP_OUT(o, MB_COMD, goto out)
3012 while (mp->mb_active & MB_COMD)
3013 ok = imap_answer(mp, 1);
3014 out: ac_free(o);
3015 return ok;
3018 enum okay
3019 imap_rename(const char *old, const char *new)
3021 sighandler_type saveint, savepipe;
3022 enum okay ok = STOP;
3024 (void)&saveint;
3025 (void)&savepipe;
3026 (void)&ok;
3027 verbose = value("verbose") != NULL;
3028 if (mb.mb_type != MB_IMAP) {
3029 fprintf(stderr, "Refusing to rename mailboxes "
3030 "in disconnected mode.\n");
3031 return STOP;
3033 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3034 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3035 "server: \"%s\" not renamed to \"%s\".\n",
3036 old, new);
3037 return STOP;
3039 imaplock = 1;
3040 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3041 safe_signal(SIGINT, maincatch);
3042 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3043 if (sigsetjmp(imapjmp, 1) == 0) {
3044 if (savepipe != SIG_IGN)
3045 safe_signal(SIGPIPE, imapcatch);
3046 ok = imap_rename1(&mb, protfile(old), protfile(new));
3048 safe_signal(SIGINT, saveint);
3049 safe_signal(SIGPIPE, savepipe);
3050 imaplock = 0;
3051 if (ok == OKAY)
3052 ok = cache_rename(old, new);
3053 if (interrupts)
3054 onintr(0);
3055 return ok;
3058 static enum okay
3059 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3061 FILE *queuefp = NULL;
3062 char *o;
3063 int os;
3064 enum okay ok = STOP;
3066 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3067 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3068 imap_quotestr(old), imap_quotestr(new));
3069 IMAP_OUT(o, MB_COMD, goto out)
3070 while (mp->mb_active & MB_COMD)
3071 ok = imap_answer(mp, 1);
3072 out: ac_free(o);
3073 return ok;
3076 enum okay
3077 imap_dequeue(struct mailbox *mp, FILE *fp)
3079 FILE *queuefp = NULL;
3080 char o[LINESIZE], *newname;
3081 char *buf, *bp, *cp, iob[4096];
3082 size_t bufsize, buflen, count;
3083 enum okay ok = OKAY, rok = OKAY;
3084 long offs, offs1, offs2, octets;
3085 int twice, gotcha = 0;
3087 buf = smalloc(bufsize = LINESIZE);
3088 buflen = 0;
3089 count = fsize(fp);
3090 while (offs1 = ftell(fp),
3091 fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
3092 != NULL) {
3093 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3094 while (*bp == ' ')
3095 bp++;
3096 twice = 0;
3097 offs = ftell(fp);
3098 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3099 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3100 cp = &bp[9];
3101 while (digitchar(*cp&0377))
3102 cp++;
3103 if (*cp != ' ')
3104 goto fail;
3105 while (*cp == ' ')
3106 cp++;
3107 if ((newname = imap_strex(cp, NULL)) == NULL)
3108 goto fail;
3109 IMAP_OUT(o, MB_COMD, continue)
3110 while (mp->mb_active & MB_COMD)
3111 ok = imap_answer(mp, twice);
3112 if (response_status == RESPONSE_NO && twice++ == 0)
3113 goto trycreate;
3114 if (response_status == RESPONSE_OK &&
3115 mp->mb_flags & MB_UIDPLUS) {
3116 imap_copyuid(mp, NULL,
3117 imap_unquotestr(newname));
3119 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3120 IMAP_OUT(o, MB_COMD, continue)
3121 while (mp->mb_active & MB_COMD)
3122 ok = imap_answer(mp, 1);
3123 if (ok == OKAY)
3124 gotcha++;
3125 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3126 if ((cp = strrchr(bp, '{')) == NULL)
3127 goto fail;
3128 octets = atol(&cp[1]) + 2;
3129 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3130 goto fail;
3131 IMAP_OUT(o, MB_COMD, continue)
3132 while (mp->mb_active & MB_COMD) {
3133 ok = imap_answer(mp, twice);
3134 if (response_type == RESPONSE_CONT)
3135 break;
3137 if (ok == STOP) {
3138 if (twice++ == 0) {
3139 fseek(fp, offs, SEEK_SET);
3140 goto trycreate;
3142 goto fail;
3144 while (octets > 0) {
3145 size_t n = (size_t)octets > sizeof iob
3146 ? sizeof iob : (size_t)octets;
3147 octets -= n;
3148 if (n != fread(iob, 1, n, fp))
3149 goto fail;
3150 swrite1(&mp->mb_sock, iob, n, 1);
3152 swrite(&mp->mb_sock, "");
3153 while (mp->mb_active & MB_COMD) {
3154 ok = imap_answer(mp, 0);
3155 if (response_status == RESPONSE_NO &&
3156 twice++ == 0) {
3157 fseek(fp, offs, SEEK_SET);
3158 goto trycreate;
3161 if (response_status == RESPONSE_OK &&
3162 mp->mb_flags & MB_UIDPLUS) {
3163 offs2 = ftell(fp);
3164 fseek(fp, offs1, SEEK_SET);
3165 if (imap_appenduid_cached(mp, fp) == STOP) {
3166 fseek(fp, offs2, SEEK_SET);
3167 goto fail;
3170 } else {
3171 fail: fprintf(stderr,
3172 "Invalid command in IMAP cache queue: \"%s\"\n",
3173 bp);
3174 rok = STOP;
3176 continue;
3177 trycreate:
3178 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3179 tag(1), newname);
3180 IMAP_OUT(o, MB_COMD, continue)
3181 while (mp->mb_active & MB_COMD)
3182 ok = imap_answer(mp, 1);
3183 if (ok == OKAY)
3184 goto again;
3186 fflush(fp);
3187 rewind(fp);
3188 ftruncate(fileno(fp), 0);
3189 if (gotcha)
3190 imap_close(mp);
3191 return rok;
3194 static char *
3195 imap_strex(const char *cp, char **xp)
3197 const char *cq;
3198 char *n;
3200 if (*cp != '"')
3201 return NULL;
3202 for (cq = &cp[1]; *cq; cq++) {
3203 if (*cq == '\\')
3204 cq++;
3205 else if (*cq == '"')
3206 break;
3208 if (*cq != '"')
3209 return NULL;
3210 n = salloc(cq - cp + 2);
3211 memcpy(n, cp, cq - cp + 1);
3212 n[cq - cp + 1] = '\0';
3213 if (xp)
3214 *xp = (char *)&cq[1];
3215 return n;
3218 static enum okay
3219 check_expunged(void)
3221 if (expunged_messages > 0) {
3222 fprintf(stderr,
3223 "Command not executed - messages have been expunged\n");
3224 return STOP;
3226 return OKAY;
3229 /*ARGSUSED*/
3230 int
3231 cconnect(void *vp)
3233 char *cp, *cq;
3234 int omsgCount = msgCount;
3235 (void)vp;
3237 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3238 fprintf(stderr, "Already connected.\n");
3239 return 1;
3241 unset_allow_undefined = 1;
3242 unset_internal("disconnected");
3243 cp = protbase(mailname);
3244 if (strncmp(cp, "imap://", 7) == 0)
3245 cp += 7;
3246 else if (strncmp(cp, "imaps://", 8) == 0)
3247 cp += 8;
3248 if ((cq = strchr(cp, ':')) != NULL)
3249 *cq = '\0';
3250 unset_internal(savecat("disconnected-", cp));
3251 unset_allow_undefined = 0;
3252 if (mb.mb_type == MB_CACHE) {
3253 imap_setfile1(mailname, 0, edit, 1);
3254 if (msgCount > omsgCount)
3255 newmailinfo(omsgCount);
3257 return 0;
3260 int
3261 cdisconnect(void *vp)
3263 int *msgvec = vp;
3265 if (mb.mb_type == MB_CACHE) {
3266 fprintf(stderr, "Not connected.\n");
3267 return 1;
3268 } else if (mb.mb_type == MB_IMAP) {
3269 if (cached_uidvalidity(&mb) == 0) {
3270 fprintf(stderr, "The current mailbox is not cached.\n");
3271 return 1;
3274 if (*msgvec)
3275 ccache(vp);
3276 assign("disconnected", "");
3277 if (mb.mb_type == MB_IMAP) {
3278 sclose(&mb.mb_sock);
3279 imap_setfile1(mailname, 0, edit, 1);
3281 return 0;
3283 int
3284 ccache(void *vp)
3286 int *msgvec = vp, *ip;
3287 struct message *mp;
3289 if (mb.mb_type != MB_IMAP) {
3290 fprintf(stderr, "Not connected to an IMAP server.\n");
3291 return 1;
3293 if (cached_uidvalidity(&mb) == 0) {
3294 fprintf(stderr, "The current mailbox is not cached.\n");
3295 return 1;
3297 for (ip = msgvec; *ip; ip++) {
3298 mp = &message[*ip-1];
3299 if (!(mp->m_have & HAVE_BODY))
3300 get_body(mp);
3302 return 0;
3304 #else /* !USE_IMAP */
3306 #include "extern.h"
3308 static void
3309 noimap(void)
3311 fprintf(stderr, catgets(catd, CATSET, 269,
3312 "No IMAP support compiled in.\n"));
3315 int
3316 imap_setfile(const char *server, int newmail, int isedit)
3318 (void)server;
3319 (void)newmail;
3320 (void)isedit;
3321 noimap();
3322 return -1;
3325 enum okay
3326 imap_header(struct message *mp)
3328 (void)mp;
3329 noimap();
3330 return STOP;
3333 enum okay
3334 imap_body(struct message *mp)
3336 (void)mp;
3337 noimap();
3338 return STOP;
3341 void
3342 imap_getheaders(int bot, int top)
3344 (void)bot;
3345 (void)top;
3348 void
3349 imap_quit(void)
3351 noimap();
3354 /*ARGSUSED*/
3355 int
3356 imap_imap(void *vp)
3358 (void)vp;
3359 noimap();
3360 return 1;
3363 /*ARGSUSED*/
3364 int
3365 imap_newmail(int dummy)
3367 (void)dummy;
3368 return 0;
3371 /*ARGSUSED*/
3372 enum okay
3373 imap_undelete(struct message *m, int n)
3375 (void)m;
3376 (void)n;
3377 return STOP;
3380 /*ARGSUSED*/
3381 enum okay
3382 imap_unread(struct message *m, int n)
3384 (void)m;
3385 (void)n;
3386 return STOP;
3389 /*ARGSUSED*/
3390 enum okay
3391 imap_append(const char *server, FILE *fp)
3393 (void)server;
3394 (void)fp;
3395 noimap();
3396 return STOP;
3399 /*ARGSUSED*/
3400 void
3401 imap_folders(const char *name, int strip)
3403 (void)name;
3404 (void)strip;
3405 noimap();
3408 /*ARGSUSED*/
3409 enum okay
3410 imap_remove(const char *name)
3412 (void)name;
3413 noimap();
3414 return STOP;
3417 /*ARGSUSED*/
3418 enum okay
3419 imap_rename(const char *old, const char *new)
3421 (void)old;
3422 (void)new;
3423 noimap();
3424 return STOP;
3427 enum okay
3428 imap_copy(struct message *m, int n, const char *name)
3430 (void)m;
3431 (void)n;
3432 (void)name;
3433 noimap();
3434 return STOP;
3437 /*ARGSUSED*/
3438 enum okay
3439 imap_search1(const char *spec, int f)
3441 (void)spec;
3442 (void)f;
3443 return STOP;
3446 int
3447 imap_thisaccount(const char *cp)
3449 (void)cp;
3450 return 0;
3453 enum okay
3454 imap_noop(void)
3456 noimap();
3457 return STOP;
3460 /*ARGSUSED*/
3461 int
3462 cconnect(void *vp)
3464 (void)vp;
3465 noimap();
3466 return 1;
3469 /*ARGSUSED*/
3470 int
3471 cdisconnect(void *vp)
3473 (void)vp;
3474 noimap();
3475 return 1;
3478 /*ARGSUSED*/
3479 int
3480 ccache(void *vp)
3482 (void)vp;
3483 noimap();
3484 return 1;
3486 #endif /* USE_IMAP */
3488 time_t
3489 imap_read_date_time(const char *cp)
3491 time_t t;
3492 int i, year, month, day, hour, minute, second;
3493 int sign = -1;
3494 char buf[3];
3497 * "25-Jul-2004 15:33:44 +0200"
3498 * | | | | | |
3499 * 0 5 10 15 20 25
3501 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3502 goto invalid;
3503 day = strtol(&cp[1], NULL, 10);
3504 for (i = 0; month_names[i]; i++)
3505 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3506 break;
3507 if (month_names[i] == NULL)
3508 goto invalid;
3509 month = i + 1;
3510 year = strtol(&cp[8], NULL, 10);
3511 hour = strtol(&cp[13], NULL, 10);
3512 minute = strtol(&cp[16], NULL, 10);
3513 second = strtol(&cp[19], NULL, 10);
3514 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3515 (time_t)-1)
3516 goto invalid;
3517 switch (cp[22]) {
3518 case '-':
3519 sign = 1;
3520 break;
3521 case '+':
3522 break;
3523 default:
3524 goto invalid;
3526 buf[2] = '\0';
3527 buf[0] = cp[23];
3528 buf[1] = cp[24];
3529 t += strtol(buf, NULL, 10) * sign * 3600;
3530 buf[0] = cp[25];
3531 buf[1] = cp[26];
3532 t += strtol(buf, NULL, 10) * sign * 60;
3533 return t;
3534 invalid:
3535 time(&t);
3536 return t;
3539 time_t
3540 imap_read_date(const char *cp)
3542 time_t t;
3543 int year, month, day, i, tzdiff;
3544 struct tm *tmptr;
3545 char *xp, *yp;
3547 if (*cp == '"')
3548 cp++;
3549 day = strtol(cp, &xp, 10);
3550 if (day <= 0 || day > 31 || *xp++ != '-')
3551 return -1;
3552 for (i = 0; month_names[i]; i++)
3553 if (ascncasecmp(xp, month_names[i], 3) == 0)
3554 break;
3555 if (month_names[i] == NULL)
3556 return -1;
3557 month = i+1;
3558 if (xp[3] != '-')
3559 return -1;
3560 year = strtol(&xp[4], &yp, 10);
3561 if (year < 1970 || year > 2037 || yp != &xp[8])
3562 return -1;
3563 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3564 return -1;
3565 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3566 return -1;
3567 tzdiff = t - mktime(gmtime(&t));
3568 tmptr = localtime(&t);
3569 if (tmptr->tm_isdst > 0)
3570 tzdiff += 3600;
3571 t -= tzdiff;
3572 return t;
3575 const char *
3576 imap_make_date_time(time_t t)
3578 static char s[30];
3579 struct tm *tmptr;
3580 int tzdiff, tzdiff_hour, tzdiff_min;
3582 tzdiff = t - mktime(gmtime(&t));
3583 tzdiff_hour = (int)(tzdiff / 60);
3584 tzdiff_min = tzdiff_hour % 60;
3585 tzdiff_hour /= 60;
3586 tmptr = localtime(&t);
3587 if (tmptr->tm_isdst > 0)
3588 tzdiff_hour++;
3589 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3590 tmptr->tm_mday,
3591 month_names[tmptr->tm_mon],
3592 tmptr->tm_year + 1900,
3593 tmptr->tm_hour,
3594 tmptr->tm_min,
3595 tmptr->tm_sec,
3596 tzdiff_hour,
3597 tzdiff_min);
3598 return s;
3601 char *
3602 imap_quotestr(const char *s)
3604 char *n, *np;
3606 np = n = salloc(2 * strlen(s) + 3);
3607 *np++ = '"';
3608 while (*s) {
3609 if (*s == '"' || *s == '\\')
3610 *np++ = '\\';
3611 *np++ = *s++;
3613 *np++ = '"';
3614 *np = '\0';
3615 return n;
3618 char *
3619 imap_unquotestr(const char *s)
3621 char *n, *np;
3623 if (*s != '"')
3624 return savestr(s);
3625 np = n = salloc(strlen(s) + 1);
3626 while (*++s) {
3627 if (*s == '\\')
3628 s++;
3629 else if (*s == '"')
3630 break;
3631 *np++ = *s;
3633 *np = '\0';
3634 return n;