Gavin Troy reported SEGV: partially revert [ac694bb]..
[s-mailx.git] / imap.c
blob66f820b8b3b692136453ab22290faeecce9d0eac
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 - 2014 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 #endif
56 #ifdef HAVE_IMAP
57 #define IMAP_ANSWER() \
59 if (mp->mb_type != MB_CACHE) {\
60 enum okay ok = OKAY;\
61 while (mp->mb_active & MB_COMD)\
62 ok = imap_answer(mp, 1);\
63 if (ok == STOP)\
64 return STOP;\
68 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
69 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
70 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
71 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
72 * TODO I.e., that should be a function, not a macro ... or so.
73 * TODO This entire module needs MASSIVE work! */
74 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
75 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
76 do {\
77 if (mp->mb_type != MB_CACHE) {\
78 if (imap_finish(mp) == STOP) {\
79 ACTIONBAIL;\
81 if (options & OPT_VERBOSE)\
82 fprintf(stderr, ">>> %s", X);\
83 mp->mb_active |= Y;\
84 if (swrite(&mp->mb_sock, X) == STOP) {\
85 ACTIONERR;\
87 } else {\
88 if (queuefp != NULL)\
89 fputs(X, queuefp);\
91 } while (0);
93 static struct record {
94 struct record *rec_next;
95 unsigned long rec_count;
96 enum rec_type {
97 REC_EXISTS,
98 REC_EXPUNGE
99 } rec_type;
100 } *record, *recend;
102 static enum {
103 RESPONSE_TAGGED,
104 RESPONSE_DATA,
105 RESPONSE_FATAL,
106 RESPONSE_CONT,
107 RESPONSE_ILLEGAL
108 } response_type;
110 static enum {
111 RESPONSE_OK,
112 RESPONSE_NO,
113 RESPONSE_BAD,
114 RESPONSE_PREAUTH,
115 RESPONSE_BYE,
116 RESPONSE_OTHER,
117 RESPONSE_UNKNOWN
118 } response_status;
120 static char *responded_tag;
121 static char *responded_text;
122 static char *responded_other_text;
123 static long responded_other_number;
125 static enum {
126 MAILBOX_DATA_FLAGS,
127 MAILBOX_DATA_LIST,
128 MAILBOX_DATA_LSUB,
129 MAILBOX_DATA_MAILBOX,
130 MAILBOX_DATA_SEARCH,
131 MAILBOX_DATA_STATUS,
132 MAILBOX_DATA_EXISTS,
133 MAILBOX_DATA_RECENT,
134 MESSAGE_DATA_EXPUNGE,
135 MESSAGE_DATA_FETCH,
136 CAPABILITY_DATA,
137 RESPONSE_OTHER_UNKNOWN
138 } response_other;
140 static enum list_attributes {
141 LIST_NONE = 000,
142 LIST_NOINFERIORS = 001,
143 LIST_NOSELECT = 002,
144 LIST_MARKED = 004,
145 LIST_UNMARKED = 010
146 } list_attributes;
148 static int list_hierarchy_delimiter;
149 static char *list_name;
151 struct list_item {
152 struct list_item *l_next;
153 char *l_name;
154 char *l_base;
155 enum list_attributes l_attr;
156 int l_delim;
157 int l_level;
158 int l_has_children;
161 static char *imapbuf; /* TODO not static, use pool */
162 static size_t imapbufsize;
163 static sigjmp_buf imapjmp;
164 static sighandler_type savealrm;
165 static int imapkeepalive;
166 static long had_exists = -1;
167 static long had_expunge = -1;
168 static long expunged_messages;
169 static int volatile imaplock;
170 static int same_imap_account;
172 static void imap_other_get(char *pp);
173 static void imap_response_get(const char **cp);
174 static void imap_response_parse(void);
175 static enum okay imap_answer(struct mailbox *mp, int errprnt);
176 static enum okay imap_parse_list(void);
177 static enum okay imap_finish(struct mailbox *mp);
178 static void imap_timer_off(void);
179 static void imapcatch(int s);
180 static void _imap_maincatch(int s);
181 static enum okay imap_noop1(struct mailbox *mp);
182 static void rec_queue(enum rec_type type, unsigned long cnt);
183 static enum okay rec_dequeue(void);
184 static void rec_rmqueue(void);
185 static void imapalarm(int s);
186 static int imap_use_starttls(const char *uhp);
187 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
188 const char *uhp);
189 static enum okay imap_capability(struct mailbox *mp);
190 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
191 char *xuser, char *pass);
192 #ifdef HAVE_MD5
193 static enum okay imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass);
194 #endif
195 static enum okay imap_login(struct mailbox *mp, char *xuser, char *xpass);
196 #ifdef HAVE_GSSAPI
197 static enum okay imap_gss(struct mailbox *mp, char *user);
198 #endif
199 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
200 static void imap_init(struct mailbox *mp, int n);
201 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
202 int *prevcount);
203 static void imap_split(char **server, const char **sp, int *use_ssl,
204 const char **cp, char const **uhp, char const **mbx,
205 char **pass, char **user);
206 static int imap_setfile1(const char *xserver, int nmail, int isedit,
207 int transparent);
208 static int imap_fetchdata(struct mailbox *mp, struct message *m,
209 size_t expected, int need, const char *head,
210 size_t headsize, long headlines);
211 static void imap_putstr(struct mailbox *mp, struct message *m,
212 const char *str, const char *head, size_t headsize,
213 long headlines);
214 static enum okay imap_get(struct mailbox *mp, struct message *m,
215 enum needspec need);
216 static void commitmsg(struct mailbox *mp, struct message *to,
217 struct message *from, enum havespec have);
218 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
219 int bot, int top);
220 static enum okay imap_exit(struct mailbox *mp);
221 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
222 int needstat);
223 static enum okay imap_close(struct mailbox *mp);
224 static enum okay imap_update(struct mailbox *mp);
225 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
226 int c, const char *sp, int needstat);
227 static enum okay imap_unstore(struct message *m, int n, const char *flag);
228 static const char *tag(int new);
229 static char * imap_putflags(int f);
230 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
231 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
232 off_t off1, long xsize, enum mflag flag, time_t t);
233 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
234 static enum okay imap_list1(struct mailbox *mp, const char *base,
235 struct list_item **list, struct list_item **lend,
236 int level);
237 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
238 FILE *fp);
239 static void dopr(FILE *fp);
240 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
241 const char *name);
242 static enum okay imap_copyuid_parse(const char *cp,
243 unsigned long *uidvalidity, unsigned long *olduid,
244 unsigned long *newuid);
245 static enum okay imap_appenduid_parse(const char *cp,
246 unsigned long *uidvalidity, unsigned long *uid);
247 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
248 const char *name);
249 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
250 long off1, long xsize, long size, long lines, int flag,
251 const char *name);
252 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
253 #ifdef HAVE_IMAP_SEARCH
254 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
255 const char *spec, int f);
256 #endif
257 static enum okay imap_remove1(struct mailbox *mp, const char *name);
258 static enum okay imap_rename1(struct mailbox *mp, const char *old,
259 const char *new);
260 static char * imap_strex(char const *cp, char const **xp);
261 static enum okay check_expunged(void);
263 static void
264 imap_other_get(char *pp)
266 char *xp;
267 NYD_ENTER;
269 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
270 pp += 6;
271 response_other = MAILBOX_DATA_FLAGS;
272 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
273 pp += 5;
274 response_other = MAILBOX_DATA_LIST;
275 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
276 pp += 5;
277 response_other = MAILBOX_DATA_LSUB;
278 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
279 pp += 8;
280 response_other = MAILBOX_DATA_MAILBOX;
281 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
282 pp += 7;
283 response_other = MAILBOX_DATA_SEARCH;
284 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
285 pp += 7;
286 response_other = MAILBOX_DATA_STATUS;
287 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
288 pp += 11;
289 response_other = CAPABILITY_DATA;
290 } else {
291 responded_other_number = strtol(pp, &xp, 10);
292 while (*xp == ' ')
293 ++xp;
294 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
295 response_other = MAILBOX_DATA_EXISTS;
296 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
297 response_other = MAILBOX_DATA_RECENT;
298 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
299 response_other = MESSAGE_DATA_EXPUNGE;
300 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
301 pp = &xp[6];
302 response_other = MESSAGE_DATA_FETCH;
303 } else
304 response_other = RESPONSE_OTHER_UNKNOWN;
306 responded_other_text = pp;
307 NYD_LEAVE;
310 static void
311 imap_response_get(const char **cp)
313 NYD_ENTER;
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;
331 NYD_LEAVE;
334 static void
335 imap_response_parse(void)
337 static char *parsebuf; /* TODO Use pool */
338 static size_t parsebufsize;
340 const char *ip = imapbuf;
341 char *pp;
342 NYD_ENTER;
344 if (parsebufsize < imapbufsize)
345 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
346 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
347 pp = parsebuf;
348 switch (*ip) {
349 case '+':
350 response_type = RESPONSE_CONT;
351 ip++;
352 pp++;
353 while (*ip == ' ') {
354 ip++;
355 pp++;
357 break;
358 case '*':
359 ip++;
360 pp++;
361 while (*ip == ' ') {
362 ip++;
363 pp++;
365 imap_response_get(&ip);
366 pp = &parsebuf[ip - imapbuf];
367 switch (response_status) {
368 case RESPONSE_BYE:
369 response_type = RESPONSE_FATAL;
370 break;
371 default:
372 response_type = RESPONSE_DATA;
374 break;
375 default:
376 responded_tag = parsebuf;
377 while (*pp && *pp != ' ')
378 pp++;
379 if (*pp == '\0') {
380 response_type = RESPONSE_ILLEGAL;
381 break;
383 *pp++ = '\0';
384 while (*pp && *pp == ' ')
385 pp++;
386 if (*pp == '\0') {
387 response_type = RESPONSE_ILLEGAL;
388 break;
390 ip = &imapbuf[pp - parsebuf];
391 response_type = RESPONSE_TAGGED;
392 imap_response_get(&ip);
393 pp = &parsebuf[ip - imapbuf];
395 responded_text = pp;
396 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
397 response_status == RESPONSE_OTHER)
398 imap_other_get(pp);
399 NYD_LEAVE;
402 static enum okay
403 imap_answer(struct mailbox *mp, int errprnt)
405 int i, complete;
406 enum okay rv;
407 NYD_ENTER;
409 rv = OKAY;
410 if (mp->mb_type == MB_CACHE)
411 goto jleave;
412 rv = STOP;
413 jagain:
414 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
415 if (options & OPT_VERBOSE)
416 fputs(imapbuf, stderr);
417 imap_response_parse();
418 if (response_type == RESPONSE_ILLEGAL)
419 goto jagain;
420 if (response_type == RESPONSE_CONT) {
421 rv = OKAY;
422 goto jleave;
424 if (response_status == RESPONSE_OTHER) {
425 if (response_other == MAILBOX_DATA_EXISTS) {
426 had_exists = responded_other_number;
427 rec_queue(REC_EXISTS, responded_other_number);
428 if (had_expunge > 0)
429 had_expunge = 0;
430 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
431 rec_queue(REC_EXPUNGE, responded_other_number);
432 if (had_expunge < 0)
433 had_expunge = 0;
434 had_expunge++;
435 expunged_messages++;
438 complete = 0;
439 if (response_type == RESPONSE_TAGGED) {
440 if (asccasecmp(responded_tag, tag(0)) == 0)
441 complete |= 1;
442 else
443 goto jagain;
445 switch (response_status) {
446 case RESPONSE_PREAUTH:
447 mp->mb_active &= ~MB_PREAUTH;
448 /*FALLTHRU*/
449 case RESPONSE_OK:
450 jokay:
451 rv = OKAY;
452 complete |= 2;
453 break;
454 case RESPONSE_NO:
455 case RESPONSE_BAD:
456 jstop:
457 rv = STOP;
458 complete |= 2;
459 if (errprnt)
460 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
461 break;
462 case RESPONSE_UNKNOWN: /* does not happen */
463 case RESPONSE_BYE:
464 i = mp->mb_active;
465 mp->mb_active = MB_NONE;
466 if (i & MB_BYE)
467 goto jokay;
468 goto jstop;
469 case RESPONSE_OTHER:
470 rv = OKAY;
471 break;
473 if (response_status != RESPONSE_OTHER &&
474 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
475 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
476 if (complete == 3)
477 mp->mb_active &= ~MB_COMD;
478 } else {
479 rv = STOP;
480 mp->mb_active = MB_NONE;
482 jleave:
483 NYD_LEAVE;
484 return rv;
487 static enum okay
488 imap_parse_list(void)
490 char *cp;
491 enum okay rv;
492 NYD_ENTER;
494 rv = STOP;
496 cp = responded_other_text;
497 list_attributes = LIST_NONE;
498 if (*cp == '(') {
499 while (*cp && *cp != ')') {
500 if (*cp == '\\') {
501 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
502 list_attributes |= LIST_NOINFERIORS;
503 cp += 12;
504 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
505 list_attributes |= LIST_NOSELECT;
506 cp += 9;
507 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
508 list_attributes |= LIST_MARKED;
509 cp += 7;
510 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
511 list_attributes |= LIST_UNMARKED;
512 cp += 9;
515 cp++;
517 if (*++cp != ' ')
518 goto jleave;
519 while (*cp == ' ')
520 cp++;
523 list_hierarchy_delimiter = EOF;
524 if (*cp == '"') {
525 if (*++cp == '\\')
526 cp++;
527 list_hierarchy_delimiter = *cp++ & 0377;
528 if (cp[0] != '"' || cp[1] != ' ')
529 goto jleave;
530 cp++;
531 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
532 list_hierarchy_delimiter = EOF;
533 cp += 3;
536 while (*cp == ' ')
537 cp++;
538 list_name = cp;
539 while (*cp && *cp != '\r')
540 cp++;
541 *cp = '\0';
542 rv = OKAY;
543 jleave:
544 NYD_LEAVE;
545 return rv;
548 static enum okay
549 imap_finish(struct mailbox *mp)
551 NYD_ENTER;
552 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
553 imap_answer(mp, 1);
554 NYD_LEAVE;
555 return OKAY;
558 static void
559 imap_timer_off(void)
561 NYD_ENTER;
562 if (imapkeepalive > 0) {
563 alarm(0);
564 safe_signal(SIGALRM, savealrm);
566 NYD_LEAVE;
569 static void
570 imapcatch(int s)
572 NYD_X; /* Signal handler */
573 switch (s) {
574 case SIGINT:
575 fprintf(stderr, tr(102, "Interrupt\n"));
576 siglongjmp(imapjmp, 1);
577 /*NOTREACHED*/
578 case SIGPIPE:
579 fprintf(stderr, tr(98, "Received SIGPIPE during IMAP operation\n"));
580 break;
584 static void
585 _imap_maincatch(int s)
587 NYD_X; /* Signal handler */
588 UNUSED(s);
589 if (interrupts++ == 0) {
590 fprintf(stderr, tr(102, "Interrupt\n"));
591 return;
593 onintr(0);
596 static enum okay
597 imap_noop1(struct mailbox *mp)
599 char o[LINESIZE];
600 FILE *queuefp = NULL;
601 NYD_X;
603 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
604 IMAP_OUT(o, MB_COMD, return STOP)
605 IMAP_ANSWER()
606 return OKAY;
609 FL char const *
610 imap_fileof(char const *xcp)
612 char const *cp = xcp;
613 int state = 0;
614 NYD_ENTER;
616 while (*cp) {
617 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
618 cp += 3;
619 state = 1;
621 if (cp[0] == '/' && state == 1) {
622 ++cp;
623 goto jleave;
625 if (cp[0] == '/') {
626 cp = xcp;
627 goto jleave;
629 ++cp;
631 jleave:
632 NYD_LEAVE;
633 return cp;
636 FL enum okay
637 imap_noop(void)
639 sighandler_type volatile oldint, oldpipe;
640 enum okay rv = STOP;
641 NYD_ENTER;
643 if (mb.mb_type != MB_IMAP)
644 goto jleave;
646 imaplock = 1;
647 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
648 safe_signal(SIGINT, &_imap_maincatch);
649 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
650 if (sigsetjmp(imapjmp, 1) == 0) {
651 if (oldpipe != SIG_IGN)
652 safe_signal(SIGPIPE, imapcatch);
654 rv = imap_noop1(&mb);
656 safe_signal(SIGINT, oldint);
657 safe_signal(SIGPIPE, oldpipe);
658 imaplock = 0;
659 jleave:
660 NYD_LEAVE;
661 if (interrupts)
662 onintr(0);
663 return rv;
666 static void
667 rec_queue(enum rec_type rt, unsigned long cnt)
669 struct record *rp;
670 NYD_ENTER;
672 rp = scalloc(1, sizeof *rp);
673 rp->rec_type = rt;
674 rp->rec_count = cnt;
675 if (record && recend) {
676 recend->rec_next = rp;
677 recend = rp;
678 } else
679 record = recend = rp;
680 NYD_LEAVE;
683 static enum okay
684 rec_dequeue(void)
686 struct message *omessage;
687 struct record *rp, *rq;
688 uiz_t exists = 0, i;
689 enum okay rv = STOP;
690 NYD_ENTER;
692 if (record == NULL)
693 goto jleave;
695 omessage = message;
696 message = smalloc((msgCount+1) * sizeof *message);
697 if (msgCount)
698 memcpy(message, omessage, msgCount * sizeof *message);
699 memset(&message[msgCount], 0, sizeof *message);
701 rp = record, rq = NULL;
702 rv = OKAY;
703 while (rp != NULL) {
704 switch (rp->rec_type) {
705 case REC_EXISTS:
706 exists = rp->rec_count;
707 break;
708 case REC_EXPUNGE:
709 if (rp->rec_count == 0) {
710 rv = STOP;
711 break;
713 if (rp->rec_count > (unsigned long)msgCount) {
714 if (exists == 0 || rp->rec_count > exists--)
715 rv = STOP;
716 break;
718 if (exists > 0)
719 exists--;
720 delcache(&mb, &message[rp->rec_count-1]);
721 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
722 ((msgCount - rp->rec_count + 1) * sizeof *message));
723 --msgCount;
724 /* If the message was part of a collapsed thread,
725 * the m_collapsed field of one of its ancestors
726 * should be incremented. It seems hardly possible
727 * to do this with the current message structure,
728 * though. The result is that a '+' may be shown
729 * in the header summary even if no collapsed
730 * children exists */
731 break;
733 if (rq != NULL)
734 free(rq);
735 rq = rp;
736 rp = rp->rec_next;
738 if (rq != NULL)
739 free(rq);
741 record = recend = NULL;
742 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
743 message = srealloc(message, (exists + 1) * sizeof *message);
744 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
745 for (i = msgCount; i < exists; ++i)
746 imap_init(&mb, i);
747 imap_flags(&mb, msgCount+1, exists);
748 msgCount = exists;
751 if (rv == STOP) {
752 free(message);
753 message = omessage;
755 jleave:
756 NYD_LEAVE;
757 return rv;
760 static void
761 rec_rmqueue(void)
763 struct record *rp;
764 NYD_ENTER;
766 for (rp = record; rp != NULL;) {
767 struct record *tmp = rp;
768 rp = rp->rec_next;
769 free(tmp);
771 record = recend = NULL;
772 NYD_LEAVE;
775 /*ARGSUSED*/
776 static void
777 imapalarm(int s)
779 sighandler_type volatile saveint, savepipe;
780 NYD_X; /* Signal handler */
781 UNUSED(s);
783 if (imaplock++ == 0) {
784 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
785 safe_signal(SIGINT, &_imap_maincatch);
786 savepipe = safe_signal(SIGPIPE, SIG_IGN);
787 if (sigsetjmp(imapjmp, 1)) {
788 safe_signal(SIGINT, saveint);
789 safe_signal(SIGPIPE, savepipe);
790 goto jbrk;
792 if (savepipe != SIG_IGN)
793 safe_signal(SIGPIPE, imapcatch);
794 if (imap_noop1(&mb) != OKAY) {
795 safe_signal(SIGINT, saveint);
796 safe_signal(SIGPIPE, savepipe);
797 goto jleave;
799 safe_signal(SIGINT, saveint);
800 safe_signal(SIGPIPE, savepipe);
802 jbrk:
803 alarm(imapkeepalive);
804 jleave:
805 --imaplock;
808 static int
809 imap_use_starttls(const char *uhp)
811 int rv;
812 NYD_ENTER;
814 if (ok_blook(imap_use_starttls))
815 rv = 1;
816 else {
817 char *var = savecat("imap-use-starttls-", uhp);
818 rv = vok_blook(var);
820 NYD_LEAVE;
821 return rv;
824 static enum okay
825 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
827 char *cp;
828 NYD_X;
830 mp->mb_active |= MB_PREAUTH;
831 imap_answer(mp, 1);
832 if ((cp = strchr(xserver, ':')) != NULL) {
833 char *x = salloc(cp - xserver + 1);
834 memcpy(x, xserver, cp - xserver);
835 x[cp - xserver] = '\0';
836 xserver = x;
838 #ifdef HAVE_SSL
839 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
840 FILE *queuefp = NULL;
841 char o[LINESIZE];
843 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
844 IMAP_OUT(o, MB_COMD, return STOP)
845 IMAP_ANSWER()
846 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
847 return STOP;
849 #else
850 if (imap_use_starttls(uhp)) {
851 fprintf(stderr, "No SSL support compiled in.\n");
852 return STOP;
854 #endif
855 imap_capability(mp);
856 return OKAY;
859 static enum okay
860 imap_capability(struct mailbox *mp)
862 char o[LINESIZE];
863 FILE *queuefp = NULL;
864 enum okay ok = STOP;
865 const char *cp;
866 NYD_X;
868 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
869 IMAP_OUT(o, MB_COMD, return STOP)
870 while (mp->mb_active & MB_COMD) {
871 ok = imap_answer(mp, 0);
872 if (response_status == RESPONSE_OTHER &&
873 response_other == CAPABILITY_DATA) {
874 cp = responded_other_text;
875 while (*cp) {
876 while (spacechar(*cp))
877 ++cp;
878 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
879 /* RFC 2359 */
880 mp->mb_flags |= MB_UIDPLUS;
881 while (*cp && !spacechar(*cp))
882 ++cp;
886 return ok;
889 static enum okay
890 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, char *pass)
892 char *var, *auth;
893 enum okay rv;
894 NYD_ENTER;
896 if (!(mp->mb_active & MB_PREAUTH)) {
897 rv = OKAY;
898 goto jleave;
901 if ((auth = ok_vlook(imap_auth)) == NULL) {
902 size_t i = strlen(uhp) + 1;
903 var = ac_alloc(i + 10);
904 memcpy(var, "imap-auth-", 10);
905 memcpy(var + 10, uhp, i);
906 auth = vok_vlook(var);
907 ac_free(var);
910 if (auth == NULL || !strcmp(auth, "login"))
911 rv = imap_login(mp, xuser, pass);
912 else if (!strcmp(auth, "cram-md5")) {
913 #ifdef HAVE_MD5
914 rv = imap_cram_md5(mp, xuser, pass);
915 #else
916 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
917 rv = STOP;
918 #endif
919 } else if (!strcmp(auth, "gssapi")) {
920 #ifdef HAVE_GSSAPI
921 rv = imap_gss(mp, xuser);
922 #else
923 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
924 rv = STOP;
925 #endif
926 } else {
927 fprintf(stderr, tr(273,
928 "Unknown IMAP authentication method: %s\n"), auth);
929 rv = STOP;
931 jleave:
932 NYD_LEAVE;
933 return rv;
936 /* Implementation of RFC 2194 */
937 #ifdef HAVE_MD5
938 static enum okay
939 imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass)
941 char o[LINESIZE], *user, *pass, *cp;
942 FILE *queuefp = NULL;
943 enum okay rv = STOP;
944 NYD_ENTER;
946 jretry:
947 user = xuser;
948 pass = xpass;
949 if (!getcredentials(&user, &pass))
950 goto jleave;
952 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
953 IMAP_XOUT(o, 0, goto jleave, goto jleave);
954 imap_answer(mp, 1);
955 if (response_type != RESPONSE_CONT)
956 goto jleave;
958 cp = cram_md5_string(user, pass, responded_text);
959 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
960 while (mp->mb_active & MB_COMD)
961 rv = imap_answer(mp, 1);
962 if (rv == STOP) {
963 xpass = NULL;
964 goto jretry;
966 jleave:
967 NYD_LEAVE;
968 return rv;
970 #endif /* HAVE_MD5 */
972 static enum okay
973 imap_login(struct mailbox *mp, char *xuser, char *xpass)
975 char o[LINESIZE];
976 char *user, *pass;
977 FILE *queuefp = NULL;
978 enum okay rv = STOP;
979 NYD_ENTER;
981 jretry:
982 user = xuser;
983 pass = xpass;
984 if (!getcredentials(&user, &pass))
985 goto jleave;
987 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
988 tag(1), imap_quotestr(user), imap_quotestr(pass));
989 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
990 while (mp->mb_active & MB_COMD)
991 rv = imap_answer(mp, 1);
992 if (rv == STOP) {
993 xpass = NULL;
994 goto jretry;
996 jleave:
997 NYD_LEAVE;
998 return rv;
1001 #ifdef HAVE_GSSAPI
1002 # include "imap_gssapi.h"
1003 #endif
1005 FL enum okay
1006 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
1008 enum okay ok = OKAY;
1009 char const *cp;
1010 char o[LINESIZE];
1011 FILE *queuefp = NULL;
1012 NYD_X;
1013 UNUSED(size);
1015 mp->mb_uidvalidity = 0;
1016 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
1017 IMAP_OUT(o, MB_COMD, return STOP)
1018 while (mp->mb_active & MB_COMD) {
1019 ok = imap_answer(mp, 1);
1020 if (response_status != RESPONSE_OTHER &&
1021 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1022 mp->mb_uidvalidity = atol(&cp[13]);
1024 *cnt = (had_exists > 0) ? had_exists : 0;
1025 if (response_status != RESPONSE_OTHER &&
1026 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1027 mp->mb_perm = 0;
1028 return ok;
1031 static enum okay
1032 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1034 char o[LINESIZE];
1035 FILE *queuefp = NULL;
1036 char const *cp;
1037 struct message *m;
1038 unsigned x = X, y = Y, n;
1039 NYD_X;
1041 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1042 IMAP_OUT(o, MB_COMD, return STOP)
1043 while (mp->mb_active & MB_COMD) {
1044 imap_answer(mp, 1);
1045 if (response_status == RESPONSE_OTHER &&
1046 response_other == MESSAGE_DATA_FETCH) {
1047 n = responded_other_number;
1048 if (n < x || n > y)
1049 continue;
1050 m = &message[n-1];
1051 m->m_xsize = 0;
1052 } else
1053 continue;
1055 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1056 cp += 5;
1057 while (*cp == ' ')
1058 cp++;
1059 if (*cp == '(')
1060 imap_getflags(cp, &cp, &m->m_flag);
1063 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1064 m->m_uid = strtoul(&cp[4], NULL, 10);
1065 getcache1(mp, m, NEED_UNSPEC, 1);
1066 m->m_flag &= ~MHIDDEN;
1069 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1070 x++;
1071 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1072 y--;
1073 if (x <= y) {
1074 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1075 tag(1), x, y);
1076 IMAP_OUT(o, MB_COMD, return STOP)
1077 while (mp->mb_active & MB_COMD) {
1078 imap_answer(mp, 1);
1079 if (response_status == RESPONSE_OTHER &&
1080 response_other == MESSAGE_DATA_FETCH) {
1081 n = responded_other_number;
1082 if (n < x || n > y)
1083 continue;
1084 m = &message[n-1];
1085 } else
1086 continue;
1087 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1088 m->m_xsize = strtol(&cp[12], NULL, 10);
1089 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1090 m->m_time = imap_read_date_time(&cp[13]);
1094 for (n = X; n <= Y; n++)
1095 putcache(mp, &message[n-1]);
1096 return OKAY;
1099 static void
1100 imap_init(struct mailbox *mp, int n)
1102 struct message *m;
1103 NYD_ENTER;
1104 UNUSED(mp);
1106 m = message + n;
1107 m->m_flag = MUSED | MNOFROM;
1108 m->m_block = 0;
1109 m->m_offset = 0;
1110 NYD_LEAVE;
1113 static void
1114 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1116 struct message *omessage = 0;
1117 int i, omsgCount = 0;
1118 enum okay dequeued = STOP;
1119 NYD_ENTER;
1121 if (nmail || transparent) {
1122 omessage = message;
1123 omsgCount = msgCount;
1125 if (nmail)
1126 dequeued = rec_dequeue();
1128 if (had_exists >= 0) {
1129 if (dequeued != OKAY)
1130 msgCount = had_exists;
1131 had_exists = -1;
1133 if (had_expunge >= 0) {
1134 if (dequeued != OKAY)
1135 msgCount -= had_expunge;
1136 had_expunge = -1;
1139 if (nmail && expunged_messages)
1140 printf("Expunged %ld message%s.\n", expunged_messages,
1141 (expunged_messages != 1 ? "s" : ""));
1142 *prevcount = omsgCount - expunged_messages;
1143 expunged_messages = 0;
1144 if (msgCount < 0) {
1145 fputs("IMAP error: Negative message count\n", stderr);
1146 msgCount = 0;
1149 if (dequeued != OKAY) {
1150 message = scalloc(msgCount + 1, sizeof *message);
1151 for (i = 0; i < msgCount; i++)
1152 imap_init(mp, i);
1153 if (!nmail && mp->mb_type == MB_IMAP)
1154 initcache(mp);
1155 if (msgCount > 0)
1156 imap_flags(mp, 1, msgCount);
1157 message[msgCount].m_size = 0;
1158 message[msgCount].m_lines = 0;
1159 rec_rmqueue();
1161 if (nmail || transparent)
1162 transflags(omessage, omsgCount, transparent);
1163 else
1164 setdot(message);
1165 NYD_LEAVE;
1168 static void
1169 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1170 char const **uhp, char const **mbx, char **pass, char **user)
1172 NYD_ENTER;
1173 *sp = *server;
1174 if (strncmp(*sp, "imap://", 7) == 0) {
1175 *sp = &(*sp)[7];
1176 *use_ssl = 0;
1177 #ifdef HAVE_SSL
1178 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1179 *sp = &(*sp)[8];
1180 *use_ssl = 1;
1181 #endif
1182 } else
1183 *use_ssl = 0; /* (silence CC) */
1185 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1186 char *x = savestr(*sp);
1187 x[*cp - *sp] = '\0';
1188 *uhp = x;
1189 *mbx = &(*cp)[1];
1190 } else {
1191 if (*cp)
1192 (*server)[*cp - *server] = '\0';
1193 *uhp = *sp;
1194 *mbx = "INBOX";
1197 *pass = lookup_password_for_token(*uhp);
1198 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1199 *user = salloc(*cp - *uhp + 1);
1200 memcpy(*user, *uhp, *cp - *uhp);
1201 (*user)[*cp - *uhp] = '\0';
1202 *sp = &(*cp)[1];
1203 *user = urlxdec(*user);
1204 } else {
1205 *user = NULL;
1206 *sp = *uhp;
1208 NYD_LEAVE;
1211 FL int
1212 imap_setfile(const char *xserver, int nmail, int isedit)
1214 int rv;
1215 NYD_ENTER;
1217 rv = imap_setfile1(xserver, nmail, isedit, 0);
1218 NYD_LEAVE;
1219 return rv;
1222 static int
1223 imap_setfile1(const char *xserver, int nmail, int isedit,
1224 int volatile transparent)
1226 struct sock so;
1227 sighandler_type volatile saveint, savepipe;
1228 char *server, *user, *pass, *acc;
1229 char const *cp, *sp, * volatile mbx, *uhp;
1230 int rv, use_ssl = 0, prevcount = 0;
1231 enum mbflags same_flags;
1232 NYD_ENTER;
1234 server = savestr(xserver);
1235 if (nmail) {
1236 saveint = safe_signal(SIGINT, SIG_IGN);
1237 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1238 if (saveint != SIG_IGN)
1239 safe_signal(SIGINT, imapcatch);
1240 if (savepipe != SIG_IGN)
1241 safe_signal(SIGPIPE, imapcatch);
1242 imaplock = 1;
1243 goto jnmail;
1246 same_flags = mb.mb_flags;
1247 same_imap_account = 0;
1248 sp = protbase(server);
1249 if (mb.mb_imap_account && mb.mb_type == MB_IMAP) {
1250 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1251 strcmp(mb.mb_imap_account, sp) == 0 &&
1252 disconnected(mb.mb_imap_account) == 0)
1253 same_imap_account = 1;
1255 acc = sstrdup(sp);
1258 char const *xmbx;
1259 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &xmbx, &pass, &user);
1260 mbx = xmbx;
1262 so.s_fd = -1;
1263 if (!same_imap_account) {
1264 if (!disconnected(acc) &&
1265 sopen(sp, &so, use_ssl, uhp, (use_ssl ? "imaps" : "imap")
1266 ) != OKAY) {
1267 free(acc);
1268 rv = -1;
1269 goto jleave;
1271 } else
1272 so = mb.mb_sock;
1273 if (!transparent)
1274 quit();
1276 edit = (isedit != 0);
1277 if (mb.mb_imap_account != NULL)
1278 free(mb.mb_imap_account);
1279 mb.mb_imap_account = acc;
1280 if (!same_imap_account) {
1281 if (mb.mb_sock.s_fd >= 0)
1282 sclose(&mb.mb_sock);
1284 same_imap_account = 0;
1286 if (!transparent) {
1287 if (mb.mb_itf) {
1288 fclose(mb.mb_itf);
1289 mb.mb_itf = NULL;
1291 if (mb.mb_otf) {
1292 fclose(mb.mb_otf);
1293 mb.mb_otf = NULL;
1295 if (mb.mb_imap_mailbox != NULL)
1296 free(mb.mb_imap_mailbox);
1297 mb.mb_imap_mailbox = sstrdup(mbx);
1298 initbox(server);
1300 mb.mb_type = MB_VOID;
1301 mb.mb_active = MB_NONE;
1303 imaplock = 1;
1304 saveint = safe_signal(SIGINT, SIG_IGN);
1305 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1306 if (sigsetjmp(imapjmp, 1)) {
1307 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1308 sclose(&mb.mb_sock);
1309 safe_signal(SIGINT, saveint);
1310 safe_signal(SIGPIPE, savepipe);
1311 imaplock = 0;
1313 mb.mb_type = MB_VOID;
1314 mb.mb_active = MB_NONE;
1315 rv = -1;
1316 goto jleave;
1318 if (saveint != SIG_IGN)
1319 safe_signal(SIGINT, imapcatch);
1320 if (savepipe != SIG_IGN)
1321 safe_signal(SIGPIPE, imapcatch);
1323 if (mb.mb_sock.s_fd < 0) {
1324 if (disconnected(mb.mb_imap_account)) {
1325 if (cache_setptr(transparent) == STOP)
1326 fprintf(stderr,
1327 "Mailbox \"%s\" is not cached.\n",
1328 server);
1329 goto jdone;
1331 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1332 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1333 savealrm = safe_signal(SIGALRM, imapalarm);
1334 alarm(imapkeepalive);
1338 mb.mb_sock = so;
1339 mb.mb_sock.s_desc = "IMAP";
1340 mb.mb_sock.s_onclose = imap_timer_off;
1341 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1342 imap_auth(&mb, uhp, user, pass) != OKAY) {
1343 sclose(&mb.mb_sock);
1344 imap_timer_off();
1345 safe_signal(SIGINT, saveint);
1346 safe_signal(SIGPIPE, savepipe);
1347 imaplock = 0;
1348 rv = -1;
1349 goto jleave;
1351 } else /* same account */
1352 mb.mb_flags |= same_flags;
1354 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1355 mb.mb_type = MB_IMAP;
1356 cache_dequeue(&mb);
1357 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1358 /*sclose(&mb.mb_sock);
1359 imap_timer_off();*/
1360 safe_signal(SIGINT, saveint);
1361 safe_signal(SIGPIPE, savepipe);
1362 imaplock = 0;
1363 mb.mb_type = MB_VOID;
1364 rv = -1;
1365 goto jleave;
1368 jnmail:
1369 imap_setptr(&mb, nmail, transparent, &prevcount);
1370 jdone:
1371 setmsize(msgCount);
1372 if (!nmail && !transparent)
1373 sawcom = FAL0;
1374 safe_signal(SIGINT, saveint);
1375 safe_signal(SIGPIPE, savepipe);
1376 imaplock = 0;
1378 if (!nmail && mb.mb_type == MB_IMAP)
1379 purgecache(&mb, message, msgCount);
1380 if ((nmail || transparent) && mb.mb_sorted) {
1381 mb.mb_threaded = 0;
1382 c_sort((void*)-1);
1385 if (!nmail && !edit && msgCount == 0) {
1386 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1387 !ok_blook(emptystart))
1388 fprintf(stderr, tr(258, "No mail at %s\n"), server);
1389 rv = 1;
1390 goto jleave;
1392 if (nmail)
1393 newmailinfo(prevcount);
1394 rv = 0;
1395 jleave:
1396 NYD_LEAVE;
1397 return rv;
1400 static int
1401 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1402 int need, const char *head, size_t headsize, long headlines)
1404 char *line = NULL, *lp;
1405 size_t linesize = 0, linelen, size = 0;
1406 int emptyline = 0, lines = 0, excess = 0;
1407 off_t offset;
1408 NYD_ENTER;
1410 fseek(mp->mb_otf, 0L, SEEK_END);
1411 offset = ftell(mp->mb_otf);
1413 if (head)
1414 fwrite(head, 1, headsize, mp->mb_otf);
1416 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1417 lp = line;
1418 if (linelen > expected) {
1419 excess = linelen - expected;
1420 linelen = expected;
1422 /* TODO >>
1423 * Need to mask 'From ' lines. This cannot be done properly
1424 * since some servers pass them as 'From ' and others as
1425 * '>From '. Although one could identify the first kind of
1426 * server in principle, it is not possible to identify the
1427 * second as '>From ' may also come from a server of the
1428 * first type as actual data. So do what is absolutely
1429 * necessary only - mask 'From '.
1431 * If the line is the first line of the message header, it
1432 * is likely a real 'From ' line. In this case, it is just
1433 * ignored since it violates all standards.
1434 * TODO can the latter *really* happen??
1435 * TODO <<
1437 /* Since we simply copy over data without doing any transfer
1438 * encoding reclassification/adjustment we *have* to perform
1439 * RFC 4155 compliant From_ quoting here */
1440 if (is_head(lp, linelen)) {
1441 if (lines + headlines == 0)
1442 goto jskip;
1443 fputc('>', mp->mb_otf);
1444 ++size;
1446 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1447 emptyline = linelen <= 2;
1448 if (linelen > 2) {
1449 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1450 size += linelen - 1;
1451 } else
1452 ++size;
1453 fputc('\n', mp->mb_otf);
1454 } else {
1455 emptyline = 0;
1456 fwrite(lp, 1, linelen, mp->mb_otf);
1457 size += linelen;
1459 ++lines;
1460 jskip:
1461 if ((expected -= linelen) <= 0)
1462 break;
1464 if (!emptyline) {
1465 /* This is very ugly; but some IMAP daemons don't end a
1466 * message with \r\n\r\n, and we need \n\n for mbox format */
1467 fputc('\n', mp->mb_otf);
1468 ++lines;
1469 ++size;
1471 fflush(mp->mb_otf);
1473 if (m != NULL) {
1474 m->m_size = size + headsize;
1475 m->m_lines = lines + headlines;
1476 m->m_block = mailx_blockof(offset);
1477 m->m_offset = mailx_offsetof(offset);
1478 switch (need) {
1479 case NEED_HEADER:
1480 m->m_have |= HAVE_HEADER;
1481 break;
1482 case NEED_BODY:
1483 m->m_have |= HAVE_HEADER | HAVE_BODY;
1484 m->m_xlines = m->m_lines;
1485 m->m_xsize = m->m_size;
1486 break;
1489 free(line);
1490 NYD_LEAVE;
1491 return excess;
1494 static void
1495 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1496 const char *head, size_t headsize, long headlines)
1498 off_t offset;
1499 size_t len;
1500 NYD_ENTER;
1502 len = strlen(str);
1503 fseek(mp->mb_otf, 0L, SEEK_END);
1504 offset = ftell(mp->mb_otf);
1505 if (head)
1506 fwrite(head, 1, headsize, mp->mb_otf);
1507 if (len > 0) {
1508 fwrite(str, 1, len, mp->mb_otf);
1509 fputc('\n', mp->mb_otf);
1510 ++len;
1512 fflush(mp->mb_otf);
1514 if (m != NULL) {
1515 m->m_size = headsize + len;
1516 m->m_lines = headlines + 1;
1517 m->m_block = mailx_blockof(offset);
1518 m->m_offset = mailx_offsetof(offset);
1519 m->m_have |= HAVE_HEADER | HAVE_BODY;
1520 m->m_xlines = m->m_lines;
1521 m->m_xsize = m->m_size;
1523 NYD_LEAVE;
1526 static enum okay
1527 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1529 char o[LINESIZE];
1530 struct message mt;
1531 sighandler_type volatile saveint, savepipe;
1532 char * volatile head;
1533 char const *cp, *loc, * volatile item, * volatile resp;
1534 size_t expected;
1535 size_t volatile headsize;
1536 int number;
1537 FILE *queuefp;
1538 long volatile headlines;
1539 long n;
1540 unsigned long u;
1541 enum okay ok;
1542 NYD_X;
1544 saveint = savepipe = SIG_IGN;
1545 head = NULL;
1546 cp = loc = item = resp = NULL;
1547 headsize = 0;
1548 number = (int)PTR2SIZE(m - message + 1);
1549 queuefp = NULL;
1550 headlines = 0;
1551 n = -1;
1552 u = 0;
1553 ok = STOP;
1555 if (getcache(mp, m, need) == OKAY)
1556 return OKAY;
1557 if (mp->mb_type == MB_CACHE) {
1558 fprintf(stderr, "Message %u not available.\n", number);
1559 return STOP;
1562 if (mp->mb_sock.s_fd < 0) {
1563 fprintf(stderr, "IMAP connection closed.\n");
1564 return STOP;
1567 switch (need) {
1568 case NEED_HEADER:
1569 resp = item = "RFC822.HEADER";
1570 break;
1571 case NEED_BODY:
1572 item = "BODY.PEEK[]";
1573 resp = "BODY[]";
1574 if (m->m_flag & HAVE_HEADER && m->m_size) {
1575 char *hdr = smalloc(m->m_size);
1576 fflush(mp->mb_otf);
1577 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1578 SEEK_SET) < 0 ||
1579 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1580 free(hdr);
1581 break;
1583 head = hdr;
1584 headsize = m->m_size;
1585 headlines = m->m_lines;
1586 item = "BODY.PEEK[TEXT]";
1587 resp = "BODY[TEXT]";
1589 break;
1590 case NEED_UNSPEC:
1591 return STOP;
1594 imaplock = 1;
1595 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1596 if (sigsetjmp(imapjmp, 1)) {
1597 safe_signal(SIGINT, saveint);
1598 safe_signal(SIGPIPE, savepipe);
1599 imaplock = 0;
1600 return STOP;
1602 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1603 safe_signal(SIGINT, &_imap_maincatch);
1604 if (savepipe != SIG_IGN)
1605 safe_signal(SIGPIPE, imapcatch);
1607 if (m->m_uid)
1608 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1609 tag(1), m->m_uid, item);
1610 else {
1611 if (check_expunged() == STOP)
1612 goto out;
1613 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1615 IMAP_OUT(o, MB_COMD, goto out)
1616 for (;;) {
1617 ok = imap_answer(mp, 1);
1618 if (ok == STOP)
1619 break;
1620 if (response_status != RESPONSE_OTHER ||
1621 response_other != MESSAGE_DATA_FETCH)
1622 continue;
1623 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1624 continue;
1625 if (m->m_uid) {
1626 if ((cp = asccasestr(responded_other_text, "UID "))) {
1627 u = atol(&cp[4]);
1628 n = 0;
1629 } else {
1630 n = -1;
1631 u = 0;
1633 } else
1634 n = responded_other_number;
1635 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1636 if (m->m_uid ? m->m_uid != u : n != number)
1637 continue;
1638 if ((cp = strchr(loc, '"')) != NULL) {
1639 cp = imap_unquotestr(cp);
1640 imap_putstr(mp, m, cp, head, headsize, headlines);
1641 } else {
1642 m->m_have |= HAVE_HEADER|HAVE_BODY;
1643 m->m_xlines = m->m_lines;
1644 m->m_xsize = m->m_size;
1646 goto out;
1648 expected = atol(&cp[1]);
1649 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1650 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1651 continue;
1653 mt = *m;
1654 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1655 if (n >= 0) {
1656 commitmsg(mp, m, &mt, mt.m_have);
1657 break;
1659 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1660 if (options & OPT_VERBOSE)
1661 fputs(imapbuf, stderr);
1662 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1663 u = atol(&cp[4]);
1664 if (u == m->m_uid) {
1665 commitmsg(mp, m, &mt, mt.m_have);
1666 break;
1671 out:
1672 while (mp->mb_active & MB_COMD)
1673 ok = imap_answer(mp, 1);
1675 if (saveint != SIG_IGN)
1676 safe_signal(SIGINT, saveint);
1677 if (savepipe != SIG_IGN)
1678 safe_signal(SIGPIPE, savepipe);
1679 imaplock--;
1681 if (ok == OKAY)
1682 putcache(mp, m);
1683 if (head != NULL)
1684 free(head);
1685 if (interrupts)
1686 onintr(0);
1687 return ok;
1690 FL enum okay
1691 imap_header(struct message *m)
1693 enum okay rv;
1694 NYD_ENTER;
1696 rv = imap_get(&mb, m, NEED_HEADER);
1697 NYD_LEAVE;
1698 return rv;
1702 FL enum okay
1703 imap_body(struct message *m)
1705 enum okay rv;
1706 NYD_ENTER;
1708 rv = imap_get(&mb, m, NEED_BODY);
1709 NYD_LEAVE;
1710 return rv;
1713 static void
1714 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1715 enum havespec have)
1717 NYD_ENTER;
1718 tomp->m_size = frommp->m_size;
1719 tomp->m_lines = frommp->m_lines;
1720 tomp->m_block = frommp->m_block;
1721 tomp->m_offset = frommp->m_offset;
1722 tomp->m_have = have;
1723 if (have & HAVE_BODY) {
1724 tomp->m_xlines = frommp->m_lines;
1725 tomp->m_xsize = frommp->m_size;
1727 putcache(mp, tomp);
1728 NYD_LEAVE;
1731 static enum okay
1732 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1734 /* bot > topp */
1735 char o[LINESIZE];
1736 char const *cp;
1737 struct message mt;
1738 size_t expected;
1739 int n = 0, u;
1740 FILE *queuefp = NULL;
1741 enum okay ok;
1742 NYD_X;
1744 if (m[bot].m_uid)
1745 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1746 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1747 else {
1748 if (check_expunged() == STOP)
1749 return STOP;
1750 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1751 tag(1), bot, topp);
1753 IMAP_OUT(o, MB_COMD, return STOP)
1754 for (;;) {
1755 ok = imap_answer(mp, 1);
1756 if (response_status != RESPONSE_OTHER)
1757 break;
1758 if (response_other != MESSAGE_DATA_FETCH)
1759 continue;
1760 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1761 return STOP;
1762 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1763 continue;
1764 expected = atol(&cp[1]);
1765 if (m[bot-1].m_uid) {
1766 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1767 u = atoi(&cp[4]);
1768 for (n = bot; n <= topp; n++)
1769 if ((unsigned long)u == m[n-1].m_uid)
1770 break;
1771 if (n > topp) {
1772 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1773 continue;
1775 } else
1776 n = -1;
1777 } else {
1778 n = responded_other_number;
1779 if (n <= 0 || n > msgCount) {
1780 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1781 continue;
1784 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1785 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1786 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1787 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1788 if (options & OPT_VERBOSE)
1789 fputs(imapbuf, stderr);
1790 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1791 u = atoi(&cp[4]);
1792 for (n = bot; n <= topp; n++)
1793 if ((unsigned long)u == m[n-1].m_uid)
1794 break;
1795 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1796 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1797 n = 0;
1801 while (mp->mb_active & MB_COMD)
1802 ok = imap_answer(mp, 1);
1803 return ok;
1806 FL void
1807 imap_getheaders(int volatile bot, int topp) /* TODO should take iterator!! */
1809 sighandler_type saveint, savepipe;
1810 /* enum okay ok = STOP;*/
1811 int i, chunk = 256;
1812 NYD_X;
1814 if (mb.mb_type == MB_CACHE)
1815 return;
1816 if (bot < 1)
1817 bot = 1;
1818 if (topp > msgCount)
1819 topp = msgCount;
1820 for (i = bot; i < topp; i++) {
1821 if (message[i-1].m_have & HAVE_HEADER ||
1822 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1823 bot = i+1;
1824 else
1825 break;
1827 for (i = topp; i > bot; i--) {
1828 if (message[i-1].m_have & HAVE_HEADER ||
1829 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1830 topp = i-1;
1831 else
1832 break;
1834 if (bot >= topp)
1835 return;
1837 imaplock = 1;
1838 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1839 safe_signal(SIGINT, &_imap_maincatch);
1840 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1841 if (sigsetjmp(imapjmp, 1) == 0) {
1842 if (savepipe != SIG_IGN)
1843 safe_signal(SIGPIPE, imapcatch);
1845 for (i = bot; i <= topp; i += chunk) {
1846 int j = i + chunk - 1;
1847 /* FIXME Gavin reported SEGV>800 msgs if (visible(message + j)) */
1848 /*ok = */imap_fetchheaders(&mb, message, i, (j < topp ? j : topp));
1849 if (interrupts)
1850 onintr(0); /* XXX imaplock? */
1853 safe_signal(SIGINT, saveint);
1854 safe_signal(SIGPIPE, savepipe);
1855 imaplock = 0;
1858 static enum okay
1859 __imap_exit(struct mailbox *mp)
1861 char o[LINESIZE];
1862 FILE *queuefp = NULL;
1863 NYD_X;
1865 mp->mb_active |= MB_BYE;
1866 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1867 IMAP_OUT(o, MB_COMD, return STOP)
1868 IMAP_ANSWER()
1869 return OKAY;
1872 static enum okay
1873 imap_exit(struct mailbox *mp)
1875 enum okay rv;
1876 NYD_ENTER;
1878 rv = __imap_exit(mp);
1879 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1880 free(mp->mb_imap_account);
1881 free(mp->mb_imap_mailbox);
1882 if (mp->mb_cache_directory != NULL)
1883 free(mp->mb_cache_directory);
1884 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1885 mp->mb_imap_account =
1886 mp->mb_imap_mailbox =
1887 mp->mb_cache_directory = "";
1888 #else
1889 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1890 mp->mb_imap_mailbox = NULL;
1891 mp->mb_cache_directory = NULL;
1892 #endif
1893 #endif
1894 sclose(&mp->mb_sock);
1895 NYD_LEAVE;
1896 return rv;
1899 static enum okay
1900 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1902 NYD_ENTER;
1903 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1904 if (mp->mb_type == MB_IMAP)
1905 delcache(mp, m);
1906 NYD_LEAVE;
1907 return OKAY;
1910 static enum okay
1911 imap_close(struct mailbox *mp)
1913 char o[LINESIZE];
1914 FILE *queuefp = NULL;
1915 NYD_X;
1917 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1918 IMAP_OUT(o, MB_COMD, return STOP)
1919 IMAP_ANSWER()
1920 return OKAY;
1923 static enum okay
1924 imap_update(struct mailbox *mp)
1926 struct message *m;
1927 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1928 NYD_ENTER;
1930 if (!edit && mp->mb_perm != 0) {
1931 holdbits();
1932 c = 0;
1933 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1934 if (m->m_flag & MBOX)
1935 ++c;
1936 if (c > 0)
1937 if (makembox() == STOP)
1938 goto jbypass;
1941 gotcha = held = 0;
1942 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1943 if (mp->mb_perm == 0)
1944 dodel = 0;
1945 else if (edit)
1946 dodel = ((m->m_flag & MDELETED) != 0);
1947 else
1948 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1950 /* Fetch the result after around each 800 STORE commands
1951 * sent (approx. 32k data sent). Otherwise, servers will
1952 * try to flush the return queue at some point, leading
1953 * to a deadlock if we are still writing commands but not
1954 * reading their results */
1955 needstat = stored > 0 && stored % 800 == 0;
1956 /* Even if this message has been deleted, continue
1957 * to set further flags. This is necessary to support
1958 * Gmail semantics, where "delete" actually means
1959 * "archive", and the flags are applied to the copy
1960 * in "All Mail" */
1961 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1962 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1963 stored++;
1965 if (m->m_flag & MFLAG) {
1966 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1967 stored++;
1969 if (m->m_flag & MUNFLAG) {
1970 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1971 stored++;
1973 if (m->m_flag & MANSWER) {
1974 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1975 stored++;
1977 if (m->m_flag & MUNANSWER) {
1978 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1979 stored++;
1981 if (m->m_flag & MDRAFT) {
1982 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1983 stored++;
1985 if (m->m_flag & MUNDRAFT) {
1986 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1987 stored++;
1990 if (dodel) {
1991 imap_delete(mp, m-message+1, m, needstat);
1992 stored++;
1993 gotcha++;
1994 } else if (mp->mb_type != MB_CACHE ||
1995 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1996 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1997 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1998 held++;
1999 if (m->m_flag & MNEW) {
2000 m->m_flag &= ~MNEW;
2001 m->m_flag |= MSTATUS;
2004 jbypass:
2005 if (gotcha)
2006 imap_close(mp);
2008 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2009 if (!(m->m_flag & MUNLINKED) &&
2010 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2011 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2012 putcache(mp, m);
2013 modflags++;
2015 if ((gotcha || modflags) && edit) {
2016 printf(tr(168, "\"%s\" "), displayname);
2017 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2018 ? tr(170, "complete\n") : tr(212, "updated.\n"));
2019 } else if (held && !edit && mp->mb_perm != 0) {
2020 if (held == 1)
2021 printf(tr(155, "Held 1 message in %s\n"), displayname);
2022 else
2023 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
2025 fflush(stdout);
2026 NYD_LEAVE;
2027 return OKAY;
2030 FL void
2031 imap_quit(void)
2033 sighandler_type volatile saveint, savepipe;
2034 NYD_ENTER;
2036 if (mb.mb_type == MB_CACHE) {
2037 imap_update(&mb);
2038 goto jleave;
2041 if (mb.mb_sock.s_fd < 0) {
2042 fprintf(stderr, "IMAP connection closed.\n");
2043 goto jleave;
2046 imaplock = 1;
2047 saveint = safe_signal(SIGINT, SIG_IGN);
2048 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2049 if (sigsetjmp(imapjmp, 1)) {
2050 safe_signal(SIGINT, saveint);
2051 safe_signal(SIGPIPE, saveint);
2052 imaplock = 0;
2053 goto jleave;
2055 if (saveint != SIG_IGN)
2056 safe_signal(SIGINT, imapcatch);
2057 if (savepipe != SIG_IGN)
2058 safe_signal(SIGPIPE, imapcatch);
2060 imap_update(&mb);
2061 if (!same_imap_account)
2062 imap_exit(&mb);
2064 safe_signal(SIGINT, saveint);
2065 safe_signal(SIGPIPE, savepipe);
2066 imaplock = 0;
2067 jleave:
2068 NYD_LEAVE;
2071 static enum okay
2072 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2073 int needstat)
2075 char o[LINESIZE];
2076 FILE *queuefp = NULL;
2077 NYD_X;
2079 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2080 return STOP;
2081 if (m->m_uid)
2082 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2083 tag(1), m->m_uid, c, sp);
2084 else {
2085 if (check_expunged() == STOP)
2086 return STOP;
2087 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2089 IMAP_OUT(o, MB_COMD, return STOP)
2090 if (needstat)
2091 IMAP_ANSWER()
2092 else
2093 mb.mb_active &= ~MB_COMD;
2094 if (queuefp != NULL)
2095 Fclose(queuefp);
2096 return OKAY;
2099 FL enum okay
2100 imap_undelete(struct message *m, int n)
2102 enum okay rv;
2103 NYD_ENTER;
2105 rv = imap_unstore(m, n, "\\Deleted");
2106 NYD_LEAVE;
2107 return rv;
2110 FL enum okay
2111 imap_unread(struct message *m, int n)
2113 enum okay rv;
2114 NYD_ENTER;
2116 rv = imap_unstore(m, n, "\\Seen");
2117 NYD_LEAVE;
2118 return rv;
2121 static enum okay
2122 imap_unstore(struct message *m, int n, const char *flag)
2124 sighandler_type saveint, savepipe;
2125 enum okay rv = STOP;
2126 NYD_ENTER;
2128 imaplock = 1;
2129 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2130 safe_signal(SIGINT, &_imap_maincatch);
2131 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2132 if (sigsetjmp(imapjmp, 1) == 0) {
2133 if (savepipe != SIG_IGN)
2134 safe_signal(SIGPIPE, imapcatch);
2136 rv = imap_store(&mb, m, n, '-', flag, 1);
2138 safe_signal(SIGINT, saveint);
2139 safe_signal(SIGPIPE, savepipe);
2140 imaplock = 0;
2142 NYD_LEAVE;
2143 if (interrupts)
2144 onintr(0);
2145 return rv;
2148 static const char *
2149 tag(int new)
2151 static char ts[20];
2152 static long n;
2153 NYD_ENTER;
2155 if (new)
2156 ++n;
2157 snprintf(ts, sizeof ts, "T%lu", n);
2158 NYD_LEAVE;
2159 return ts;
2162 FL int
2163 c_imap_imap(void *vp)
2165 char o[LINESIZE];
2166 sighandler_type saveint, savepipe;
2167 struct mailbox *mp = &mb;
2168 FILE *queuefp = NULL;
2169 enum okay ok = STOP;
2170 NYD_X;
2172 if (mp->mb_type != MB_IMAP) {
2173 printf("Not operating on an IMAP mailbox.\n");
2174 return 1;
2176 imaplock = 1;
2177 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2178 safe_signal(SIGINT, &_imap_maincatch);
2179 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2180 if (sigsetjmp(imapjmp, 1) == 0) {
2181 if (savepipe != SIG_IGN)
2182 safe_signal(SIGPIPE, imapcatch);
2184 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2185 IMAP_OUT(o, MB_COMD, goto out)
2186 while (mp->mb_active & MB_COMD) {
2187 ok = imap_answer(mp, 0);
2188 fputs(responded_text, stdout);
2191 out:
2192 safe_signal(SIGINT, saveint);
2193 safe_signal(SIGPIPE, savepipe);
2194 imaplock = 0;
2196 if (interrupts)
2197 onintr(0);
2198 return ok != OKAY;
2201 FL int
2202 imap_newmail(int nmail)
2204 NYD_ENTER;
2206 if (nmail && had_exists < 0 && had_expunge < 0) {
2207 imaplock = 1;
2208 imap_noop();
2209 imaplock = 0;
2212 if (had_exists == msgCount && had_expunge < 0)
2213 /* Some servers always respond with EXISTS to NOOP. If
2214 * the mailbox has been changed but the number of messages
2215 * has not, an EXPUNGE must also had been sent; otherwise,
2216 * nothing has changed */
2217 had_exists = -1;
2218 NYD_LEAVE;
2219 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2222 static char *
2223 imap_putflags(int f)
2225 const char *cp;
2226 char *buf, *bp;
2227 NYD_ENTER;
2229 bp = buf = salloc(100);
2230 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2231 *bp++ = '(';
2232 if (f & MREAD) {
2233 if (bp[-1] != '(')
2234 *bp++ = ' ';
2235 for (cp = "\\Seen"; *cp; cp++)
2236 *bp++ = *cp;
2238 if (f & MFLAGGED) {
2239 if (bp[-1] != '(')
2240 *bp++ = ' ';
2241 for (cp = "\\Flagged"; *cp; cp++)
2242 *bp++ = *cp;
2244 if (f & MANSWERED) {
2245 if (bp[-1] != '(')
2246 *bp++ = ' ';
2247 for (cp = "\\Answered"; *cp; cp++)
2248 *bp++ = *cp;
2250 if (f & MDRAFT) {
2251 if (bp[-1] != '(')
2252 *bp++ = ' ';
2253 for (cp = "\\Draft"; *cp; cp++)
2254 *bp++ = *cp;
2256 *bp++ = ')';
2257 *bp++ = ' ';
2259 *bp = '\0';
2260 NYD_LEAVE;
2261 return buf;
2264 static void
2265 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2267 NYD_ENTER;
2268 while (*cp != ')') {
2269 if (*cp == '\\') {
2270 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2271 *f |= MREAD;
2272 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2273 *f |= MNEW;
2274 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2275 *f |= MDELETED;
2276 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2277 *f |= MFLAGGED;
2278 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2279 *f |= MANSWERED;
2280 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2281 *f |= MDRAFTED;
2283 cp++;
2286 if (xp != NULL)
2287 *xp = cp;
2288 NYD_LEAVE;
2291 static enum okay
2292 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2293 long xsize, enum mflag flag, time_t t)
2295 char o[LINESIZE], *buf;
2296 size_t bufsize, buflen, cnt;
2297 long size, lines, ysize;
2298 int twice = 0;
2299 FILE *queuefp = NULL;
2300 enum okay rv;
2301 NYD_ENTER;
2303 if (mp->mb_type == MB_CACHE) {
2304 queuefp = cache_queue(mp);
2305 if (queuefp == NULL) {
2306 rv = STOP;
2307 buf = NULL;
2308 goto jleave;
2310 rv = OKAY;
2311 } else
2312 rv = STOP;
2314 buf = smalloc(bufsize = LINESIZE);
2315 buflen = 0;
2316 jagain:
2317 size = xsize;
2318 cnt = fsize(fp);
2319 if (fseek(fp, off1, SEEK_SET) < 0) {
2320 rv = STOP;
2321 goto jleave;
2324 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2325 tag(1), imap_quotestr(name), imap_putflags(flag),
2326 imap_make_date_time(t), size);
2327 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2328 while (mp->mb_active & MB_COMD) {
2329 rv = imap_answer(mp, twice);
2330 if (response_type == RESPONSE_CONT)
2331 break;
2334 if (mp->mb_type != MB_CACHE && rv == STOP) {
2335 if (twice == 0)
2336 goto jtrycreate;
2337 else
2338 goto jleave;
2341 lines = ysize = 0;
2342 while (size > 0) {
2343 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2344 lines++;
2345 ysize += buflen;
2346 buf[buflen - 1] = '\r';
2347 buf[buflen] = '\n';
2348 if (mp->mb_type != MB_CACHE)
2349 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2350 else if (queuefp)
2351 fwrite(buf, 1, buflen+1, queuefp);
2352 size -= buflen + 1;
2354 if (mp->mb_type != MB_CACHE)
2355 swrite(&mp->mb_sock, "\r\n");
2356 else if (queuefp)
2357 fputs("\r\n", queuefp);
2358 while (mp->mb_active & MB_COMD) {
2359 rv = imap_answer(mp, 0);
2360 if (response_status == RESPONSE_NO /*&&
2361 ascncasecmp(responded_text,
2362 "[TRYCREATE] ", 12) == 0*/) {
2363 jtrycreate:
2364 if (twice++) {
2365 rv = STOP;
2366 goto jleave;
2368 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2369 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2370 while (mp->mb_active & MB_COMD)
2371 rv = imap_answer(mp, 1);
2372 if (rv == STOP)
2373 goto jleave;
2374 imap_created_mailbox++;
2375 goto jagain;
2376 } else if (rv != OKAY)
2377 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
2378 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2379 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2381 jleave:
2382 if (queuefp != NULL)
2383 Fclose(queuefp);
2384 if (buf != NULL)
2385 free(buf);
2386 NYD_LEAVE;
2387 return rv;
2390 static enum okay
2391 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2393 char *buf, *bp, *lp;
2394 size_t bufsize, buflen, cnt;
2395 off_t off1 = -1, offs;
2396 int inhead = 1, flag = MNEW | MNEWEST;
2397 long size = 0;
2398 time_t tim;
2399 enum okay rv;
2400 NYD_ENTER;
2402 buf = smalloc(bufsize = LINESIZE);
2403 buflen = 0;
2404 cnt = fsize(fp);
2405 offs = ftell(fp);
2406 time(&tim);
2408 do {
2409 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2410 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2411 if (off1 != (off_t)-1) {
2412 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2413 if (rv == STOP)
2414 goto jleave;
2415 fseek(fp, offs+buflen, SEEK_SET);
2417 off1 = offs + buflen;
2418 size = 0;
2419 inhead = 1;
2420 flag = MNEW;
2421 if (bp != NULL)
2422 tim = unixtime(buf);
2423 } else
2424 size += buflen+1;
2425 offs += buflen;
2427 if (bp && buf[0] == '\n')
2428 inhead = 0;
2429 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2430 lp = &buf[6];
2431 while (whitechar(*lp))
2432 lp++;
2433 if (*lp == ':')
2434 while (*++lp != '\0')
2435 switch (*lp) {
2436 case 'R':
2437 flag |= MREAD;
2438 break;
2439 case 'O':
2440 flag &= ~MNEW;
2441 break;
2443 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2444 lp = &buf[8];
2445 while (whitechar(*lp))
2446 lp++;
2447 if (*lp == ':')
2448 while (*++lp != '\0')
2449 switch (*lp) {
2450 case 'F':
2451 flag |= MFLAGGED;
2452 break;
2453 case 'A':
2454 flag |= MANSWERED;
2455 break;
2456 case 'T':
2457 flag |= MDRAFTED;
2458 break;
2461 } while (bp != NULL);
2462 rv = OKAY;
2463 jleave:
2464 free(buf);
2465 NYD_LEAVE;
2466 return rv;
2469 FL enum okay
2470 imap_append(const char *xserver, FILE *fp)
2472 sighandler_type volatile saveint, savepipe;
2473 char *server, *user, *pass;
2474 char const *sp, *cp, * volatile mbx, *uhp;
2475 int volatile use_ssl;
2476 enum okay rv = STOP;
2477 NYD_ENTER;
2479 server = savestr(xserver);
2481 int xus;
2482 char const *xmbx;
2483 imap_split(&server, &sp, &xus, &cp, &uhp, &xmbx, &pass, &user);
2484 use_ssl = xus;
2485 mbx = xmbx;
2488 imaplock = 1;
2489 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2490 safe_signal(SIGINT, &_imap_maincatch);
2491 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2492 if (sigsetjmp(imapjmp, 1))
2493 goto jleave;
2494 if (savepipe != SIG_IGN)
2495 safe_signal(SIGPIPE, imapcatch);
2497 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2498 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2499 rv = imap_append0(&mb, mbx, fp);
2500 } else {
2501 struct mailbox mx;
2503 memset(&mx, 0, sizeof mx);
2504 if (disconnected(server) == 0) {
2505 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2506 (use_ssl ? "imaps" : "imap")) != OKAY)
2507 goto jfail;
2508 mx.mb_sock.s_desc = "IMAP";
2509 mx.mb_type = MB_IMAP;
2510 mx.mb_imap_account = (char *)protbase(server);
2511 /* TODO the code now did
2512 * TODO mx.mb_imap_mailbox = mbx;
2513 * TODO though imap_mailbox is sfree()d and mbx
2514 * TODO is possibly even a constant
2515 * TODO i changed this to sstrdup() sofar, as is used
2516 * TODO somewhere else in this file for this! */
2517 mx.mb_imap_mailbox = sstrdup(mbx);
2518 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2519 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2520 sclose(&mx.mb_sock);
2521 goto jfail;
2523 rv = imap_append0(&mx, mbx, fp);
2524 imap_exit(&mx);
2525 } else {
2526 mx.mb_imap_account = (char*)protbase(server);
2527 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2528 mx.mb_type = MB_CACHE;
2529 rv = imap_append0(&mx, mbx, fp);
2531 jfail:
2534 jleave:
2535 safe_signal(SIGINT, saveint);
2536 safe_signal(SIGPIPE, savepipe);
2537 imaplock = 0;
2539 NYD_LEAVE;
2540 if (interrupts)
2541 onintr(0);
2542 return rv;
2545 static enum okay
2546 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2547 struct list_item **lend, int level)
2549 char o[LINESIZE], *cp;
2550 const char *bp;
2551 FILE *queuefp = NULL;
2552 struct list_item *lp;
2553 enum okay ok = STOP;
2554 NYD_X;
2556 *list = *lend = NULL;
2557 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2558 IMAP_OUT(o, MB_COMD, return STOP)
2559 while (mp->mb_active & MB_COMD) {
2560 ok = imap_answer(mp, 1);
2561 if (response_status == RESPONSE_OTHER &&
2562 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2563 cp = imap_unquotestr(list_name);
2564 lp = csalloc(1, sizeof *lp);
2565 lp->l_name = cp;
2566 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2567 ++cp;
2568 lp->l_base = *cp ? cp : savestr(base);
2569 lp->l_attr = list_attributes;
2570 lp->l_level = level+1;
2571 lp->l_delim = list_hierarchy_delimiter;
2572 if (*list && *lend) {
2573 (*lend)->l_next = lp;
2574 *lend = lp;
2575 } else
2576 *list = *lend = lp;
2579 return ok;
2582 static enum okay
2583 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2585 struct list_item *list, *lend, *lp, *lx, *ly;
2586 int n, depth;
2587 const char *bp;
2588 char *cp;
2589 enum okay rv;
2590 NYD_ENTER;
2592 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2593 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2594 goto jleave;
2595 rv = OKAY;
2596 if (list == NULL || lend == NULL)
2597 goto jleave;
2599 for (lp = list; lp; lp = lp->l_next)
2600 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2601 !(lp->l_attr & LIST_NOINFERIORS)) {
2602 cp = salloc((n = strlen(lp->l_name)) + 2);
2603 memcpy(cp, lp->l_name, n);
2604 cp[n] = lp->l_delim;
2605 cp[n+1] = '\0';
2606 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2607 lp->l_has_children = 1;
2608 if (strcmp(cp, lx->l_name) == 0)
2609 lx = lx->l_next;
2610 if (lx) {
2611 lend->l_next = lx;
2612 lend = ly;
2617 for (lp = list; lp; lp = lp->l_next) {
2618 if (strip) {
2619 cp = lp->l_name;
2620 for (bp = base; *bp && *bp == *cp; bp++)
2621 cp++;
2622 } else
2623 cp = lp->l_name;
2624 if (!(lp->l_attr & LIST_NOSELECT))
2625 fprintf(fp, "%s\n", *cp ? cp : base);
2626 else if (lp->l_has_children == 0)
2627 fprintf(fp, "%s%c\n", *cp ? cp : base,
2628 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2630 jleave:
2631 NYD_LEAVE;
2632 return rv;
2635 FL void
2636 imap_folders(const char * volatile name, int strip)
2638 sighandler_type saveint, savepipe;
2639 const char *fold, *cp, *sp;
2640 FILE * volatile fp;
2641 NYD_ENTER;
2643 cp = protbase(name);
2644 sp = mb.mb_imap_account;
2645 if (sp == NULL || strcmp(cp, sp)) {
2646 fprintf(stderr, tr(502,
2647 "Cannot perform `folders' but when on the very IMAP "
2648 "account; the current one is\n `%s' -- "
2649 "try `folders @'.\n"),
2650 (sp != NULL ? sp : tr(503, "[NONE]")));
2651 goto jleave;
2654 fold = imap_fileof(name);
2655 if (options & OPT_TTYOUT) {
2656 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2657 0600)) == NULL) {
2658 perror("tmpfile");
2659 goto jleave;
2661 } else
2662 fp = stdout;
2664 imaplock = 1;
2665 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2666 safe_signal(SIGINT, &_imap_maincatch);
2667 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2668 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2669 goto junroll;
2670 if (savepipe != SIG_IGN)
2671 safe_signal(SIGPIPE, imapcatch);
2673 if (mb.mb_type == MB_CACHE)
2674 cache_list(&mb, fold, strip, fp);
2675 else
2676 imap_list(&mb, fold, strip, fp);
2678 imaplock = 0;
2679 if (interrupts) {
2680 if (options & OPT_TTYOUT)
2681 Fclose(fp);
2682 goto jleave;
2684 fflush(fp);
2686 if (options & OPT_TTYOUT) {
2687 rewind(fp);
2688 if (fsize(fp) > 0)
2689 dopr(fp);
2690 else
2691 fprintf(stderr, "Folder not found.\n");
2693 junroll:
2694 safe_signal(SIGINT, saveint);
2695 safe_signal(SIGPIPE, savepipe);
2696 if (options & OPT_TTYOUT)
2697 Fclose(fp);
2698 jleave:
2699 NYD_LEAVE;
2700 if (interrupts)
2701 onintr(0);
2704 static void
2705 dopr(FILE *fp)
2707 char o[LINESIZE];
2708 int c;
2709 long n = 0, mx = 0, columns, width;
2710 FILE *out;
2711 NYD_ENTER;
2713 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2714 == NULL) {
2715 perror("tmpfile");
2716 goto jleave;
2719 while ((c = getc(fp)) != EOF) {
2720 if (c == '\n') {
2721 if (n > mx)
2722 mx = n;
2723 n = 0;
2724 } else
2725 ++n;
2727 rewind(fp);
2729 width = scrnwidth;
2730 if (mx < width / 2) {
2731 columns = width / (mx+2);
2732 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2733 } else
2734 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2735 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2736 try_pager(out);
2737 Fclose(out);
2738 jleave:
2739 NYD_LEAVE;
2742 static enum okay
2743 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2745 char o[LINESIZE];
2746 const char *qname;
2747 int twice = 0, stored = 0;
2748 FILE *queuefp = NULL;
2749 enum okay ok = STOP;
2750 NYD_X;
2752 if (mp->mb_type == MB_CACHE) {
2753 if ((queuefp = cache_queue(mp)) == NULL)
2754 return STOP;
2755 ok = OKAY;
2757 qname = imap_quotestr(name = imap_fileof(name));
2758 /* Since it is not possible to set flags on the copy, recently
2759 * set flags must be set on the original to include it in the copy */
2760 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2761 imap_store(mp, m, n, '+', "\\Seen", 0);
2762 if (m->m_flag&MFLAG)
2763 imap_store(mp, m, n, '+', "\\Flagged", 0);
2764 if (m->m_flag&MUNFLAG)
2765 imap_store(mp, m, n, '-', "\\Flagged", 0);
2766 if (m->m_flag&MANSWER)
2767 imap_store(mp, m, n, '+', "\\Answered", 0);
2768 if (m->m_flag&MUNANSWER)
2769 imap_store(mp, m, n, '-', "\\Flagged", 0);
2770 if (m->m_flag&MDRAFT)
2771 imap_store(mp, m, n, '+', "\\Draft", 0);
2772 if (m->m_flag&MUNDRAFT)
2773 imap_store(mp, m, n, '-', "\\Draft", 0);
2774 again:
2775 if (m->m_uid)
2776 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2777 else {
2778 if (check_expunged() == STOP)
2779 goto out;
2780 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2782 IMAP_OUT(o, MB_COMD, goto out)
2783 while (mp->mb_active & MB_COMD)
2784 ok = imap_answer(mp, twice);
2786 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2787 response_status == RESPONSE_OK)
2788 imap_copyuid(mp, m, name);
2790 if (response_status == RESPONSE_NO && twice++ == 0) {
2791 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2792 IMAP_OUT(o, MB_COMD, goto out)
2793 while (mp->mb_active & MB_COMD)
2794 ok = imap_answer(mp, 1);
2795 if (ok == OKAY) {
2796 imap_created_mailbox++;
2797 goto again;
2801 if (queuefp != NULL)
2802 Fclose(queuefp);
2804 /* ... and reset the flag to its initial value so that the 'exit'
2805 * command still leaves the message unread */
2806 out:
2807 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2808 imap_store(mp, m, n, '-', "\\Seen", 0);
2809 stored++;
2811 if (m->m_flag & MFLAG) {
2812 imap_store(mp, m, n, '-', "\\Flagged", 0);
2813 stored++;
2815 if (m->m_flag & MUNFLAG) {
2816 imap_store(mp, m, n, '+', "\\Flagged", 0);
2817 stored++;
2819 if (m->m_flag & MANSWER) {
2820 imap_store(mp, m, n, '-', "\\Answered", 0);
2821 stored++;
2823 if (m->m_flag & MUNANSWER) {
2824 imap_store(mp, m, n, '+', "\\Answered", 0);
2825 stored++;
2827 if (m->m_flag & MDRAFT) {
2828 imap_store(mp, m, n, '-', "\\Draft", 0);
2829 stored++;
2831 if (m->m_flag & MUNDRAFT) {
2832 imap_store(mp, m, n, '+', "\\Draft", 0);
2833 stored++;
2835 if (stored) {
2836 mp->mb_active |= MB_COMD;
2837 (void)imap_finish(mp);
2839 return ok;
2842 FL enum okay
2843 imap_copy(struct message *m, int n, const char *name)
2845 sighandler_type saveint, savepipe;
2846 enum okay rv = STOP;
2847 NYD_ENTER;
2849 imaplock = 1;
2850 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2851 safe_signal(SIGINT, &_imap_maincatch);
2852 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2853 if (sigsetjmp(imapjmp, 1) == 0) {
2854 if (savepipe != SIG_IGN)
2855 safe_signal(SIGPIPE, imapcatch);
2857 rv = imap_copy1(&mb, m, n, name);
2859 safe_signal(SIGINT, saveint);
2860 safe_signal(SIGPIPE, savepipe);
2861 imaplock = 0;
2863 NYD_LEAVE;
2864 if (interrupts)
2865 onintr(0);
2866 return rv;
2869 static enum okay
2870 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2871 unsigned long *olduid, unsigned long *newuid)
2873 char *xp, *yp, *zp;
2874 enum okay rv;
2875 NYD_ENTER;
2877 *uidvalidity = strtoul(cp, &xp, 10);
2878 *olduid = strtoul(xp, &yp, 10);
2879 *newuid = strtoul(yp, &zp, 10);
2880 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2881 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2882 NYD_LEAVE;
2883 return rv;
2886 static enum okay
2887 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2888 unsigned long *uid)
2890 char *xp, *yp;
2891 enum okay rv;
2892 NYD_ENTER;
2894 *uidvalidity = strtoul(cp, &xp, 10);
2895 *uid = strtoul(xp, &yp, 10);
2896 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2897 *yp == ']');
2898 NYD_LEAVE;
2899 return rv;
2902 static enum okay
2903 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2905 struct mailbox xmb;
2906 struct message xm;
2907 const char *cp;
2908 unsigned long uidvalidity, olduid, newuid;
2909 enum okay rv;
2910 NYD_ENTER;
2912 rv = STOP;
2913 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2914 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2915 goto jleave;
2916 rv = OKAY;
2918 xmb = *mp;
2919 xmb.mb_cache_directory = NULL;
2920 xmb.mb_imap_mailbox = savestr(name);
2921 xmb.mb_uidvalidity = uidvalidity;
2922 initcache(&xmb);
2923 if (m == NULL) {
2924 memset(&xm, 0, sizeof xm);
2925 xm.m_uid = olduid;
2926 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2927 goto jleave;
2928 getcache(mp, &xm, NEED_HEADER);
2929 getcache(mp, &xm, NEED_BODY);
2930 } else {
2931 if ((m->m_flag & HAVE_HEADER) == 0)
2932 getcache(mp, m, NEED_HEADER);
2933 if ((m->m_flag & HAVE_BODY) == 0)
2934 getcache(mp, m, NEED_BODY);
2935 xm = *m;
2937 xm.m_uid = newuid;
2938 xm.m_flag &= ~MFULLYCACHED;
2939 putcache(&xmb, &xm);
2940 jleave:
2941 NYD_LEAVE;
2942 return rv;
2945 static enum okay
2946 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2947 long size, long lines, int flag, const char *name)
2949 struct mailbox xmb;
2950 struct message xm;
2951 const char *cp;
2952 unsigned long uidvalidity, uid;
2953 enum okay rv;
2954 NYD_ENTER;
2956 rv = STOP;
2957 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2958 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2959 goto jleave;
2960 rv = OKAY;
2962 xmb = *mp;
2963 xmb.mb_cache_directory = NULL;
2964 xmb.mb_imap_mailbox = savestr(name);
2965 xmb.mb_uidvalidity = uidvalidity;
2966 xmb.mb_otf = xmb.mb_itf = fp;
2967 initcache(&xmb);
2968 memset(&xm, 0, sizeof xm);
2969 xm.m_flag = (flag & MREAD) | MNEW;
2970 xm.m_time = t;
2971 xm.m_block = mailx_blockof(off1);
2972 xm.m_offset = mailx_offsetof(off1);
2973 xm.m_size = size;
2974 xm.m_xsize = xsize;
2975 xm.m_lines = xm.m_xlines = lines;
2976 xm.m_uid = uid;
2977 xm.m_have = HAVE_HEADER | HAVE_BODY;
2978 putcache(&xmb, &xm);
2979 jleave:
2980 NYD_LEAVE;
2981 return rv;
2984 static enum okay
2985 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2987 FILE *tp = NULL;
2988 time_t t;
2989 long size, xsize, ysize, lines;
2990 enum mflag flag = MNEW;
2991 char *name, *buf, *bp;
2992 char const *cp;
2993 size_t bufsize, buflen, cnt;
2994 enum okay rv = STOP;
2995 NYD_ENTER;
2997 buf = smalloc(bufsize = LINESIZE);
2998 buflen = 0;
2999 cnt = fsize(fp);
3000 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3001 goto jstop;
3003 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3005 while (*bp == ' ')
3006 ++bp;
3008 if ((cp = strrchr(bp, '{')) == NULL)
3009 goto jstop;
3011 xsize = atol(&cp[1]) + 2;
3012 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3013 goto jstop;
3014 while (*cp == ' ')
3015 cp++;
3017 if (*cp == '(') {
3018 imap_getflags(cp, &cp, &flag);
3019 while (*++cp == ' ')
3022 t = imap_read_date_time(cp);
3024 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3025 NULL)
3026 goto jstop;
3028 size = xsize;
3029 ysize = lines = 0;
3030 while (size > 0) {
3031 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3032 goto jstop;
3033 size -= buflen;
3034 buf[--buflen] = '\0';
3035 buf[buflen-1] = '\n';
3036 fwrite(buf, 1, buflen, tp);
3037 ysize += buflen;
3038 ++lines;
3040 fflush(tp);
3041 rewind(tp);
3043 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3044 imap_unquotestr(name));
3045 rv = OKAY;
3046 jstop:
3047 free(buf);
3048 if (tp)
3049 Fclose(tp);
3050 NYD_LEAVE;
3051 return rv;
3054 #ifdef HAVE_IMAP_SEARCH
3055 static enum okay
3056 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3057 int f)
3059 char *o, *xp, *cs, c;
3060 size_t osize;
3061 FILE *queuefp = NULL;
3062 int i;
3063 unsigned long n;
3064 const char *cp;
3065 enum okay ok = STOP;
3066 NYD_X;
3068 c = 0;
3069 for (cp = spec; *cp; cp++)
3070 c |= *cp;
3071 if (c & 0200) {
3072 cp = charset_get_lc();
3073 # ifdef HAVE_ICONV
3074 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3075 iconv_t it;
3076 char *nsp, *nspec;
3077 size_t sz, nsz;
3079 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3080 sz = strlen(spec) + 1;
3081 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3082 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3083 sz == 0) {
3084 spec = nspec;
3085 cp = "utf-8";
3087 n_iconv_close(it);
3090 # endif
3091 cp = imap_quotestr(cp);
3092 cs = salloc(n = strlen(cp) + 10);
3093 snprintf(cs, n, "CHARSET %s ", cp);
3094 } else
3095 cs = UNCONST("");
3097 o = ac_alloc(osize = strlen(spec) + 60);
3098 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3099 IMAP_OUT(o, MB_COMD, goto out)
3100 while (mp->mb_active & MB_COMD) {
3101 ok = imap_answer(mp, 0);
3102 if (response_status == RESPONSE_OTHER &&
3103 response_other == MAILBOX_DATA_SEARCH) {
3104 xp = responded_other_text;
3105 while (*xp && *xp != '\r') {
3106 n = strtoul(xp, &xp, 10);
3107 for (i = 0; i < cnt; i++)
3108 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3109 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3110 mark(i+1, f);
3114 out:
3115 ac_free(o);
3116 return ok;
3119 FL enum okay
3120 imap_search1(const char * volatile spec, int f)
3122 sighandler_type saveint, savepipe;
3123 enum okay rv = STOP;
3124 NYD_ENTER;
3126 if (mb.mb_type != MB_IMAP)
3127 goto jleave;
3129 imaplock = 1;
3130 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3131 safe_signal(SIGINT, &_imap_maincatch);
3132 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3133 if (sigsetjmp(imapjmp, 1) == 0) {
3134 if (savepipe != SIG_IGN)
3135 safe_signal(SIGPIPE, imapcatch);
3137 rv = imap_search2(&mb, message, msgCount, spec, f);
3139 safe_signal(SIGINT, saveint);
3140 safe_signal(SIGPIPE, savepipe);
3141 imaplock = 0;
3142 jleave:
3143 NYD_LEAVE;
3144 if (interrupts)
3145 onintr(0);
3146 return rv;
3148 #endif /* HAVE_IMAP_SEARCH */
3150 FL int
3151 imap_thisaccount(const char *cp)
3153 int rv;
3154 NYD_ENTER;
3156 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3157 rv = 0;
3158 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3159 mb.mb_imap_account == NULL)
3160 rv = 0;
3161 else
3162 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3163 NYD_LEAVE;
3164 return rv;
3167 FL enum okay
3168 imap_remove(const char * volatile name)
3170 sighandler_type volatile saveint, savepipe;
3171 enum okay rv = STOP;
3172 NYD_ENTER;
3174 if (mb.mb_type != MB_IMAP) {
3175 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3176 name);
3177 goto jleave;
3180 if (!imap_thisaccount(name)) {
3181 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3182 "server: \"%s\" not removed.\n", name);
3183 goto jleave;
3186 imaplock = 1;
3187 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3188 safe_signal(SIGINT, &_imap_maincatch);
3189 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3190 if (sigsetjmp(imapjmp, 1) == 0) {
3191 if (savepipe != SIG_IGN)
3192 safe_signal(SIGPIPE, imapcatch);
3194 rv = imap_remove1(&mb, imap_fileof(name));
3196 safe_signal(SIGINT, saveint);
3197 safe_signal(SIGPIPE, savepipe);
3198 imaplock = 0;
3200 if (rv == OKAY)
3201 rv = cache_remove(name);
3202 jleave:
3203 NYD_LEAVE;
3204 if (interrupts)
3205 onintr(0);
3206 return rv;
3209 static enum okay
3210 imap_remove1(struct mailbox *mp, const char *name)
3212 FILE *queuefp = NULL;
3213 char *o;
3214 int os;
3215 enum okay ok = STOP;
3216 NYD_X;
3218 o = ac_alloc(os = 2*strlen(name) + 100);
3219 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3220 IMAP_OUT(o, MB_COMD, goto out)
3221 while (mp->mb_active & MB_COMD)
3222 ok = imap_answer(mp, 1);
3223 out:
3224 ac_free(o);
3225 return ok;
3228 FL enum okay
3229 imap_rename(const char *old, const char *new)
3231 sighandler_type saveint, savepipe;
3232 enum okay rv = STOP;
3233 NYD_ENTER;
3235 if (mb.mb_type != MB_IMAP) {
3236 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3237 goto jleave;
3240 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3241 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3242 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3243 goto jleave;
3246 imaplock = 1;
3247 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3248 safe_signal(SIGINT, &_imap_maincatch);
3249 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3250 if (sigsetjmp(imapjmp, 1) == 0) {
3251 if (savepipe != SIG_IGN)
3252 safe_signal(SIGPIPE, imapcatch);
3254 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3256 safe_signal(SIGINT, saveint);
3257 safe_signal(SIGPIPE, savepipe);
3258 imaplock = 0;
3260 if (rv == OKAY)
3261 rv = cache_rename(old, new);
3262 jleave:
3263 NYD_LEAVE;
3264 if (interrupts)
3265 onintr(0);
3266 return rv;
3269 static enum okay
3270 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3272 FILE *queuefp = NULL;
3273 char *o;
3274 int os;
3275 enum okay ok = STOP;
3276 NYD_X;
3278 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3279 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3280 imap_quotestr(new));
3281 IMAP_OUT(o, MB_COMD, goto out)
3282 while (mp->mb_active & MB_COMD)
3283 ok = imap_answer(mp, 1);
3284 out:
3285 ac_free(o);
3286 return ok;
3289 FL enum okay
3290 imap_dequeue(struct mailbox *mp, FILE *fp)
3292 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3293 size_t bufsize, buflen, cnt;
3294 long offs, offs1, offs2, octets;
3295 int twice, gotcha = 0;
3296 FILE *queuefp = NULL;
3297 enum okay ok = OKAY, rok = OKAY;
3298 NYD_X;
3300 buf = smalloc(bufsize = LINESIZE);
3301 buflen = 0;
3302 cnt = fsize(fp);
3303 while ((offs1 = ftell(fp)) >= 0 &&
3304 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3305 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3307 while (*bp == ' ')
3308 ++bp;
3309 twice = 0;
3310 if ((offs = ftell(fp)) < 0)
3311 goto fail;
3312 again:
3313 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3314 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3315 cp = &bp[9];
3316 while (digitchar(*cp))
3317 cp++;
3318 if (*cp != ' ')
3319 goto fail;
3320 while (*cp == ' ')
3321 cp++;
3322 if ((newname = imap_strex(cp, NULL)) == NULL)
3323 goto fail;
3324 IMAP_OUT(o, MB_COMD, continue)
3325 while (mp->mb_active & MB_COMD)
3326 ok = imap_answer(mp, twice);
3327 if (response_status == RESPONSE_NO && twice++ == 0)
3328 goto trycreate;
3329 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3330 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3332 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3333 IMAP_OUT(o, MB_COMD, continue)
3334 while (mp->mb_active & MB_COMD)
3335 ok = imap_answer(mp, 1);
3336 if (ok == OKAY)
3337 gotcha++;
3338 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3339 if ((cp = strrchr(bp, '{')) == NULL)
3340 goto fail;
3341 octets = atol(&cp[1]) + 2;
3342 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3343 goto fail;
3344 IMAP_OUT(o, MB_COMD, continue)
3345 while (mp->mb_active & MB_COMD) {
3346 ok = imap_answer(mp, twice);
3347 if (response_type == RESPONSE_CONT)
3348 break;
3350 if (ok == STOP) {
3351 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3352 goto trycreate;
3353 goto fail;
3355 while (octets > 0) {
3356 size_t n = (UICMP(z, octets, >, sizeof iob)
3357 ? sizeof iob : (size_t)octets);
3358 octets -= n;
3359 if (n != fread(iob, 1, n, fp))
3360 goto fail;
3361 swrite1(&mp->mb_sock, iob, n, 1);
3363 swrite(&mp->mb_sock, "");
3364 while (mp->mb_active & MB_COMD) {
3365 ok = imap_answer(mp, 0);
3366 if (response_status == RESPONSE_NO && twice++ == 0) {
3367 if (fseek(fp, offs, SEEK_SET) < 0)
3368 goto fail;
3369 goto trycreate;
3372 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3373 if ((offs2 = ftell(fp)) < 0)
3374 goto fail;
3375 fseek(fp, offs1, SEEK_SET);
3376 if (imap_appenduid_cached(mp, fp) == STOP) {
3377 (void)fseek(fp, offs2, SEEK_SET);
3378 goto fail;
3381 } else {
3382 fail:
3383 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3384 rok = STOP;
3386 continue;
3387 trycreate:
3388 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3389 IMAP_OUT(o, MB_COMD, continue)
3390 while (mp->mb_active & MB_COMD)
3391 ok = imap_answer(mp, 1);
3392 if (ok == OKAY)
3393 goto again;
3395 fflush(fp);
3396 rewind(fp);
3397 ftruncate(fileno(fp), 0);
3398 if (gotcha)
3399 imap_close(mp);
3400 free(buf);
3401 return rok;
3404 static char *
3405 imap_strex(char const *cp, char const **xp)
3407 char const *cq;
3408 char *n = NULL;
3409 NYD_ENTER;
3411 if (*cp != '"')
3412 goto jleave;
3414 for (cq = cp + 1; *cq != '\0'; ++cq) {
3415 if (*cq == '\\')
3416 cq++;
3417 else if (*cq == '"')
3418 break;
3420 if (*cq != '"')
3421 goto jleave;
3423 n = salloc(cq - cp + 2);
3424 memcpy(n, cp, cq - cp +1);
3425 n[cq - cp + 1] = '\0';
3426 if (xp != NULL)
3427 *xp = cq + 1;
3428 jleave:
3429 NYD_LEAVE;
3430 return n;
3433 static enum okay
3434 check_expunged(void)
3436 enum okay rv;
3437 NYD_ENTER;
3439 if (expunged_messages > 0) {
3440 fprintf(stderr, "Command not executed - messages have been expunged\n");
3441 rv = STOP;
3442 } else
3443 rv = OKAY;
3444 NYD_LEAVE;
3445 return rv;
3448 FL int
3449 c_connect(void *vp)
3451 char *cp, *cq;
3452 int rv, omsgCount = msgCount;
3453 NYD_ENTER;
3454 UNUSED(vp);
3456 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3457 fprintf(stderr, "Already connected.\n");
3458 rv = 1;
3459 goto jleave;
3462 var_clear_allow_undefined = TRU1;
3463 ok_bclear(disconnected);
3465 cp = protbase(mailname);
3466 if (strncmp(cp, "imap://", 7) == 0)
3467 cp += 7;
3468 else if (strncmp(cp, "imaps://", 8) == 0)
3469 cp += 8;
3470 if ((cq = strchr(cp, ':')) != NULL)
3471 *cq = '\0';
3473 vok_bclear(savecat("disconnected-", cp));
3474 var_clear_allow_undefined = FAL0;
3476 if (mb.mb_type == MB_CACHE) {
3477 imap_setfile1(mailname, 0, edit, 1);
3478 if (msgCount > omsgCount)
3479 newmailinfo(omsgCount);
3481 rv = 0;
3482 jleave:
3483 NYD_LEAVE;
3484 return rv;
3487 FL int
3488 c_disconnect(void *vp)
3490 int rv = 1, *msgvec = vp;
3491 NYD_ENTER;
3493 if (mb.mb_type == MB_CACHE) {
3494 fprintf(stderr, "Not connected.\n");
3495 goto jleave;
3497 if (mb.mb_type == MB_IMAP) {
3498 if (cached_uidvalidity(&mb) == 0) {
3499 fprintf(stderr, "The current mailbox is not cached.\n");
3500 goto jleave;
3504 if (*msgvec)
3505 c_cache(vp);
3506 ok_bset(disconnected, TRU1);
3507 if (mb.mb_type == MB_IMAP) {
3508 sclose(&mb.mb_sock);
3509 imap_setfile1(mailname, 0, edit, 1);
3511 rv = 0;
3512 jleave:
3513 NYD_LEAVE;
3514 return rv;
3517 FL int
3518 c_cache(void *vp)
3520 int rv = 1, *msgvec = vp, *ip;
3521 struct message *mp;
3522 NYD_ENTER;
3524 if (mb.mb_type != MB_IMAP) {
3525 fprintf(stderr, "Not connected to an IMAP server.\n");
3526 goto jleave;
3528 if (cached_uidvalidity(&mb) == 0) {
3529 fprintf(stderr, "The current mailbox is not cached.\n");
3530 goto jleave;
3533 for (ip = msgvec; *ip; ++ip) {
3534 mp = &message[*ip - 1];
3535 if (!(mp->m_have & HAVE_BODY))
3536 get_body(mp);
3538 rv = 0;
3539 jleave:
3540 NYD_LEAVE;
3541 return rv;
3544 FL int
3545 disconnected(const char *file)
3547 char *cp, *cq, *vp;
3548 int vs, r = 1;
3549 NYD_ENTER;
3551 if (ok_blook(disconnected))
3552 goto jleave;
3554 cp = protbase(file);
3555 if (strncmp(cp, "imap://", 7) == 0)
3556 cp += 7;
3557 else if (strncmp(cp, "imaps://", 8) == 0)
3558 cp += 8;
3559 else {
3560 r = 0;
3561 goto jleave;
3564 if ((cq = strchr(cp, ':')) != NULL)
3565 *cq = '\0';
3566 vp = ac_alloc(vs = strlen(cp) + 14);
3567 snprintf(vp, vs, "disconnected-%s", cp);
3568 r = vok_blook(vp);
3569 ac_free(vp);
3570 jleave:
3571 NYD_LEAVE;
3572 return r;
3575 FL void
3576 transflags(struct message *omessage, long omsgCount, int transparent)
3578 struct message *omp, *nmp, *newdot, *newprevdot;
3579 int hf;
3580 NYD_ENTER;
3582 omp = omessage;
3583 nmp = message;
3584 newdot = message;
3585 newprevdot = NULL;
3586 while (PTRCMP(omp, <, omessage + omsgCount) &&
3587 PTRCMP(nmp, <, message + msgCount)) {
3588 if (dot && nmp->m_uid == dot->m_uid)
3589 newdot = nmp;
3590 if (prevdot && nmp->m_uid == prevdot->m_uid)
3591 newprevdot = nmp;
3592 if (omp->m_uid == nmp->m_uid) {
3593 hf = nmp->m_flag & MHIDDEN;
3594 if (transparent && mb.mb_type == MB_IMAP)
3595 omp->m_flag &= ~MHIDDEN;
3596 *nmp++ = *omp++;
3597 if (transparent && mb.mb_type == MB_CACHE)
3598 nmp[-1].m_flag |= hf;
3599 } else if (omp->m_uid < nmp->m_uid)
3600 ++omp;
3601 else
3602 ++nmp;
3604 dot = newdot;
3605 setdot(newdot);
3606 prevdot = newprevdot;
3607 free(omessage);
3608 NYD_LEAVE;
3611 FL time_t
3612 imap_read_date_time(const char *cp)
3614 char buf[3];
3615 time_t t;
3616 int i, year, month, day, hour, minute, second, sign = -1;
3617 NYD_ENTER;
3619 /* "25-Jul-2004 15:33:44 +0200"
3620 * | | | | | |
3621 * 0 5 10 15 20 25 */
3622 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3623 goto jinvalid;
3624 day = strtol(&cp[1], NULL, 10);
3625 for (i = 0;;) {
3626 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3627 break;
3628 if (month_names[++i][0] == '\0')
3629 goto jinvalid;
3631 month = i + 1;
3632 year = strtol(&cp[8], NULL, 10);
3633 hour = strtol(&cp[13], NULL, 10);
3634 minute = strtol(&cp[16], NULL, 10);
3635 second = strtol(&cp[19], NULL, 10);
3636 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3637 goto jinvalid;
3638 switch (cp[22]) {
3639 case '-':
3640 sign = 1;
3641 break;
3642 case '+':
3643 break;
3644 default:
3645 goto jinvalid;
3647 buf[2] = '\0';
3648 buf[0] = cp[23];
3649 buf[1] = cp[24];
3650 t += strtol(buf, NULL, 10) * sign * 3600;
3651 buf[0] = cp[25];
3652 buf[1] = cp[26];
3653 t += strtol(buf, NULL, 10) * sign * 60;
3654 jleave:
3655 NYD_LEAVE;
3656 return t;
3657 jinvalid:
3658 time(&t);
3659 goto jleave;
3662 FL const char *
3663 imap_make_date_time(time_t t)
3665 static char s[30];
3666 struct tm *tmptr;
3667 int tzdiff, tzdiff_hour, tzdiff_min;
3668 NYD_ENTER;
3670 tzdiff = t - mktime(gmtime(&t));
3671 tzdiff_hour = (int)(tzdiff / 60);
3672 tzdiff_min = tzdiff_hour % 60;
3673 tzdiff_hour /= 60;
3674 tmptr = localtime(&t);
3675 if (tmptr->tm_isdst > 0)
3676 tzdiff_hour++;
3677 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3678 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3679 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3680 NYD_LEAVE;
3681 return s;
3683 #endif /* HAVE_IMAP */
3685 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3686 FL char *
3687 imap_quotestr(char const *s)
3689 char *n, *np;
3690 NYD_ENTER;
3692 np = n = salloc(2 * strlen(s) + 3);
3693 *np++ = '"';
3694 while (*s) {
3695 if (*s == '"' || *s == '\\')
3696 *np++ = '\\';
3697 *np++ = *s++;
3699 *np++ = '"';
3700 *np = '\0';
3701 NYD_LEAVE;
3702 return n;
3705 FL char *
3706 imap_unquotestr(char const *s)
3708 char *n, *np;
3709 NYD_ENTER;
3711 if (*s != '"') {
3712 n = savestr(s);
3713 goto jleave;
3716 np = n = salloc(strlen(s) + 1);
3717 while (*++s) {
3718 if (*s == '\\')
3719 s++;
3720 else if (*s == '"')
3721 break;
3722 *np++ = *s;
3724 *np = '\0';
3725 jleave:
3726 NYD_LEAVE;
3727 return n;
3729 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3731 /* vim:set fenc=utf-8:s-it-mode */