Offer a simple coloured display..
[s-mailx.git] / imap.c
blob254c7760a10ea05d650d2bae839ce2f87353b4c2
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #ifdef HAVE_IMAP
45 # include <sys/socket.h>
47 # include <netdb.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
53 # endif
54 # ifdef HAVE_MD5
55 # include "md5.h"
56 # endif
57 #endif
59 #ifdef HAVE_IMAP
60 #define IMAP_ANSWER() { \
61 if (mp->mb_type != MB_CACHE) { \
62 enum okay ok = OKAY; \
63 while (mp->mb_active & MB_COMD) \
64 ok = imap_answer(mp, 1); \
65 if (ok == STOP) \
66 return STOP; \
67 } \
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL)\
77 do {\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
80 ACTIONBAIL;\
82 if (options & OPT_VERBOSE)\
83 fprintf(stderr, ">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static volatile int imaplock;
172 static int same_imap_account;
174 static void imap_other_get(char *pp);
175 static void imap_response_get(const char **cp);
176 static void imap_response_parse(void);
177 static enum okay imap_answer(struct mailbox *mp, int errprnt);
178 static enum okay imap_parse_list(void);
179 static enum okay imap_finish(struct mailbox *mp);
180 static void imap_timer_off(void);
181 static void imapcatch(int s);
182 static void _imap_maincatch(int s);
183 static enum okay imap_noop1(struct mailbox *mp);
184 static void rec_queue(enum rec_type type, unsigned long cnt);
185 static enum okay rec_dequeue(void);
186 static void rec_rmqueue(void);
187 static void imapalarm(int s);
188 static int imap_use_starttls(const char *uhp);
189 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
190 const char *uhp);
191 static enum okay imap_capability(struct mailbox *mp);
192 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
193 char *xuser, char *pass);
194 #ifdef HAVE_MD5
195 static enum okay imap_cram_md5(struct mailbox *mp,
196 char *xuser, char *xpass);
197 #endif
198 static enum okay imap_login(struct mailbox *mp, char *xuser, char *xpass);
199 #ifdef HAVE_GSSAPI
200 static enum okay imap_gss(struct mailbox *mp, char *user);
201 #endif
202 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
203 static void imap_init(struct mailbox *mp, int n);
204 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
205 int *prevcount);
206 static void imap_split(char **server, const char **sp, int *use_ssl,
207 const char **cp, char const **uhp, char const **mbx,
208 char **pass, char **user);
209 static int imap_setfile1(const char *xserver, int nmail, int isedit,
210 int transparent);
211 static int imap_fetchdata(struct mailbox *mp, struct message *m,
212 size_t expected, int need,
213 const char *head, size_t headsize, long headlines);
214 static void imap_putstr(struct mailbox *mp, struct message *m,
215 const char *str,
216 const char *head, size_t headsize, long headlines);
217 static enum okay imap_get(struct mailbox *mp, struct message *m,
218 enum needspec need);
219 static void commitmsg(struct mailbox *mp, struct message *to,
220 struct message *from, enum havespec have);
221 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
222 int bot, int top);
223 static enum okay imap_exit(struct mailbox *mp);
224 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
225 needstat);
226 static enum okay imap_close(struct mailbox *mp);
227 static enum okay imap_update(struct mailbox *mp);
228 static enum okay imap_store(struct mailbox *mp, struct message *m,
229 int n, int c, const char *sp, int needstat);
230 static enum okay imap_unstore(struct message *m, int n, const char *flag);
231 static const char *tag(int new);
232 static char *imap_putflags(int f);
233 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
234 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
235 off_t off1, long xsize, enum mflag flag, time_t t);
236 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
237 static enum okay imap_list1(struct mailbox *mp, const char *base,
238 struct list_item **list, struct list_item **lend, int level);
239 static enum okay imap_list(struct mailbox *mp, const char *base,
240 int strip, FILE *fp);
241 static void dopr(FILE *fp);
242 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
243 const char *name);
244 static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
245 unsigned long *olduid, unsigned long *newuid);
246 static enum okay imap_appenduid_parse(const char *cp,
247 unsigned long *uidvalidity, unsigned long *uid);
248 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
249 const char *name);
250 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
251 long off1, long xsize, long size, long lines,
252 int flag, const char *name);
253 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
254 static enum okay imap_search2(struct mailbox *mp, struct message *m,
255 int cnt, const char *spec, int f);
256 static enum okay imap_remove1(struct mailbox *mp, const char *name);
257 static enum okay imap_rename1(struct mailbox *mp, const char *old,
258 const char *new);
259 static char *imap_strex(char const *cp, char const **xp);
260 static enum okay check_expunged(void);
262 static void
263 imap_other_get(char *pp)
265 char *xp;
267 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
268 pp += 6;
269 response_other = MAILBOX_DATA_FLAGS;
270 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
271 pp += 5;
272 response_other = MAILBOX_DATA_LIST;
273 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
274 pp += 5;
275 response_other = MAILBOX_DATA_LSUB;
276 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
277 pp += 8;
278 response_other = MAILBOX_DATA_MAILBOX;
279 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
280 pp += 7;
281 response_other = MAILBOX_DATA_SEARCH;
282 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
283 pp += 7;
284 response_other = MAILBOX_DATA_STATUS;
285 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
286 pp += 11;
287 response_other = CAPABILITY_DATA;
288 } else {
289 responded_other_number = strtol(pp, &xp, 10);
290 while (*xp == ' ')
291 xp++;
292 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
293 response_other = MAILBOX_DATA_EXISTS;
294 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
295 response_other = MAILBOX_DATA_RECENT;
296 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
297 response_other = MESSAGE_DATA_EXPUNGE;
298 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
299 pp = &xp[6];
300 response_other = MESSAGE_DATA_FETCH;
301 } else
302 response_other = RESPONSE_OTHER_UNKNOWN;
304 responded_other_text = pp;
307 static void
308 imap_response_get(const char **cp)
310 if (ascncasecmp(*cp, "OK ", 3) == 0) {
311 *cp += 3;
312 response_status = RESPONSE_OK;
313 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
314 *cp += 3;
315 response_status = RESPONSE_NO;
316 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
317 *cp += 4;
318 response_status = RESPONSE_BAD;
319 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
320 *cp += 8;
321 response_status = RESPONSE_PREAUTH;
322 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
323 *cp += 4;
324 response_status = RESPONSE_BYE;
325 } else
326 response_status = RESPONSE_OTHER;
329 static void
330 imap_response_parse(void)
332 static char *parsebuf; /* TODO Use pool */
333 static size_t parsebufsize;
334 const char *ip = imapbuf;
335 char *pp;
337 if (parsebufsize < imapbufsize)
338 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
339 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
340 pp = parsebuf;
341 switch (*ip) {
342 case '+':
343 response_type = RESPONSE_CONT;
344 ip++;
345 pp++;
346 while (*ip == ' ') {
347 ip++;
348 pp++;
350 break;
351 case '*':
352 ip++;
353 pp++;
354 while (*ip == ' ') {
355 ip++;
356 pp++;
358 imap_response_get(&ip);
359 pp = &parsebuf[ip - imapbuf];
360 switch (response_status) {
361 case RESPONSE_BYE:
362 response_type = RESPONSE_FATAL;
363 break;
364 default:
365 response_type = RESPONSE_DATA;
367 break;
368 default:
369 responded_tag = parsebuf;
370 while (*pp && *pp != ' ')
371 pp++;
372 if (*pp == '\0') {
373 response_type = RESPONSE_ILLEGAL;
374 break;
376 *pp++ = '\0';
377 while (*pp && *pp == ' ')
378 pp++;
379 if (*pp == '\0') {
380 response_type = RESPONSE_ILLEGAL;
381 break;
383 ip = &imapbuf[pp - parsebuf];
384 response_type = RESPONSE_TAGGED;
385 imap_response_get(&ip);
386 pp = &parsebuf[ip - imapbuf];
388 responded_text = pp;
389 if (response_type != RESPONSE_CONT &&
390 response_type != RESPONSE_ILLEGAL &&
391 response_status == RESPONSE_OTHER)
392 imap_other_get(pp);
395 static enum okay
396 imap_answer(struct mailbox *mp, int errprnt)
398 int i, complete;
399 enum okay ok = STOP;
401 if (mp->mb_type == MB_CACHE)
402 return OKAY;
403 again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
404 if (options & OPT_VERBOSE)
405 fputs(imapbuf, stderr);
406 imap_response_parse();
407 if (response_type == RESPONSE_ILLEGAL)
408 goto again;
409 if (response_type == RESPONSE_CONT)
410 return OKAY;
411 if (response_status == RESPONSE_OTHER) {
412 if (response_other == MAILBOX_DATA_EXISTS) {
413 had_exists = responded_other_number;
414 rec_queue(REC_EXISTS, responded_other_number);
415 if (had_expunge > 0)
416 had_expunge = 0;
417 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
418 rec_queue(REC_EXPUNGE, responded_other_number);
419 if (had_expunge < 0)
420 had_expunge = 0;
421 had_expunge++;
422 expunged_messages++;
425 complete = 0;
426 if (response_type == RESPONSE_TAGGED) {
427 if (asccasecmp(responded_tag, tag(0)) == 0)
428 complete |= 1;
429 else
430 goto again;
432 switch (response_status) {
433 case RESPONSE_PREAUTH:
434 mp->mb_active &= ~MB_PREAUTH;
435 /*FALLTHRU*/
436 case RESPONSE_OK:
437 okay: ok = OKAY;
438 complete |= 2;
439 break;
440 case RESPONSE_NO:
441 case RESPONSE_BAD:
442 stop: ok = STOP;
443 complete |= 2;
444 if (errprnt)
445 fprintf(stderr, tr(270, "IMAP error: %s"),
446 responded_text);
447 break;
448 case RESPONSE_UNKNOWN: /* does not happen */
449 case RESPONSE_BYE:
450 i = mp->mb_active;
451 mp->mb_active = MB_NONE;
452 if (i & MB_BYE)
453 goto okay;
454 else
455 goto stop;
456 case RESPONSE_OTHER:
457 ok = OKAY;
459 if (response_status != RESPONSE_OTHER &&
460 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
461 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
462 if (complete == 3)
463 mp->mb_active &= ~MB_COMD;
464 } else {
465 ok = STOP;
466 mp->mb_active = MB_NONE;
468 return ok;
471 static enum okay
472 imap_parse_list(void)
474 char *cp;
476 cp = responded_other_text;
477 list_attributes = LIST_NONE;
478 if (*cp == '(') {
479 while (*cp && *cp != ')') {
480 if (*cp == '\\') {
481 if (ascncasecmp(&cp[1], "Noinferiors ", 12)
482 == 0) {
483 list_attributes |= LIST_NOINFERIORS;
484 cp += 12;
485 } else if (ascncasecmp(&cp[1], "Noselect ", 9)
486 == 0) {
487 list_attributes |= LIST_NOSELECT;
488 cp += 9;
489 } else if (ascncasecmp(&cp[1], "Marked ", 7)
490 == 0) {
491 list_attributes |= LIST_MARKED;
492 cp += 7;
493 } else if (ascncasecmp(&cp[1], "Unmarked ", 9)
494 == 0) {
495 list_attributes |= LIST_UNMARKED;
496 cp += 9;
499 cp++;
501 if (*++cp != ' ')
502 return STOP;
503 while (*cp == ' ')
504 cp++;
506 list_hierarchy_delimiter = EOF;
507 if (*cp == '"') {
508 if (*++cp == '\\')
509 cp++;
510 list_hierarchy_delimiter = *cp++ & 0377;
511 if (cp[0] != '"' || cp[1] != ' ')
512 return STOP;
513 cp++;
514 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
515 cp[3] == ' ') {
516 list_hierarchy_delimiter = EOF;
517 cp += 3;
519 while (*cp == ' ')
520 cp++;
521 list_name = cp;
522 while (*cp && *cp != '\r')
523 cp++;
524 *cp = '\0';
525 return OKAY;
528 static enum okay
529 imap_finish(struct mailbox *mp)
531 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
532 imap_answer(mp, 1);
533 return OKAY;
536 static void
537 imap_timer_off(void)
539 if (imapkeepalive > 0) {
540 alarm(0);
541 safe_signal(SIGALRM, savealrm);
545 static void
546 imapcatch(int s)
548 termios_state_reset();
549 switch (s) {
550 case SIGINT:
551 fprintf(stderr, tr(102, "Interrupt\n"));
552 siglongjmp(imapjmp, 1);
553 /*NOTREACHED*/
554 case SIGPIPE:
555 fprintf(stderr, tr(98,
556 "Received SIGPIPE during IMAP operation\n"));
557 break;
561 static void
562 _imap_maincatch(int s)
564 (void)s;
565 if (interrupts++ == 0) {
566 fprintf(stderr, tr(102, "Interrupt\n"));
567 return;
569 onintr(0);
572 static enum okay
573 imap_noop1(struct mailbox *mp)
575 char o[LINESIZE];
576 FILE *queuefp = NULL;
578 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
579 IMAP_OUT(o, MB_COMD, return STOP)
580 IMAP_ANSWER()
581 return OKAY;
584 FL char const *
585 imap_fileof(char const *xcp)
587 char const *cp = xcp;
588 int state = 0;
590 while (*cp) {
591 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
592 cp += 3;
593 state = 1;
595 if (cp[0] == '/' && state == 1)
596 return (cp + 1);
597 if (cp[0] == '/')
598 return (xcp);
599 ++cp;
601 return (cp);
604 FL enum okay
605 imap_noop(void)
607 sighandler_type volatile oldint, oldpipe;
608 enum okay ok = STOP;
610 if (mb.mb_type != MB_IMAP)
611 return STOP;
612 imaplock = 1;
613 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
614 safe_signal(SIGINT, &_imap_maincatch);
615 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
616 if (sigsetjmp(imapjmp, 1) == 0) {
617 if (oldpipe != SIG_IGN)
618 safe_signal(SIGPIPE, imapcatch);
619 ok = imap_noop1(&mb);
621 safe_signal(SIGINT, oldint);
622 safe_signal(SIGPIPE, oldpipe);
623 imaplock = 0;
624 if (interrupts)
625 onintr(0);
626 return ok;
629 static void
630 rec_queue(enum rec_type rt, unsigned long cnt)
632 struct record *rp;
634 rp = scalloc(1, sizeof *rp);
635 rp->rec_type = rt;
636 rp->rec_count = cnt;
637 if (record && recend) {
638 recend->rec_next = rp;
639 recend = rp;
640 } else
641 record = recend = rp;
644 static enum okay
645 rec_dequeue(void)
647 struct message *omessage;
648 enum okay ok = OKAY;
649 struct record *rp = record, *rq = NULL;
650 unsigned long exists = 0, i;
652 if (record == NULL)
653 return STOP;
654 omessage = message;
655 message = smalloc((msgCount+1) * sizeof *message);
656 if (msgCount)
657 memcpy(message, omessage, msgCount * sizeof *message);
658 memset(&message[msgCount], 0, sizeof *message);
659 while (rp) {
660 switch (rp->rec_type) {
661 case REC_EXISTS:
662 exists = rp->rec_count;
663 break;
664 case REC_EXPUNGE:
665 if (rp->rec_count == 0) {
666 ok = STOP;
667 break;
669 if (rp->rec_count > (unsigned long)msgCount) {
670 if (exists == 0 || rp->rec_count > exists--)
671 ok = STOP;
672 break;
674 if (exists > 0)
675 exists--;
676 delcache(&mb, &message[rp->rec_count-1]);
677 memmove(&message[rp->rec_count-1],
678 &message[rp->rec_count],
679 (msgCount - rp->rec_count + 1) *
680 sizeof *message);
681 msgCount--;
683 * If the message was part of a collapsed thread,
684 * the m_collapsed field of one of its ancestors
685 * should be incremented. It seems hardly possible
686 * to do this with the current message structure,
687 * though. The result is that a '+' may be shown
688 * in the header summary even if no collapsed
689 * children exists.
691 break;
693 if (rq != NULL)
694 free(rq);
695 rq = rp;
696 rp = rp->rec_next;
698 if (rq != NULL)
699 free(rq);
700 record = recend = NULL;
701 if (ok == OKAY && exists > (unsigned long)msgCount) {
702 message = srealloc(message,
703 (exists + 1) * sizeof *message);
704 memset(&message[msgCount], 0,
705 (exists - msgCount + 1) * sizeof *message);
706 for (i = msgCount; i < exists; i++)
707 imap_init(&mb, i);
708 imap_flags(&mb, msgCount+1, exists);
709 msgCount = exists;
711 if (ok == STOP) {
712 free(message);
713 message = omessage;
715 return ok;
718 static void
719 rec_rmqueue(void)
721 struct record *rp;
723 for (rp = record; rp != NULL;) {
724 struct record *tmp = rp;
725 rp = rp->rec_next;
726 free(tmp);
728 record = recend = NULL;
731 /*ARGSUSED*/
732 static void
733 imapalarm(int s)
735 sighandler_type volatile saveint, savepipe;
736 UNUSED(s);
738 if (imaplock++ == 0) {
739 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
740 safe_signal(SIGINT, &_imap_maincatch);
741 savepipe = safe_signal(SIGPIPE, SIG_IGN);
742 if (sigsetjmp(imapjmp, 1)) {
743 safe_signal(SIGINT, saveint);
744 safe_signal(SIGPIPE, savepipe);
745 goto brk;
747 if (savepipe != SIG_IGN)
748 safe_signal(SIGPIPE, imapcatch);
749 if (imap_noop1(&mb) != OKAY) {
750 safe_signal(SIGINT, saveint);
751 safe_signal(SIGPIPE, savepipe);
752 goto out;
754 safe_signal(SIGINT, saveint);
755 safe_signal(SIGPIPE, savepipe);
757 brk: alarm(imapkeepalive);
758 out: imaplock--;
761 static int
762 imap_use_starttls(const char *uhp)
764 char *var;
766 if (ok_blook(imap_use_starttls))
767 return 1;
768 var = savecat("imap-use-starttls-", uhp);
769 return vok_blook(var);
772 static enum okay
773 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
775 char *cp;
777 mp->mb_active |= MB_PREAUTH;
778 imap_answer(mp, 1);
779 if ((cp = strchr(xserver, ':')) != NULL) {
780 char *x = salloc(cp - xserver + 1);
781 memcpy(x, xserver, cp - xserver);
782 x[cp - xserver] = '\0';
783 xserver = x;
785 #ifdef HAVE_SSL
786 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
787 FILE *queuefp = NULL;
788 char o[LINESIZE];
790 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
791 IMAP_OUT(o, MB_COMD, return STOP)
792 IMAP_ANSWER()
793 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
794 return STOP;
796 #else
797 if (imap_use_starttls(uhp)) {
798 fprintf(stderr, "No SSL support compiled in.\n");
799 return STOP;
801 #endif
802 imap_capability(mp);
803 return OKAY;
806 static enum okay
807 imap_capability(struct mailbox *mp)
809 char o[LINESIZE];
810 FILE *queuefp = NULL;
811 enum okay ok = STOP;
812 const char *cp;
814 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
815 IMAP_OUT(o, MB_COMD, return STOP)
816 while (mp->mb_active & MB_COMD) {
817 ok = imap_answer(mp, 0);
818 if (response_status == RESPONSE_OTHER &&
819 response_other == CAPABILITY_DATA) {
820 cp = responded_other_text;
821 while (*cp) {
822 while (spacechar(*cp&0377))
823 cp++;
824 if (strncmp(cp, "UIDPLUS", 7) == 0 &&
825 spacechar(cp[7]&0377))
826 /* RFC 2359 */
827 mp->mb_flags |= MB_UIDPLUS;
828 while (*cp && !spacechar(*cp&0377))
829 cp++;
833 return ok;
836 static enum okay
837 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, char *pass)
839 char *var;
840 char *auth;
842 if (!(mp->mb_active & MB_PREAUTH))
843 return OKAY;
844 if ((auth = ok_vlook(imap_auth)) == NULL) {
845 size_t i = strlen(uhp) + 1;
846 var = ac_alloc(i + 10);
847 memcpy(var, "imap-auth-", 10);
848 memcpy(var + 10, uhp, i);
849 auth = vok_vlook(var);
850 ac_free(var);
852 if (auth == NULL || strcmp(auth, "login") == 0)
853 return imap_login(mp, xuser, pass);
854 if (strcmp(auth, "cram-md5") == 0) {
855 #ifdef HAVE_MD5
856 return imap_cram_md5(mp, xuser, pass);
857 #else
858 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
859 return (STOP);
860 #endif
862 if (strcmp(auth, "gssapi") == 0) {
863 #ifdef HAVE_GSSAPI
864 return imap_gss(mp, xuser);
865 #else
866 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
867 return STOP;
868 #endif
870 fprintf(stderr, tr(273, "Unknown IMAP authentication method: %s\n"),
871 auth);
872 return STOP;
876 * Implementation of RFC 2194.
878 #ifdef HAVE_MD5
879 static enum okay
880 imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass)
882 char o[LINESIZE], *user, *pass, *cp;
883 FILE *queuefp = NULL;
884 enum okay ok = STOP;
886 jretry:
887 user = xuser;
888 pass = xpass;
889 if (! getcredentials(&user, &pass))
890 goto jleave;
892 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
893 IMAP_XOUT(o, 0, goto jleave, goto jleave);
894 imap_answer(mp, 1);
895 if (response_type != RESPONSE_CONT)
896 goto jleave;
898 cp = cram_md5_string(user, pass, responded_text);
899 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
900 while (mp->mb_active & MB_COMD)
901 ok = imap_answer(mp, 1);
902 if (ok == STOP) {
903 xpass = NULL;
904 goto jretry;
906 jleave:
907 return ok;
909 #endif /* HAVE_MD5 */
911 static enum okay
912 imap_login(struct mailbox *mp, char *xuser, char *xpass)
914 char o[LINESIZE];
915 char *user, *pass;
916 FILE *queuefp = NULL;
917 enum okay ok = STOP;
919 jretry:
920 user = xuser;
921 pass = xpass;
922 if (! getcredentials(&user, &pass))
923 goto jleave;
925 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
926 tag(1), imap_quotestr(user), imap_quotestr(pass));
927 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
928 while (mp->mb_active & MB_COMD)
929 ok = imap_answer(mp, 1);
930 if (ok == STOP) {
931 xpass = NULL;
932 goto jretry;
934 jleave:
935 return ok;
938 #ifdef HAVE_GSSAPI
939 # include "imap_gssapi.h"
940 #endif
942 FL enum okay
943 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
945 enum okay ok = OKAY;
946 char const*cp;
947 char o[LINESIZE];
948 FILE *queuefp = NULL;
949 (void)size;
951 mp->mb_uidvalidity = 0;
952 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
953 IMAP_OUT(o, MB_COMD, return STOP)
954 while (mp->mb_active & MB_COMD) {
955 ok = imap_answer(mp, 1);
956 if (response_status != RESPONSE_OTHER &&
957 (cp = asccasestr(responded_text,
958 "[UIDVALIDITY ")) != NULL)
959 mp->mb_uidvalidity = atol(&cp[13]);
961 *cnt = had_exists > 0 ? had_exists : 0;
962 if (response_status != RESPONSE_OTHER &&
963 ascncasecmp(responded_text, "[READ-ONLY] ", 12)
964 == 0)
965 mp->mb_perm = 0;
966 return ok;
969 static enum okay
970 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
972 char o[LINESIZE];
973 FILE *queuefp = NULL;
974 char const *cp;
975 struct message *m;
976 unsigned x = X, y = Y, n;
978 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
979 IMAP_OUT(o, MB_COMD, return STOP)
980 while (mp->mb_active & MB_COMD) {
981 imap_answer(mp, 1);
982 if (response_status == RESPONSE_OTHER &&
983 response_other == MESSAGE_DATA_FETCH) {
984 n = responded_other_number;
985 if (n < x || n > y)
986 continue;
987 m = &message[n-1];
988 m->m_xsize = 0;
989 } else
990 continue;
991 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
992 cp += 5;
993 while (*cp == ' ')
994 cp++;
995 if (*cp == '(')
996 imap_getflags(cp, &cp, &m->m_flag);
998 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
999 m->m_uid = strtoul(&cp[4], NULL, 10);
1000 getcache1(mp, m, NEED_UNSPEC, 1);
1001 m->m_flag &= ~MHIDDEN;
1003 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1004 x++;
1005 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1006 y--;
1007 if (x <= y) {
1008 snprintf(o, sizeof o,
1009 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1010 tag(1), x, y);
1011 IMAP_OUT(o, MB_COMD, return STOP)
1012 while (mp->mb_active & MB_COMD) {
1013 imap_answer(mp, 1);
1014 if (response_status == RESPONSE_OTHER &&
1015 response_other == MESSAGE_DATA_FETCH) {
1016 n = responded_other_number;
1017 if (n < x || n > y)
1018 continue;
1019 m = &message[n-1];
1020 } else
1021 continue;
1022 if ((cp = asccasestr(responded_other_text,
1023 "RFC822.SIZE ")) != NULL)
1024 m->m_xsize = strtol(&cp[12], NULL, 10);
1025 if ((cp = asccasestr(responded_other_text,
1026 "INTERNALDATE ")) != NULL)
1027 m->m_time = imap_read_date_time(&cp[13]);
1030 for (n = X; n <= Y; n++)
1031 putcache(mp, &message[n-1]);
1032 return OKAY;
1035 static void
1036 imap_init(struct mailbox *mp, int n)
1038 struct message *m = &message[n];
1039 (void)mp;
1041 m->m_flag = MUSED|MNOFROM;
1042 m->m_block = 0;
1043 m->m_offset = 0;
1046 static void
1047 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1049 struct message *omessage = 0;
1050 int i, omsgCount = 0;
1051 enum okay dequeued = STOP;
1053 if (nmail || transparent) {
1054 omessage = message;
1055 omsgCount = msgCount;
1057 if (nmail)
1058 dequeued = rec_dequeue();
1059 if (had_exists >= 0) {
1060 if (dequeued != OKAY)
1061 msgCount = had_exists;
1062 had_exists = -1;
1064 if (had_expunge >= 0) {
1065 if (dequeued != OKAY)
1066 msgCount -= had_expunge;
1067 had_expunge = -1;
1069 if (nmail && expunged_messages)
1070 printf("Expunged %ld message%s.\n",
1071 expunged_messages,
1072 expunged_messages != 1 ? "s" : "");
1073 *prevcount = omsgCount - expunged_messages;
1074 expunged_messages = 0;
1075 if (msgCount < 0) {
1076 fputs("IMAP error: Negative message count\n", stderr);
1077 msgCount = 0;
1079 if (dequeued != OKAY) {
1080 message = scalloc(msgCount + 1, sizeof *message);
1081 for (i = 0; i < msgCount; i++)
1082 imap_init(mp, i);
1083 if (!nmail && mp->mb_type == MB_IMAP)
1084 initcache(mp);
1085 if (msgCount > 0)
1086 imap_flags(mp, 1, msgCount);
1087 message[msgCount].m_size = 0;
1088 message[msgCount].m_lines = 0;
1089 rec_rmqueue();
1091 if (nmail || transparent)
1092 transflags(omessage, omsgCount, transparent);
1093 else
1094 setdot(message);
1097 static void
1098 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1099 char const **uhp, char const **mbx, char **pass, char **user)
1101 *sp = *server;
1102 if (strncmp(*sp, "imap://", 7) == 0) {
1103 *sp = &(*sp)[7];
1104 *use_ssl = 0;
1105 #ifdef HAVE_SSL
1106 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1107 *sp = &(*sp)[8];
1108 *use_ssl = 1;
1109 #endif
1110 } else
1111 *use_ssl = 0; /* (silence CC) */
1112 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1113 char *x = savestr(*sp);
1114 x[*cp - *sp] = '\0';
1115 *uhp = x;
1116 *mbx = &(*cp)[1];
1117 } else {
1118 if (*cp)
1119 (*server)[*cp - *server] = '\0';
1120 *uhp = *sp;
1121 *mbx = "INBOX";
1123 *pass = lookup_password_for_token(*uhp);
1124 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1125 *user = salloc(*cp - *uhp + 1);
1126 memcpy(*user, *uhp, *cp - *uhp);
1127 (*user)[*cp - *uhp] = '\0';
1128 *sp = &(*cp)[1];
1129 *user = urlxdec(*user);
1130 } else {
1131 *user = NULL;
1132 *sp = *uhp;
1136 FL int
1137 imap_setfile(const char *xserver, int nmail, int isedit)
1139 return imap_setfile1(xserver, nmail, isedit, 0);
1142 static int
1143 imap_setfile1(const char *xserver, int nmail, int isedit,
1144 int volatile transparent)
1146 struct sock so;
1147 sighandler_type volatile saveint, savepipe;
1148 char *server, *user, *pass, *acc;
1149 char const *cp, *sp, * volatile mbx, *uhp;
1150 int use_ssl = 0, prevcount = 0;
1151 enum mbflags same_flags;
1153 server = savestr(xserver);
1154 if (nmail) {
1155 saveint = safe_signal(SIGINT, SIG_IGN);
1156 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1157 if (saveint != SIG_IGN)
1158 safe_signal(SIGINT, imapcatch);
1159 if (savepipe != SIG_IGN)
1160 safe_signal(SIGPIPE, imapcatch);
1161 imaplock = 1;
1162 goto jnmail;
1164 same_flags = mb.mb_flags;
1165 same_imap_account = 0;
1166 sp = protbase(server);
1167 if (mb.mb_imap_account && mb.mb_type == MB_IMAP) {
1168 if (mb.mb_sock.s_fd > 0 &&
1169 strcmp(mb.mb_imap_account, sp) == 0 &&
1170 disconnected(mb.mb_imap_account) == 0)
1171 same_imap_account = 1;
1173 acc = sstrdup(sp);
1175 char const *xmbx;
1176 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &xmbx, &pass, &user);
1177 mbx = xmbx;
1179 so.s_fd = -1;
1180 if (!same_imap_account) {
1181 if (!disconnected(acc) &&
1182 sopen(sp, &so, use_ssl, uhp,
1183 use_ssl ? "imaps" : "imap",
1184 (options & OPT_VERBOSE) != 0) != OKAY) {
1185 free(acc);
1186 return -1;
1188 } else
1189 so = mb.mb_sock;
1190 if (!transparent)
1191 quit();
1192 edit = (isedit != 0);
1193 if (mb.mb_imap_account != NULL)
1194 free(mb.mb_imap_account);
1195 mb.mb_imap_account = acc;
1196 if (!same_imap_account) {
1197 if (mb.mb_sock.s_fd >= 0)
1198 sclose(&mb.mb_sock);
1200 same_imap_account = 0;
1201 if (!transparent) {
1202 if (mb.mb_itf) {
1203 fclose(mb.mb_itf);
1204 mb.mb_itf = NULL;
1206 if (mb.mb_otf) {
1207 fclose(mb.mb_otf);
1208 mb.mb_otf = NULL;
1210 if (mb.mb_imap_mailbox != 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 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1222 sclose(&mb.mb_sock);
1223 safe_signal(SIGINT, saveint);
1224 safe_signal(SIGPIPE, savepipe);
1225 imaplock = 0;
1226 mb.mb_type = MB_VOID;
1227 mb.mb_active = MB_NONE;
1228 return -1;
1230 if (saveint != SIG_IGN)
1231 safe_signal(SIGINT, imapcatch);
1232 if (savepipe != SIG_IGN)
1233 safe_signal(SIGPIPE, imapcatch);
1234 if (mb.mb_sock.s_fd < 0) {
1235 if (disconnected(mb.mb_imap_account)) {
1236 if (cache_setptr(transparent) == STOP)
1237 fprintf(stderr,
1238 "Mailbox \"%s\" is not cached.\n",
1239 server);
1240 goto done;
1242 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1243 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1244 savealrm = safe_signal(SIGALRM, imapalarm);
1245 alarm(imapkeepalive);
1248 mb.mb_sock = so;
1249 mb.mb_sock.s_desc = "IMAP";
1250 mb.mb_sock.s_onclose = imap_timer_off;
1251 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1252 imap_auth(&mb, uhp, user, pass) != OKAY) {
1253 sclose(&mb.mb_sock);
1254 imap_timer_off();
1255 safe_signal(SIGINT, saveint);
1256 safe_signal(SIGPIPE, savepipe);
1257 imaplock = 0;
1258 return -1;
1260 } else /* same account */
1261 mb.mb_flags |= same_flags;
1262 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1263 mb.mb_type = MB_IMAP;
1264 cache_dequeue(&mb);
1265 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1266 /*sclose(&mb.mb_sock);
1267 imap_timer_off();*/
1268 safe_signal(SIGINT, saveint);
1269 safe_signal(SIGPIPE, savepipe);
1270 imaplock = 0;
1271 mb.mb_type = MB_VOID;
1272 return -1;
1274 jnmail:
1275 imap_setptr(&mb, nmail, transparent, &prevcount);
1276 done: setmsize(msgCount);
1277 if (!nmail && !transparent)
1278 sawcom = FAL0;
1279 safe_signal(SIGINT, saveint);
1280 safe_signal(SIGPIPE, savepipe);
1281 imaplock = 0;
1282 if (!nmail && mb.mb_type == MB_IMAP)
1283 purgecache(&mb, message, msgCount);
1284 if ((nmail || transparent) && mb.mb_sorted) {
1285 mb.mb_threaded = 0;
1286 sort((void *)-1);
1288 if (!nmail && !edit && msgCount == 0) {
1289 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1290 !ok_blook(emptystart))
1291 fprintf(stderr, tr(258, "No mail at %s\n"), server);
1292 return 1;
1294 if (nmail)
1295 newmailinfo(prevcount);
1296 return 0;
1299 static int
1300 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1301 int need,
1302 const char *head, size_t headsize, long headlines)
1304 char *line = NULL, *lp;
1305 size_t linesize = 0, linelen, size = 0;
1306 int emptyline = 0, lines = 0, excess = 0;
1307 off_t offset;
1309 fseek(mp->mb_otf, 0L, SEEK_END);
1310 offset = ftell(mp->mb_otf);
1311 if (head)
1312 fwrite(head, 1, headsize, mp->mb_otf);
1313 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1314 lp = line;
1315 if (linelen > expected) {
1316 excess = linelen - expected;
1317 linelen = expected;
1319 /* TODO >>
1320 * Need to mask 'From ' lines. This cannot be done properly
1321 * since some servers pass them as 'From ' and others as
1322 * '>From '. Although one could identify the first kind of
1323 * server in principle, it is not possible to identify the
1324 * second as '>From ' may also come from a server of the
1325 * first type as actual data. So do what is absolutely
1326 * necessary only - mask 'From '.
1328 * If the line is the first line of the message header, it
1329 * is likely a real 'From ' line. In this case, it is just
1330 * ignored since it violates all standards.
1331 * TODO can the latter *really* happen??
1332 * TODO <<
1335 * Since we simply copy over data without doing any transfer
1336 * encoding reclassification/adjustment we *have* to perform
1337 * RFC 4155 compliant From_ quoting here
1339 if (is_head(lp, linelen)) {
1340 if (lines + headlines == 0)
1341 goto skip;
1342 fputc('>', mp->mb_otf);
1343 ++size;
1345 if (lp[linelen-1] == '\n' && (linelen == 1 ||
1346 lp[linelen-2] == '\r')) {
1347 emptyline = linelen <= 2;
1348 if (linelen > 2) {
1349 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1350 size += linelen - 1;
1351 } else
1352 size++;
1353 fputc('\n', mp->mb_otf);
1354 } else {
1355 emptyline = 0;
1356 fwrite(lp, 1, linelen, mp->mb_otf);
1357 size += linelen;
1359 lines++;
1360 skip: if ((expected -= linelen) <= 0)
1361 break;
1363 if (!emptyline) {
1365 * This is very ugly; but some IMAP daemons don't end a
1366 * message with \r\n\r\n, and we need \n\n for mbox format.
1368 fputc('\n', mp->mb_otf);
1369 lines++;
1370 size++;
1372 fflush(mp->mb_otf);
1373 if (m != NULL) {
1374 m->m_size = size + headsize;
1375 m->m_lines = lines + headlines;
1376 m->m_block = mailx_blockof(offset);
1377 m->m_offset = mailx_offsetof(offset);
1378 switch (need) {
1379 case NEED_HEADER:
1380 m->m_have |= HAVE_HEADER;
1381 break;
1382 case NEED_BODY:
1383 m->m_have |= HAVE_HEADER|HAVE_BODY;
1384 m->m_xlines = m->m_lines;
1385 m->m_xsize = m->m_size;
1386 break;
1389 free(line);
1390 return excess;
1393 static void
1394 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1395 const char *head, size_t headsize, long headlines)
1397 off_t offset;
1398 size_t len;
1400 len = strlen(str);
1401 fseek(mp->mb_otf, 0L, SEEK_END);
1402 offset = ftell(mp->mb_otf);
1403 if (head)
1404 fwrite(head, 1, headsize, mp->mb_otf);
1405 if (len > 0) {
1406 fwrite(str, 1, len, mp->mb_otf);
1407 fputc('\n', mp->mb_otf);
1408 len++;
1410 fflush(mp->mb_otf);
1411 if (m != NULL) {
1412 m->m_size = headsize + len;
1413 m->m_lines = headlines + 1;
1414 m->m_block = mailx_blockof(offset);
1415 m->m_offset = mailx_offsetof(offset);
1416 m->m_have |= HAVE_HEADER|HAVE_BODY;
1417 m->m_xlines = m->m_lines;
1418 m->m_xsize = m->m_size;
1422 static enum okay
1423 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1425 char o[LINESIZE];
1426 struct message mt;
1427 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
1428 char *volatile head = NULL;
1429 char const *cp = NULL, *loc = NULL,
1430 *volatile item = NULL, *volatile resp = NULL;
1431 size_t expected;
1432 size_t volatile headsize = 0;
1433 int number = m - message + 1;
1434 enum okay ok = STOP;
1435 FILE *queuefp = NULL;
1436 long volatile headlines = 0;
1437 long n = -1;
1438 unsigned long u = 0;
1440 if (getcache(mp, m, need) == OKAY)
1441 return OKAY;
1442 if (mp->mb_type == MB_CACHE) {
1443 fprintf(stderr, "Message %u not available.\n", number);
1444 return STOP;
1446 if (mp->mb_sock.s_fd < 0) {
1447 fprintf(stderr, "IMAP connection closed.\n");
1448 return STOP;
1450 switch (need) {
1451 case NEED_HEADER:
1452 resp = item = "RFC822.HEADER";
1453 break;
1454 case NEED_BODY:
1455 item = "BODY.PEEK[]";
1456 resp = "BODY[]";
1457 if (m->m_flag & HAVE_HEADER && m->m_size) {
1458 char *hdr = smalloc(m->m_size);
1459 fflush(mp->mb_otf);
1460 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
1461 m->m_offset), SEEK_SET) < 0 ||
1462 fread(hdr, 1, m->m_size, mp->mb_itf)
1463 != m->m_size) {
1464 free(hdr);
1465 break;
1467 head = hdr;
1468 headsize = m->m_size;
1469 headlines = m->m_lines;
1470 item = "BODY.PEEK[TEXT]";
1471 resp = "BODY[TEXT]";
1473 break;
1474 case NEED_UNSPEC:
1475 return STOP;
1477 imaplock = 1;
1478 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1479 if (sigsetjmp(imapjmp, 1)) {
1480 safe_signal(SIGINT, saveint);
1481 safe_signal(SIGPIPE, savepipe);
1482 imaplock = 0;
1483 return STOP;
1485 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1486 safe_signal(SIGINT, &_imap_maincatch);
1487 if (savepipe != SIG_IGN)
1488 safe_signal(SIGPIPE, imapcatch);
1489 if (m->m_uid)
1490 snprintf(o, sizeof o,
1491 "%s UID FETCH %lu (%s)\r\n",
1492 tag(1), m->m_uid, item);
1493 else {
1494 if (check_expunged() == STOP)
1495 goto out;
1496 snprintf(o, sizeof o,
1497 "%s FETCH %u (%s)\r\n",
1498 tag(1), number, item);
1500 IMAP_OUT(o, MB_COMD, goto out)
1501 for (;;) {
1502 ok = imap_answer(mp, 1);
1503 if (ok == STOP)
1504 break;
1505 if (response_status != RESPONSE_OTHER ||
1506 response_other != MESSAGE_DATA_FETCH)
1507 continue;
1508 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1509 continue;
1510 if (m->m_uid) {
1511 if ((cp = asccasestr(responded_other_text, "UID "))) {
1512 u = atol(&cp[4]);
1513 n = 0;
1514 } else {
1515 n = -1;
1516 u = 0;
1518 } else
1519 n = responded_other_number;
1520 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1521 if (m->m_uid ? m->m_uid != u : n != number)
1522 continue;
1523 if ((cp = strchr(loc, '"')) != NULL) {
1524 cp = imap_unquotestr(cp);
1525 imap_putstr(mp, m, cp,
1526 head, headsize, headlines);
1527 } else {
1528 m->m_have |= HAVE_HEADER|HAVE_BODY;
1529 m->m_xlines = m->m_lines;
1530 m->m_xsize = m->m_size;
1532 goto out;
1534 expected = atol(&cp[1]);
1535 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1536 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1537 continue;
1539 mt = *m;
1540 imap_fetchdata(mp, &mt, expected, need,
1541 head, headsize, headlines);
1542 if (n >= 0) {
1543 commitmsg(mp, m, &mt, mt.m_have);
1544 break;
1546 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1547 &mp->mb_sock) > 0) {
1548 if (options & OPT_VERBOSE)
1549 fputs(imapbuf, stderr);
1550 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1551 u = atol(&cp[4]);
1552 if (u == m->m_uid) {
1553 commitmsg(mp, m, &mt, mt.m_have);
1554 break;
1559 out: while (mp->mb_active & MB_COMD)
1560 ok = imap_answer(mp, 1);
1561 if (saveint != SIG_IGN)
1562 safe_signal(SIGINT, saveint);
1563 if (savepipe != SIG_IGN)
1564 safe_signal(SIGPIPE, savepipe);
1565 imaplock--;
1566 if (ok == OKAY)
1567 putcache(mp, m);
1568 if (head != NULL)
1569 free(head);
1570 if (interrupts)
1571 onintr(0);
1572 return ok;
1575 FL enum okay
1576 imap_header(struct message *m)
1578 return imap_get(&mb, m, NEED_HEADER);
1582 FL enum okay
1583 imap_body(struct message *m)
1585 return imap_get(&mb, m, NEED_BODY);
1588 static void
1589 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1590 enum havespec have)
1592 tomp->m_size = frommp->m_size;
1593 tomp->m_lines = frommp->m_lines;
1594 tomp->m_block = frommp->m_block;
1595 tomp->m_offset = frommp->m_offset;
1596 tomp->m_have = have;
1597 if (have & HAVE_BODY) {
1598 tomp->m_xlines = frommp->m_lines;
1599 tomp->m_xsize = frommp->m_size;
1601 putcache(mp, tomp);
1604 static enum okay
1605 imap_fetchheaders (
1606 struct mailbox *mp,
1607 struct message *m,
1608 int bot,
1609 int topp /* bot > topp */
1612 char o[LINESIZE];
1613 char const *cp;
1614 struct message mt;
1615 size_t expected;
1616 enum okay ok;
1617 int n = 0, u;
1618 FILE *queuefp = NULL;
1620 if (m[bot].m_uid)
1621 snprintf(o, sizeof o,
1622 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1623 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1624 else {
1625 if (check_expunged() == STOP)
1626 return STOP;
1627 snprintf(o, sizeof o,
1628 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1629 tag(1), bot, topp);
1631 IMAP_OUT(o, MB_COMD, return STOP)
1632 for (;;) {
1633 ok = imap_answer(mp, 1);
1634 if (response_status != RESPONSE_OTHER)
1635 break;
1636 if (response_other != MESSAGE_DATA_FETCH)
1637 continue;
1638 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1639 return STOP;
1640 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1641 continue;
1642 expected = atol(&cp[1]);
1643 if (m[bot-1].m_uid) {
1644 if ((cp=asccasestr(responded_other_text, "UID "))) {
1645 u = atoi(&cp[4]);
1646 for (n = bot; n <= topp; n++)
1647 if ((unsigned long)u == m[n-1].m_uid)
1648 break;
1649 if (n > topp) {
1650 imap_fetchdata(mp, NULL, expected,
1651 NEED_HEADER,
1652 NULL, 0, 0);
1653 continue;
1655 } else
1656 n = -1;
1657 } else {
1658 n = responded_other_number;
1659 if (n <= 0 || n > msgCount) {
1660 imap_fetchdata(mp, NULL, expected, NEED_HEADER,
1661 NULL, 0, 0);
1662 continue;
1665 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1666 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1667 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1668 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
1669 &mp->mb_sock) > 0) {
1670 if (options & OPT_VERBOSE)
1671 fputs(imapbuf, stderr);
1672 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1673 u = atoi(&cp[4]);
1674 for (n = bot; n <= topp; n++)
1675 if ((unsigned long)u == m[n-1].m_uid)
1676 break;
1677 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1678 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1679 n = 0;
1683 while (mp->mb_active & MB_COMD)
1684 ok = imap_answer(mp, 1);
1685 return ok;
1688 FL void
1689 imap_getheaders(int volatile bot, int topp)
1691 sighandler_type saveint, savepipe;
1692 /* XXX enum okay ok = STOP;*/
1693 int i, chunk = 256;
1695 if (mb.mb_type == MB_CACHE)
1696 return;
1697 if (bot < 1)
1698 bot = 1;
1699 if (topp > msgCount)
1700 topp = msgCount;
1701 for (i = bot; i < topp; i++) {
1702 if (message[i-1].m_have & HAVE_HEADER ||
1703 getcache(&mb, &message[i-1], NEED_HEADER)
1704 == OKAY)
1705 bot = i+1;
1706 else
1707 break;
1709 for (i = topp; i > bot; i--) {
1710 if (message[i-1].m_have & HAVE_HEADER ||
1711 getcache(&mb, &message[i-1], NEED_HEADER)
1712 == OKAY)
1713 topp = i-1;
1714 else
1715 break;
1717 if (bot >= topp)
1718 return;
1719 imaplock = 1;
1720 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1721 safe_signal(SIGINT, &_imap_maincatch);
1722 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1723 if (sigsetjmp(imapjmp, 1) == 0) {
1724 if (savepipe != SIG_IGN)
1725 safe_signal(SIGPIPE, imapcatch);
1726 for (i = bot; i <= topp; i += chunk) {
1727 int j = i + chunk - 1;
1728 /*ok = */imap_fetchheaders(&mb, message, i,
1729 j < topp ? j : topp);
1730 if (interrupts)
1731 onintr(0);
1734 safe_signal(SIGINT, saveint);
1735 safe_signal(SIGPIPE, savepipe);
1736 imaplock = 0;
1739 static enum okay
1740 __imap_exit(struct mailbox *mp)
1742 char o[LINESIZE];
1743 FILE *queuefp = NULL;
1745 mp->mb_active |= MB_BYE;
1746 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1747 IMAP_OUT(o, MB_COMD, return STOP)
1748 IMAP_ANSWER()
1749 return OKAY;
1752 static enum okay
1753 imap_exit(struct mailbox *mp)
1755 enum okay ret = __imap_exit(mp);
1756 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1757 free(mp->mb_imap_account);
1758 free(mp->mb_imap_mailbox);
1759 if (mp->mb_cache_directory != NULL)
1760 free(mp->mb_cache_directory);
1761 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1762 mp->mb_imap_account =
1763 mp->mb_imap_mailbox =
1764 mp->mb_cache_directory = "";
1765 #else
1766 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1767 mp->mb_imap_mailbox = NULL;
1768 mp->mb_cache_directory = NULL;
1769 #endif
1770 #endif
1771 sclose(&mp->mb_sock);
1772 return ret;
1775 static enum okay
1776 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1778 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1779 if (mp->mb_type == MB_IMAP)
1780 delcache(mp, m);
1781 return OKAY;
1784 static enum okay
1785 imap_close(struct mailbox *mp)
1787 char o[LINESIZE];
1788 FILE *queuefp = NULL;
1790 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1791 IMAP_OUT(o, MB_COMD, return STOP)
1792 IMAP_ANSWER()
1793 return OKAY;
1796 static enum okay
1797 imap_update(struct mailbox *mp)
1799 struct message *m;
1800 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1802 if (!edit && mp->mb_perm != 0) {
1803 holdbits();
1804 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
1805 if (m->m_flag & MBOX)
1806 c++;
1808 if (c > 0)
1809 if (makembox() == STOP)
1810 goto bypass;
1812 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
1813 if (mp->mb_perm == 0) {
1814 dodel = 0;
1815 } else if (edit) {
1816 dodel = m->m_flag & MDELETED;
1817 } else {
1818 dodel = !((m->m_flag&MPRESERVE) ||
1819 (m->m_flag&MTOUCH) == 0);
1822 * Fetch the result after around each 800 STORE commands
1823 * sent (approx. 32k data sent). Otherwise, servers will
1824 * try to flush the return queue at some point, leading
1825 * to a deadlock if we are still writing commands but not
1826 * reading their results.
1828 needstat = stored > 0 && stored % 800 == 0;
1830 * Even if this message has been deleted, continue
1831 * to set further flags. This is necessary to support
1832 * Gmail semantics, where "delete" actually means
1833 * "archive", and the flags are applied to the copy
1834 * in "All Mail".
1836 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
1837 imap_store(mp, m, m-message+1,
1838 '+', "\\Seen", needstat);
1839 stored++;
1841 if (m->m_flag & MFLAG) {
1842 imap_store(mp, m, m-message+1,
1843 '+', "\\Flagged", needstat);
1844 stored++;
1846 if (m->m_flag & MUNFLAG) {
1847 imap_store(mp, m, m-message+1,
1848 '-', "\\Flagged", needstat);
1849 stored++;
1851 if (m->m_flag & MANSWER) {
1852 imap_store(mp, m, m-message+1,
1853 '+', "\\Answered", needstat);
1854 stored++;
1856 if (m->m_flag & MUNANSWER) {
1857 imap_store(mp, m, m-message+1,
1858 '-', "\\Answered", needstat);
1859 stored++;
1861 if (m->m_flag & MDRAFT) {
1862 imap_store(mp, m, m-message+1,
1863 '+', "\\Draft", needstat);
1864 stored++;
1866 if (m->m_flag & MUNDRAFT) {
1867 imap_store(mp, m, m-message+1,
1868 '-', "\\Draft", needstat);
1869 stored++;
1871 if (dodel) {
1872 imap_delete(mp, m-message+1, m, needstat);
1873 stored++;
1874 gotcha++;
1875 } else if (mp->mb_type != MB_CACHE ||
1876 (! edit && ! (m->m_flag&(MBOXED|MSAVED|MDELETED))) ||
1877 (m->m_flag & (MBOXED|MPRESERVE|MTOUCH)) ==
1878 (MPRESERVE|MTOUCH) ||
1879 (edit && ! (m->m_flag & MDELETED)))
1880 held++;
1881 if (m->m_flag & MNEW) {
1882 m->m_flag &= ~MNEW;
1883 m->m_flag |= MSTATUS;
1886 bypass:
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(tr(168, "\"%s\" "), displayname);
1899 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1900 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1901 } else if (held && !edit && mp->mb_perm != 0) {
1902 if (held == 1)
1903 printf(tr(155, "Held 1 message in %s\n"), displayname);
1904 else
1905 printf(tr(156, "Held %d messages in %s\n"), held,
1906 displayname);
1908 fflush(stdout);
1909 return OKAY;
1912 FL void
1913 imap_quit(void)
1915 sighandler_type volatile saveint, savepipe;
1917 if (mb.mb_type == MB_CACHE) {
1918 imap_update(&mb);
1919 return;
1921 if (mb.mb_sock.s_fd < 0) {
1922 fprintf(stderr, "IMAP connection closed.\n");
1923 return;
1925 imaplock = 1;
1926 saveint = safe_signal(SIGINT, SIG_IGN);
1927 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1928 if (sigsetjmp(imapjmp, 1)) {
1929 safe_signal(SIGINT, saveint);
1930 safe_signal(SIGPIPE, saveint);
1931 imaplock = 0;
1932 return;
1934 if (saveint != SIG_IGN)
1935 safe_signal(SIGINT, imapcatch);
1936 if (savepipe != SIG_IGN)
1937 safe_signal(SIGPIPE, imapcatch);
1938 imap_update(&mb);
1939 if (!same_imap_account) {
1940 imap_exit(&mb);
1942 safe_signal(SIGINT, saveint);
1943 safe_signal(SIGPIPE, savepipe);
1944 imaplock = 0;
1947 static enum okay
1948 imap_store(struct mailbox *mp, struct message *m, int n,
1949 int c, const char *sp, int needstat)
1951 char o[LINESIZE];
1952 FILE *queuefp = NULL;
1954 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
1955 return STOP;
1956 if (m->m_uid)
1957 snprintf(o, sizeof o,
1958 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1959 tag(1), m->m_uid, c, sp);
1960 else {
1961 if (check_expunged() == STOP)
1962 return STOP;
1963 snprintf(o, sizeof o,
1964 "%s STORE %u %cFLAGS (%s)\r\n",
1965 tag(1), n, c, sp);
1967 IMAP_OUT(o, MB_COMD, return STOP)
1968 if (needstat)
1969 IMAP_ANSWER()
1970 else
1971 mb.mb_active &= ~MB_COMD;
1972 if (queuefp != NULL)
1973 Fclose(queuefp);
1974 return OKAY;
1977 FL enum okay
1978 imap_undelete(struct message *m, int n)
1980 return imap_unstore(m, n, "\\Deleted");
1983 FL enum okay
1984 imap_unread(struct message *m, int n)
1986 return imap_unstore(m, n, "\\Seen");
1989 static enum okay
1990 imap_unstore(struct message *m, int n, const char *flag)
1992 sighandler_type saveint, savepipe;
1993 enum okay ok = STOP;
1995 (void)&saveint;
1996 (void)&savepipe;
1997 (void)&ok;
1998 imaplock = 1;
1999 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2000 safe_signal(SIGINT, &_imap_maincatch);
2001 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2002 if (sigsetjmp(imapjmp, 1) == 0) {
2003 if (savepipe != SIG_IGN)
2004 safe_signal(SIGPIPE, imapcatch);
2005 ok = imap_store(&mb, m, n, '-', flag, 1);
2007 safe_signal(SIGINT, saveint);
2008 safe_signal(SIGPIPE, savepipe);
2009 imaplock = 0;
2010 if (interrupts)
2011 onintr(0);
2012 return ok;
2015 static const char *
2016 tag(int new)
2018 static char ts[20];
2019 static long n;
2021 if (new)
2022 n++;
2023 snprintf(ts, sizeof ts, "T%lu", n);
2024 return ts;
2027 FL int
2028 imap_imap(void *vp)
2030 sighandler_type saveint, savepipe;
2031 char o[LINESIZE];
2032 enum okay ok = STOP;
2033 struct mailbox *mp = &mb;
2034 FILE *queuefp = NULL;
2036 (void)&saveint;
2037 (void)&savepipe;
2038 (void)&ok;
2039 if (mp->mb_type != MB_IMAP) {
2040 printf("Not operating on an IMAP mailbox.\n");
2041 return 1;
2043 imaplock = 1;
2044 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2045 safe_signal(SIGINT, &_imap_maincatch);
2046 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2047 if (sigsetjmp(imapjmp, 1) == 0) {
2048 if (savepipe != SIG_IGN)
2049 safe_signal(SIGPIPE, imapcatch);
2050 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2051 IMAP_OUT(o, MB_COMD, goto out)
2052 while (mp->mb_active & MB_COMD) {
2053 ok = imap_answer(mp, 0);
2054 fputs(responded_text, stdout);
2057 out: safe_signal(SIGINT, saveint);
2058 safe_signal(SIGPIPE, savepipe);
2059 imaplock = 0;
2060 if (interrupts)
2061 onintr(0);
2062 return ok != OKAY;
2065 FL int
2066 imap_newmail(int autoinc)
2068 if (autoinc && had_exists < 0 && had_expunge < 0) {
2069 imaplock = 1;
2070 imap_noop();
2071 imaplock = 0;
2073 if (had_exists == msgCount && had_expunge < 0)
2075 * Some servers always respond with EXISTS to NOOP. If
2076 * the mailbox has been changed but the number of messages
2077 * has not, an EXPUNGE must also had been sent; otherwise,
2078 * nothing has changed.
2080 had_exists = -1;
2081 return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
2084 static char *
2085 imap_putflags(int f)
2087 const char *cp;
2088 char *buf, *bp;
2090 bp = buf = salloc(100);
2091 if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
2092 *bp++ = '(';
2093 if (f & MREAD) {
2094 if (bp[-1] != '(')
2095 *bp++ = ' ';
2096 for (cp = "\\Seen"; *cp; cp++)
2097 *bp++ = *cp;
2099 if (f & MFLAGGED) {
2100 if (bp[-1] != '(')
2101 *bp++ = ' ';
2102 for (cp = "\\Flagged"; *cp; cp++)
2103 *bp++ = *cp;
2105 if (f & MANSWERED) {
2106 if (bp[-1] != '(')
2107 *bp++ = ' ';
2108 for (cp = "\\Answered"; *cp; cp++)
2109 *bp++ = *cp;
2111 if (f & MDRAFT) {
2112 if (bp[-1] != '(')
2113 *bp++ = ' ';
2114 for (cp = "\\Draft"; *cp; cp++)
2115 *bp++ = *cp;
2117 *bp++ = ')';
2118 *bp++ = ' ';
2120 *bp = '\0';
2121 return buf;
2124 static void
2125 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2127 while (*cp != ')') {
2128 if (*cp == '\\') {
2129 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2130 *f |= MREAD;
2131 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2132 *f |= MNEW;
2133 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2134 *f |= MDELETED;
2135 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2136 *f |= MFLAGGED;
2137 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2138 *f |= MANSWERED;
2139 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2140 *f |= MDRAFTED;
2142 cp++;
2144 if (xp)
2145 *xp = cp;
2148 static enum okay
2149 imap_append1(struct mailbox *mp, const char *name, FILE *fp,
2150 off_t off1, long xsize, enum mflag flag, time_t t)
2152 char o[LINESIZE];
2153 char *buf;
2154 size_t bufsize, buflen, cnt;
2155 enum okay ok = STOP;
2156 long size, lines, ysize;
2157 int twice = 0;
2158 FILE *queuefp = NULL;
2160 if (mp->mb_type == MB_CACHE) {
2161 queuefp = cache_queue(mp);
2162 if (queuefp == NULL)
2163 return STOP;
2164 ok = OKAY;
2166 buf = smalloc(bufsize = LINESIZE);
2167 buflen = 0;
2168 again: size = xsize;
2169 cnt = fsize(fp);
2170 if (fseek(fp, off1, SEEK_SET) < 0) {
2171 ok = STOP;
2172 goto out;
2174 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2175 tag(1), imap_quotestr(name),
2176 imap_putflags(flag),
2177 imap_make_date_time(t),
2178 size);
2179 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2180 while (mp->mb_active & MB_COMD) {
2181 ok = imap_answer(mp, twice);
2182 if (response_type == RESPONSE_CONT)
2183 break;
2185 if (mp->mb_type != MB_CACHE && ok == STOP) {
2186 if (twice == 0)
2187 goto trycreate;
2188 else
2189 goto out;
2191 lines = ysize = 0;
2192 while (size > 0) {
2193 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2194 lines++;
2195 ysize += buflen;
2196 buf[buflen-1] = '\r';
2197 buf[buflen] = '\n';
2198 if (mp->mb_type != MB_CACHE)
2199 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2200 else if (queuefp)
2201 fwrite(buf, 1, buflen+1, queuefp);
2202 size -= buflen+1;
2204 if (mp->mb_type != MB_CACHE)
2205 swrite(&mp->mb_sock, "\r\n");
2206 else if (queuefp)
2207 fputs("\r\n", queuefp);
2208 while (mp->mb_active & MB_COMD) {
2209 ok = imap_answer(mp, 0);
2210 if (response_status == RESPONSE_NO /*&&
2211 ascncasecmp(responded_text,
2212 "[TRYCREATE] ", 12) == 0*/) {
2213 trycreate: if (twice++) {
2214 ok = STOP;
2215 goto out;
2217 snprintf(o, sizeof o, "%s CREATE %s\r\n",
2218 tag(1),
2219 imap_quotestr(name));
2220 IMAP_XOUT(o, MB_COMD, goto out, ok = STOP;goto out)
2221 while (mp->mb_active & MB_COMD)
2222 ok = imap_answer(mp, 1);
2223 if (ok == STOP)
2224 goto out;
2225 imap_created_mailbox++;
2226 goto again;
2227 } else if (ok != OKAY)
2228 fprintf(stderr, tr(270, "IMAP error: %s"),
2229 responded_text);
2230 else if (response_status == RESPONSE_OK &&
2231 mp->mb_flags & MB_UIDPLUS)
2232 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
2233 flag, name);
2235 out: if (queuefp != NULL)
2236 Fclose(queuefp);
2237 free(buf);
2238 return ok;
2241 static enum okay
2242 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2244 char *buf, *bp, *lp;
2245 size_t bufsize, buflen, cnt;
2246 off_t off1 = -1, offs;
2247 int inhead = 1;
2248 int flag = MNEW|MNEWEST;
2249 long size = 0;
2250 time_t tim;
2251 enum okay ok;
2253 buf = smalloc(bufsize = LINESIZE);
2254 buflen = 0;
2255 cnt = fsize(fp);
2256 offs = ftell(fp);
2257 time(&tim);
2258 do {
2259 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2260 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2261 if (off1 != (off_t)-1) {
2262 ok=imap_append1(mp, name, fp, off1,
2263 size, flag, tim);
2264 if (ok == STOP)
2265 return STOP;
2266 fseek(fp, offs+buflen, SEEK_SET);
2268 off1 = offs + buflen;
2269 size = 0;
2270 inhead = 1;
2271 flag = MNEW;
2272 if (bp != NULL)
2273 tim = unixtime(buf);
2274 } else
2275 size += buflen+1;
2276 offs += buflen;
2277 if (bp && buf[0] == '\n')
2278 inhead = 0;
2279 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2280 lp = &buf[6];
2281 while (whitechar(*lp&0377))
2282 lp++;
2283 if (*lp == ':')
2284 while (*++lp != '\0')
2285 switch (*lp) {
2286 case 'R':
2287 flag |= MREAD;
2288 break;
2289 case 'O':
2290 flag &= ~MNEW;
2291 break;
2293 } else if (bp && inhead &&
2294 ascncasecmp(buf, "x-status", 8) == 0) {
2295 lp = &buf[8];
2296 while (whitechar(*lp&0377))
2297 lp++;
2298 if (*lp == ':')
2299 while (*++lp != '\0')
2300 switch (*lp) {
2301 case 'F':
2302 flag |= MFLAGGED;
2303 break;
2304 case 'A':
2305 flag |= MANSWERED;
2306 break;
2307 case 'T':
2308 flag |= MDRAFTED;
2309 break;
2312 } while (bp != NULL);
2313 free(buf);
2314 return OKAY;
2317 FL enum okay
2318 imap_append(const char *xserver, FILE *fp)
2320 sighandler_type volatile saveint, savepipe;
2321 char *server, *user, *pass;
2322 char const *sp, *cp, * volatile mbx, *uhp;
2323 int volatile use_ssl;
2324 enum okay ok = STOP;
2326 server = savestr(xserver);
2328 int xus;
2329 char const *xmbx;
2330 imap_split(&server, &sp, &xus, &cp, &uhp, &xmbx, &pass, &user);
2331 use_ssl = xus;
2332 mbx = xmbx;
2335 imaplock = 1;
2336 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2337 safe_signal(SIGINT, &_imap_maincatch);
2338 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2339 if (sigsetjmp(imapjmp, 1))
2340 goto out;
2341 if (savepipe != SIG_IGN)
2342 safe_signal(SIGPIPE, imapcatch);
2343 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
2344 mb.mb_imap_account &&
2345 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2346 ok = imap_append0(&mb, mbx, fp);
2348 else {
2349 struct mailbox mx;
2351 memset(&mx, 0, sizeof mx);
2352 if (disconnected(server) == 0) {
2353 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2354 use_ssl ? "imaps" : "imap",
2355 (options & OPT_VERBOSE) != 0) != OKAY)
2356 goto fail;
2357 mx.mb_sock.s_desc = "IMAP";
2358 mx.mb_type = MB_IMAP;
2359 mx.mb_imap_account = (char *)protbase(server);
2360 /* TODO the code now did
2361 * TODO mx.mb_imap_mailbox = mbx;
2362 * TODO though imap_mailbox is sfree()d and mbx
2363 * TODO is possibly even a constant
2364 * TODO i changed this to sstrdup() sofar, as is used
2365 * TODO somewhere else in this file for this! */
2366 mx.mb_imap_mailbox = sstrdup(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 } else {
2375 mx.mb_imap_account = (char *)protbase(server);
2376 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2377 mx.mb_type = MB_CACHE;
2378 ok = imap_append0(&mx, mbx, fp);
2380 fail:;
2382 out: safe_signal(SIGINT, saveint);
2383 safe_signal(SIGPIPE, savepipe);
2384 imaplock = 0;
2385 if (interrupts)
2386 onintr(0);
2387 return ok;
2390 static enum okay
2391 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2392 struct list_item **lend, int level)
2394 char o[LINESIZE];
2395 enum okay ok = STOP;
2396 char *cp;
2397 const char *bp;
2398 FILE *queuefp = NULL;
2399 struct list_item *lp;
2401 *list = *lend = NULL;
2402 snprintf(o, sizeof o, "%s LIST %s %%\r\n",
2403 tag(1), imap_quotestr(base));
2404 IMAP_OUT(o, MB_COMD, return STOP)
2405 while (mp->mb_active & MB_COMD) {
2406 ok = imap_answer(mp, 1);
2407 if (response_status == RESPONSE_OTHER &&
2408 response_other == MAILBOX_DATA_LIST &&
2409 imap_parse_list() == OKAY) {
2410 cp = imap_unquotestr(list_name);
2411 lp = csalloc(1, sizeof *lp);
2412 lp->l_name = cp;
2413 for (bp = base; *bp && *bp == *cp; bp++)
2414 cp++;
2415 lp->l_base = *cp ? cp : savestr(base);
2416 lp->l_attr = list_attributes;
2417 lp->l_level = level+1;
2418 lp->l_delim = list_hierarchy_delimiter;
2419 if (*list && *lend) {
2420 (*lend)->l_next = lp;
2421 *lend = lp;
2422 } else
2423 *list = *lend = lp;
2426 return ok;
2429 static enum okay
2430 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2432 struct list_item *list, *lend, *lp, *lx, *ly;
2433 int n;
2434 const char *bp;
2435 char *cp;
2436 int depth;
2438 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2439 if (imap_list1(mp, base, &list, &lend, 0) == STOP)
2440 return STOP;
2441 if (list == NULL || lend == NULL)
2442 return OKAY;
2443 for (lp = list; lp; lp = lp->l_next)
2444 if (lp->l_delim != '/' && lp->l_delim != EOF &&
2445 lp->l_level < depth &&
2446 (lp->l_attr&LIST_NOINFERIORS) == 0) {
2447 cp = salloc((n = strlen(lp->l_name)) + 2);
2448 memcpy(cp, lp->l_name, n);
2449 cp[n] = lp->l_delim;
2450 cp[n+1] = '\0';
2451 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
2452 lx && ly) {
2453 lp->l_has_children = 1;
2454 if (strcmp(cp, lx->l_name) == 0)
2455 lx = lx->l_next;
2456 if (lx) {
2457 lend->l_next = lx;
2458 lend = ly;
2462 for (lp = list; lp; lp = lp->l_next) {
2463 if (strip) {
2464 cp = lp->l_name;
2465 for (bp = base; *bp && *bp == *cp; bp++)
2466 cp++;
2467 } else
2468 cp = lp->l_name;
2469 if ((lp->l_attr&LIST_NOSELECT) == 0)
2470 fprintf(fp, "%s\n", *cp ? cp : base);
2471 else if (lp->l_has_children == 0)
2472 fprintf(fp, "%s%c\n", *cp ? cp : base,
2473 lp->l_delim != EOF ? lp->l_delim : '\n');
2475 return OKAY;
2478 FL void
2479 imap_folders(const char * volatile name, int strip)
2481 sighandler_type saveint, savepipe;
2482 const char *fold, *cp, *sp;
2483 char *tempfn;
2484 FILE *volatile fp;
2486 cp = protbase(name);
2487 sp = mb.mb_imap_account;
2488 if (sp == NULL || strcmp(cp, sp)) {
2489 fprintf(stderr, tr(502,
2490 "Cannot perform `folders' but when on the very IMAP "
2491 "account; the current one is\n `%s' -- "
2492 "try `folders @'.\n"),
2493 (sp != NULL) ? sp : tr(503, "[NONE]"));
2494 return;
2496 fold = imap_fileof(name);
2497 if (options & OPT_TTYOUT) {
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, &_imap_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 (options & OPT_TTYOUT)
2521 Fclose(fp);
2522 onintr(0);
2524 fflush(fp);
2525 if (options & OPT_TTYOUT) {
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 (options & OPT_TTYOUT)
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(XSHELL, 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 = imap_fileof(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 (void)imap_finish(mp);
2673 return ok;
2676 FL 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 imaplock = 1;
2686 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2687 safe_signal(SIGINT, &_imap_maincatch);
2688 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2689 if (sigsetjmp(imapjmp, 1) == 0) {
2690 if (savepipe != SIG_IGN)
2691 safe_signal(SIGPIPE, imapcatch);
2692 ok = imap_copy1(&mb, m, n, name);
2694 safe_signal(SIGINT, saveint);
2695 safe_signal(SIGPIPE, savepipe);
2696 imaplock = 0;
2697 if (interrupts)
2698 onintr(0);
2699 return ok;
2702 static enum okay
2703 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2704 unsigned long *olduid, unsigned long *newuid)
2706 char *xp, *yp, *zp;
2708 *uidvalidity = strtoul(cp, &xp, 10);
2709 *olduid = strtoul(xp, &yp, 10);
2710 *newuid = strtoul(yp, &zp, 10);
2711 return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2712 yp > xp && *yp == ' ' && zp > yp && *zp == ']';
2715 static enum okay
2716 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2717 unsigned long *uid)
2719 char *xp, *yp;
2721 *uidvalidity = strtoul(cp, &xp, 10);
2722 *uid = strtoul(xp, &yp, 10);
2723 return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
2724 yp > xp && *yp == ']';
2727 static enum okay
2728 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2730 const char *cp;
2731 unsigned long uidvalidity, olduid, newuid;
2732 struct mailbox xmb;
2733 struct message xm;
2735 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2736 imap_copyuid_parse(&cp[9], &uidvalidity,
2737 &olduid, &newuid) == STOP)
2738 return STOP;
2739 xmb = *mp;
2740 xmb.mb_cache_directory = NULL;
2741 xmb.mb_imap_mailbox = savestr(name);
2742 xmb.mb_uidvalidity = uidvalidity;
2743 initcache(&xmb);
2744 if (m == NULL) {
2745 memset(&xm, 0, sizeof xm);
2746 xm.m_uid = olduid;
2747 if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
2748 return STOP;
2749 getcache(mp, &xm, NEED_HEADER);
2750 getcache(mp, &xm, NEED_BODY);
2751 } else {
2752 if ((m->m_flag & HAVE_HEADER) == 0)
2753 getcache(mp, m, NEED_HEADER);
2754 if ((m->m_flag & HAVE_BODY) == 0)
2755 getcache(mp, m, NEED_BODY);
2756 xm = *m;
2758 xm.m_uid = newuid;
2759 xm.m_flag &= ~MFULLYCACHED;
2760 putcache(&xmb, &xm);
2761 return OKAY;
2764 static enum okay
2765 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
2766 long xsize, long size, long lines, int flag, const char *name)
2768 const char *cp;
2769 unsigned long uidvalidity, uid;
2770 struct mailbox xmb;
2771 struct message xm;
2773 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2774 imap_appenduid_parse(&cp[11], &uidvalidity,
2775 &uid) == STOP)
2776 return STOP;
2777 xmb = *mp;
2778 xmb.mb_cache_directory = NULL;
2779 xmb.mb_imap_mailbox = savestr(name);
2780 xmb.mb_uidvalidity = uidvalidity;
2781 xmb.mb_otf = xmb.mb_itf = fp;
2782 initcache(&xmb);
2783 memset(&xm, 0, sizeof xm);
2784 xm.m_flag = (flag & MREAD) | MNEW;
2785 xm.m_time = t;
2786 xm.m_block = mailx_blockof(off1);
2787 xm.m_offset = mailx_offsetof(off1);
2788 xm.m_size = size;
2789 xm.m_xsize = xsize;
2790 xm.m_lines = xm.m_xlines = lines;
2791 xm.m_uid = uid;
2792 xm.m_have = HAVE_HEADER|HAVE_BODY;
2793 putcache(&xmb, &xm);
2794 return OKAY;
2797 static enum okay
2798 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2800 FILE *tp = NULL;
2801 time_t t;
2802 long size, xsize, ysize, lines;
2803 enum mflag flag = MNEW;
2804 char *name, *buf, *bp, *tempCopy;
2805 char const *cp;
2806 size_t bufsize, buflen, cnt;
2807 enum okay ok = STOP;
2809 buf = smalloc(bufsize = LINESIZE);
2810 buflen = 0;
2811 cnt = fsize(fp);
2812 if (fgetline(&buf, &bufsize, &cnt, &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, &cnt, &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 cnt,
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 = charset_get_lc();
2874 #ifdef HAVE_ICONV
2875 if (asccasecmp(cp, "utf-8")) {
2876 iconv_t it;
2877 char *nsp, *nspec;
2878 size_t sz, nsz;
2879 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
2880 sz = strlen(spec) + 1;
2881 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
2882 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz,
2883 FAL0) == 0 && sz == 0) {
2884 spec = nspec;
2885 cp = "utf-8";
2887 n_iconv_close(it);
2890 #endif
2891 cp = imap_quotestr(cp);
2892 cs = salloc(n = strlen(cp) + 10);
2893 snprintf(cs, n, "CHARSET %s ", cp);
2894 } else
2895 cs = UNCONST("");
2896 o = ac_alloc(osize = strlen(spec) + 60);
2897 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
2898 IMAP_OUT(o, MB_COMD, goto out)
2899 while (mp->mb_active & MB_COMD) {
2900 ok = imap_answer(mp, 0);
2901 if (response_status == RESPONSE_OTHER &&
2902 response_other == MAILBOX_DATA_SEARCH) {
2903 xp = responded_other_text;
2904 while (*xp && *xp != '\r') {
2905 n = strtoul(xp, &xp, 10);
2906 for (i = 0; i < cnt; i++)
2907 if (m[i].m_uid == n &&
2908 (m[i].m_flag&MHIDDEN)
2909 == 0 &&
2910 (f == MDELETED ||
2911 (m[i].m_flag&MDELETED)
2912 == 0))
2913 mark(i+1, f);
2917 out: ac_free(o);
2918 return ok;
2921 FL enum okay
2922 imap_search1(const char *volatile spec, int f)
2924 sighandler_type saveint, savepipe;
2925 enum okay ok = STOP;
2927 (void)&saveint;
2928 (void)&savepipe;
2929 (void)&ok;
2930 if (mb.mb_type != MB_IMAP)
2931 return STOP;
2932 imaplock = 1;
2933 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2934 safe_signal(SIGINT, &_imap_maincatch);
2935 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2936 if (sigsetjmp(imapjmp, 1) == 0) {
2937 if (savepipe != SIG_IGN)
2938 safe_signal(SIGPIPE, imapcatch);
2939 ok = imap_search2(&mb, message, msgCount, spec, f);
2941 safe_signal(SIGINT, saveint);
2942 safe_signal(SIGPIPE, savepipe);
2943 imaplock = 0;
2944 if (interrupts)
2945 onintr(0);
2946 return ok;
2949 FL int
2950 imap_thisaccount(const char *cp)
2952 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
2953 return 0;
2954 if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
2955 mb.mb_imap_account == NULL)
2956 return 0;
2957 return strcmp(protbase(cp), mb.mb_imap_account) == 0;
2960 FL enum okay
2961 imap_remove(const char * volatile name)
2963 sighandler_type volatile saveint, savepipe;
2964 enum okay ok = STOP;
2966 if (mb.mb_type != MB_IMAP) {
2967 fprintf(stderr, "Refusing to remove \"%s\" "
2968 "in disconnected mode.\n", name);
2969 return STOP;
2971 if (!imap_thisaccount(name)) {
2972 fprintf(stderr, "Can only remove mailboxes on current IMAP "
2973 "server: \"%s\" not removed.\n", name);
2974 return STOP;
2976 imaplock = 1;
2977 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2978 safe_signal(SIGINT, &_imap_maincatch);
2979 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2980 if (sigsetjmp(imapjmp, 1) == 0) {
2981 if (savepipe != SIG_IGN)
2982 safe_signal(SIGPIPE, imapcatch);
2983 ok = imap_remove1(&mb, imap_fileof(name));
2985 safe_signal(SIGINT, saveint);
2986 safe_signal(SIGPIPE, savepipe);
2987 imaplock = 0;
2988 if (ok == OKAY)
2989 ok = cache_remove(name);
2990 if (interrupts)
2991 onintr(0);
2992 return ok;
2995 static enum okay
2996 imap_remove1(struct mailbox *mp, const char *name)
2998 FILE *queuefp = NULL;
2999 char *o;
3000 int os;
3001 enum okay ok = STOP;
3003 o = ac_alloc(os = 2*strlen(name) + 100);
3004 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3005 IMAP_OUT(o, MB_COMD, goto out)
3006 while (mp->mb_active & MB_COMD)
3007 ok = imap_answer(mp, 1);
3008 out: ac_free(o);
3009 return ok;
3012 FL enum okay
3013 imap_rename(const char *old, const char *new)
3015 sighandler_type saveint, savepipe;
3016 enum okay ok = STOP;
3018 (void)&saveint;
3019 (void)&savepipe;
3020 (void)&ok;
3021 if (mb.mb_type != MB_IMAP) {
3022 fprintf(stderr, "Refusing to rename mailboxes "
3023 "in disconnected mode.\n");
3024 return STOP;
3026 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3027 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3028 "server: \"%s\" not renamed to \"%s\".\n",
3029 old, new);
3030 return STOP;
3032 imaplock = 1;
3033 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3034 safe_signal(SIGINT, &_imap_maincatch);
3035 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3036 if (sigsetjmp(imapjmp, 1) == 0) {
3037 if (savepipe != SIG_IGN)
3038 safe_signal(SIGPIPE, imapcatch);
3039 ok = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3041 safe_signal(SIGINT, saveint);
3042 safe_signal(SIGPIPE, savepipe);
3043 imaplock = 0;
3044 if (ok == OKAY)
3045 ok = cache_rename(old, new);
3046 if (interrupts)
3047 onintr(0);
3048 return ok;
3051 static enum okay
3052 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3054 FILE *queuefp = NULL;
3055 char *o;
3056 int os;
3057 enum okay ok = STOP;
3059 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3060 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
3061 imap_quotestr(old), imap_quotestr(new));
3062 IMAP_OUT(o, MB_COMD, goto out)
3063 while (mp->mb_active & MB_COMD)
3064 ok = imap_answer(mp, 1);
3065 out: ac_free(o);
3066 return ok;
3069 FL enum okay
3070 imap_dequeue(struct mailbox *mp, FILE *fp)
3072 FILE *queuefp = NULL;
3073 char o[LINESIZE], *newname;
3074 char *buf, *bp, *cp, iob[4096];
3075 size_t bufsize, buflen, cnt;
3076 enum okay ok = OKAY, rok = OKAY;
3077 long offs, offs1, offs2, octets;
3078 int twice, gotcha = 0;
3080 buf = smalloc(bufsize = LINESIZE);
3081 buflen = 0;
3082 cnt = fsize(fp);
3083 while ((offs1 = ftell(fp)) >= 0 &&
3084 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0)
3085 != NULL) {
3086 for (bp = buf; *bp != ' '; bp++); /* strip old tag */
3087 while (*bp == ' ')
3088 bp++;
3089 twice = 0;
3090 if ((offs = ftell(fp)) < 0)
3091 goto fail;
3092 again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
3093 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3094 cp = &bp[9];
3095 while (digitchar(*cp&0377))
3096 cp++;
3097 if (*cp != ' ')
3098 goto fail;
3099 while (*cp == ' ')
3100 cp++;
3101 if ((newname = imap_strex(cp, NULL)) == NULL)
3102 goto fail;
3103 IMAP_OUT(o, MB_COMD, continue)
3104 while (mp->mb_active & MB_COMD)
3105 ok = imap_answer(mp, twice);
3106 if (response_status == RESPONSE_NO && twice++ == 0)
3107 goto trycreate;
3108 if (response_status == RESPONSE_OK &&
3109 mp->mb_flags & MB_UIDPLUS) {
3110 imap_copyuid(mp, NULL,
3111 imap_unquotestr(newname));
3113 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3114 IMAP_OUT(o, MB_COMD, continue)
3115 while (mp->mb_active & MB_COMD)
3116 ok = imap_answer(mp, 1);
3117 if (ok == OKAY)
3118 gotcha++;
3119 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3120 if ((cp = strrchr(bp, '{')) == NULL)
3121 goto fail;
3122 octets = atol(&cp[1]) + 2;
3123 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3124 goto fail;
3125 IMAP_OUT(o, MB_COMD, continue)
3126 while (mp->mb_active & MB_COMD) {
3127 ok = imap_answer(mp, twice);
3128 if (response_type == RESPONSE_CONT)
3129 break;
3131 if (ok == STOP) {
3132 if (twice++ == 0 &&
3133 fseek(fp, offs, SEEK_SET) >= 0)
3134 goto trycreate;
3135 goto fail;
3137 while (octets > 0) {
3138 size_t n = (size_t)octets > sizeof iob
3139 ? sizeof iob : (size_t)octets;
3140 octets -= n;
3141 if (n != fread(iob, 1, n, fp))
3142 goto fail;
3143 swrite1(&mp->mb_sock, iob, n, 1);
3145 swrite(&mp->mb_sock, "");
3146 while (mp->mb_active & MB_COMD) {
3147 ok = imap_answer(mp, 0);
3148 if (response_status == RESPONSE_NO &&
3149 twice++ == 0) {
3150 if (fseek(fp, offs, SEEK_SET) < 0)
3151 goto fail;
3152 goto trycreate;
3155 if (response_status == RESPONSE_OK &&
3156 mp->mb_flags & MB_UIDPLUS) {
3157 if ((offs2 = ftell(fp)) < 0)
3158 goto fail;
3159 fseek(fp, offs1, SEEK_SET);
3160 if (imap_appenduid_cached(mp, fp) == STOP) {
3161 (void)fseek(fp, offs2, SEEK_SET);
3162 goto fail;
3165 } else {
3166 fail: fprintf(stderr,
3167 "Invalid command in IMAP cache queue: \"%s\"\n",
3168 bp);
3169 rok = STOP;
3171 continue;
3172 trycreate:
3173 snprintf(o, sizeof o, "%s CREATE %s\r\n",
3174 tag(1), newname);
3175 IMAP_OUT(o, MB_COMD, continue)
3176 while (mp->mb_active & MB_COMD)
3177 ok = imap_answer(mp, 1);
3178 if (ok == OKAY)
3179 goto again;
3181 fflush(fp);
3182 rewind(fp);
3183 ftruncate(fileno(fp), 0);
3184 if (gotcha)
3185 imap_close(mp);
3186 free(buf);
3187 return rok;
3190 static char *
3191 imap_strex(char const *cp, char const **xp)
3193 char const *cq;
3194 char *n;
3196 if (*cp != '"')
3197 return NULL;
3198 for (cq = &cp[1]; *cq; cq++) {
3199 if (*cq == '\\')
3200 cq++;
3201 else if (*cq == '"')
3202 break;
3204 if (*cq != '"')
3205 return NULL;
3206 n = salloc(cq - cp + 2);
3207 memcpy(n, cp, cq - cp + 1);
3208 n[cq - cp + 1] = '\0';
3209 if (xp)
3210 *xp = &cq[1];
3211 return n;
3214 static enum okay
3215 check_expunged(void)
3217 if (expunged_messages > 0) {
3218 fprintf(stderr,
3219 "Command not executed - messages have been expunged\n");
3220 return STOP;
3222 return OKAY;
3225 /*ARGSUSED*/
3226 FL int
3227 cconnect(void *vp)
3229 char *cp, *cq;
3230 int omsgCount = msgCount;
3231 (void)vp;
3233 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3234 fprintf(stderr, "Already connected.\n");
3235 return 1;
3237 var_clear_allow_undefined = TRU1;
3238 ok_bclear(disconnected);
3239 cp = protbase(mailname);
3240 if (strncmp(cp, "imap://", 7) == 0)
3241 cp += 7;
3242 else if (strncmp(cp, "imaps://", 8) == 0)
3243 cp += 8;
3244 if ((cq = strchr(cp, ':')) != NULL)
3245 *cq = '\0';
3246 vok_bclear(savecat("disconnected-", cp));
3247 var_clear_allow_undefined = FAL0;
3248 if (mb.mb_type == MB_CACHE) {
3249 imap_setfile1(mailname, 0, edit, 1);
3250 if (msgCount > omsgCount)
3251 newmailinfo(omsgCount);
3253 return 0;
3256 FL int
3257 cdisconnect(void *vp)
3259 int *msgvec = vp;
3261 if (mb.mb_type == MB_CACHE) {
3262 fprintf(stderr, "Not connected.\n");
3263 return 1;
3264 } else if (mb.mb_type == MB_IMAP) {
3265 if (cached_uidvalidity(&mb) == 0) {
3266 fprintf(stderr, "The current mailbox is not cached.\n");
3267 return 1;
3270 if (*msgvec)
3271 ccache(vp);
3272 ok_bset(disconnected, TRU1);
3273 if (mb.mb_type == MB_IMAP) {
3274 sclose(&mb.mb_sock);
3275 imap_setfile1(mailname, 0, edit, 1);
3277 return 0;
3280 FL int
3281 ccache(void *vp)
3283 int *msgvec = vp, *ip;
3284 struct message *mp;
3286 if (mb.mb_type != MB_IMAP) {
3287 fprintf(stderr, "Not connected to an IMAP server.\n");
3288 return 1;
3290 if (cached_uidvalidity(&mb) == 0) {
3291 fprintf(stderr, "The current mailbox is not cached.\n");
3292 return 1;
3294 for (ip = msgvec; *ip; ip++) {
3295 mp = &message[*ip-1];
3296 if (!(mp->m_have & HAVE_BODY))
3297 get_body(mp);
3299 return 0;
3302 FL int
3303 disconnected(const char *file)
3305 char *cp, *cq, *vp;
3306 int vs, r;
3308 if (ok_blook(disconnected))
3309 return 1;
3310 cp = protbase(file);
3311 if (strncmp(cp, "imap://", 7) == 0)
3312 cp += 7;
3313 else if (strncmp(cp, "imaps://", 8) == 0)
3314 cp += 8;
3315 else
3316 return 0;
3317 if ((cq = strchr(cp, ':')) != NULL)
3318 *cq = '\0';
3319 vp = ac_alloc(vs = strlen(cp) + 14);
3320 snprintf(vp, vs, "disconnected-%s", cp);
3321 r = vok_blook(vp);
3322 ac_free(vp);
3323 return r;
3326 FL void
3327 transflags(struct message *omessage, long omsgCount, int transparent)
3329 struct message *omp, *nmp, *newdot, *newprevdot;
3330 int hf;
3332 omp = omessage;
3333 nmp = message;
3334 newdot = message;
3335 newprevdot = NULL;
3336 while (omp < &omessage[omsgCount] &&
3337 nmp < &message[msgCount]) {
3338 if (dot && nmp->m_uid == dot->m_uid)
3339 newdot = nmp;
3340 if (prevdot && nmp->m_uid == prevdot->m_uid)
3341 newprevdot = nmp;
3342 if (omp->m_uid == nmp->m_uid) {
3343 hf = nmp->m_flag & MHIDDEN;
3344 if (transparent && mb.mb_type == MB_IMAP)
3345 omp->m_flag &= ~MHIDDEN;
3346 *nmp++ = *omp++;
3347 if (transparent && mb.mb_type == MB_CACHE)
3348 nmp[-1].m_flag |= hf;
3349 } else if (omp->m_uid < nmp->m_uid)
3350 omp++;
3351 else
3352 nmp++;
3354 dot = newdot;
3355 setdot(newdot);
3356 prevdot = newprevdot;
3357 free(omessage);
3360 FL time_t
3361 imap_read_date_time(const char *cp)
3363 time_t t;
3364 int i, year, month, day, hour, minute, second;
3365 int sign = -1;
3366 char buf[3];
3369 * "25-Jul-2004 15:33:44 +0200"
3370 * | | | | | |
3371 * 0 5 10 15 20 25
3373 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3374 goto invalid;
3375 day = strtol(&cp[1], NULL, 10);
3376 for (i = 0;;) {
3377 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3378 break;
3379 if (month_names[++i][0] == '\0')
3380 goto invalid;
3382 month = i + 1;
3383 year = strtol(&cp[8], NULL, 10);
3384 hour = strtol(&cp[13], NULL, 10);
3385 minute = strtol(&cp[16], NULL, 10);
3386 second = strtol(&cp[19], NULL, 10);
3387 if ((t = combinetime(year, month, day, hour, minute, second)) ==
3388 (time_t)-1)
3389 goto invalid;
3390 switch (cp[22]) {
3391 case '-':
3392 sign = 1;
3393 break;
3394 case '+':
3395 break;
3396 default:
3397 goto invalid;
3399 buf[2] = '\0';
3400 buf[0] = cp[23];
3401 buf[1] = cp[24];
3402 t += strtol(buf, NULL, 10) * sign * 3600;
3403 buf[0] = cp[25];
3404 buf[1] = cp[26];
3405 t += strtol(buf, NULL, 10) * sign * 60;
3406 return t;
3407 invalid:
3408 time(&t);
3409 return t;
3412 FL const char *
3413 imap_make_date_time(time_t t)
3415 static char s[30];
3416 struct tm *tmptr;
3417 int tzdiff, tzdiff_hour, tzdiff_min;
3419 tzdiff = t - mktime(gmtime(&t));
3420 tzdiff_hour = (int)(tzdiff / 60);
3421 tzdiff_min = tzdiff_hour % 60;
3422 tzdiff_hour /= 60;
3423 tmptr = localtime(&t);
3424 if (tmptr->tm_isdst > 0)
3425 tzdiff_hour++;
3426 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3427 tmptr->tm_mday,
3428 month_names[tmptr->tm_mon],
3429 tmptr->tm_year + 1900,
3430 tmptr->tm_hour,
3431 tmptr->tm_min,
3432 tmptr->tm_sec,
3433 tzdiff_hour,
3434 tzdiff_min);
3435 return s;
3437 #endif /* HAVE_IMAP */
3439 FL time_t
3440 imap_read_date(const char *cp)
3442 time_t t;
3443 int year, month, day, i, tzdiff;
3444 struct tm *tmptr;
3445 char *xp, *yp;
3447 if (*cp == '"')
3448 cp++;
3449 day = strtol(cp, &xp, 10);
3450 if (day <= 0 || day > 31 || *xp++ != '-')
3451 return -1;
3452 for (i = 0;;) {
3453 if (ascncasecmp(xp, month_names[i], 3) == 0)
3454 break;
3455 if (month_names[++i][0] == '\0')
3456 return -1;
3458 month = i+1;
3459 if (xp[3] != '-')
3460 return -1;
3461 year = strtol(&xp[4], &yp, 10);
3462 if (year < 1970 || year > 2037 || yp != &xp[8])
3463 return -1;
3464 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3465 return -1;
3466 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3467 return -1;
3468 tzdiff = t - mktime(gmtime(&t));
3469 tmptr = localtime(&t);
3470 if (tmptr->tm_isdst > 0)
3471 tzdiff += 3600;
3472 t -= tzdiff;
3473 return t;
3476 FL char *
3477 imap_quotestr(const char *s)
3479 char *n, *np;
3481 np = n = salloc(2 * strlen(s) + 3);
3482 *np++ = '"';
3483 while (*s) {
3484 if (*s == '"' || *s == '\\')
3485 *np++ = '\\';
3486 *np++ = *s++;
3488 *np++ = '"';
3489 *np = '\0';
3490 return n;
3493 FL char *
3494 imap_unquotestr(const char *s)
3496 char *n, *np;
3498 if (*s != '"')
3499 return savestr(s);
3500 np = n = salloc(strlen(s) + 1);
3501 while (*++s) {
3502 if (*s == '\\')
3503 s++;
3504 else if (*s == '"')
3505 break;
3506 *np++ = *s;
3508 *np = '\0';
3509 return n;