Drop undocumented *broken-mbox* variable..
[s-mailx.git] / imap.c
blobe01437499cb35583ec2aa5c9edeec78607b2043d
1 /*
2 * S-nail - 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 #include "config.h"
43 * Mail -- a mail program
45 * IMAP v4r1 client following RFC 2060.
48 #include "rcv.h"
49 #include <errno.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <time.h>
54 #ifdef USE_IMAP
55 # ifdef USE_MD5
56 # include "md5.h"
57 # endif
59 #include <sys/socket.h>
60 #include <netdb.h>
61 #include <netinet/in.h>
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
64 #endif /* HAVE_ARPA_INET_H */
66 #include "extern.h"
68 static int verbose;
70 #define IMAP_ANSWER() { \
71 if (mp->mb_type != MB_CACHE) { \
72 enum okay ok = OKAY; \
73 while (mp->mb_active & MB_COMD) \
74 ok = imap_answer(mp, 1); \
75 if (ok == STOP) \
76 return STOP; \
77 } \
79 #define IMAP_OUT(x, y, action) \
80 { \
81 if (mp->mb_type != MB_CACHE) { \
82 if (imap_finish(mp) == STOP) \
83 return STOP; \
84 if (verbose) \
85 fprintf(stderr, ">>> %s", x); \
86 mp->mb_active |= (y); \
87 if (swrite(&mp->mb_sock, x) == STOP) \
88 action; \
89 } else { \
90 if (queuefp != NULL) \
91 fputs(x, queuefp); \
92 } \
95 static struct record {
96 struct record *rec_next;
97 unsigned long rec_count;
98 enum rec_type {
99 REC_EXISTS,
100 REC_EXPUNGE
101 } rec_type;
102 } *record, *recend;
104 static enum {
105 RESPONSE_TAGGED,
106 RESPONSE_DATA,
107 RESPONSE_FATAL,
108 RESPONSE_CONT,
109 RESPONSE_ILLEGAL
110 } response_type;
112 static enum {
113 RESPONSE_OK,
114 RESPONSE_NO,
115 RESPONSE_BAD,
116 RESPONSE_PREAUTH,
117 RESPONSE_BYE,
118 RESPONSE_OTHER,
119 RESPONSE_UNKNOWN
120 } response_status;
122 static char *responded_tag;
123 static char *responded_text;
124 static char *responded_other_text;
125 static long responded_other_number;
127 static enum {
128 MAILBOX_DATA_FLAGS,
129 MAILBOX_DATA_LIST,
130 MAILBOX_DATA_LSUB,
131 MAILBOX_DATA_MAILBOX,
132 MAILBOX_DATA_SEARCH,
133 MAILBOX_DATA_STATUS,
134 MAILBOX_DATA_EXISTS,
135 MAILBOX_DATA_RECENT,
136 MESSAGE_DATA_EXPUNGE,
137 MESSAGE_DATA_FETCH,
138 CAPABILITY_DATA,
139 RESPONSE_OTHER_UNKNOWN
140 } response_other;
142 static enum list_attributes {
143 LIST_NONE = 000,
144 LIST_NOINFERIORS = 001,
145 LIST_NOSELECT = 002,
146 LIST_MARKED = 004,
147 LIST_UNMARKED = 010
148 } list_attributes;
150 static int list_hierarchy_delimiter;
151 static char *list_name;
153 struct list_item {
154 struct list_item *l_next;
155 char *l_name;
156 char *l_base;
157 enum list_attributes l_attr;
158 int l_delim;
159 int l_level;
160 int l_has_children;
163 static char *imapbuf;
164 static size_t imapbufsize;
165 static sigjmp_buf imapjmp;
166 static sighandler_type savealrm;
167 static int reset_tio;
168 static struct termios otio;
169 static int imapkeepalive;
170 static long had_exists = -1;
171 static long had_expunge = -1;
172 static long expunged_messages;
173 static volatile int imaplock;
175 static int same_imap_account;
177 static void imap_other_get(char *pp);
178 static void imap_response_get(const char **cp);
179 static void imap_response_parse(void);
180 static enum okay imap_answer(struct mailbox *mp, int errprnt);
181 static enum okay imap_parse_list(void);
182 static enum okay imap_finish(struct mailbox *mp);
183 static void imap_timer_off(void);
184 static void imapcatch(int s);
185 static void maincatch(int s);
186 static enum okay imap_noop1(struct mailbox *mp);
187 static void rec_queue(enum rec_type type, unsigned long count);
188 static enum okay rec_dequeue(void);
189 static void rec_rmqueue(void);
190 static void imapalarm(int s);
191 static int imap_use_starttls(const char *uhp);
192 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
193 const char *uhp);
194 static enum okay imap_capability(struct mailbox *mp);
195 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
196 char *xuser, const char *pass);
197 #ifdef USE_MD5
198 static enum okay imap_cram_md5(struct mailbox *mp,
199 char *xuser, const char *xpass);
200 #endif
201 static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass);
202 #ifdef USE_GSSAPI
203 static enum okay imap_gss(struct mailbox *mp, char *user);
204 #endif /* USE_GSSAPI */
205 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
206 static void imap_init(struct mailbox *mp, int n);
207 static void imap_setptr(struct mailbox *mp, int newmail, int transparent,
208 int *prevcount);
209 static char *imap_have_password(const char *server);
210 static void imap_split(char **server, const char **sp, int *use_ssl,
211 const char **cp, char **uhp, char **mbx,
212 const char **pass, char **user);
213 static int imap_setfile1(const char *xserver, int newmail, int isedit,
214 int transparent);
215 static int imap_fetchdata(struct mailbox *mp, struct message *m,
216 size_t expected, int need,
217 const char *head, size_t headsize, long headlines);
218 static void imap_putstr(struct mailbox *mp, struct message *m,
219 const char *str,
220 const char *head, size_t headsize, long headlines);
221 static enum okay imap_get(struct mailbox *mp, struct message *m,
222 enum needspec need);
223 static void commitmsg(struct mailbox *mp, struct message *to,
224 struct message from, enum havespec have);
225 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
226 int bot, int top);
227 static enum okay imap_exit(struct mailbox *mp);
228 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
229 needstat);
230 static enum okay imap_close(struct mailbox *mp);
231 static enum okay imap_update(struct mailbox *mp);
232 static enum okay imap_store(struct mailbox *mp, struct message *m,
233 int n, int c, const char *sp, int needstat);
234 static enum okay imap_unstore(struct message *m, int n, const char *flag);
235 static const char *tag(int new);
236 static char *imap_putflags(int f);
237 static void imap_getflags(const char *cp, char **xp, enum mflag *f);
238 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
239 off_t off1, long xsize, enum mflag flag, time_t t);
240 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
241 static enum okay imap_list1(struct mailbox *mp, const char *base,
242 struct list_item **list, struct list_item **lend, int level);
243 static enum okay imap_list(struct mailbox *mp, const char *base,
244 int strip, FILE *fp);
245 static void dopr(FILE *fp);
246 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
247 const char *name);
248 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
249 unsigned long *olduid, unsigned long *newuid);
250 static enum okay imap_appenduid_parse(const char *cp,
251 unsigned long *uidvalidity, unsigned long *uid);
252 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
253 const char *name);
254 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
255 long off1, long xsize, long size, long lines,
256 int flag, const char *name);
257 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
258 static enum okay imap_search2(struct mailbox *mp, struct message *m,
259 int count, const char *spec, int f);
260 static enum okay imap_remove1(struct mailbox *mp, const char *name);
261 static enum okay imap_rename1(struct mailbox *mp, const char *old,
262 const char *new);
263 static char *imap_strex(const char *cp, char **xp);
264 static enum okay check_expunged(void);
266 static void
267 imap_other_get(char *pp)
269 char *xp;
271 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
272 pp += 6;
273 response_other = MAILBOX_DATA_FLAGS;
274 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
275 pp += 5;
276 response_other = MAILBOX_DATA_LIST;
277 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
278 pp += 5;
279 response_other = MAILBOX_DATA_LSUB;
280 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
281 pp += 8;
282 response_other = MAILBOX_DATA_MAILBOX;
283 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
284 pp += 7;
285 response_other = MAILBOX_DATA_SEARCH;
286 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
287 pp += 7;
288 response_other = MAILBOX_DATA_STATUS;
289 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
290 pp += 11;
291 response_other = CAPABILITY_DATA;
292 } else {
293 responded_other_number = strtol(pp, &xp, 10);
294 while (*xp == ' ')
295 xp++;
296 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
297 response_other = MAILBOX_DATA_EXISTS;
298 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
299 response_other = MAILBOX_DATA_RECENT;
300 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
301 response_other = MESSAGE_DATA_EXPUNGE;
302 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
303 pp = &xp[6];
304 response_other = MESSAGE_DATA_FETCH;
305 } else
306 response_other = RESPONSE_OTHER_UNKNOWN;
308 responded_other_text = pp;
311 static void
312 imap_response_get(const char **cp)
314 if (ascncasecmp(*cp, "OK ", 3) == 0) {
315 *cp += 3;
316 response_status = RESPONSE_OK;
317 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
318 *cp += 3;
319 response_status = RESPONSE_NO;
320 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
321 *cp += 4;
322 response_status = RESPONSE_BAD;
323 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
324 *cp += 8;
325 response_status = RESPONSE_PREAUTH;
326 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
327 *cp += 4;
328 response_status = RESPONSE_BYE;
329 } else
330 response_status = RESPONSE_OTHER;
333 static void
334 imap_response_parse(void)
336 static char *parsebuf;
337 static size_t parsebufsize;
338 const char *ip = imapbuf;
339 char *pp;
341 if (parsebufsize < imapbufsize) {
342 free(parsebuf);
343 parsebuf = smalloc(parsebufsize = imapbufsize);
345 strcpy(parsebuf, imapbuf);
346 pp = parsebuf;
347 switch (*ip) {
348 case '+':
349 response_type = RESPONSE_CONT;
350 ip++;
351 pp++;
352 while (*ip == ' ') {
353 ip++;
354 pp++;
356 break;
357 case '*':
358 ip++;
359 pp++;
360 while (*ip == ' ') {
361 ip++;
362 pp++;
364 imap_response_get(&ip);
365 pp = &parsebuf[ip - imapbuf];
366 switch (response_status) {
367 case RESPONSE_BYE:
368 response_type = RESPONSE_FATAL;
369 break;
370 default:
371 response_type = RESPONSE_DATA;
373 break;
374 default:
375 responded_tag = parsebuf;
376 while (*pp && *pp != ' ')
377 pp++;
378 if (*pp == '\0') {
379 response_type = RESPONSE_ILLEGAL;
380 break;
382 *pp++ = '\0';
383 while (*pp && *pp == ' ')
384 pp++;
385 if (*pp == '\0') {
386 response_type = RESPONSE_ILLEGAL;
387 break;
389 ip = &imapbuf[pp - parsebuf];
390 response_type = RESPONSE_TAGGED;
391 imap_response_get(&ip);
392 pp = &parsebuf[ip - imapbuf];
394 responded_text = pp;
395 if (response_type != RESPONSE_CONT &&
396 response_type != RESPONSE_ILLEGAL &&
397 response_status == RESPONSE_OTHER)
398 imap_other_get(pp);
401 static enum okay
402 imap_answer(struct mailbox *mp, int errprnt)
404 int i, complete;
405 enum okay ok = STOP;
407 if (mp->mb_type == MB_CACHE)
408 return OKAY;
409 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
410 if (verbose)
411 fputs(imapbuf, stderr);
412 imap_response_parse();
413 if (response_type == RESPONSE_ILLEGAL)
414 goto again;
415 if (response_type == RESPONSE_CONT)
416 return OKAY;
417 if (response_status == RESPONSE_OTHER) {
418 if (response_other == MAILBOX_DATA_EXISTS) {
419 had_exists = responded_other_number;
420 rec_queue(REC_EXISTS, responded_other_number);
421 if (had_expunge > 0)
422 had_expunge = 0;
423 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
424 rec_queue(REC_EXPUNGE, responded_other_number);
425 if (had_expunge < 0)
426 had_expunge = 0;
427 had_expunge++;
428 expunged_messages++;
431 complete = 0;
432 if (response_type == RESPONSE_TAGGED) {
433 if (asccasecmp(responded_tag, tag(0)) == 0)
434 complete |= 1;
435 else
436 goto again;
438 switch (response_status) {
439 case RESPONSE_PREAUTH:
440 mp->mb_active &= ~MB_PREAUTH;
441 /*FALLTHRU*/
442 case RESPONSE_OK:
443 okay: ok = OKAY;
444 complete |= 2;
445 break;
446 case RESPONSE_NO:
447 case RESPONSE_BAD:
448 stop: ok = STOP;
449 complete |= 2;
450 if (errprnt)
451 fprintf(stderr, catgets(catd, CATSET, 218,
452 "IMAP error: %s"), responded_text);
453 break;
454 case RESPONSE_UNKNOWN: /* does not happen */
455 case RESPONSE_BYE:
456 i = mp->mb_active;
457 mp->mb_active = MB_NONE;
458 if (i & MB_BYE)
459 goto okay;
460 else
461 goto stop;
462 case RESPONSE_OTHER:
463 ok = OKAY;
465 if (response_status != RESPONSE_OTHER &&
466 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
467 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
468 if (complete == 3)
469 mp->mb_active &= ~MB_COMD;
470 } else {
471 ok = STOP;
472 mp->mb_active = MB_NONE;
474 return ok;
477 static enum okay
478 imap_parse_list(void)
480 char *cp;
482 cp = responded_other_text;
483 list_attributes = LIST_NONE;
484 if (*cp == '(') {
485 while (*cp && *cp != ')') {
486 if (*cp == '\\') {
487 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
488 == 0) {
489 list_attributes |= LIST_NOINFERIORS;
490 cp += 12;
491 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
492 == 0) {
493 list_attributes |= LIST_NOSELECT;
494 cp += 9;
495 } else if (ascncasecmp(&cp[1], "Marked ", 7)
496 == 0) {
497 list_attributes |= LIST_MARKED;
498 cp += 7;
499 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
500 == 0) {
501 list_attributes |= LIST_UNMARKED;
502 cp += 9;
505 cp++;
507 if (*++cp != ' ')
508 return STOP;
509 while (*cp == ' ')
510 cp++;
512 list_hierarchy_delimiter = EOF;
513 if (*cp == '"') {
514 if (*++cp == '\\')
515 cp++;
516 list_hierarchy_delimiter = *cp++ & 0377;
517 if (cp[0] != '"' || cp[1] != ' ')
518 return STOP;
519 cp++;
520 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
521 cp[3] == ' ') {
522 list_hierarchy_delimiter = EOF;
523 cp += 3;
525 while (*cp == ' ')
526 cp++;
527 list_name = cp;
528 while (*cp && *cp != '\r')
529 cp++;
530 *cp = '\0';
531 return OKAY;
534 static enum okay
535 imap_finish(struct mailbox *mp)
537 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
538 imap_answer(mp, 1);
539 return OKAY;
542 static void
543 imap_timer_off(void)
545 if (imapkeepalive > 0) {
546 alarm(0);
547 safe_signal(SIGALRM, savealrm);
551 static void
552 imapcatch(int s)
554 if (reset_tio)
555 tcsetattr(0, TCSADRAIN, &otio);
556 switch (s) {
557 case SIGINT:
558 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
559 siglongjmp(imapjmp, 1);
560 /*NOTREACHED*/
561 case SIGPIPE:
562 fprintf(stderr, "Received SIGPIPE during IMAP operation\n");
563 break;
567 static void
568 maincatch(int s)
570 (void)s;
571 if (interrupts++ == 0) {
572 fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
573 return;
575 onintr(0);
578 static enum okay
579 imap_noop1(struct mailbox *mp)
581 char o[LINESIZE];
582 FILE *queuefp = NULL;
584 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
585 IMAP_OUT(o, MB_COMD, return STOP)
586 IMAP_ANSWER()
587 return OKAY;
590 enum okay
591 imap_noop(void)
593 sighandler_type saveint, savepipe;
594 enum okay ok = STOP;
596 (void)&saveint;
597 (void)&savepipe;
598 (void)&ok;
599 if (mb.mb_type != MB_IMAP)
600 return STOP;
601 verbose = value("verbose") != NULL;
602 imaplock = 1;
603 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
604 safe_signal(SIGINT, maincatch);
605 savepipe = safe_signal(SIGPIPE, SIG_IGN);
606 if (sigsetjmp(imapjmp, 1) == 0) {
607 if (savepipe != SIG_IGN)
608 safe_signal(SIGPIPE, imapcatch);
609 ok = imap_noop1(&mb);
611 safe_signal(SIGINT, saveint);
612 safe_signal(SIGPIPE, savepipe);
613 imaplock = 0;
614 if (interrupts)
615 onintr(0);
616 return ok;
619 static void
620 rec_queue(enum rec_type type, unsigned long count)
622 struct record *rp;
624 rp = scalloc(1, sizeof *rp);
625 rp->rec_type = type;
626 rp->rec_count = count;
627 if (record && recend) {
628 recend->rec_next = rp;
629 recend = rp;
630 } else
631 record = recend = rp;
634 static enum okay
635 rec_dequeue(void)
637 struct message *omessage;
638 enum okay ok = OKAY;
639 struct record *rp = record, *rq = NULL;
640 unsigned long exists = 0, i;
642 if (record == NULL)
643 return STOP;
644 omessage = message;
645 message = smalloc((msgCount+1) * sizeof *message);
646 if (msgCount)
647 memcpy(message, omessage, msgCount * sizeof *message);
648 memset(&message[msgCount], 0, sizeof *message);
649 while (rp) {
650 switch (rp->rec_type) {
651 case REC_EXISTS:
652 exists = rp->rec_count;
653 break;
654 case REC_EXPUNGE:
655 if (rp->rec_count == 0) {
656 ok = STOP;
657 break;
659 if (rp->rec_count > (unsigned long)msgCount) {
660 if (exists == 0 || rp->rec_count > exists--)
661 ok = STOP;
662 break;
664 if (exists > 0)
665 exists--;
666 delcache(&mb, &message[rp->rec_count-1]);
667 memmove(&message[rp->rec_count-1],
668 &message[rp->rec_count],
669 (msgCount - rp->rec_count + 1) *
670 sizeof *message);
671 msgCount--;
673 * If the message was part of a collapsed thread,
674 * the m_collapsed field of one of its ancestors
675 * should be incremented. It seems hardly possible
676 * to do this with the current message structure,
677 * though. The result is that a '+' may be shown
678 * in the header summary even if no collapsed
679 * children exists.
681 break;
683 free(rq);
684 rq = rp;
685 rp = rp->rec_next;
687 free(rq);
688 record = recend = NULL;
689 if (ok == OKAY && exists > (unsigned long)msgCount) {
690 message = srealloc(message,
691 (exists + 1) * sizeof *message);
692 memset(&message[msgCount], 0,
693 (exists - msgCount + 1) * sizeof *message);
694 for (i = msgCount; i < exists; i++)
695 imap_init(&mb, i);
696 imap_flags(&mb, msgCount+1, exists);
697 msgCount = exists;
699 if (ok == STOP) {
700 free(message);
701 message = omessage;
703 return ok;
706 static void
707 rec_rmqueue(void)
709 struct record *rp, *rq = NULL;
711 for (rp = record; rp; rp = rp->rec_next) {
712 free(rq);
713 rq = rp;
715 free(rq);
716 record = recend = NULL;
719 /*ARGSUSED*/
720 static void
721 imapalarm(int s)
723 sighandler_type saveint;
724 sighandler_type savepipe;
725 (void)s;
727 if (imaplock++ == 0) {
728 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
729 safe_signal(SIGINT, maincatch);
730 savepipe = safe_signal(SIGPIPE, SIG_IGN);
731 if (sigsetjmp(imapjmp, 1)) {
732 safe_signal(SIGINT, saveint);
733 safe_signal(SIGPIPE, savepipe);
734 goto brk;
736 if (savepipe != SIG_IGN)
737 safe_signal(SIGPIPE, imapcatch);
738 if (imap_noop1(&mb) != OKAY) {
739 safe_signal(SIGINT, saveint);
740 safe_signal(SIGPIPE, savepipe);
741 goto out;
743 safe_signal(SIGINT, saveint);
744 safe_signal(SIGPIPE, savepipe);
746 brk: alarm(imapkeepalive);
747 out: imaplock--;
750 static int
751 imap_use_starttls(const char *uhp)
753 char *var;
755 if (value("imap-use-starttls"))
756 return 1;
757 var = savecat("imap-use-starttls-", uhp);
758 return value(var) != NULL;
761 static enum okay
762 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
764 char *server, *cp;
766 mp->mb_active |= MB_PREAUTH;
767 imap_answer(mp, 1);
768 if ((cp = strchr(xserver, ':')) != NULL) {
769 server = salloc(cp - xserver + 1);
770 memcpy(server, xserver, cp - xserver);
771 server[cp - xserver] = '\0';
772 } else
773 server = (char *)xserver;
774 #ifdef USE_SSL
775 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
776 FILE *queuefp = NULL;
777 char o[LINESIZE];
779 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
780 IMAP_OUT(o, MB_COMD, return STOP);
781 IMAP_ANSWER()
782 if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
783 return STOP;
785 #else /* !USE_SSL */
786 if (imap_use_starttls(uhp)) {
787 fprintf(stderr, "No SSL support compiled in.\n");
788 return STOP;
790 #endif /* !USE_SSL */
791 imap_capability(mp);
792 return OKAY;
795 static enum okay
796 imap_capability(struct mailbox *mp)
798 char o[LINESIZE];
799 FILE *queuefp = NULL;
800 enum okay ok = STOP;
801 const char *cp;
803 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
804 IMAP_OUT(o, MB_COMD, return STOP)
805 while (mp->mb_active & MB_COMD) {
806 ok = imap_answer(mp, 0);
807 if (response_status == RESPONSE_OTHER &&
808 response_other == CAPABILITY_DATA) {
809 cp = responded_other_text;
810 while (*cp) {
811 while (spacechar(*cp&0377))
812 cp++;
813 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
814 spacechar(cp[7]&0377))
815 /* RFC 2359 */
816 mp->mb_flags |= MB_UIDPLUS;
817 while (*cp && !spacechar(*cp&0377))
818 cp++;
822 return ok;
825 static enum okay
826 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass)
828 char *var;
829 char *auth;
831 if (!(mp->mb_active & MB_PREAUTH))
832 return OKAY;
833 if ((auth = value("imap-auth")) == NULL) {
834 var = ac_alloc(strlen(uhp) + 11);
835 strcpy(var, "imap-auth-");
836 strcpy(&var[10], uhp);
837 auth = value(var);
838 ac_free(var);
840 if (auth == NULL || strcmp(auth, "login") == 0)
841 return imap_login(mp, xuser, pass);
842 if (strcmp(auth, "cram-md5") == 0) {
843 #ifdef USE_MD5
844 return imap_cram_md5(mp, xuser, pass);
845 #else
846 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
847 return (STOP);
848 #endif
850 if (strcmp(auth, "gssapi") == 0) {
851 #ifdef USE_GSSAPI
852 return imap_gss(mp, xuser);
853 #else /* !USE_GSSAPI */
854 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
855 return STOP;
856 #endif /* !USE_GSSAPI */
858 fprintf(stderr, tr(273, "Unknown IMAP authentication method: %s\n"),
859 auth);
860 return STOP;
864 * Implementation of RFC 2194.
866 #ifdef USE_MD5
867 static enum okay
868 imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
870 char o[LINESIZE];
871 const char *user, *pass;
872 char *cp;
873 FILE *queuefp = NULL;
874 enum okay ok = STOP;
876 retry: if (xuser == NULL) {
877 if ((user = getuser()) == NULL)
878 return STOP;
879 } else
880 user = xuser;
881 if (xpass == NULL) {
882 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
883 return STOP;
884 } else
885 pass = xpass;
886 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
887 IMAP_OUT(o, 0, return STOP)
888 imap_answer(mp, 1);
889 if (response_type != RESPONSE_CONT)
890 return STOP;
891 cp = cram_md5_string(user, pass, responded_text);
892 IMAP_OUT(cp, MB_COMD, return STOP)
893 while (mp->mb_active & MB_COMD)
894 ok = imap_answer(mp, 1);
895 if (ok == STOP) {
896 xpass = NULL;
897 goto retry;
899 return ok;
901 #endif /* USE_MD5 */
903 static enum okay
904 imap_login(struct mailbox *mp, char *xuser, const char *xpass)
906 char o[LINESIZE];
907 const char *user, *pass;
908 FILE *queuefp = NULL;
909 enum okay ok = STOP;
911 retry: if (xuser == NULL) {
912 if ((user = getuser()) == NULL)
913 return STOP;
914 } else
915 user = xuser;
916 if (xpass == NULL) {
917 if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
918 return STOP;
919 } else
920 pass = xpass;
921 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
922 tag(1), imap_quotestr(user), imap_quotestr(pass));
923 IMAP_OUT(o, MB_COMD, return STOP)
924 while (mp->mb_active & MB_COMD)
925 ok = imap_answer(mp, 1);
926 if (ok == STOP) {
927 xpass = NULL;
928 goto retry;
930 return OKAY;
933 #ifdef USE_GSSAPI
934 #include "imap_gssapi.c"
935 #endif /* USE_GSSAPI */
937 enum okay
938 imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
940 enum okay ok = OKAY;
941 char *cp;
942 char o[LINESIZE];
943 FILE *queuefp = NULL;
944 (void)size;
946 mp->mb_uidvalidity = 0;
947 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
948 IMAP_OUT(o, MB_COMD, return STOP)
949 while (mp->mb_active & MB_COMD) {
950 ok = imap_answer(mp, 1);
951 if (response_status != RESPONSE_OTHER &&
952 (cp = asccasestr(responded_text,
953 "[UIDVALIDITY ")) != NULL)
954 mp->mb_uidvalidity = atol(&cp[13]);
956 *count = had_exists > 0 ? had_exists : 0;
957 if (response_status != RESPONSE_OTHER &&
958 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
959 == 0)
960 mp->mb_perm = 0;
961 return ok;
964 static enum okay
965 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
967 char o[LINESIZE];
968 FILE *queuefp = NULL;
969 char *cp;
970 struct message *m;
971 unsigned x = X, y = Y, n;
973 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
974 IMAP_OUT(o, MB_COMD, return STOP)
975 while (mp->mb_active & MB_COMD) {
976 imap_answer(mp, 1);
977 if (response_status == RESPONSE_OTHER &&
978 response_other == MESSAGE_DATA_FETCH) {
979 n = responded_other_number;
980 if (n < x || n > y)
981 continue;
982 m = &message[n-1];
983 m->m_xsize = 0;
984 } else
985 continue;
986 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
987 cp += 5;
988 while (*cp == ' ')
989 cp++;
990 if (*cp == '(')
991 imap_getflags(cp, &cp, &m->m_flag);
993 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
994 m->m_uid = strtoul(&cp[4], NULL, 10);
995 getcache1(mp, m, NEED_UNSPEC, 1);
996 m->m_flag &= ~MHIDDEN;
998 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
999 x++;
1000 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1001 y--;
1002 if (x <= y) {
1003 snprintf(o, sizeof o,
1004 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1005 tag(1), x, y);
1006 IMAP_OUT(o, MB_COMD, return STOP)
1007 while (mp->mb_active & MB_COMD) {
1008 imap_answer(mp, 1);
1009 if (response_status == RESPONSE_OTHER &&
1010 response_other == MESSAGE_DATA_FETCH) {
1011 n = responded_other_number;
1012 if (n < x || n > y)
1013 continue;
1014 m = &message[n-1];
1015 } else
1016 continue;
1017 if ((cp = asccasestr(responded_other_text,
1018 "RFC822.SIZE ")) != NULL)
1019 m->m_xsize = strtol(&cp[12], NULL, 10);
1020 if ((cp = asccasestr(responded_other_text,
1021 "INTERNALDATE ")) != NULL)
1022 m->m_time = imap_read_date_time(&cp[13]);
1025 for (n = X; n <= Y; n++)
1026 putcache(mp, &message[n-1]);
1027 return OKAY;
1030 static void
1031 imap_init(struct mailbox *mp, int n)
1033 struct message *m = &message[n];
1034 (void)mp;
1036 m->m_flag = MUSED|MNOFROM;
1037 m->m_block = 0;
1038 m->m_offset = 0;
1041 static void
1042 imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
1044 struct message *omessage = 0;
1045 int i, omsgCount = 0;
1046 enum okay dequeued = STOP;
1048 if (newmail || transparent) {
1049 omessage = message;
1050 omsgCount = msgCount;
1052 if (newmail)
1053 dequeued = rec_dequeue();
1054 if (had_exists >= 0) {
1055 if (dequeued != OKAY)
1056 msgCount = had_exists;
1057 had_exists = -1;
1059 if (had_expunge >= 0) {
1060 if (dequeued != OKAY)
1061 msgCount -= had_expunge;
1062 had_expunge = -1;
1064 if (newmail && expunged_messages)
1065 printf("Expunged %ld message%s.\n",
1066 expunged_messages,
1067 expunged_messages != 1 ? "s" : "");
1068 *prevcount = omsgCount - expunged_messages;
1069 expunged_messages = 0;
1070 if (msgCount < 0) {
1071 fputs("IMAP error: Negative message count\n", stderr);
1072 msgCount = 0;
1074 if (dequeued != OKAY) {
1075 message = scalloc(msgCount + 1, sizeof *message);
1076 for (i = 0; i < msgCount; i++)
1077 imap_init(mp, i);
1078 if (!newmail && mp->mb_type == MB_IMAP)
1079 initcache(mp);
1080 if (msgCount > 0)
1081 imap_flags(mp, 1, msgCount);
1082 message[msgCount].m_size = 0;
1083 message[msgCount].m_lines = 0;
1084 rec_rmqueue();
1086 if (newmail || transparent)
1087 transflags(omessage, omsgCount, transparent);
1088 else
1089 setdot(message);
1092 static char *
1093 imap_have_password(const char *server)
1095 char *var, *cp;
1097 var = ac_alloc(strlen(server) + 10);
1098 strcpy(var, "password-");
1099 strcpy(&var[9], server);
1100 if ((cp = value(var)) != NULL)
1101 cp = savestr(cp);
1102 ac_free(var);
1103 return cp;
1106 static void
1107 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1108 char **uhp, char **mbx, const char **pass, char **user)
1110 *sp = *server;
1111 if (strncmp(*sp, "imap://", 7) == 0) {
1112 *sp = &(*sp)[7];
1113 *use_ssl = 0;
1114 #ifdef USE_SSL
1115 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1116 *sp = &(*sp)[8];
1117 *use_ssl = 1;
1118 #endif /* USE_SSL */
1120 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1121 *uhp = savestr((char *)(*sp));
1122 (*uhp)[*cp - *sp] = '\0';
1123 *mbx = (char *)&(*cp)[1];
1124 } else {
1125 if (*cp)
1126 (*server)[*cp - *server] = '\0';
1127 *uhp = (char *)(*sp);
1128 *mbx = "INBOX";
1130 *pass = imap_have_password(*uhp);
1131 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1132 *user = salloc(*cp - *uhp + 1);
1133 memcpy(*user, *uhp, *cp - *uhp);
1134 (*user)[*cp - *uhp] = '\0';
1135 *sp = &(*cp)[1];
1136 *user = strdec(*user);
1137 } else {
1138 *user = NULL;
1139 *sp = *uhp;
1143 int
1144 imap_setfile(const char *xserver, int newmail, int isedit)
1146 return imap_setfile1(xserver, newmail, isedit, 0);
1149 static int
1150 imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
1152 struct sock so;
1153 sighandler_type saveint;
1154 sighandler_type savepipe;
1155 char *server, *user, *account;
1156 const char *cp, *sp, *pass;
1157 char *uhp, *mbx;
1158 int use_ssl = 0;
1159 enum mbflags same_flags;
1160 int prevcount = 0;
1162 (void)&sp;
1163 (void)&use_ssl;
1164 (void)&saveint;
1165 (void)&savepipe;
1166 server = savestr((char *)xserver);
1167 verbose = value("verbose") != NULL;
1168 if (newmail) {
1169 saveint = safe_signal(SIGINT, SIG_IGN);
1170 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1171 if (saveint != SIG_IGN)
1172 safe_signal(SIGINT, imapcatch);
1173 if (savepipe != SIG_IGN)
1174 safe_signal(SIGPIPE, imapcatch);
1175 imaplock = 1;
1176 goto newmail;
1178 same_flags = mb.mb_flags;
1179 same_imap_account = 0;
1180 sp = protbase(server);
1181 if (mb.mb_imap_account) {
1182 if (mb.mb_sock.s_fd > 0 &&
1183 strcmp(mb.mb_imap_account, sp) == 0 &&
1184 disconnected(mb.mb_imap_account) == 0)
1185 same_imap_account = 1;
1187 account = sstrdup(sp);
1188 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
1189 so.s_fd = -1;
1190 if (!same_imap_account) {
1191 if (!disconnected(account) &&
1192 sopen(sp, &so, use_ssl, uhp,
1193 use_ssl ? "imaps" : "imap", verbose) != OKAY)
1194 return -1;
1195 } else
1196 so = mb.mb_sock;
1197 if (!transparent)
1198 quit();
1199 edit = isedit;
1200 free(mb.mb_imap_account);
1201 mb.mb_imap_account = account;
1202 if (!same_imap_account) {
1203 if (mb.mb_sock.s_fd >= 0)
1204 sclose(&mb.mb_sock);
1206 same_imap_account = 0;
1207 if (!transparent) {
1208 if (mb.mb_itf) {
1209 fclose(mb.mb_itf);
1210 mb.mb_itf = NULL;
1212 if (mb.mb_otf) {
1213 fclose(mb.mb_otf);
1214 mb.mb_otf = NULL;
1216 free(mb.mb_imap_mailbox);
1217 mb.mb_imap_mailbox = sstrdup(mbx);
1218 initbox(server);
1220 mb.mb_type = MB_VOID;
1221 mb.mb_active = MB_NONE;;
1222 imaplock = 1;
1223 saveint = safe_signal(SIGINT, SIG_IGN);
1224 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1225 if (sigsetjmp(imapjmp, 1)) {
1226 sclose(&so);
1227 safe_signal(SIGINT, saveint);
1228 safe_signal(SIGPIPE, savepipe);
1229 imaplock = 0;
1230 return -1;
1232 if (saveint != SIG_IGN)
1233 safe_signal(SIGINT, imapcatch);
1234 if (savepipe != SIG_IGN)
1235 safe_signal(SIGPIPE, imapcatch);
1236 if (mb.mb_sock.s_fd < 0) {
1237 if (disconnected(mb.mb_imap_account)) {
1238 if (cache_setptr(transparent) == STOP)
1239 fprintf(stderr,
1240 "Mailbox \"%s\" is not cached.\n",
1241 server);
1242 goto done;
1244 if ((cp = value("imap-keepalive")) != NULL) {
1245 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1246 savealrm = safe_signal(SIGALRM, imapalarm);
1247 alarm(imapkeepalive);
1250 mb.mb_sock = so;
1251 mb.mb_sock.s_desc = "IMAP";
1252 mb.mb_sock.s_onclose = imap_timer_off;
1253 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1254 imap_auth(&mb, uhp, user, pass) != OKAY) {
1255 sclose(&mb.mb_sock);
1256 imap_timer_off();
1257 safe_signal(SIGINT, saveint);
1258 safe_signal(SIGPIPE, savepipe);
1259 imaplock = 0;
1260 return -1;
1262 } else /* same account */
1263 mb.mb_flags |= same_flags;
1264 mb.mb_perm = Rflag ? 0 : MB_DELE;
1265 mb.mb_type = MB_IMAP;
1266 cache_dequeue(&mb);
1267 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1268 /*sclose(&mb.mb_sock);
1269 imap_timer_off();*/
1270 safe_signal(SIGINT, saveint);
1271 safe_signal(SIGPIPE, savepipe);
1272 imaplock = 0;
1273 mb.mb_type = MB_VOID;
1274 return -1;
1276 newmail:
1277 imap_setptr(&mb, newmail, transparent, &prevcount);
1278 done: setmsize(msgCount);
1279 if (!newmail && !transparent)
1280 sawcom = 0;
1281 safe_signal(SIGINT, saveint);
1282 safe_signal(SIGPIPE, savepipe);
1283 imaplock = 0;
1284 if (!newmail && mb.mb_type == MB_IMAP)
1285 purgecache(&mb, message, msgCount);
1286 if ((newmail || transparent) && mb.mb_sorted) {
1287 mb.mb_threaded = 0;
1288 sort((void *)-1);
1290 if (!newmail && !edit && msgCount == 0) {
1291 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1292 value("emptystart") == NULL)
1293 fprintf(stderr, catgets(catd, CATSET, 258,
1294 "No mail at %s\n"), server);
1295 return 1;
1297 if (newmail)
1298 newmailinfo(prevcount);
1299 return 0;
1302 static int
1303 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1304 int need,
1305 const char *head, size_t headsize, long headlines)
1307 char *line = NULL, *lp;
1308 size_t linesize = 0, linelen, size = 0;
1309 int emptyline = 0, lines = 0, excess = 0;
1310 off_t offset;
1312 fseek(mp->mb_otf, 0L, SEEK_END);
1313 offset = ftell(mp->mb_otf);
1314 if (head)
1315 fwrite(head, 1, headsize, mp->mb_otf);
1316 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1317 lp = line;
1318 if (linelen > expected) {
1319 excess = linelen - expected;
1320 linelen = expected;
1323 * Need to mask 'From ' lines. This cannot be done properly
1324 * since some servers pass them as 'From ' and others as
1325 * '>From '. Although one could identify the first kind of
1326 * server in principle, it is not possible to identify the
1327 * second as '>From ' may also come from a server of the
1328 * first type as actual data. So do what is absolutely
1329 * necessary only - mask 'From '.
1331 * If the line is the first line of the message header, it
1332 * is likely a real 'From ' line. In this case, it is just
1333 * ignored since it violates all standards.
1335 if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
1336 lp[3] == 'm' && lp[4] == ' ') {
1337 if (lines + headlines != 0) {
1338 fputc('>', mp->mb_otf);
1339 size++;
1340 } else
1341 goto skip;
1343 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1344 lp[linelen-2] == '\r')) {
1345 emptyline = linelen <= 2;
1346 if (linelen > 2) {
1347 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1348 size += linelen - 1;
1349 } else
1350 size++;
1351 fputc('\n', mp->mb_otf);
1352 } else {
1353 emptyline = 0;
1354 fwrite(lp, 1, linelen, mp->mb_otf);
1355 size += linelen;
1357 lines++;
1358 skip: if ((expected -= linelen) <= 0)
1359 break;
1361 if (!emptyline) {
1363 * This is very ugly; but some IMAP daemons don't end a
1364 * message with \r\n\r\n, and we need \n\n for mbox format.
1366 fputc('\n', mp->mb_otf);
1367 lines++;
1368 size++;
1370 fflush(mp->mb_otf);
1371 if (m != NULL) {
1372 m->m_size = size + headsize;
1373 m->m_lines = lines + headlines;
1374 m->m_block = mailx_blockof(offset);
1375 m->m_offset = mailx_offsetof(offset);
1376 switch (need) {
1377 case NEED_HEADER:
1378 m->m_have |= HAVE_HEADER;
1379 break;
1380 case NEED_BODY:
1381 m->m_have |= HAVE_HEADER|HAVE_BODY;
1382 m->m_xlines = m->m_lines;
1383 m->m_xsize = m->m_size;
1384 break;
1387 free(line);
1388 return excess;
1391 static void
1392 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1393 const char *head, size_t headsize, long headlines)
1395 off_t offset;
1396 size_t len;
1398 len = strlen(str);
1399 fseek(mp->mb_otf, 0L, SEEK_END);
1400 offset = ftell(mp->mb_otf);
1401 if (head)
1402 fwrite(head, 1, headsize, mp->mb_otf);
1403 if (len > 0) {
1404 fwrite(str, 1, len, mp->mb_otf);
1405 fputc('\n', mp->mb_otf);
1406 len++;
1408 fflush(mp->mb_otf);
1409 if (m != NULL) {
1410 m->m_size = headsize + len;
1411 m->m_lines = headlines + 1;
1412 m->m_block = mailx_blockof(offset);
1413 m->m_offset = mailx_offsetof(offset);
1414 m->m_have |= HAVE_HEADER|HAVE_BODY;
1415 m->m_xlines = m->m_lines;
1416 m->m_xsize = m->m_size;
1420 static enum okay
1421 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1423 sighandler_type saveint = SIG_IGN;
1424 sighandler_type savepipe = SIG_IGN;
1425 char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL;
1426 size_t expected, headsize = 0;
1427 int number = m - message + 1;
1428 enum okay ok = STOP;
1429 FILE *queuefp = NULL;
1430 char *head = NULL;
1431 long headlines = 0;
1432 struct message mt;
1433 long n = -1;
1434 unsigned long u = 0;
1436 (void)&saveint;
1437 (void)&savepipe;
1438 (void)&number;
1439 (void)&need;
1440 (void)&cp;
1441 (void)&loc;
1442 (void)&ok;
1443 (void)&headsize;
1444 (void)&head;
1445 (void)&headlines;
1446 (void)&item;
1447 (void)&resp;
1448 (void)&u;
1449 verbose = value("verbose") != NULL;
1450 if (getcache(mp, m, need) == OKAY)
1451 return OKAY;
1452 if (mp->mb_type == MB_CACHE) {
1453 fprintf(stderr, "Message %u not available.\n", number);
1454 return STOP;
1456 if (mp->mb_sock.s_fd < 0) {
1457 fprintf(stderr, "IMAP connection closed.\n");
1458 return STOP;
1460 switch (need) {
1461 case NEED_HEADER:
1462 resp = item = "RFC822.HEADER";
1463 break;
1464 case NEED_BODY:
1465 item = "BODY.PEEK[]";
1466 resp = "BODY[]";
1467 if (m->m_flag & HAVE_HEADER && m->m_size) {
1468 char *hdr = smalloc(m->m_size);
1469 fflush(mp->mb_otf);
1470 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1471 m->m_offset), SEEK_SET) < 0 ||
1472 fread(hdr, 1, m->m_size, mp->mb_itf)
1473 != m->m_size) {
1474 free(hdr);
1475 break;
1477 head = hdr;
1478 headsize = m->m_size;
1479 headlines = m->m_lines;
1480 item = "BODY.PEEK[TEXT]";
1481 resp = "BODY[TEXT]";
1483 break;
1484 case NEED_UNSPEC:
1485 return STOP;
1487 imaplock = 1;
1488 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1489 if (sigsetjmp(imapjmp, 1)) {
1490 safe_signal(SIGINT, saveint);
1491 safe_signal(SIGPIPE, savepipe);
1492 imaplock = 0;
1493 return STOP;
1495 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1496 safe_signal(SIGINT, maincatch);
1497 if (savepipe != SIG_IGN)
1498 safe_signal(SIGPIPE, imapcatch);
1499 if (m->m_uid)
1500 snprintf(o, sizeof o,
1501 "%s UID FETCH %lu (%s)\r\n",
1502 tag(1), m->m_uid, item);
1503 else {
1504 if (check_expunged() == STOP)
1505 goto out;
1506 snprintf(o, sizeof o,
1507 "%s FETCH %u (%s)\r\n",
1508 tag(1), number, item);
1510 IMAP_OUT(o, MB_COMD, goto out)
1511 for (;;) {
1512 ok = imap_answer(mp, 1);
1513 if (ok == STOP)
1514 break;
1515 if (response_status != RESPONSE_OTHER ||
1516 response_other != MESSAGE_DATA_FETCH)
1517 continue;
1518 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1519 continue;
1520 if (m->m_uid) {
1521 if ((cp = asccasestr(responded_other_text, "UID "))) {
1522 u = atol(&cp[4]);
1523 n = 0;
1524 } else {
1525 n = -1;
1526 u = 0;
1528 } else
1529 n = responded_other_number;
1530 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1531 if (m->m_uid ? m->m_uid != u : n != number)
1532 continue;
1533 if ((cp = strchr(loc, '"')) != NULL) {
1534 cp = imap_unquotestr(cp);
1535 imap_putstr(mp, m, cp,
1536 head, headsize, headlines);
1537 } else {
1538 m->m_have |= HAVE_HEADER|HAVE_BODY;
1539 m->m_xlines = m->m_lines;
1540 m->m_xsize = m->m_size;
1542 goto out;
1544 expected = atol(&cp[1]);
1545 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1546 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1547 continue;
1549 mt = *m;
1550 imap_fetchdata(mp, &mt, expected, need,
1551 head, headsize, headlines);
1552 if (n >= 0) {
1553 commitmsg(mp, m, mt, mt.m_have);
1554 break;
1556 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1557 &mp->mb_sock) > 0) {
1558 if (verbose)
1559 fputs(imapbuf, stderr);
1560 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1561 u = atol(&cp[4]);
1562 if (u == m->m_uid) {
1563 commitmsg(mp, m, mt, mt.m_have);
1564 break;
1569 out: while (mp->mb_active & MB_COMD)
1570 ok = imap_answer(mp, 1);
1571 if (saveint != SIG_IGN)
1572 safe_signal(SIGINT, saveint);
1573 if (savepipe != SIG_IGN)
1574 safe_signal(SIGPIPE, savepipe);
1575 imaplock--;
1576 if (ok == OKAY)
1577 putcache(mp, m);
1578 free(head);
1579 if (interrupts)
1580 onintr(0);
1581 return ok;
1584 enum okay
1585 imap_header(struct message *m)
1587 return imap_get(&mb, m, NEED_HEADER);
1591 enum okay
1592 imap_body(struct message *m)
1594 return imap_get(&mb, m, NEED_BODY);
1597 static void
1598 commitmsg(struct mailbox *mp, struct message *to,
1599 struct message from, enum havespec have)
1601 to->m_size = from.m_size;
1602 to->m_lines = from.m_lines;
1603 to->m_block = from.m_block;
1604 to->m_offset = from.m_offset;
1605 to->m_have = have;
1606 if (have & HAVE_BODY) {
1607 to->m_xlines = from.m_lines;
1608 to->m_xsize = from.m_size;
1610 putcache(mp, to);
1613 static enum okay
1614 imap_fetchheaders (
1615 struct mailbox *mp,
1616 struct message *m,
1617 int bot,
1618 int top /* bot > top */
1621 char o[LINESIZE], *cp;
1622 struct message mt;
1623 size_t expected;
1624 enum okay ok;
1625 int n = 0, u;
1626 FILE *queuefp = NULL;
1628 if (m[bot].m_uid)
1629 snprintf(o, sizeof o,
1630 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1631 tag(1), m[bot-1].m_uid, m[top-1].m_uid);
1632 else {
1633 if (check_expunged() == STOP)
1634 return STOP;
1635 snprintf(o, sizeof o,
1636 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1637 tag(1), bot, top);
1639 IMAP_OUT(o, MB_COMD, return STOP)
1640 for (;;) {
1641 ok = imap_answer(mp, 1);
1642 if (response_status != RESPONSE_OTHER)
1643 break;
1644 if (response_other != MESSAGE_DATA_FETCH)
1645 continue;
1646 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1647 return STOP;
1648 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1649 continue;
1650 expected = atol(&cp[1]);
1651 if (m[bot-1].m_uid) {
1652 if ((cp=asccasestr(responded_other_text, "UID "))) {
1653 u = atoi(&cp[4]);
1654 for (n = bot; n <= top; n++)
1655 if ((unsigned long)u == m[n-1].m_uid)
1656 break;
1657 if (n > top) {
1658 imap_fetchdata(mp, NULL, expected,
1659 NEED_HEADER,
1660 NULL, 0, 0);
1661 continue;
1663 } else
1664 n = -1;
1665 } else {
1666 n = responded_other_number;
1667 if (n <= 0 || n > msgCount) {
1668 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1669 NULL, 0, 0);
1670 continue;
1673 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1674 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1675 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1676 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1677 &mp->mb_sock) > 0) {
1678 if (verbose)
1679 fputs(imapbuf, stderr);
1680 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1681 u = atoi(&cp[4]);
1682 for (n = bot; n <= top; n++)
1683 if ((unsigned long)u == m[n-1].m_uid)
1684 break;
1685 if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
1686 commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
1687 n = 0;
1691 while (mp->mb_active & MB_COMD)
1692 ok = imap_answer(mp, 1);
1693 return ok;
1696 void
1697 imap_getheaders(int bot, int top)
1699 sighandler_type saveint, savepipe;
1700 enum okay ok = STOP;
1701 int i, chunk = 256;
1703 (void)&saveint;
1704 (void)&savepipe;
1705 (void)&ok;
1706 (void)&bot;
1707 (void)&top;
1708 verbose = value("verbose") != NULL;
1709 if (mb.mb_type == MB_CACHE)
1710 return;
1711 if (bot < 1)
1712 bot = 1;
1713 if (top > msgCount)
1714 top = msgCount;
1715 for (i = bot; i < top; i++) {
1716 if (message[i-1].m_have & HAVE_HEADER ||
1717 getcache(&mb, &message[i-1], NEED_HEADER)
1718 == OKAY)
1719 bot = i+1;
1720 else
1721 break;
1723 for (i = top; i > bot; i--) {
1724 if (message[i-1].m_have & HAVE_HEADER ||
1725 getcache(&mb, &message[i-1], NEED_HEADER)
1726 == OKAY)
1727 top = i-1;
1728 else
1729 break;
1731 if (bot >= top)
1732 return;
1733 imaplock = 1;
1734 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1735 safe_signal(SIGINT, maincatch);
1736 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1737 if (sigsetjmp(imapjmp, 1) == 0) {
1738 if (savepipe != SIG_IGN)
1739 safe_signal(SIGPIPE, imapcatch);
1740 for (i = bot; i <= top; i += chunk) {
1741 ok = imap_fetchheaders(&mb, message, i,
1742 i+chunk-1 < top ? i+chunk-1 : top);
1743 if (interrupts)
1744 onintr(0);
1747 safe_signal(SIGINT, saveint);
1748 safe_signal(SIGPIPE, savepipe);
1749 imaplock = 0;
1752 static enum okay
1753 imap_exit(struct mailbox *mp)
1755 char o[LINESIZE];
1756 FILE *queuefp = NULL;
1758 verbose = value("verbose") != NULL;
1759 mp->mb_active |= MB_BYE;
1760 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1761 IMAP_OUT(o, MB_COMD, return STOP)
1762 IMAP_ANSWER()
1763 return OKAY;
1766 static enum okay
1767 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1769 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1770 if (mp->mb_type == MB_IMAP)
1771 delcache(mp, m);
1772 return OKAY;
1775 static enum okay
1776 imap_close(struct mailbox *mp)
1778 char o[LINESIZE];
1779 FILE *queuefp = NULL;
1781 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1782 IMAP_OUT(o, MB_COMD, return STOP)
1783 IMAP_ANSWER()
1784 return OKAY;
1787 static enum okay
1788 imap_update(struct mailbox *mp)
1790 FILE *readstat = NULL;
1791 struct message *m;
1792 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1794 verbose = value("verbose") != NULL;
1795 if (Tflag != NULL) {
1796 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
1797 Tflag = NULL;
1799 if (!edit && mp->mb_perm != 0) {
1800 holdbits();
1801 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1802 if (m->m_flag & MBOX)
1803 c++;
1805 if (c > 0)
1806 if (makembox() == STOP)
1807 goto bypass;
1809 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1810 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
1811 char *id;
1813 if ((id = hfield1("message-id", m)) != NULL ||
1814 (id = hfieldX("article-id", m)) != NULL)
1815 fprintf(readstat, "%s\n", id);
1817 if (mp->mb_perm == 0) {
1818 dodel = 0;
1819 } else if (edit) {
1820 dodel = m->m_flag & MDELETED;
1821 } else {
1822 dodel = !((m->m_flag&MPRESERVE) ||
1823 (m->m_flag&MTOUCH) == 0);
1826 * Fetch the result after around each 800 STORE commands
1827 * sent (approx. 32k data sent). Otherwise, servers will
1828 * try to flush the return queue at some point, leading
1829 * to a deadlock if we are still writing commands but not
1830 * reading their results.
1832 needstat = stored > 0 && stored % 800 == 0;
1834 * Even if this message has been deleted, continue
1835 * to set further flags. This is necessary to support
1836 * Gmail semantics, where "delete" actually means
1837 * "archive", and the flags are applied to the copy
1838 * in "All Mail".
1840 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1841 imap_store(mp, m, m-message+1,
1842 '+', "\\Seen", needstat);
1843 stored++;
1845 if (m->m_flag & MFLAG) {
1846 imap_store(mp, m, m-message+1,
1847 '+', "\\Flagged", needstat);
1848 stored++;
1850 if (m->m_flag & MUNFLAG) {
1851 imap_store(mp, m, m-message+1,
1852 '-', "\\Flagged", needstat);
1853 stored++;
1855 if (m->m_flag & MANSWER) {
1856 imap_store(mp, m, m-message+1,
1857 '+', "\\Answered", needstat);
1858 stored++;
1860 if (m->m_flag & MUNANSWER) {
1861 imap_store(mp, m, m-message+1,
1862 '-', "\\Answered", needstat);
1863 stored++;
1865 if (m->m_flag & MDRAFT) {
1866 imap_store(mp, m, m-message+1,
1867 '+', "\\Draft", needstat);
1868 stored++;
1870 if (m->m_flag & MUNDRAFT) {
1871 imap_store(mp, m, m-message+1,
1872 '-', "\\Draft", needstat);
1873 stored++;
1875 if (dodel) {
1876 imap_delete(mp, m-message+1, m, needstat);
1877 stored++;
1878 gotcha++;
1879 } else if (mp->mb_type != MB_CACHE ||
1880 (! edit && ! (m->m_flag&(MBOXED|MSAVED|MDELETED))) ||
1881 (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) ==
1882 (MPRESERVE|MTOUCH) ||
1883 (edit && ! (m->m_flag & MDELETED)))
1884 held++;
1885 if (m->m_flag & MNEW) {
1886 m->m_flag &= ~MNEW;
1887 m->m_flag |= MSTATUS;
1890 bypass: if (readstat != NULL)
1891 Fclose(readstat);
1892 if (gotcha)
1893 imap_close(mp);
1894 for (m = &message[0]; m < &message[msgCount]; m++)
1895 if (!(m->m_flag&MUNLINKED) &&
1896 m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
1897 MFLAG|MUNFLAG|MANSWER|MUNANSWER|
1898 MDRAFT|MUNDRAFT)) {
1899 putcache(mp, m);
1900 modflags++;
1902 if ((gotcha || modflags) && edit) {
1903 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
1904 printf(value("bsdcompat") || value("bsdmsgs") ?
1905 catgets(catd, CATSET, 170, "complete\n") :
1906 catgets(catd, CATSET, 212, "updated.\n"));
1907 } else if (held && !edit && mp->mb_perm != 0) {
1908 if (held == 1)
1909 printf(catgets(catd, CATSET, 155,
1910 "Held 1 message in %s\n"), mailname);
1911 else if (held > 1)
1912 printf(catgets(catd, CATSET, 156,
1913 "Held %d messages in %s\n"), held, mailname);
1915 fflush(stdout);
1916 return OKAY;
1919 void
1920 imap_quit(void)
1922 sighandler_type saveint;
1923 sighandler_type savepipe;
1925 verbose = value("verbose") != NULL;
1926 if (mb.mb_type == MB_CACHE) {
1927 imap_update(&mb);
1928 return;
1930 if (mb.mb_sock.s_fd < 0) {
1931 fprintf(stderr, "IMAP connection closed.\n");
1932 return;
1934 imaplock = 1;
1935 saveint = safe_signal(SIGINT, SIG_IGN);
1936 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1937 if (sigsetjmp(imapjmp, 1)) {
1938 safe_signal(SIGINT, saveint);
1939 safe_signal(SIGPIPE, saveint);
1940 imaplock = 0;
1941 return;
1943 if (saveint != SIG_IGN)
1944 safe_signal(SIGINT, imapcatch);
1945 if (savepipe != SIG_IGN)
1946 safe_signal(SIGPIPE, imapcatch);
1947 imap_update(&mb);
1948 if (!same_imap_account) {
1949 imap_exit(&mb);
1950 sclose(&mb.mb_sock);
1952 safe_signal(SIGINT, saveint);
1953 safe_signal(SIGPIPE, savepipe);
1954 imaplock = 0;
1957 static enum okay
1958 imap_store(struct mailbox *mp, struct message *m, int n,
1959 int c, const char *sp, int needstat)
1961 char o[LINESIZE];
1962 FILE *queuefp = NULL;
1964 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1965 return STOP;
1966 if (m->m_uid)
1967 snprintf(o, sizeof o,
1968 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1969 tag(1), m->m_uid, c, sp);
1970 else {
1971 if (check_expunged() == STOP)
1972 return STOP;
1973 snprintf(o, sizeof o,
1974 "%s STORE %u %cFLAGS (%s)\r\n",
1975 tag(1), n, c, sp);
1977 IMAP_OUT(o, MB_COMD, return STOP)
1978 if (needstat)
1979 IMAP_ANSWER()
1980 else
1981 mb.mb_active &= ~MB_COMD;
1982 if (queuefp != NULL)
1983 Fclose(queuefp);
1984 return OKAY;
1987 enum okay
1988 imap_undelete(struct message *m, int n)
1990 return imap_unstore(m, n, "\\Deleted");
1993 enum okay
1994 imap_unread(struct message *m, int n)
1996 return imap_unstore(m, n, "\\Seen");
1999 static enum okay
2000 imap_unstore(struct message *m, int n, const char *flag)
2002 sighandler_type saveint, savepipe;
2003 enum okay ok = STOP;
2005 (void)&saveint;
2006 (void)&savepipe;
2007 (void)&ok;
2008 verbose = value("verbose") != NULL;
2009 imaplock = 1;
2010 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2011 safe_signal(SIGINT, maincatch);
2012 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2013 if (sigsetjmp(imapjmp, 1) == 0) {
2014 if (savepipe != SIG_IGN)
2015 safe_signal(SIGPIPE, imapcatch);
2016 ok = imap_store(&mb, m, n, '-', flag, 1);
2018 safe_signal(SIGINT, saveint);
2019 safe_signal(SIGPIPE, savepipe);
2020 imaplock = 0;
2021 if (interrupts)
2022 onintr(0);
2023 return ok;
2026 static const char *
2027 tag(int new)
2029 static char ts[20];
2030 static long n;
2032 if (new)
2033 n++;
2034 snprintf(ts, sizeof ts, "T%lu", n);
2035 return ts;
2038 int
2039 imap_imap(void *vp)
2041 sighandler_type saveint, savepipe;
2042 char o[LINESIZE];
2043 enum okay ok = STOP;
2044 struct mailbox *mp = &mb;
2045 FILE *queuefp = NULL;
2047 (void)&saveint;
2048 (void)&savepipe;
2049 (void)&ok;
2050 verbose = value("verbose") != NULL;
2051 if (mp->mb_type != MB_IMAP) {
2052 printf("Not operating on an IMAP mailbox.\n");
2053 return 1;
2055 imaplock = 1;
2056 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2057 safe_signal(SIGINT, maincatch);
2058 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2059 if (sigsetjmp(imapjmp, 1) == 0) {
2060 if (savepipe != SIG_IGN)
2061 safe_signal(SIGPIPE, imapcatch);
2062 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2063 IMAP_OUT(o, MB_COMD, goto out)
2064 while (mp->mb_active & MB_COMD) {
2065 ok = imap_answer(mp, 0);
2066 fputs(responded_text, stdout);
2069 out: safe_signal(SIGINT, saveint);
2070 safe_signal(SIGPIPE, savepipe);
2071 imaplock = 0;
2072 if (interrupts)
2073 onintr(0);
2074 return ok != OKAY;
2077 int
2078 imap_newmail(int autoinc)
2080 if (autoinc && had_exists < 0 && had_expunge < 0) {
2081 verbose = value("verbose") != NULL;
2082 imaplock = 1;
2083 imap_noop();
2084 imaplock = 0;
2086 if (had_exists == msgCount && had_expunge < 0)
2088 * Some servers always respond with EXISTS to NOOP. If
2089 * the mailbox has been changed but the number of messages
2090 * has not, an EXPUNGE must also had been sent; otherwise,
2091 * nothing has changed.
2093 had_exists = -1;
2094 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2097 static char *
2098 imap_putflags(int f)
2100 const char *cp;
2101 char *buf, *bp;
2103 bp = buf = salloc(100);
2104 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2105 *bp++ = '(';
2106 if (f & MREAD) {
2107 if (bp[-1] != '(')
2108 *bp++ = ' ';
2109 for (cp = "\\Seen"; *cp; cp++)
2110 *bp++ = *cp;
2112 if (f & MFLAGGED) {
2113 if (bp[-1] != '(')
2114 *bp++ = ' ';
2115 for (cp = "\\Flagged"; *cp; cp++)
2116 *bp++ = *cp;
2118 if (f & MANSWERED) {
2119 if (bp[-1] != '(')
2120 *bp++ = ' ';
2121 for (cp = "\\Answered"; *cp; cp++)
2122 *bp++ = *cp;
2124 if (f & MDRAFT) {
2125 if (bp[-1] != '(')
2126 *bp++ = ' ';
2127 for (cp = "\\Draft"; *cp; cp++)
2128 *bp++ = *cp;
2130 *bp++ = ')';
2131 *bp++ = ' ';
2133 *bp = '\0';
2134 return buf;
2137 static void
2138 imap_getflags(const char *cp, char **xp, enum mflag *f)
2140 while (*cp != ')') {
2141 if (*cp == '\\') {
2142 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2143 *f |= MREAD;
2144 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2145 *f |= MNEW;
2146 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2147 *f |= MDELETED;
2148 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2149 *f |= MFLAGGED;
2150 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2151 *f |= MANSWERED;
2152 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2153 *f |= MDRAFTED;
2155 cp++;
2157 if (xp)
2158 *xp = (char *)cp;
2161 static enum okay
2162 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2163 off_t off1, long xsize, enum mflag flag, time_t t)
2165 char o[LINESIZE];
2166 char *buf;
2167 size_t bufsize, buflen, count;
2168 enum okay ok = STOP;
2169 long size, lines, ysize;
2170 int twice = 0;
2171 FILE *queuefp = NULL;
2173 if (mp->mb_type == MB_CACHE) {
2174 queuefp = cache_queue(mp);
2175 if (queuefp == NULL)
2176 return STOP;
2177 ok = OKAY;
2179 buf = smalloc(bufsize = LINESIZE);
2180 buflen = 0;
2181 again: size = xsize;
2182 count = fsize(fp);
2183 fseek(fp, off1, SEEK_SET);
2184 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2185 tag(1), imap_quotestr(name),
2186 imap_putflags(flag),
2187 imap_make_date_time(t),
2188 size);
2189 IMAP_OUT(o, MB_COMD, goto out)
2190 while (mp->mb_active & MB_COMD) {
2191 ok = imap_answer(mp, twice);
2192 if (response_type == RESPONSE_CONT)
2193 break;
2195 if (mp->mb_type != MB_CACHE && ok == STOP) {
2196 if (twice == 0)
2197 goto trycreate;
2198 else
2199 goto out;
2201 lines = ysize = 0;
2202 while (size > 0) {
2203 fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2204 lines++;
2205 ysize += buflen;
2206 buf[buflen-1] = '\r';
2207 buf[buflen] = '\n';
2208 if (mp->mb_type != MB_CACHE)
2209 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2210 else if (queuefp)
2211 fwrite(buf, 1, buflen+1, queuefp);
2212 size -= buflen+1;
2214 if (mp->mb_type != MB_CACHE)
2215 swrite(&mp->mb_sock, "\r\n");
2216 else if (queuefp)
2217 fputs("\r\n", queuefp);
2218 while (mp->mb_active & MB_COMD) {
2219 ok = imap_answer(mp, 0);
2220 if (response_status == RESPONSE_NO /*&&
2221 ascncasecmp(responded_text,
2222 "[TRYCREATE] ", 12) == 0*/) {
2223 trycreate: if (twice++) {
2224 ok = STOP;
2225 goto out;
2227 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2228 tag(1),
2229 imap_quotestr(name));
2230 IMAP_OUT(o, MB_COMD, goto out);
2231 while (mp->mb_active & MB_COMD)
2232 ok = imap_answer(mp, 1);
2233 if (ok == STOP)
2234 goto out;
2235 imap_created_mailbox++;
2236 goto again;
2237 } else if (ok != OKAY)
2238 fprintf(stderr, "IMAP error: %s", responded_text);
2239 else if (response_status == RESPONSE_OK &&
2240 mp->mb_flags & MB_UIDPLUS)
2241 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2242 flag, name);
2244 out: if (queuefp != NULL)
2245 Fclose(queuefp);
2246 free(buf);
2247 return ok;
2250 static enum okay
2251 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2253 char *buf, *bp, *lp;
2254 size_t bufsize, buflen, count;
2255 off_t off1 = -1, offs;
2256 int inhead = 1;
2257 int flag = MNEW|MNEWEST;
2258 long size = 0;
2259 time_t tim;
2260 enum okay ok;
2262 buf = smalloc(bufsize = LINESIZE);
2263 buflen = 0;
2264 count = fsize(fp);
2265 offs = ftell(fp);
2266 time(&tim);
2267 do {
2268 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
2269 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2270 if (off1 != (off_t)-1) {
2271 ok=imap_append1(mp, name, fp, off1,
2272 size, flag, tim);
2273 if (ok == STOP)
2274 return STOP;
2275 fseek(fp, offs+buflen, SEEK_SET);
2277 off1 = offs + buflen;
2278 size = 0;
2279 inhead = 1;
2280 flag = MNEW;
2281 if (bp != NULL)
2282 tim = unixtime(buf);
2283 } else
2284 size += buflen+1;
2285 offs += buflen;
2286 if (bp && buf[0] == '\n')
2287 inhead = 0;
2288 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2289 lp = &buf[6];
2290 while (whitechar(*lp&0377))
2291 lp++;
2292 if (*lp == ':')
2293 while (*++lp != '\0')
2294 switch (*lp) {
2295 case 'R':
2296 flag |= MREAD;
2297 break;
2298 case 'O':
2299 flag &= ~MNEW;
2300 break;
2302 } else if (bp && inhead &&
2303 ascncasecmp(buf, "x-status", 8) == 0) {
2304 lp = &buf[8];
2305 while (whitechar(*lp&0377))
2306 lp++;
2307 if (*lp == ':')
2308 while (*++lp != '\0')
2309 switch (*lp) {
2310 case 'F':
2311 flag |= MFLAGGED;
2312 break;
2313 case 'A':
2314 flag |= MANSWERED;
2315 break;
2316 case 'T':
2317 flag |= MDRAFTED;
2318 break;
2321 } while (bp != NULL);
2322 free(buf);
2323 return OKAY;
2326 enum okay
2327 imap_append(const char *xserver, FILE *fp)
2329 sighandler_type saveint, savepipe;
2330 char *server, *uhp, *mbx, *user;
2331 const char *sp, *cp, *pass;
2332 int use_ssl;
2333 enum okay ok = STOP;
2335 (void)&saveint;
2336 (void)&savepipe;
2337 (void)&ok;
2338 verbose = value("verbose") != NULL;
2339 server = savestr((char *)xserver);
2340 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
2341 imaplock = 1;
2342 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2343 safe_signal(SIGINT, maincatch);
2344 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2345 if (sigsetjmp(imapjmp, 1))
2346 goto out;
2347 if (savepipe != SIG_IGN)
2348 safe_signal(SIGPIPE, imapcatch);
2349 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2350 mb.mb_imap_account &&
2351 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2352 ok = imap_append0(&mb, mbx, fp);
2354 else {
2355 struct mailbox mx;
2357 memset(&mx, 0, sizeof mx);
2358 if (disconnected(server) == 0) {
2359 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2360 use_ssl ? "imaps" : "imap",
2361 verbose) != OKAY)
2362 goto fail;
2363 mx.mb_sock.s_desc = "IMAP";
2364 mx.mb_type = MB_IMAP;
2365 mx.mb_imap_account = (char *)protbase(server);
2366 mx.mb_imap_mailbox = mbx;
2367 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2368 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2369 sclose(&mx.mb_sock);
2370 goto fail;
2372 ok = imap_append0(&mx, mbx, fp);
2373 imap_exit(&mx);
2374 sclose(&mx.mb_sock);
2375 } else {
2376 mx.mb_imap_account = (char *)protbase(server);
2377 mx.mb_imap_mailbox = mbx;
2378 mx.mb_type = MB_CACHE;
2379 ok = imap_append0(&mx, mbx, fp);
2381 fail:;
2383 out: safe_signal(SIGINT, saveint);
2384 safe_signal(SIGPIPE, savepipe);
2385 imaplock = 0;
2386 if (interrupts)
2387 onintr(0);
2388 return ok;
2391 static enum okay
2392 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2393 struct list_item **lend, int level)
2395 char o[LINESIZE];
2396 enum okay ok = STOP;
2397 char *cp;
2398 const char *bp;
2399 FILE *queuefp = NULL;
2400 struct list_item *lp;
2402 *list = *lend = NULL;
2403 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2404 tag(1), imap_quotestr(base));
2405 IMAP_OUT(o, MB_COMD, return STOP);
2406 while (mp->mb_active & MB_COMD) {
2407 ok = imap_answer(mp, 1);
2408 if (response_status == RESPONSE_OTHER &&
2409 response_other == MAILBOX_DATA_LIST &&
2410 imap_parse_list() == OKAY) {
2411 cp = imap_unquotestr(list_name);
2412 lp = csalloc(1, sizeof *lp);
2413 lp->l_name = cp;
2414 for (bp = base; *bp && *bp == *cp; bp++)
2415 cp++;
2416 lp->l_base = *cp ? cp : savestr(base);
2417 lp->l_attr = list_attributes;
2418 lp->l_level = level+1;
2419 lp->l_delim = list_hierarchy_delimiter;
2420 if (*list && *lend) {
2421 (*lend)->l_next = lp;
2422 *lend = lp;
2423 } else
2424 *list = *lend = lp;
2427 return ok;
2430 static enum okay
2431 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2433 struct list_item *list, *lend, *lp, *lx, *ly;
2434 int n;
2435 const char *bp;
2436 char *cp;
2437 int depth;
2439 verbose = value("verbose") != NULL;
2440 depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
2441 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2442 return STOP;
2443 if (list == NULL || lend == NULL)
2444 return OKAY;
2445 for (lp = list; lp; lp = lp->l_next)
2446 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2447 lp->l_level < depth &&
2448 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2449 cp = salloc((n = strlen(lp->l_name)) + 2);
2450 strcpy(cp, lp->l_name);
2451 cp[n] = lp->l_delim;
2452 cp[n+1] = '\0';
2453 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2454 lx && ly) {
2455 lp->l_has_children = 1;
2456 if (strcmp(cp, lx->l_name) == 0)
2457 lx = lx->l_next;
2458 if (lx) {
2459 lend->l_next = lx;
2460 lend = ly;
2464 for (lp = list; lp; lp = lp->l_next) {
2465 if (strip) {
2466 cp = lp->l_name;
2467 for (bp = base; *bp && *bp == *cp; bp++)
2468 cp++;
2469 } else
2470 cp = lp->l_name;
2471 if ((lp->l_attr&LIST_NOSELECT) == 0)
2472 fprintf(fp, "%s\n", *cp ? cp : base);
2473 else if (lp->l_has_children == 0)
2474 fprintf(fp, "%s%c\n", *cp ? cp : base,
2475 lp->l_delim != EOF ? lp->l_delim : '\n');
2477 return OKAY;
2480 void
2481 imap_folders(const char *name, int strip)
2483 sighandler_type saveint, savepipe;
2484 const char *fold, *cp, *sp;
2485 char *tempfn;
2486 FILE *fp;
2487 int columnize = is_a_tty[1];
2489 (void)&saveint;
2490 (void)&savepipe;
2491 (void)&fp;
2492 (void)&fold;
2493 cp = protbase(name);
2494 sp = mb.mb_imap_account;
2495 if (strcmp(cp, sp)) {
2496 fprintf(stderr, "Cannot list folders on other than the "
2497 "current IMAP account,\n\"%s\". "
2498 "Try \"folders @\".\n", sp);
2499 return;
2501 fold = protfile(name);
2502 if (columnize) {
2503 if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
2504 perror("tmpfile");
2505 return;
2507 rm(tempfn);
2508 Ftfree(&tempfn);
2509 } else
2510 fp = stdout;
2511 imaplock = 1;
2512 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2513 safe_signal(SIGINT, maincatch);
2514 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2515 if (sigsetjmp(imapjmp, 1))
2516 goto out;
2517 if (savepipe != SIG_IGN)
2518 safe_signal(SIGPIPE, imapcatch);
2519 if (mb.mb_type == MB_CACHE)
2520 cache_list(&mb, fold, strip, fp);
2521 else
2522 imap_list(&mb, fold, strip, fp);
2523 imaplock = 0;
2524 if (interrupts) {
2525 if (columnize)
2526 Fclose(fp);
2527 onintr(0);
2529 fflush(fp);
2530 if (columnize) {
2531 rewind(fp);
2532 if (fsize(fp) > 0)
2533 dopr(fp);
2534 else
2535 fprintf(stderr, "Folder not found.\n");
2537 out:
2538 safe_signal(SIGINT, saveint);
2539 safe_signal(SIGPIPE, savepipe);
2540 if (columnize)
2541 Fclose(fp);
2544 static void
2545 dopr(FILE *fp)
2547 char o[LINESIZE], *tempfn;
2548 int c;
2549 long n = 0, mx = 0, columns, width;
2550 FILE *out;
2552 if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
2553 perror("tmpfile");
2554 return;
2556 rm(tempfn);
2557 Ftfree(&tempfn);
2558 while ((c = getc(fp)) != EOF) {
2559 if (c == '\n') {
2560 if (n > mx)
2561 mx = n;
2562 n = 0;
2563 } else
2564 n++;
2566 rewind(fp);
2567 width = scrnwidth;
2568 if (mx < width / 2) {
2569 columns = width / (mx+2);
2570 snprintf(o, sizeof o,
2571 "sort | pr -%lu -w%lu -t",
2572 columns, width);
2573 } else
2574 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2575 run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2576 try_pager(out);
2577 Fclose(out);
2580 static enum okay
2581 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2583 char o[LINESIZE];
2584 const char *qname;
2585 enum okay ok = STOP;
2586 int twice = 0;
2587 int stored = 0;
2588 FILE *queuefp = NULL;
2590 if (mp->mb_type == MB_CACHE) {
2591 if ((queuefp = cache_queue(mp)) == NULL)
2592 return STOP;
2593 ok = OKAY;
2595 qname = imap_quotestr(name = protfile(name));
2597 * Since it is not possible to set flags on the copy, recently
2598 * set flags must be set on the original to include it in the copy.
2600 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
2601 imap_store(mp, m, n, '+', "\\Seen", 0);
2602 if (m->m_flag&MFLAG)
2603 imap_store(mp, m, n, '+', "\\Flagged", 0);
2604 if (m->m_flag&MUNFLAG)
2605 imap_store(mp, m, n, '-', "\\Flagged", 0);
2606 if (m->m_flag&MANSWER)
2607 imap_store(mp, m, n, '+', "\\Answered", 0);
2608 if (m->m_flag&MUNANSWER)
2609 imap_store(mp, m, n, '-', "\\Flagged", 0);
2610 if (m->m_flag&MDRAFT)
2611 imap_store(mp, m, n, '+', "\\Draft", 0);
2612 if (m->m_flag&MUNDRAFT)
2613 imap_store(mp, m, n, '-', "\\Draft", 0);
2614 again: if (m->m_uid)
2615 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
2616 tag(1), m->m_uid, qname);
2617 else {
2618 if (check_expunged() == STOP)
2619 goto out;
2620 snprintf(o, sizeof o, "%s COPY %u %s\r\n",
2621 tag(1), n, qname);
2623 IMAP_OUT(o, MB_COMD, goto out)
2624 while (mp->mb_active & MB_COMD)
2625 ok = imap_answer(mp, twice);
2626 if (mp->mb_type == MB_IMAP &&
2627 mp->mb_flags & MB_UIDPLUS &&
2628 response_status == RESPONSE_OK)
2629 imap_copyuid(mp, m, name);
2630 if (response_status == RESPONSE_NO && twice++ == 0) {
2631 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2632 IMAP_OUT(o, MB_COMD, goto out)
2633 while (mp->mb_active & MB_COMD)
2634 ok = imap_answer(mp, 1);
2635 if (ok == OKAY) {
2636 imap_created_mailbox++;
2637 goto again;
2640 if (queuefp != NULL)
2641 Fclose(queuefp);
2643 * ... and reset the flag to its initial value so that
2644 * the 'exit' command still leaves the message unread.
2646 out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
2647 imap_store(mp, m, n, '-', "\\Seen", 0);
2648 stored++;
2650 if (m->m_flag&MFLAG) {
2651 imap_store(mp, m, n, '-', "\\Flagged", 0);
2652 stored++;
2654 if (m->m_flag&MUNFLAG) {
2655 imap_store(mp, m, n, '+', "\\Flagged", 0);
2656 stored++;
2658 if (m->m_flag&MANSWER) {
2659 imap_store(mp, m, n, '-', "\\Answered", 0);
2660 stored++;
2662 if (m->m_flag&MUNANSWER) {
2663 imap_store(mp, m, n, '+', "\\Answered", 0);
2664 stored++;
2666 if (m->m_flag&MDRAFT) {
2667 imap_store(mp, m, n, '-', "\\Draft", 0);
2668 stored++;
2670 if (m->m_flag&MUNDRAFT) {
2671 imap_store(mp, m, n, '+', "\\Draft", 0);
2672 stored++;
2674 if (stored) {
2675 mp->mb_active |= MB_COMD;
2676 imap_finish(mp);
2678 return ok;
2681 enum okay
2682 imap_copy(struct message *m, int n, const char *name)
2684 sighandler_type saveint, savepipe;
2685 enum okay ok = STOP;
2687 (void)&saveint;
2688 (void)&savepipe;
2689 (void)&ok;
2690 verbose = value("verbose") != NULL;
2691 imaplock = 1;
2692 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2693 safe_signal(SIGINT, maincatch);
2694 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2695 if (sigsetjmp(imapjmp, 1) == 0) {
2696 if (savepipe != SIG_IGN)
2697 safe_signal(SIGPIPE, imapcatch);
2698 ok = imap_copy1(&mb, m, n, name);
2700 safe_signal(SIGINT, saveint);
2701 safe_signal(SIGPIPE, savepipe);
2702 imaplock = 0;
2703 if (interrupts)
2704 onintr(0);
2705 return ok;
2708 static enum okay
2709 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2710 unsigned long *olduid, unsigned long *newuid)
2712 char *xp, *yp, *zp;
2714 *uidvalidity = strtoul(cp, &xp, 10);
2715 *olduid = strtoul(xp, &yp, 10);
2716 *newuid = strtoul(yp, &zp, 10);
2717 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2718 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2721 static enum okay
2722 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2723 unsigned long *uid)
2725 char *xp, *yp;
2727 *uidvalidity = strtoul(cp, &xp, 10);
2728 *uid = strtoul(xp, &yp, 10);
2729 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2730 yp > xp && *yp == ']';
2733 static enum okay
2734 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2736 const char *cp;
2737 unsigned long uidvalidity, olduid, newuid;
2738 struct mailbox xmb;
2739 struct message xm;
2741 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2742 imap_copyuid_parse(&cp[9], &uidvalidity,
2743 &olduid, &newuid) == STOP)
2744 return STOP;
2745 xmb = *mp;
2746 xmb.mb_cache_directory = NULL;
2747 xmb.mb_imap_mailbox = savestr((char *)name);
2748 xmb.mb_uidvalidity = uidvalidity;
2749 initcache(&xmb);
2750 if (m == NULL) {
2751 memset(&xm, 0, sizeof xm);
2752 xm.m_uid = olduid;
2753 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2754 return STOP;
2755 getcache(mp, &xm, NEED_HEADER);
2756 getcache(mp, &xm, NEED_BODY);
2757 } else {
2758 if ((m->m_flag & HAVE_HEADER) == 0)
2759 getcache(mp, m, NEED_HEADER);
2760 if ((m->m_flag & HAVE_BODY) == 0)
2761 getcache(mp, m, NEED_BODY);
2762 xm = *m;
2764 xm.m_uid = newuid;
2765 xm.m_flag &= ~MFULLYCACHED;
2766 putcache(&xmb, &xm);
2767 return OKAY;
2770 static enum okay
2771 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2772 long xsize, long size, long lines, int flag, const char *name)
2774 const char *cp;
2775 unsigned long uidvalidity, uid;
2776 struct mailbox xmb;
2777 struct message xm;
2779 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2780 imap_appenduid_parse(&cp[11], &uidvalidity,
2781 &uid) == STOP)
2782 return STOP;
2783 xmb = *mp;
2784 xmb.mb_cache_directory = NULL;
2785 xmb.mb_imap_mailbox = savestr((char *)name);
2786 xmb.mb_uidvalidity = uidvalidity;
2787 xmb.mb_otf = xmb.mb_itf = fp;
2788 initcache(&xmb);
2789 memset(&xm, 0, sizeof xm);
2790 xm.m_flag = (flag & MREAD) | MNEW;
2791 xm.m_time = t;
2792 xm.m_block = mailx_blockof(off1);
2793 xm.m_offset = mailx_offsetof(off1);
2794 xm.m_size = size;
2795 xm.m_xsize = xsize;
2796 xm.m_lines = xm.m_xlines = lines;
2797 xm.m_uid = uid;
2798 xm.m_have = HAVE_HEADER|HAVE_BODY;
2799 putcache(&xmb, &xm);
2800 return OKAY;
2803 static enum okay
2804 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2806 FILE *tp = NULL;
2807 time_t t;
2808 long size, xsize, ysize, lines;
2809 enum mflag flag = MNEW;
2810 char *name, *buf, *bp, *cp, *tempCopy;
2811 size_t bufsize, buflen, count;
2812 enum okay ok = STOP;
2814 buf = smalloc(bufsize = LINESIZE);
2815 buflen = 0;
2816 count = fsize(fp);
2817 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2818 goto stop;
2819 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
2820 while (*bp == ' ')
2821 bp++;
2822 if ((cp = strrchr(bp, '{')) == NULL)
2823 goto stop;
2824 xsize = atol(&cp[1]) + 2;
2825 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2826 goto stop;
2827 while (*cp == ' ')
2828 cp++;
2829 if (*cp == '(') {
2830 imap_getflags(cp, &cp, &flag);
2831 while (*++cp == ' ');
2833 t = imap_read_date_time(cp);
2834 if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
2835 goto stop;
2836 rm(tempCopy);
2837 Ftfree(&tempCopy);
2838 size = xsize;
2839 ysize = lines = 0;
2840 while (size > 0) {
2841 if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
2842 goto stop;
2843 size -= buflen;
2844 buf[--buflen] = '\0';
2845 buf[buflen-1] = '\n';
2846 fwrite(buf, 1, buflen, tp);
2847 ysize += buflen;
2848 lines++;
2850 fflush(tp);
2851 rewind(tp);
2852 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2853 imap_unquotestr(name));
2854 ok = OKAY;
2855 stop: free(buf);
2856 if (tp)
2857 Fclose(tp);
2858 return ok;
2861 static enum okay
2862 imap_search2(struct mailbox *mp, struct message *m, int count,
2863 const char *spec, int f)
2865 char *o;
2866 size_t osize;
2867 FILE *queuefp = NULL;
2868 enum okay ok = STOP;
2869 int i;
2870 unsigned long n;
2871 const char *cp;
2872 char *xp, *cs, c;
2874 c = 0;
2875 for (cp = spec; *cp; cp++)
2876 c |= *cp;
2877 if (c & 0200) {
2878 cp = gettcharset();
2879 #ifdef HAVE_ICONV
2880 if (asccasecmp(cp, "utf-8")) {
2881 iconv_t it;
2882 char *sp, *nsp, *nspec;
2883 size_t sz, nsz;
2884 if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
2885 sz = strlen(spec) + 1;
2886 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2887 sp = (char *)spec;
2888 if (iconv_ft(it, &sp, &sz, &nsp, &nsz, 0)
2889 != (size_t)-1 && sz == 0) {
2890 spec = nspec;
2891 cp = "utf-8";
2893 iconv_close(it);
2896 #endif /* HAVE_ICONV */
2897 cp = imap_quotestr(cp);
2898 cs = salloc(n = strlen(cp) + 10);
2899 snprintf(cs, n, "CHARSET %s ", cp);
2900 } else
2901 cs = "";
2902 o = ac_alloc(osize = strlen(spec) + 60);
2903 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2904 IMAP_OUT(o, MB_COMD, goto out)
2905 while (mp->mb_active & MB_COMD) {
2906 ok = imap_answer(mp, 0);
2907 if (response_status == RESPONSE_OTHER &&
2908 response_other == MAILBOX_DATA_SEARCH) {
2909 xp = responded_other_text;
2910 while (*xp && *xp != '\r') {
2911 n = strtoul(xp, &xp, 10);
2912 for (i = 0; i < count; i++)
2913 if (m[i].m_uid == n &&
2914 (m[i].m_flag&MHIDDEN)
2915 == 0 &&
2916 (f == MDELETED ||
2917 (m[i].m_flag&MDELETED)
2918 == 0))
2919 mark(i+1, f);
2923 out: ac_free(o);
2924 return ok;
2927 enum okay
2928 imap_search1(const char *spec, int f)
2930 sighandler_type saveint, savepipe;
2931 enum okay ok = STOP;
2933 (void)&saveint;
2934 (void)&savepipe;
2935 (void)&ok;
2936 if (mb.mb_type != MB_IMAP)
2937 return STOP;
2938 verbose = value("verbose") != NULL;
2939 imaplock = 1;
2940 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2941 safe_signal(SIGINT, maincatch);
2942 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2943 if (sigsetjmp(imapjmp, 1) == 0) {
2944 if (savepipe != SIG_IGN)
2945 safe_signal(SIGPIPE, imapcatch);
2946 ok = imap_search2(&mb, message, msgCount, spec, f);
2948 safe_signal(SIGINT, saveint);
2949 safe_signal(SIGPIPE, savepipe);
2950 imaplock = 0;
2951 if (interrupts)
2952 onintr(0);
2953 return ok;
2956 int
2957 imap_thisaccount(const char *cp)
2959 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2960 return 0;
2961 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2962 mb.mb_imap_account == NULL)
2963 return 0;
2964 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2967 enum okay
2968 imap_remove(const char *name)
2970 sighandler_type saveint, savepipe;
2971 enum okay ok = STOP;
2973 (void)&saveint;
2974 (void)&savepipe;
2975 (void)&ok;
2976 verbose = value("verbose") != NULL;
2977 if (mb.mb_type != MB_IMAP) {
2978 fprintf(stderr, "Refusing to remove \"%s\" "
2979 "in disconnected mode.\n", name);
2980 return STOP;
2982 if (!imap_thisaccount(name)) {
2983 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2984 "server: \"%s\" not removed.\n", name);
2985 return STOP;
2987 imaplock = 1;
2988 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2989 safe_signal(SIGINT, maincatch);
2990 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2991 if (sigsetjmp(imapjmp, 1) == 0) {
2992 if (savepipe != SIG_IGN)
2993 safe_signal(SIGPIPE, imapcatch);
2994 ok = imap_remove1(&mb, protfile(name));
2996 safe_signal(SIGINT, saveint);
2997 safe_signal(SIGPIPE, savepipe);
2998 imaplock = 0;
2999 if (ok == OKAY)
3000 ok = cache_remove(name);
3001 if (interrupts)
3002 onintr(0);
3003 return ok;
3006 static enum okay
3007 imap_remove1(struct mailbox *mp, const char *name)
3009 FILE *queuefp = NULL;
3010 char *o;
3011 int os;
3012 enum okay ok = STOP;
3014 o = ac_alloc(os = 2*strlen(name) + 100);
3015 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3016 IMAP_OUT(o, MB_COMD, goto out)
3017 while (mp->mb_active & MB_COMD)
3018 ok = imap_answer(mp, 1);
3019 out: ac_free(o);
3020 return ok;
3023 enum okay
3024 imap_rename(const char *old, const char *new)
3026 sighandler_type saveint, savepipe;
3027 enum okay ok = STOP;
3029 (void)&saveint;
3030 (void)&savepipe;
3031 (void)&ok;
3032 verbose = value("verbose") != NULL;
3033 if (mb.mb_type != MB_IMAP) {
3034 fprintf(stderr, "Refusing to rename mailboxes "
3035 "in disconnected mode.\n");
3036 return STOP;
3038 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3039 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3040 "server: \"%s\" not renamed to \"%s\".\n",
3041 old, new);
3042 return STOP;
3044 imaplock = 1;
3045 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3046 safe_signal(SIGINT, maincatch);
3047 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3048 if (sigsetjmp(imapjmp, 1) == 0) {
3049 if (savepipe != SIG_IGN)
3050 safe_signal(SIGPIPE, imapcatch);
3051 ok = imap_rename1(&mb, protfile(old), protfile(new));
3053 safe_signal(SIGINT, saveint);
3054 safe_signal(SIGPIPE, savepipe);
3055 imaplock = 0;
3056 if (ok == OKAY)
3057 ok = cache_rename(old, new);
3058 if (interrupts)
3059 onintr(0);
3060 return ok;
3063 static enum okay
3064 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3066 FILE *queuefp = NULL;
3067 char *o;
3068 int os;
3069 enum okay ok = STOP;
3071 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3072 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3073 imap_quotestr(old), imap_quotestr(new));
3074 IMAP_OUT(o, MB_COMD, goto out)
3075 while (mp->mb_active & MB_COMD)
3076 ok = imap_answer(mp, 1);
3077 out: ac_free(o);
3078 return ok;
3081 enum okay
3082 imap_dequeue(struct mailbox *mp, FILE *fp)
3084 FILE *queuefp = NULL;
3085 char o[LINESIZE], *newname;
3086 char *buf, *bp, *cp, iob[4096];
3087 size_t bufsize, buflen, count;
3088 enum okay ok = OKAY, rok = OKAY;
3089 long offs, offs1, offs2, octets;
3090 int twice, gotcha = 0;
3092 buf = smalloc(bufsize = LINESIZE);
3093 buflen = 0;
3094 count = fsize(fp);
3095 while (offs1 = ftell(fp),
3096 fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
3097 != NULL) {
3098 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3099 while (*bp == ' ')
3100 bp++;
3101 twice = 0;
3102 offs = ftell(fp);
3103 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3104 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3105 cp = &bp[9];
3106 while (digitchar(*cp&0377))
3107 cp++;
3108 if (*cp != ' ')
3109 goto fail;
3110 while (*cp == ' ')
3111 cp++;
3112 if ((newname = imap_strex(cp, NULL)) == NULL)
3113 goto fail;
3114 IMAP_OUT(o, MB_COMD, continue)
3115 while (mp->mb_active & MB_COMD)
3116 ok = imap_answer(mp, twice);
3117 if (response_status == RESPONSE_NO && twice++ == 0)
3118 goto trycreate;
3119 if (response_status == RESPONSE_OK &&
3120 mp->mb_flags & MB_UIDPLUS) {
3121 imap_copyuid(mp, NULL,
3122 imap_unquotestr(newname));
3124 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3125 IMAP_OUT(o, MB_COMD, continue)
3126 while (mp->mb_active & MB_COMD)
3127 ok = imap_answer(mp, 1);
3128 if (ok == OKAY)
3129 gotcha++;
3130 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3131 if ((cp = strrchr(bp, '{')) == NULL)
3132 goto fail;
3133 octets = atol(&cp[1]) + 2;
3134 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3135 goto fail;
3136 IMAP_OUT(o, MB_COMD, continue)
3137 while (mp->mb_active & MB_COMD) {
3138 ok = imap_answer(mp, twice);
3139 if (response_type == RESPONSE_CONT)
3140 break;
3142 if (ok == STOP) {
3143 if (twice++ == 0) {
3144 fseek(fp, offs, SEEK_SET);
3145 goto trycreate;
3147 goto fail;
3149 while (octets > 0) {
3150 size_t n = (size_t)octets > sizeof iob
3151 ? sizeof iob : (size_t)octets;
3152 octets -= n;
3153 if (n != fread(iob, 1, n, fp))
3154 goto fail;
3155 swrite1(&mp->mb_sock, iob, n, 1);
3157 swrite(&mp->mb_sock, "");
3158 while (mp->mb_active & MB_COMD) {
3159 ok = imap_answer(mp, 0);
3160 if (response_status == RESPONSE_NO &&
3161 twice++ == 0) {
3162 fseek(fp, offs, SEEK_SET);
3163 goto trycreate;
3166 if (response_status == RESPONSE_OK &&
3167 mp->mb_flags & MB_UIDPLUS) {
3168 offs2 = ftell(fp);
3169 fseek(fp, offs1, SEEK_SET);
3170 if (imap_appenduid_cached(mp, fp) == STOP) {
3171 fseek(fp, offs2, SEEK_SET);
3172 goto fail;
3175 } else {
3176 fail: fprintf(stderr,
3177 "Invalid command in IMAP cache queue: \"%s\"\n",
3178 bp);
3179 rok = STOP;
3181 continue;
3182 trycreate:
3183 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3184 tag(1), newname);
3185 IMAP_OUT(o, MB_COMD, continue)
3186 while (mp->mb_active & MB_COMD)
3187 ok = imap_answer(mp, 1);
3188 if (ok == OKAY)
3189 goto again;
3191 fflush(fp);
3192 rewind(fp);
3193 ftruncate(fileno(fp), 0);
3194 if (gotcha)
3195 imap_close(mp);
3196 return rok;
3199 static char *
3200 imap_strex(const char *cp, char **xp)
3202 const char *cq;
3203 char *n;
3205 if (*cp != '"')
3206 return NULL;
3207 for (cq = &cp[1]; *cq; cq++) {
3208 if (*cq == '\\')
3209 cq++;
3210 else if (*cq == '"')
3211 break;
3213 if (*cq != '"')
3214 return NULL;
3215 n = salloc(cq - cp + 2);
3216 memcpy(n, cp, cq - cp + 1);
3217 n[cq - cp + 1] = '\0';
3218 if (xp)
3219 *xp = (char *)&cq[1];
3220 return n;
3223 static enum okay
3224 check_expunged(void)
3226 if (expunged_messages > 0) {
3227 fprintf(stderr,
3228 "Command not executed - messages have been expunged\n");
3229 return STOP;
3231 return OKAY;
3234 /*ARGSUSED*/
3235 int
3236 cconnect(void *vp)
3238 char *cp, *cq;
3239 int omsgCount = msgCount;
3240 (void)vp;
3242 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3243 fprintf(stderr, "Already connected.\n");
3244 return 1;
3246 unset_allow_undefined = 1;
3247 unset_internal("disconnected");
3248 cp = protbase(mailname);
3249 if (strncmp(cp, "imap://", 7) == 0)
3250 cp += 7;
3251 else if (strncmp(cp, "imaps://", 8) == 0)
3252 cp += 8;
3253 if ((cq = strchr(cp, ':')) != NULL)
3254 *cq = '\0';
3255 unset_internal(savecat("disconnected-", cp));
3256 unset_allow_undefined = 0;
3257 if (mb.mb_type == MB_CACHE) {
3258 imap_setfile1(mailname, 0, edit, 1);
3259 if (msgCount > omsgCount)
3260 newmailinfo(omsgCount);
3262 return 0;
3265 int
3266 cdisconnect(void *vp)
3268 int *msgvec = vp;
3270 if (mb.mb_type == MB_CACHE) {
3271 fprintf(stderr, "Not connected.\n");
3272 return 1;
3273 } else if (mb.mb_type == MB_IMAP) {
3274 if (cached_uidvalidity(&mb) == 0) {
3275 fprintf(stderr, "The current mailbox is not cached.\n");
3276 return 1;
3279 if (*msgvec)
3280 ccache(vp);
3281 assign("disconnected", "");
3282 if (mb.mb_type == MB_IMAP) {
3283 sclose(&mb.mb_sock);
3284 imap_setfile1(mailname, 0, edit, 1);
3286 return 0;
3288 int
3289 ccache(void *vp)
3291 int *msgvec = vp, *ip;
3292 struct message *mp;
3294 if (mb.mb_type != MB_IMAP) {
3295 fprintf(stderr, "Not connected to an IMAP server.\n");
3296 return 1;
3298 if (cached_uidvalidity(&mb) == 0) {
3299 fprintf(stderr, "The current mailbox is not cached.\n");
3300 return 1;
3302 for (ip = msgvec; *ip; ip++) {
3303 mp = &message[*ip-1];
3304 if (!(mp->m_have & HAVE_BODY))
3305 get_body(mp);
3307 return 0;
3309 #else /* !USE_IMAP */
3311 #include "extern.h"
3313 static void
3314 noimap(void)
3316 fprintf(stderr, catgets(catd, CATSET, 269,
3317 "No IMAP support compiled in.\n"));
3320 int
3321 imap_setfile(const char *server, int newmail, int isedit)
3323 (void)server;
3324 (void)newmail;
3325 (void)isedit;
3326 noimap();
3327 return -1;
3330 enum okay
3331 imap_header(struct message *mp)
3333 (void)mp;
3334 noimap();
3335 return STOP;
3338 enum okay
3339 imap_body(struct message *mp)
3341 (void)mp;
3342 noimap();
3343 return STOP;
3346 void
3347 imap_getheaders(int bot, int top)
3349 (void)bot;
3350 (void)top;
3353 void
3354 imap_quit(void)
3356 noimap();
3359 /*ARGSUSED*/
3360 int
3361 imap_imap(void *vp)
3363 (void)vp;
3364 noimap();
3365 return 1;
3368 /*ARGSUSED*/
3369 int
3370 imap_newmail(int dummy)
3372 (void)dummy;
3373 return 0;
3376 /*ARGSUSED*/
3377 enum okay
3378 imap_undelete(struct message *m, int n)
3380 (void)m;
3381 (void)n;
3382 return STOP;
3385 /*ARGSUSED*/
3386 enum okay
3387 imap_unread(struct message *m, int n)
3389 (void)m;
3390 (void)n;
3391 return STOP;
3394 /*ARGSUSED*/
3395 enum okay
3396 imap_append(const char *server, FILE *fp)
3398 (void)server;
3399 (void)fp;
3400 noimap();
3401 return STOP;
3404 /*ARGSUSED*/
3405 void
3406 imap_folders(const char *name, int strip)
3408 (void)name;
3409 (void)strip;
3410 noimap();
3413 /*ARGSUSED*/
3414 enum okay
3415 imap_remove(const char *name)
3417 (void)name;
3418 noimap();
3419 return STOP;
3422 /*ARGSUSED*/
3423 enum okay
3424 imap_rename(const char *old, const char *new)
3426 (void)old;
3427 (void)new;
3428 noimap();
3429 return STOP;
3432 enum okay
3433 imap_copy(struct message *m, int n, const char *name)
3435 (void)m;
3436 (void)n;
3437 (void)name;
3438 noimap();
3439 return STOP;
3442 /*ARGSUSED*/
3443 enum okay
3444 imap_search1(const char *spec, int f)
3446 (void)spec;
3447 (void)f;
3448 return STOP;
3451 int
3452 imap_thisaccount(const char *cp)
3454 (void)cp;
3455 return 0;
3458 enum okay
3459 imap_noop(void)
3461 noimap();
3462 return STOP;
3465 /*ARGSUSED*/
3466 int
3467 cconnect(void *vp)
3469 (void)vp;
3470 noimap();
3471 return 1;
3474 /*ARGSUSED*/
3475 int
3476 cdisconnect(void *vp)
3478 (void)vp;
3479 noimap();
3480 return 1;
3483 /*ARGSUSED*/
3484 int
3485 ccache(void *vp)
3487 (void)vp;
3488 noimap();
3489 return 1;
3491 #endif /* USE_IMAP */
3493 time_t
3494 imap_read_date_time(const char *cp)
3496 time_t t;
3497 int i, year, month, day, hour, minute, second;
3498 int sign = -1;
3499 char buf[3];
3502 * "25-Jul-2004 15:33:44 +0200"
3503 * | | | | | |
3504 * 0 5 10 15 20 25
3506 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3507 goto invalid;
3508 day = strtol(&cp[1], NULL, 10);
3509 for (i = 0; month_names[i]; i++)
3510 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3511 break;
3512 if (month_names[i] == NULL)
3513 goto invalid;
3514 month = i + 1;
3515 year = strtol(&cp[8], NULL, 10);
3516 hour = strtol(&cp[13], NULL, 10);
3517 minute = strtol(&cp[16], NULL, 10);
3518 second = strtol(&cp[19], NULL, 10);
3519 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3520 (time_t)-1)
3521 goto invalid;
3522 switch (cp[22]) {
3523 case '-':
3524 sign = 1;
3525 break;
3526 case '+':
3527 break;
3528 default:
3529 goto invalid;
3531 buf[2] = '\0';
3532 buf[0] = cp[23];
3533 buf[1] = cp[24];
3534 t += strtol(buf, NULL, 10) * sign * 3600;
3535 buf[0] = cp[25];
3536 buf[1] = cp[26];
3537 t += strtol(buf, NULL, 10) * sign * 60;
3538 return t;
3539 invalid:
3540 time(&t);
3541 return t;
3544 time_t
3545 imap_read_date(const char *cp)
3547 time_t t;
3548 int year, month, day, i, tzdiff;
3549 struct tm *tmptr;
3550 char *xp, *yp;
3552 if (*cp == '"')
3553 cp++;
3554 day = strtol(cp, &xp, 10);
3555 if (day <= 0 || day > 31 || *xp++ != '-')
3556 return -1;
3557 for (i = 0; month_names[i]; i++)
3558 if (ascncasecmp(xp, month_names[i], 3) == 0)
3559 break;
3560 if (month_names[i] == NULL)
3561 return -1;
3562 month = i+1;
3563 if (xp[3] != '-')
3564 return -1;
3565 year = strtol(&xp[4], &yp, 10);
3566 if (year < 1970 || year > 2037 || yp != &xp[8])
3567 return -1;
3568 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3569 return -1;
3570 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3571 return -1;
3572 tzdiff = t - mktime(gmtime(&t));
3573 tmptr = localtime(&t);
3574 if (tmptr->tm_isdst > 0)
3575 tzdiff += 3600;
3576 t -= tzdiff;
3577 return t;
3580 const char *
3581 imap_make_date_time(time_t t)
3583 static char s[30];
3584 struct tm *tmptr;
3585 int tzdiff, tzdiff_hour, tzdiff_min;
3587 tzdiff = t - mktime(gmtime(&t));
3588 tzdiff_hour = (int)(tzdiff / 60);
3589 tzdiff_min = tzdiff_hour % 60;
3590 tzdiff_hour /= 60;
3591 tmptr = localtime(&t);
3592 if (tmptr->tm_isdst > 0)
3593 tzdiff_hour++;
3594 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3595 tmptr->tm_mday,
3596 month_names[tmptr->tm_mon],
3597 tmptr->tm_year + 1900,
3598 tmptr->tm_hour,
3599 tmptr->tm_min,
3600 tmptr->tm_sec,
3601 tzdiff_hour,
3602 tzdiff_min);
3603 return s;
3606 char *
3607 imap_quotestr(const char *s)
3609 char *n, *np;
3611 np = n = salloc(2 * strlen(s) + 3);
3612 *np++ = '"';
3613 while (*s) {
3614 if (*s == '"' || *s == '\\')
3615 *np++ = '\\';
3616 *np++ = *s++;
3618 *np++ = '"';
3619 *np = '\0';
3620 return n;
3623 char *
3624 imap_unquotestr(const char *s)
3626 char *n, *np;
3628 if (*s != '"')
3629 return savestr(s);
3630 np = n = salloc(strlen(s) + 1);
3631 while (*++s) {
3632 if (*s == '\\')
3633 s++;
3634 else if (*s == '"')
3635 break;
3636 *np++ = *s;
3638 *np = '\0';
3639 return n;