Review: imap_search.c
[s-mailx.git] / imap.c
blob8525b401fcda6d6f95999cc333804fba936c68c2
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 termios_state_reset();
574 switch (s) {
575 case SIGINT:
576 fprintf(stderr, tr(102, "Interrupt\n"));
577 siglongjmp(imapjmp, 1);
578 /*NOTREACHED*/
579 case SIGPIPE:
580 fprintf(stderr, tr(98, "Received SIGPIPE during IMAP operation\n"));
581 break;
585 static void
586 _imap_maincatch(int s)
588 NYD_X; /* Signal handler */
589 UNUSED(s);
590 if (interrupts++ == 0) {
591 fprintf(stderr, tr(102, "Interrupt\n"));
592 return;
594 onintr(0);
597 static enum okay
598 imap_noop1(struct mailbox *mp)
600 char o[LINESIZE];
601 FILE *queuefp = NULL;
602 NYD_X;
604 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
605 IMAP_OUT(o, MB_COMD, return STOP)
606 IMAP_ANSWER()
607 return OKAY;
610 FL char const *
611 imap_fileof(char const *xcp)
613 char const *cp = xcp;
614 int state = 0;
615 NYD_ENTER;
617 while (*cp) {
618 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
619 cp += 3;
620 state = 1;
622 if (cp[0] == '/' && state == 1) {
623 ++cp;
624 goto jleave;
626 if (cp[0] == '/') {
627 cp = xcp;
628 goto jleave;
630 ++cp;
632 jleave:
633 NYD_LEAVE;
634 return cp;
637 FL enum okay
638 imap_noop(void)
640 sighandler_type volatile oldint, oldpipe;
641 enum okay rv = STOP;
642 NYD_ENTER;
644 if (mb.mb_type != MB_IMAP)
645 goto jleave;
647 imaplock = 1;
648 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
649 safe_signal(SIGINT, &_imap_maincatch);
650 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
651 if (sigsetjmp(imapjmp, 1) == 0) {
652 if (oldpipe != SIG_IGN)
653 safe_signal(SIGPIPE, imapcatch);
655 rv = imap_noop1(&mb);
657 safe_signal(SIGINT, oldint);
658 safe_signal(SIGPIPE, oldpipe);
659 imaplock = 0;
660 jleave:
661 NYD_LEAVE;
662 if (interrupts)
663 onintr(0);
664 return rv;
667 static void
668 rec_queue(enum rec_type rt, unsigned long cnt)
670 struct record *rp;
671 NYD_ENTER;
673 rp = scalloc(1, sizeof *rp);
674 rp->rec_type = rt;
675 rp->rec_count = cnt;
676 if (record && recend) {
677 recend->rec_next = rp;
678 recend = rp;
679 } else
680 record = recend = rp;
681 NYD_LEAVE;
684 static enum okay
685 rec_dequeue(void)
687 struct message *omessage;
688 struct record *rp, *rq;
689 uiz_t exists = 0, i;
690 enum okay rv = STOP;
691 NYD_ENTER;
693 if (record == NULL)
694 goto jleave;
696 omessage = message;
697 message = smalloc((msgCount+1) * sizeof *message);
698 if (msgCount)
699 memcpy(message, omessage, msgCount * sizeof *message);
700 memset(&message[msgCount], 0, sizeof *message);
702 rp = record, rq = NULL;
703 rv = OKAY;
704 while (rp != NULL) {
705 switch (rp->rec_type) {
706 case REC_EXISTS:
707 exists = rp->rec_count;
708 break;
709 case REC_EXPUNGE:
710 if (rp->rec_count == 0) {
711 rv = STOP;
712 break;
714 if (rp->rec_count > (unsigned long)msgCount) {
715 if (exists == 0 || rp->rec_count > exists--)
716 rv = STOP;
717 break;
719 if (exists > 0)
720 exists--;
721 delcache(&mb, &message[rp->rec_count-1]);
722 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
723 ((msgCount - rp->rec_count + 1) * sizeof *message));
724 --msgCount;
725 /* If the message was part of a collapsed thread,
726 * the m_collapsed field of one of its ancestors
727 * should be incremented. It seems hardly possible
728 * to do this with the current message structure,
729 * though. The result is that a '+' may be shown
730 * in the header summary even if no collapsed
731 * children exists */
732 break;
734 if (rq != NULL)
735 free(rq);
736 rq = rp;
737 rp = rp->rec_next;
739 if (rq != NULL)
740 free(rq);
742 record = recend = NULL;
743 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
744 message = srealloc(message, (exists + 1) * sizeof *message);
745 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
746 for (i = msgCount; i < exists; ++i)
747 imap_init(&mb, i);
748 imap_flags(&mb, msgCount+1, exists);
749 msgCount = exists;
752 if (rv == STOP) {
753 free(message);
754 message = omessage;
756 jleave:
757 NYD_LEAVE;
758 return rv;
761 static void
762 rec_rmqueue(void)
764 struct record *rp;
765 NYD_ENTER;
767 for (rp = record; rp != NULL;) {
768 struct record *tmp = rp;
769 rp = rp->rec_next;
770 free(tmp);
772 record = recend = NULL;
773 NYD_LEAVE;
776 /*ARGSUSED*/
777 static void
778 imapalarm(int s)
780 sighandler_type volatile saveint, savepipe;
781 NYD_X; /* Signal handler */
782 UNUSED(s);
784 if (imaplock++ == 0) {
785 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
786 safe_signal(SIGINT, &_imap_maincatch);
787 savepipe = safe_signal(SIGPIPE, SIG_IGN);
788 if (sigsetjmp(imapjmp, 1)) {
789 safe_signal(SIGINT, saveint);
790 safe_signal(SIGPIPE, savepipe);
791 goto jbrk;
793 if (savepipe != SIG_IGN)
794 safe_signal(SIGPIPE, imapcatch);
795 if (imap_noop1(&mb) != OKAY) {
796 safe_signal(SIGINT, saveint);
797 safe_signal(SIGPIPE, savepipe);
798 goto jleave;
800 safe_signal(SIGINT, saveint);
801 safe_signal(SIGPIPE, savepipe);
803 jbrk:
804 alarm(imapkeepalive);
805 jleave:
806 --imaplock;
809 static int
810 imap_use_starttls(const char *uhp)
812 int rv;
813 NYD_ENTER;
815 if (ok_blook(imap_use_starttls))
816 rv = 1;
817 else {
818 char *var = savecat("imap-use-starttls-", uhp);
819 rv = vok_blook(var);
821 NYD_LEAVE;
822 return rv;
825 static enum okay
826 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
828 char *cp;
829 NYD_X;
831 mp->mb_active |= MB_PREAUTH;
832 imap_answer(mp, 1);
833 if ((cp = strchr(xserver, ':')) != NULL) {
834 char *x = salloc(cp - xserver + 1);
835 memcpy(x, xserver, cp - xserver);
836 x[cp - xserver] = '\0';
837 xserver = x;
839 #ifdef HAVE_SSL
840 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
841 FILE *queuefp = NULL;
842 char o[LINESIZE];
844 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
845 IMAP_OUT(o, MB_COMD, return STOP)
846 IMAP_ANSWER()
847 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
848 return STOP;
850 #else
851 if (imap_use_starttls(uhp)) {
852 fprintf(stderr, "No SSL support compiled in.\n");
853 return STOP;
855 #endif
856 imap_capability(mp);
857 return OKAY;
860 static enum okay
861 imap_capability(struct mailbox *mp)
863 char o[LINESIZE];
864 FILE *queuefp = NULL;
865 enum okay ok = STOP;
866 const char *cp;
867 NYD_X;
869 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
870 IMAP_OUT(o, MB_COMD, return STOP)
871 while (mp->mb_active & MB_COMD) {
872 ok = imap_answer(mp, 0);
873 if (response_status == RESPONSE_OTHER &&
874 response_other == CAPABILITY_DATA) {
875 cp = responded_other_text;
876 while (*cp) {
877 while (spacechar(*cp))
878 ++cp;
879 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
880 /* RFC 2359 */
881 mp->mb_flags |= MB_UIDPLUS;
882 while (*cp && !spacechar(*cp))
883 ++cp;
887 return ok;
890 static enum okay
891 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, char *pass)
893 char *var, *auth;
894 enum okay rv;
895 NYD_ENTER;
897 if (!(mp->mb_active & MB_PREAUTH)) {
898 rv = OKAY;
899 goto jleave;
902 if ((auth = ok_vlook(imap_auth)) == NULL) {
903 size_t i = strlen(uhp) + 1;
904 var = ac_alloc(i + 10);
905 memcpy(var, "imap-auth-", 10);
906 memcpy(var + 10, uhp, i);
907 auth = vok_vlook(var);
908 ac_free(var);
911 if (auth == NULL || !strcmp(auth, "login"))
912 rv = imap_login(mp, xuser, pass);
913 else if (!strcmp(auth, "cram-md5")) {
914 #ifdef HAVE_MD5
915 rv = imap_cram_md5(mp, xuser, pass);
916 #else
917 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
918 rv = STOP;
919 #endif
920 } else if (!strcmp(auth, "gssapi")) {
921 #ifdef HAVE_GSSAPI
922 rv = imap_gss(mp, xuser);
923 #else
924 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
925 rv = STOP;
926 #endif
927 } else {
928 fprintf(stderr, tr(273,
929 "Unknown IMAP authentication method: %s\n"), auth);
930 rv = STOP;
932 jleave:
933 NYD_LEAVE;
934 return rv;
937 /* Implementation of RFC 2194 */
938 #ifdef HAVE_MD5
939 static enum okay
940 imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass)
942 char o[LINESIZE], *user, *pass, *cp;
943 FILE *queuefp = NULL;
944 enum okay rv = STOP;
945 NYD_ENTER;
947 jretry:
948 user = xuser;
949 pass = xpass;
950 if (!getcredentials(&user, &pass))
951 goto jleave;
953 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
954 IMAP_XOUT(o, 0, goto jleave, goto jleave);
955 imap_answer(mp, 1);
956 if (response_type != RESPONSE_CONT)
957 goto jleave;
959 cp = cram_md5_string(user, pass, responded_text);
960 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
961 while (mp->mb_active & MB_COMD)
962 rv = imap_answer(mp, 1);
963 if (rv == STOP) {
964 xpass = NULL;
965 goto jretry;
967 jleave:
968 NYD_LEAVE;
969 return rv;
971 #endif /* HAVE_MD5 */
973 static enum okay
974 imap_login(struct mailbox *mp, char *xuser, char *xpass)
976 char o[LINESIZE];
977 char *user, *pass;
978 FILE *queuefp = NULL;
979 enum okay rv = STOP;
980 NYD_ENTER;
982 jretry:
983 user = xuser;
984 pass = xpass;
985 if (!getcredentials(&user, &pass))
986 goto jleave;
988 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
989 tag(1), imap_quotestr(user), imap_quotestr(pass));
990 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
991 while (mp->mb_active & MB_COMD)
992 rv = imap_answer(mp, 1);
993 if (rv == STOP) {
994 xpass = NULL;
995 goto jretry;
997 jleave:
998 NYD_LEAVE;
999 return rv;
1002 #ifdef HAVE_GSSAPI
1003 # include "imap_gssapi.h"
1004 #endif
1006 FL enum okay
1007 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
1009 enum okay ok = OKAY;
1010 char const *cp;
1011 char o[LINESIZE];
1012 FILE *queuefp = NULL;
1013 NYD_X;
1014 UNUSED(size);
1016 mp->mb_uidvalidity = 0;
1017 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
1018 IMAP_OUT(o, MB_COMD, return STOP)
1019 while (mp->mb_active & MB_COMD) {
1020 ok = imap_answer(mp, 1);
1021 if (response_status != RESPONSE_OTHER &&
1022 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1023 mp->mb_uidvalidity = atol(&cp[13]);
1025 *cnt = (had_exists > 0) ? had_exists : 0;
1026 if (response_status != RESPONSE_OTHER &&
1027 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1028 mp->mb_perm = 0;
1029 return ok;
1032 static enum okay
1033 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1035 char o[LINESIZE];
1036 FILE *queuefp = NULL;
1037 char const *cp;
1038 struct message *m;
1039 unsigned x = X, y = Y, n;
1040 NYD_X;
1042 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1043 IMAP_OUT(o, MB_COMD, return STOP)
1044 while (mp->mb_active & MB_COMD) {
1045 imap_answer(mp, 1);
1046 if (response_status == RESPONSE_OTHER &&
1047 response_other == MESSAGE_DATA_FETCH) {
1048 n = responded_other_number;
1049 if (n < x || n > y)
1050 continue;
1051 m = &message[n-1];
1052 m->m_xsize = 0;
1053 } else
1054 continue;
1056 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1057 cp += 5;
1058 while (*cp == ' ')
1059 cp++;
1060 if (*cp == '(')
1061 imap_getflags(cp, &cp, &m->m_flag);
1064 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1065 m->m_uid = strtoul(&cp[4], NULL, 10);
1066 getcache1(mp, m, NEED_UNSPEC, 1);
1067 m->m_flag &= ~MHIDDEN;
1070 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1071 x++;
1072 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1073 y--;
1074 if (x <= y) {
1075 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1076 tag(1), x, y);
1077 IMAP_OUT(o, MB_COMD, return STOP)
1078 while (mp->mb_active & MB_COMD) {
1079 imap_answer(mp, 1);
1080 if (response_status == RESPONSE_OTHER &&
1081 response_other == MESSAGE_DATA_FETCH) {
1082 n = responded_other_number;
1083 if (n < x || n > y)
1084 continue;
1085 m = &message[n-1];
1086 } else
1087 continue;
1088 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1089 m->m_xsize = strtol(&cp[12], NULL, 10);
1090 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1091 m->m_time = imap_read_date_time(&cp[13]);
1095 for (n = X; n <= Y; n++)
1096 putcache(mp, &message[n-1]);
1097 return OKAY;
1100 static void
1101 imap_init(struct mailbox *mp, int n)
1103 struct message *m;
1104 NYD_ENTER;
1105 UNUSED(mp);
1107 m = message + n;
1108 m->m_flag = MUSED | MNOFROM;
1109 m->m_block = 0;
1110 m->m_offset = 0;
1111 NYD_LEAVE;
1114 static void
1115 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1117 struct message *omessage = 0;
1118 int i, omsgCount = 0;
1119 enum okay dequeued = STOP;
1120 NYD_ENTER;
1122 if (nmail || transparent) {
1123 omessage = message;
1124 omsgCount = msgCount;
1126 if (nmail)
1127 dequeued = rec_dequeue();
1129 if (had_exists >= 0) {
1130 if (dequeued != OKAY)
1131 msgCount = had_exists;
1132 had_exists = -1;
1134 if (had_expunge >= 0) {
1135 if (dequeued != OKAY)
1136 msgCount -= had_expunge;
1137 had_expunge = -1;
1140 if (nmail && expunged_messages)
1141 printf("Expunged %ld message%s.\n", expunged_messages,
1142 (expunged_messages != 1 ? "s" : ""));
1143 *prevcount = omsgCount - expunged_messages;
1144 expunged_messages = 0;
1145 if (msgCount < 0) {
1146 fputs("IMAP error: Negative message count\n", stderr);
1147 msgCount = 0;
1150 if (dequeued != OKAY) {
1151 message = scalloc(msgCount + 1, sizeof *message);
1152 for (i = 0; i < msgCount; i++)
1153 imap_init(mp, i);
1154 if (!nmail && mp->mb_type == MB_IMAP)
1155 initcache(mp);
1156 if (msgCount > 0)
1157 imap_flags(mp, 1, msgCount);
1158 message[msgCount].m_size = 0;
1159 message[msgCount].m_lines = 0;
1160 rec_rmqueue();
1162 if (nmail || transparent)
1163 transflags(omessage, omsgCount, transparent);
1164 else
1165 setdot(message);
1166 NYD_LEAVE;
1169 static void
1170 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1171 char const **uhp, char const **mbx, char **pass, char **user)
1173 NYD_ENTER;
1174 *sp = *server;
1175 if (strncmp(*sp, "imap://", 7) == 0) {
1176 *sp = &(*sp)[7];
1177 *use_ssl = 0;
1178 #ifdef HAVE_SSL
1179 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1180 *sp = &(*sp)[8];
1181 *use_ssl = 1;
1182 #endif
1183 } else
1184 *use_ssl = 0; /* (silence CC) */
1186 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1187 char *x = savestr(*sp);
1188 x[*cp - *sp] = '\0';
1189 *uhp = x;
1190 *mbx = &(*cp)[1];
1191 } else {
1192 if (*cp)
1193 (*server)[*cp - *server] = '\0';
1194 *uhp = *sp;
1195 *mbx = "INBOX";
1198 *pass = lookup_password_for_token(*uhp);
1199 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1200 *user = salloc(*cp - *uhp + 1);
1201 memcpy(*user, *uhp, *cp - *uhp);
1202 (*user)[*cp - *uhp] = '\0';
1203 *sp = &(*cp)[1];
1204 *user = urlxdec(*user);
1205 } else {
1206 *user = NULL;
1207 *sp = *uhp;
1209 NYD_LEAVE;
1212 FL int
1213 imap_setfile(const char *xserver, int nmail, int isedit)
1215 int rv;
1216 NYD_ENTER;
1218 rv = imap_setfile1(xserver, nmail, isedit, 0);
1219 NYD_LEAVE;
1220 return rv;
1223 static int
1224 imap_setfile1(const char *xserver, int nmail, int isedit,
1225 int volatile transparent)
1227 struct sock so;
1228 sighandler_type volatile saveint, savepipe;
1229 char *server, *user, *pass, *acc;
1230 char const *cp, *sp, * volatile mbx, *uhp;
1231 int rv, use_ssl = 0, prevcount = 0;
1232 enum mbflags same_flags;
1233 NYD_ENTER;
1235 server = savestr(xserver);
1236 if (nmail) {
1237 saveint = safe_signal(SIGINT, SIG_IGN);
1238 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1239 if (saveint != SIG_IGN)
1240 safe_signal(SIGINT, imapcatch);
1241 if (savepipe != SIG_IGN)
1242 safe_signal(SIGPIPE, imapcatch);
1243 imaplock = 1;
1244 goto jnmail;
1247 same_flags = mb.mb_flags;
1248 same_imap_account = 0;
1249 sp = protbase(server);
1250 if (mb.mb_imap_account && mb.mb_type == MB_IMAP) {
1251 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1252 strcmp(mb.mb_imap_account, sp) == 0 &&
1253 disconnected(mb.mb_imap_account) == 0)
1254 same_imap_account = 1;
1256 acc = sstrdup(sp);
1259 char const *xmbx;
1260 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &xmbx, &pass, &user);
1261 mbx = xmbx;
1263 so.s_fd = -1;
1264 if (!same_imap_account) {
1265 if (!disconnected(acc) &&
1266 sopen(sp, &so, use_ssl, uhp, (use_ssl ? "imaps" : "imap")
1267 ) != OKAY) {
1268 free(acc);
1269 rv = -1;
1270 goto jleave;
1272 } else
1273 so = mb.mb_sock;
1274 if (!transparent)
1275 quit();
1277 edit = (isedit != 0);
1278 if (mb.mb_imap_account != NULL)
1279 free(mb.mb_imap_account);
1280 mb.mb_imap_account = acc;
1281 if (!same_imap_account) {
1282 if (mb.mb_sock.s_fd >= 0)
1283 sclose(&mb.mb_sock);
1285 same_imap_account = 0;
1287 if (!transparent) {
1288 if (mb.mb_itf) {
1289 fclose(mb.mb_itf);
1290 mb.mb_itf = NULL;
1292 if (mb.mb_otf) {
1293 fclose(mb.mb_otf);
1294 mb.mb_otf = NULL;
1296 if (mb.mb_imap_mailbox != NULL)
1297 free(mb.mb_imap_mailbox);
1298 mb.mb_imap_mailbox = sstrdup(mbx);
1299 initbox(server);
1301 mb.mb_type = MB_VOID;
1302 mb.mb_active = MB_NONE;
1304 imaplock = 1;
1305 saveint = safe_signal(SIGINT, SIG_IGN);
1306 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1307 if (sigsetjmp(imapjmp, 1)) {
1308 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1309 sclose(&mb.mb_sock);
1310 safe_signal(SIGINT, saveint);
1311 safe_signal(SIGPIPE, savepipe);
1312 imaplock = 0;
1314 mb.mb_type = MB_VOID;
1315 mb.mb_active = MB_NONE;
1316 rv = -1;
1317 goto jleave;
1319 if (saveint != SIG_IGN)
1320 safe_signal(SIGINT, imapcatch);
1321 if (savepipe != SIG_IGN)
1322 safe_signal(SIGPIPE, imapcatch);
1324 if (mb.mb_sock.s_fd < 0) {
1325 if (disconnected(mb.mb_imap_account)) {
1326 if (cache_setptr(transparent) == STOP)
1327 fprintf(stderr,
1328 "Mailbox \"%s\" is not cached.\n",
1329 server);
1330 goto jdone;
1332 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1333 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1334 savealrm = safe_signal(SIGALRM, imapalarm);
1335 alarm(imapkeepalive);
1339 mb.mb_sock = so;
1340 mb.mb_sock.s_desc = "IMAP";
1341 mb.mb_sock.s_onclose = imap_timer_off;
1342 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1343 imap_auth(&mb, uhp, user, pass) != OKAY) {
1344 sclose(&mb.mb_sock);
1345 imap_timer_off();
1346 safe_signal(SIGINT, saveint);
1347 safe_signal(SIGPIPE, savepipe);
1348 imaplock = 0;
1349 rv = -1;
1350 goto jleave;
1352 } else /* same account */
1353 mb.mb_flags |= same_flags;
1355 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1356 mb.mb_type = MB_IMAP;
1357 cache_dequeue(&mb);
1358 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1359 /*sclose(&mb.mb_sock);
1360 imap_timer_off();*/
1361 safe_signal(SIGINT, saveint);
1362 safe_signal(SIGPIPE, savepipe);
1363 imaplock = 0;
1364 mb.mb_type = MB_VOID;
1365 rv = -1;
1366 goto jleave;
1369 jnmail:
1370 imap_setptr(&mb, nmail, transparent, &prevcount);
1371 jdone:
1372 setmsize(msgCount);
1373 if (!nmail && !transparent)
1374 sawcom = FAL0;
1375 safe_signal(SIGINT, saveint);
1376 safe_signal(SIGPIPE, savepipe);
1377 imaplock = 0;
1379 if (!nmail && mb.mb_type == MB_IMAP)
1380 purgecache(&mb, message, msgCount);
1381 if ((nmail || transparent) && mb.mb_sorted) {
1382 mb.mb_threaded = 0;
1383 c_sort((void*)-1);
1386 if (!nmail && !edit && msgCount == 0) {
1387 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1388 !ok_blook(emptystart))
1389 fprintf(stderr, tr(258, "No mail at %s\n"), server);
1390 rv = 1;
1391 goto jleave;
1393 if (nmail)
1394 newmailinfo(prevcount);
1395 rv = 0;
1396 jleave:
1397 NYD_LEAVE;
1398 return rv;
1401 static int
1402 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1403 int need, const char *head, size_t headsize, long headlines)
1405 char *line = NULL, *lp;
1406 size_t linesize = 0, linelen, size = 0;
1407 int emptyline = 0, lines = 0, excess = 0;
1408 off_t offset;
1409 NYD_ENTER;
1411 fseek(mp->mb_otf, 0L, SEEK_END);
1412 offset = ftell(mp->mb_otf);
1414 if (head)
1415 fwrite(head, 1, headsize, mp->mb_otf);
1417 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1418 lp = line;
1419 if (linelen > expected) {
1420 excess = linelen - expected;
1421 linelen = expected;
1423 /* TODO >>
1424 * Need to mask 'From ' lines. This cannot be done properly
1425 * since some servers pass them as 'From ' and others as
1426 * '>From '. Although one could identify the first kind of
1427 * server in principle, it is not possible to identify the
1428 * second as '>From ' may also come from a server of the
1429 * first type as actual data. So do what is absolutely
1430 * necessary only - mask 'From '.
1432 * If the line is the first line of the message header, it
1433 * is likely a real 'From ' line. In this case, it is just
1434 * ignored since it violates all standards.
1435 * TODO can the latter *really* happen??
1436 * TODO <<
1438 /* Since we simply copy over data without doing any transfer
1439 * encoding reclassification/adjustment we *have* to perform
1440 * RFC 4155 compliant From_ quoting here */
1441 if (is_head(lp, linelen)) {
1442 if (lines + headlines == 0)
1443 goto jskip;
1444 fputc('>', mp->mb_otf);
1445 ++size;
1447 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1448 emptyline = linelen <= 2;
1449 if (linelen > 2) {
1450 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1451 size += linelen - 1;
1452 } else
1453 ++size;
1454 fputc('\n', mp->mb_otf);
1455 } else {
1456 emptyline = 0;
1457 fwrite(lp, 1, linelen, mp->mb_otf);
1458 size += linelen;
1460 ++lines;
1461 jskip:
1462 if ((expected -= linelen) <= 0)
1463 break;
1465 if (!emptyline) {
1466 /* This is very ugly; but some IMAP daemons don't end a
1467 * message with \r\n\r\n, and we need \n\n for mbox format */
1468 fputc('\n', mp->mb_otf);
1469 ++lines;
1470 ++size;
1472 fflush(mp->mb_otf);
1474 if (m != NULL) {
1475 m->m_size = size + headsize;
1476 m->m_lines = lines + headlines;
1477 m->m_block = mailx_blockof(offset);
1478 m->m_offset = mailx_offsetof(offset);
1479 switch (need) {
1480 case NEED_HEADER:
1481 m->m_have |= HAVE_HEADER;
1482 break;
1483 case NEED_BODY:
1484 m->m_have |= HAVE_HEADER | HAVE_BODY;
1485 m->m_xlines = m->m_lines;
1486 m->m_xsize = m->m_size;
1487 break;
1490 free(line);
1491 NYD_LEAVE;
1492 return excess;
1495 static void
1496 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1497 const char *head, size_t headsize, long headlines)
1499 off_t offset;
1500 size_t len;
1501 NYD_ENTER;
1503 len = strlen(str);
1504 fseek(mp->mb_otf, 0L, SEEK_END);
1505 offset = ftell(mp->mb_otf);
1506 if (head)
1507 fwrite(head, 1, headsize, mp->mb_otf);
1508 if (len > 0) {
1509 fwrite(str, 1, len, mp->mb_otf);
1510 fputc('\n', mp->mb_otf);
1511 ++len;
1513 fflush(mp->mb_otf);
1515 if (m != NULL) {
1516 m->m_size = headsize + len;
1517 m->m_lines = headlines + 1;
1518 m->m_block = mailx_blockof(offset);
1519 m->m_offset = mailx_offsetof(offset);
1520 m->m_have |= HAVE_HEADER | HAVE_BODY;
1521 m->m_xlines = m->m_lines;
1522 m->m_xsize = m->m_size;
1524 NYD_LEAVE;
1527 static enum okay
1528 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1530 char o[LINESIZE];
1531 struct message mt;
1532 sighandler_type volatile saveint, savepipe;
1533 char * volatile head;
1534 char const *cp, *loc, * volatile item, * volatile resp;
1535 size_t expected;
1536 size_t volatile headsize;
1537 int number;
1538 FILE *queuefp;
1539 long volatile headlines;
1540 long n;
1541 unsigned long u;
1542 enum okay ok;
1543 NYD_X;
1545 saveint = savepipe = SIG_IGN;
1546 head = NULL;
1547 cp = loc = item = resp = NULL;
1548 headsize = 0;
1549 number = (int)PTR2SIZE(m - message + 1);
1550 queuefp = NULL;
1551 headlines = 0;
1552 n = -1;
1553 u = 0;
1554 ok = STOP;
1556 if (getcache(mp, m, need) == OKAY)
1557 return OKAY;
1558 if (mp->mb_type == MB_CACHE) {
1559 fprintf(stderr, "Message %u not available.\n", number);
1560 return STOP;
1563 if (mp->mb_sock.s_fd < 0) {
1564 fprintf(stderr, "IMAP connection closed.\n");
1565 return STOP;
1568 switch (need) {
1569 case NEED_HEADER:
1570 resp = item = "RFC822.HEADER";
1571 break;
1572 case NEED_BODY:
1573 item = "BODY.PEEK[]";
1574 resp = "BODY[]";
1575 if (m->m_flag & HAVE_HEADER && m->m_size) {
1576 char *hdr = smalloc(m->m_size);
1577 fflush(mp->mb_otf);
1578 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1579 SEEK_SET) < 0 ||
1580 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1581 free(hdr);
1582 break;
1584 head = hdr;
1585 headsize = m->m_size;
1586 headlines = m->m_lines;
1587 item = "BODY.PEEK[TEXT]";
1588 resp = "BODY[TEXT]";
1590 break;
1591 case NEED_UNSPEC:
1592 return STOP;
1595 imaplock = 1;
1596 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1597 if (sigsetjmp(imapjmp, 1)) {
1598 safe_signal(SIGINT, saveint);
1599 safe_signal(SIGPIPE, savepipe);
1600 imaplock = 0;
1601 return STOP;
1603 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1604 safe_signal(SIGINT, &_imap_maincatch);
1605 if (savepipe != SIG_IGN)
1606 safe_signal(SIGPIPE, imapcatch);
1608 if (m->m_uid)
1609 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1610 tag(1), m->m_uid, item);
1611 else {
1612 if (check_expunged() == STOP)
1613 goto out;
1614 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1616 IMAP_OUT(o, MB_COMD, goto out)
1617 for (;;) {
1618 ok = imap_answer(mp, 1);
1619 if (ok == STOP)
1620 break;
1621 if (response_status != RESPONSE_OTHER ||
1622 response_other != MESSAGE_DATA_FETCH)
1623 continue;
1624 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1625 continue;
1626 if (m->m_uid) {
1627 if ((cp = asccasestr(responded_other_text, "UID "))) {
1628 u = atol(&cp[4]);
1629 n = 0;
1630 } else {
1631 n = -1;
1632 u = 0;
1634 } else
1635 n = responded_other_number;
1636 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1637 if (m->m_uid ? m->m_uid != u : n != number)
1638 continue;
1639 if ((cp = strchr(loc, '"')) != NULL) {
1640 cp = imap_unquotestr(cp);
1641 imap_putstr(mp, m, cp, head, headsize, headlines);
1642 } else {
1643 m->m_have |= HAVE_HEADER|HAVE_BODY;
1644 m->m_xlines = m->m_lines;
1645 m->m_xsize = m->m_size;
1647 goto out;
1649 expected = atol(&cp[1]);
1650 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1651 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1652 continue;
1654 mt = *m;
1655 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1656 if (n >= 0) {
1657 commitmsg(mp, m, &mt, mt.m_have);
1658 break;
1660 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1661 if (options & OPT_VERBOSE)
1662 fputs(imapbuf, stderr);
1663 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1664 u = atol(&cp[4]);
1665 if (u == m->m_uid) {
1666 commitmsg(mp, m, &mt, mt.m_have);
1667 break;
1672 out:
1673 while (mp->mb_active & MB_COMD)
1674 ok = imap_answer(mp, 1);
1676 if (saveint != SIG_IGN)
1677 safe_signal(SIGINT, saveint);
1678 if (savepipe != SIG_IGN)
1679 safe_signal(SIGPIPE, savepipe);
1680 imaplock--;
1682 if (ok == OKAY)
1683 putcache(mp, m);
1684 if (head != NULL)
1685 free(head);
1686 if (interrupts)
1687 onintr(0);
1688 return ok;
1691 FL enum okay
1692 imap_header(struct message *m)
1694 enum okay rv;
1695 NYD_ENTER;
1697 rv = imap_get(&mb, m, NEED_HEADER);
1698 NYD_LEAVE;
1699 return rv;
1703 FL enum okay
1704 imap_body(struct message *m)
1706 enum okay rv;
1707 NYD_ENTER;
1709 rv = imap_get(&mb, m, NEED_BODY);
1710 NYD_LEAVE;
1711 return rv;
1714 static void
1715 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1716 enum havespec have)
1718 NYD_ENTER;
1719 tomp->m_size = frommp->m_size;
1720 tomp->m_lines = frommp->m_lines;
1721 tomp->m_block = frommp->m_block;
1722 tomp->m_offset = frommp->m_offset;
1723 tomp->m_have = have;
1724 if (have & HAVE_BODY) {
1725 tomp->m_xlines = frommp->m_lines;
1726 tomp->m_xsize = frommp->m_size;
1728 putcache(mp, tomp);
1729 NYD_LEAVE;
1732 static enum okay
1733 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1735 /* bot > topp */
1736 char o[LINESIZE];
1737 char const *cp;
1738 struct message mt;
1739 size_t expected;
1740 int n = 0, u;
1741 FILE *queuefp = NULL;
1742 enum okay ok;
1743 NYD_X;
1745 if (m[bot].m_uid)
1746 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1747 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1748 else {
1749 if (check_expunged() == STOP)
1750 return STOP;
1751 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1752 tag(1), bot, topp);
1754 IMAP_OUT(o, MB_COMD, return STOP)
1755 for (;;) {
1756 ok = imap_answer(mp, 1);
1757 if (response_status != RESPONSE_OTHER)
1758 break;
1759 if (response_other != MESSAGE_DATA_FETCH)
1760 continue;
1761 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1762 return STOP;
1763 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1764 continue;
1765 expected = atol(&cp[1]);
1766 if (m[bot-1].m_uid) {
1767 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1768 u = atoi(&cp[4]);
1769 for (n = bot; n <= topp; n++)
1770 if ((unsigned long)u == m[n-1].m_uid)
1771 break;
1772 if (n > topp) {
1773 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1774 continue;
1776 } else
1777 n = -1;
1778 } else {
1779 n = responded_other_number;
1780 if (n <= 0 || n > msgCount) {
1781 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1782 continue;
1785 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1786 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1787 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1788 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1789 if (options & OPT_VERBOSE)
1790 fputs(imapbuf, stderr);
1791 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1792 u = atoi(&cp[4]);
1793 for (n = bot; n <= topp; n++)
1794 if ((unsigned long)u == m[n-1].m_uid)
1795 break;
1796 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1797 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1798 n = 0;
1802 while (mp->mb_active & MB_COMD)
1803 ok = imap_answer(mp, 1);
1804 return ok;
1807 FL void
1808 imap_getheaders(int volatile bot, int topp) /* TODO should take iterator!! */
1810 sighandler_type saveint, savepipe;
1811 /* enum okay ok = STOP;*/
1812 int i, chunk = 256;
1813 NYD_X;
1815 if (mb.mb_type == MB_CACHE)
1816 return;
1817 if (bot < 1)
1818 bot = 1;
1819 if (topp > msgCount)
1820 topp = msgCount;
1821 for (i = bot; i < topp; i++) {
1822 if (message[i-1].m_have & HAVE_HEADER ||
1823 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1824 bot = i+1;
1825 else
1826 break;
1828 for (i = topp; i > bot; i--) {
1829 if (message[i-1].m_have & HAVE_HEADER ||
1830 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1831 topp = i-1;
1832 else
1833 break;
1835 if (bot >= topp)
1836 return;
1838 imaplock = 1;
1839 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1840 safe_signal(SIGINT, &_imap_maincatch);
1841 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1842 if (sigsetjmp(imapjmp, 1) == 0) {
1843 if (savepipe != SIG_IGN)
1844 safe_signal(SIGPIPE, imapcatch);
1846 for (i = bot; i <= topp; i += chunk) {
1847 int j = i + chunk - 1;
1848 if (visible(message + j))
1849 /*ok = */imap_fetchheaders(&mb, message, i, (j < topp ? j : topp));
1850 if (interrupts)
1851 onintr(0); /* XXX imaplock? */
1854 safe_signal(SIGINT, saveint);
1855 safe_signal(SIGPIPE, savepipe);
1856 imaplock = 0;
1859 static enum okay
1860 __imap_exit(struct mailbox *mp)
1862 char o[LINESIZE];
1863 FILE *queuefp = NULL;
1864 NYD_X;
1866 mp->mb_active |= MB_BYE;
1867 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1868 IMAP_OUT(o, MB_COMD, return STOP)
1869 IMAP_ANSWER()
1870 return OKAY;
1873 static enum okay
1874 imap_exit(struct mailbox *mp)
1876 enum okay rv;
1877 NYD_ENTER;
1879 rv = __imap_exit(mp);
1880 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1881 free(mp->mb_imap_account);
1882 free(mp->mb_imap_mailbox);
1883 if (mp->mb_cache_directory != NULL)
1884 free(mp->mb_cache_directory);
1885 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1886 mp->mb_imap_account =
1887 mp->mb_imap_mailbox =
1888 mp->mb_cache_directory = "";
1889 #else
1890 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1891 mp->mb_imap_mailbox = NULL;
1892 mp->mb_cache_directory = NULL;
1893 #endif
1894 #endif
1895 sclose(&mp->mb_sock);
1896 NYD_LEAVE;
1897 return rv;
1900 static enum okay
1901 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1903 NYD_ENTER;
1904 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1905 if (mp->mb_type == MB_IMAP)
1906 delcache(mp, m);
1907 NYD_LEAVE;
1908 return OKAY;
1911 static enum okay
1912 imap_close(struct mailbox *mp)
1914 char o[LINESIZE];
1915 FILE *queuefp = NULL;
1916 NYD_X;
1918 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1919 IMAP_OUT(o, MB_COMD, return STOP)
1920 IMAP_ANSWER()
1921 return OKAY;
1924 static enum okay
1925 imap_update(struct mailbox *mp)
1927 struct message *m;
1928 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1929 NYD_ENTER;
1931 if (!edit && mp->mb_perm != 0) {
1932 holdbits();
1933 c = 0;
1934 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1935 if (m->m_flag & MBOX)
1936 ++c;
1937 if (c > 0)
1938 if (makembox() == STOP)
1939 goto jbypass;
1942 gotcha = held = 0;
1943 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1944 if (mp->mb_perm == 0)
1945 dodel = 0;
1946 else if (edit)
1947 dodel = ((m->m_flag & MDELETED) != 0);
1948 else
1949 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1951 /* Fetch the result after around each 800 STORE commands
1952 * sent (approx. 32k data sent). Otherwise, servers will
1953 * try to flush the return queue at some point, leading
1954 * to a deadlock if we are still writing commands but not
1955 * reading their results */
1956 needstat = stored > 0 && stored % 800 == 0;
1957 /* Even if this message has been deleted, continue
1958 * to set further flags. This is necessary to support
1959 * Gmail semantics, where "delete" actually means
1960 * "archive", and the flags are applied to the copy
1961 * in "All Mail" */
1962 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1963 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1964 stored++;
1966 if (m->m_flag & MFLAG) {
1967 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1968 stored++;
1970 if (m->m_flag & MUNFLAG) {
1971 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1972 stored++;
1974 if (m->m_flag & MANSWER) {
1975 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1976 stored++;
1978 if (m->m_flag & MUNANSWER) {
1979 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1980 stored++;
1982 if (m->m_flag & MDRAFT) {
1983 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1984 stored++;
1986 if (m->m_flag & MUNDRAFT) {
1987 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1988 stored++;
1991 if (dodel) {
1992 imap_delete(mp, m-message+1, m, needstat);
1993 stored++;
1994 gotcha++;
1995 } else if (mp->mb_type != MB_CACHE ||
1996 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1997 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1998 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1999 held++;
2000 if (m->m_flag & MNEW) {
2001 m->m_flag &= ~MNEW;
2002 m->m_flag |= MSTATUS;
2005 jbypass:
2006 if (gotcha)
2007 imap_close(mp);
2009 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
2010 if (!(m->m_flag & MUNLINKED) &&
2011 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2012 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2013 putcache(mp, m);
2014 modflags++;
2016 if ((gotcha || modflags) && edit) {
2017 printf(tr(168, "\"%s\" "), displayname);
2018 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2019 ? tr(170, "complete\n") : tr(212, "updated.\n"));
2020 } else if (held && !edit && mp->mb_perm != 0) {
2021 if (held == 1)
2022 printf(tr(155, "Held 1 message in %s\n"), displayname);
2023 else
2024 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
2026 fflush(stdout);
2027 NYD_LEAVE;
2028 return OKAY;
2031 FL void
2032 imap_quit(void)
2034 sighandler_type volatile saveint, savepipe;
2035 NYD_ENTER;
2037 if (mb.mb_type == MB_CACHE) {
2038 imap_update(&mb);
2039 goto jleave;
2042 if (mb.mb_sock.s_fd < 0) {
2043 fprintf(stderr, "IMAP connection closed.\n");
2044 goto jleave;
2047 imaplock = 1;
2048 saveint = safe_signal(SIGINT, SIG_IGN);
2049 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2050 if (sigsetjmp(imapjmp, 1)) {
2051 safe_signal(SIGINT, saveint);
2052 safe_signal(SIGPIPE, saveint);
2053 imaplock = 0;
2054 goto jleave;
2056 if (saveint != SIG_IGN)
2057 safe_signal(SIGINT, imapcatch);
2058 if (savepipe != SIG_IGN)
2059 safe_signal(SIGPIPE, imapcatch);
2061 imap_update(&mb);
2062 if (!same_imap_account)
2063 imap_exit(&mb);
2065 safe_signal(SIGINT, saveint);
2066 safe_signal(SIGPIPE, savepipe);
2067 imaplock = 0;
2068 jleave:
2069 NYD_LEAVE;
2072 static enum okay
2073 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2074 int needstat)
2076 char o[LINESIZE];
2077 FILE *queuefp = NULL;
2078 NYD_X;
2080 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2081 return STOP;
2082 if (m->m_uid)
2083 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2084 tag(1), m->m_uid, c, sp);
2085 else {
2086 if (check_expunged() == STOP)
2087 return STOP;
2088 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2090 IMAP_OUT(o, MB_COMD, return STOP)
2091 if (needstat)
2092 IMAP_ANSWER()
2093 else
2094 mb.mb_active &= ~MB_COMD;
2095 if (queuefp != NULL)
2096 Fclose(queuefp);
2097 return OKAY;
2100 FL enum okay
2101 imap_undelete(struct message *m, int n)
2103 enum okay rv;
2104 NYD_ENTER;
2106 rv = imap_unstore(m, n, "\\Deleted");
2107 NYD_LEAVE;
2108 return rv;
2111 FL enum okay
2112 imap_unread(struct message *m, int n)
2114 enum okay rv;
2115 NYD_ENTER;
2117 rv = imap_unstore(m, n, "\\Seen");
2118 NYD_LEAVE;
2119 return rv;
2122 static enum okay
2123 imap_unstore(struct message *m, int n, const char *flag)
2125 sighandler_type saveint, savepipe;
2126 enum okay rv = STOP;
2127 NYD_ENTER;
2129 imaplock = 1;
2130 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2131 safe_signal(SIGINT, &_imap_maincatch);
2132 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2133 if (sigsetjmp(imapjmp, 1) == 0) {
2134 if (savepipe != SIG_IGN)
2135 safe_signal(SIGPIPE, imapcatch);
2137 rv = imap_store(&mb, m, n, '-', flag, 1);
2139 safe_signal(SIGINT, saveint);
2140 safe_signal(SIGPIPE, savepipe);
2141 imaplock = 0;
2143 NYD_LEAVE;
2144 if (interrupts)
2145 onintr(0);
2146 return rv;
2149 static const char *
2150 tag(int new)
2152 static char ts[20];
2153 static long n;
2154 NYD_ENTER;
2156 if (new)
2157 ++n;
2158 snprintf(ts, sizeof ts, "T%lu", n);
2159 NYD_LEAVE;
2160 return ts;
2163 FL int
2164 c_imap_imap(void *vp)
2166 char o[LINESIZE];
2167 sighandler_type saveint, savepipe;
2168 struct mailbox *mp = &mb;
2169 FILE *queuefp = NULL;
2170 enum okay ok = STOP;
2171 NYD_X;
2173 if (mp->mb_type != MB_IMAP) {
2174 printf("Not operating on an IMAP mailbox.\n");
2175 return 1;
2177 imaplock = 1;
2178 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2179 safe_signal(SIGINT, &_imap_maincatch);
2180 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2181 if (sigsetjmp(imapjmp, 1) == 0) {
2182 if (savepipe != SIG_IGN)
2183 safe_signal(SIGPIPE, imapcatch);
2185 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2186 IMAP_OUT(o, MB_COMD, goto out)
2187 while (mp->mb_active & MB_COMD) {
2188 ok = imap_answer(mp, 0);
2189 fputs(responded_text, stdout);
2192 out:
2193 safe_signal(SIGINT, saveint);
2194 safe_signal(SIGPIPE, savepipe);
2195 imaplock = 0;
2197 if (interrupts)
2198 onintr(0);
2199 return ok != OKAY;
2202 FL int
2203 imap_newmail(int nmail)
2205 NYD_ENTER;
2207 if (nmail && had_exists < 0 && had_expunge < 0) {
2208 imaplock = 1;
2209 imap_noop();
2210 imaplock = 0;
2213 if (had_exists == msgCount && had_expunge < 0)
2214 /* Some servers always respond with EXISTS to NOOP. If
2215 * the mailbox has been changed but the number of messages
2216 * has not, an EXPUNGE must also had been sent; otherwise,
2217 * nothing has changed */
2218 had_exists = -1;
2219 NYD_LEAVE;
2220 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2223 static char *
2224 imap_putflags(int f)
2226 const char *cp;
2227 char *buf, *bp;
2228 NYD_ENTER;
2230 bp = buf = salloc(100);
2231 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2232 *bp++ = '(';
2233 if (f & MREAD) {
2234 if (bp[-1] != '(')
2235 *bp++ = ' ';
2236 for (cp = "\\Seen"; *cp; cp++)
2237 *bp++ = *cp;
2239 if (f & MFLAGGED) {
2240 if (bp[-1] != '(')
2241 *bp++ = ' ';
2242 for (cp = "\\Flagged"; *cp; cp++)
2243 *bp++ = *cp;
2245 if (f & MANSWERED) {
2246 if (bp[-1] != '(')
2247 *bp++ = ' ';
2248 for (cp = "\\Answered"; *cp; cp++)
2249 *bp++ = *cp;
2251 if (f & MDRAFT) {
2252 if (bp[-1] != '(')
2253 *bp++ = ' ';
2254 for (cp = "\\Draft"; *cp; cp++)
2255 *bp++ = *cp;
2257 *bp++ = ')';
2258 *bp++ = ' ';
2260 *bp = '\0';
2261 NYD_LEAVE;
2262 return buf;
2265 static void
2266 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2268 NYD_ENTER;
2269 while (*cp != ')') {
2270 if (*cp == '\\') {
2271 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2272 *f |= MREAD;
2273 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2274 *f |= MNEW;
2275 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2276 *f |= MDELETED;
2277 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2278 *f |= MFLAGGED;
2279 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2280 *f |= MANSWERED;
2281 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2282 *f |= MDRAFTED;
2284 cp++;
2287 if (xp != NULL)
2288 *xp = cp;
2289 NYD_LEAVE;
2292 static enum okay
2293 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2294 long xsize, enum mflag flag, time_t t)
2296 char o[LINESIZE], *buf;
2297 size_t bufsize, buflen, cnt;
2298 long size, lines, ysize;
2299 int twice = 0;
2300 FILE *queuefp = NULL;
2301 enum okay rv;
2302 NYD_ENTER;
2304 if (mp->mb_type == MB_CACHE) {
2305 queuefp = cache_queue(mp);
2306 if (queuefp == NULL) {
2307 rv = STOP;
2308 buf = NULL;
2309 goto jleave;
2311 rv = OKAY;
2312 } else
2313 rv = STOP;
2315 buf = smalloc(bufsize = LINESIZE);
2316 buflen = 0;
2317 jagain:
2318 size = xsize;
2319 cnt = fsize(fp);
2320 if (fseek(fp, off1, SEEK_SET) < 0) {
2321 rv = STOP;
2322 goto jleave;
2325 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2326 tag(1), imap_quotestr(name), imap_putflags(flag),
2327 imap_make_date_time(t), size);
2328 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2329 while (mp->mb_active & MB_COMD) {
2330 rv = imap_answer(mp, twice);
2331 if (response_type == RESPONSE_CONT)
2332 break;
2335 if (mp->mb_type != MB_CACHE && rv == STOP) {
2336 if (twice == 0)
2337 goto jtrycreate;
2338 else
2339 goto jleave;
2342 lines = ysize = 0;
2343 while (size > 0) {
2344 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2345 lines++;
2346 ysize += buflen;
2347 buf[buflen - 1] = '\r';
2348 buf[buflen] = '\n';
2349 if (mp->mb_type != MB_CACHE)
2350 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2351 else if (queuefp)
2352 fwrite(buf, 1, buflen+1, queuefp);
2353 size -= buflen + 1;
2355 if (mp->mb_type != MB_CACHE)
2356 swrite(&mp->mb_sock, "\r\n");
2357 else if (queuefp)
2358 fputs("\r\n", queuefp);
2359 while (mp->mb_active & MB_COMD) {
2360 rv = imap_answer(mp, 0);
2361 if (response_status == RESPONSE_NO /*&&
2362 ascncasecmp(responded_text,
2363 "[TRYCREATE] ", 12) == 0*/) {
2364 jtrycreate:
2365 if (twice++) {
2366 rv = STOP;
2367 goto jleave;
2369 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2370 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2371 while (mp->mb_active & MB_COMD)
2372 rv = imap_answer(mp, 1);
2373 if (rv == STOP)
2374 goto jleave;
2375 imap_created_mailbox++;
2376 goto jagain;
2377 } else if (rv != OKAY)
2378 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
2379 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2380 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2382 jleave:
2383 if (queuefp != NULL)
2384 Fclose(queuefp);
2385 if (buf != NULL)
2386 free(buf);
2387 NYD_LEAVE;
2388 return rv;
2391 static enum okay
2392 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2394 char *buf, *bp, *lp;
2395 size_t bufsize, buflen, cnt;
2396 off_t off1 = -1, offs;
2397 int inhead = 1, flag = MNEW | MNEWEST;
2398 long size = 0;
2399 time_t tim;
2400 enum okay rv;
2401 NYD_ENTER;
2403 buf = smalloc(bufsize = LINESIZE);
2404 buflen = 0;
2405 cnt = fsize(fp);
2406 offs = ftell(fp);
2407 time(&tim);
2409 do {
2410 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2411 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2412 if (off1 != (off_t)-1) {
2413 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2414 if (rv == STOP)
2415 goto jleave;
2416 fseek(fp, offs+buflen, SEEK_SET);
2418 off1 = offs + buflen;
2419 size = 0;
2420 inhead = 1;
2421 flag = MNEW;
2422 if (bp != NULL)
2423 tim = unixtime(buf);
2424 } else
2425 size += buflen+1;
2426 offs += buflen;
2428 if (bp && buf[0] == '\n')
2429 inhead = 0;
2430 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2431 lp = &buf[6];
2432 while (whitechar(*lp))
2433 lp++;
2434 if (*lp == ':')
2435 while (*++lp != '\0')
2436 switch (*lp) {
2437 case 'R':
2438 flag |= MREAD;
2439 break;
2440 case 'O':
2441 flag &= ~MNEW;
2442 break;
2444 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2445 lp = &buf[8];
2446 while (whitechar(*lp))
2447 lp++;
2448 if (*lp == ':')
2449 while (*++lp != '\0')
2450 switch (*lp) {
2451 case 'F':
2452 flag |= MFLAGGED;
2453 break;
2454 case 'A':
2455 flag |= MANSWERED;
2456 break;
2457 case 'T':
2458 flag |= MDRAFTED;
2459 break;
2462 } while (bp != NULL);
2463 rv = OKAY;
2464 jleave:
2465 free(buf);
2466 NYD_LEAVE;
2467 return rv;
2470 FL enum okay
2471 imap_append(const char *xserver, FILE *fp)
2473 sighandler_type volatile saveint, savepipe;
2474 char *server, *user, *pass;
2475 char const *sp, *cp, * volatile mbx, *uhp;
2476 int volatile use_ssl;
2477 enum okay rv = STOP;
2478 NYD_ENTER;
2480 server = savestr(xserver);
2482 int xus;
2483 char const *xmbx;
2484 imap_split(&server, &sp, &xus, &cp, &uhp, &xmbx, &pass, &user);
2485 use_ssl = xus;
2486 mbx = xmbx;
2489 imaplock = 1;
2490 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2491 safe_signal(SIGINT, &_imap_maincatch);
2492 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2493 if (sigsetjmp(imapjmp, 1))
2494 goto jleave;
2495 if (savepipe != SIG_IGN)
2496 safe_signal(SIGPIPE, imapcatch);
2498 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2499 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2500 rv = imap_append0(&mb, mbx, fp);
2501 } else {
2502 struct mailbox mx;
2504 memset(&mx, 0, sizeof mx);
2505 if (disconnected(server) == 0) {
2506 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2507 (use_ssl ? "imaps" : "imap")) != OKAY)
2508 goto jfail;
2509 mx.mb_sock.s_desc = "IMAP";
2510 mx.mb_type = MB_IMAP;
2511 mx.mb_imap_account = (char *)protbase(server);
2512 /* TODO the code now did
2513 * TODO mx.mb_imap_mailbox = mbx;
2514 * TODO though imap_mailbox is sfree()d and mbx
2515 * TODO is possibly even a constant
2516 * TODO i changed this to sstrdup() sofar, as is used
2517 * TODO somewhere else in this file for this! */
2518 mx.mb_imap_mailbox = sstrdup(mbx);
2519 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2520 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2521 sclose(&mx.mb_sock);
2522 goto jfail;
2524 rv = imap_append0(&mx, mbx, fp);
2525 imap_exit(&mx);
2526 } else {
2527 mx.mb_imap_account = (char*)protbase(server);
2528 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2529 mx.mb_type = MB_CACHE;
2530 rv = imap_append0(&mx, mbx, fp);
2532 jfail:
2535 jleave:
2536 safe_signal(SIGINT, saveint);
2537 safe_signal(SIGPIPE, savepipe);
2538 imaplock = 0;
2540 NYD_LEAVE;
2541 if (interrupts)
2542 onintr(0);
2543 return rv;
2546 static enum okay
2547 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2548 struct list_item **lend, int level)
2550 char o[LINESIZE], *cp;
2551 const char *bp;
2552 FILE *queuefp = NULL;
2553 struct list_item *lp;
2554 enum okay ok = STOP;
2555 NYD_X;
2557 *list = *lend = NULL;
2558 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2559 IMAP_OUT(o, MB_COMD, return STOP)
2560 while (mp->mb_active & MB_COMD) {
2561 ok = imap_answer(mp, 1);
2562 if (response_status == RESPONSE_OTHER &&
2563 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2564 cp = imap_unquotestr(list_name);
2565 lp = csalloc(1, sizeof *lp);
2566 lp->l_name = cp;
2567 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2568 ++cp;
2569 lp->l_base = *cp ? cp : savestr(base);
2570 lp->l_attr = list_attributes;
2571 lp->l_level = level+1;
2572 lp->l_delim = list_hierarchy_delimiter;
2573 if (*list && *lend) {
2574 (*lend)->l_next = lp;
2575 *lend = lp;
2576 } else
2577 *list = *lend = lp;
2580 return ok;
2583 static enum okay
2584 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2586 struct list_item *list, *lend, *lp, *lx, *ly;
2587 int n, depth;
2588 const char *bp;
2589 char *cp;
2590 enum okay rv;
2591 NYD_ENTER;
2593 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2594 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2595 goto jleave;
2596 rv = OKAY;
2597 if (list == NULL || lend == NULL)
2598 goto jleave;
2600 for (lp = list; lp; lp = lp->l_next)
2601 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2602 !(lp->l_attr & LIST_NOINFERIORS)) {
2603 cp = salloc((n = strlen(lp->l_name)) + 2);
2604 memcpy(cp, lp->l_name, n);
2605 cp[n] = lp->l_delim;
2606 cp[n+1] = '\0';
2607 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2608 lp->l_has_children = 1;
2609 if (strcmp(cp, lx->l_name) == 0)
2610 lx = lx->l_next;
2611 if (lx) {
2612 lend->l_next = lx;
2613 lend = ly;
2618 for (lp = list; lp; lp = lp->l_next) {
2619 if (strip) {
2620 cp = lp->l_name;
2621 for (bp = base; *bp && *bp == *cp; bp++)
2622 cp++;
2623 } else
2624 cp = lp->l_name;
2625 if (!(lp->l_attr & LIST_NOSELECT))
2626 fprintf(fp, "%s\n", *cp ? cp : base);
2627 else if (lp->l_has_children == 0)
2628 fprintf(fp, "%s%c\n", *cp ? cp : base,
2629 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2631 jleave:
2632 NYD_LEAVE;
2633 return rv;
2636 FL void
2637 imap_folders(const char * volatile name, int strip)
2639 sighandler_type saveint, savepipe;
2640 const char *fold, *cp, *sp;
2641 FILE * volatile fp;
2642 NYD_ENTER;
2644 cp = protbase(name);
2645 sp = mb.mb_imap_account;
2646 if (sp == NULL || strcmp(cp, sp)) {
2647 fprintf(stderr, tr(502,
2648 "Cannot perform `folders' but when on the very IMAP "
2649 "account; the current one is\n `%s' -- "
2650 "try `folders @'.\n"),
2651 (sp != NULL ? sp : tr(503, "[NONE]")));
2652 goto jleave;
2655 fold = imap_fileof(name);
2656 if (options & OPT_TTYOUT) {
2657 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2658 0600)) == NULL) {
2659 perror("tmpfile");
2660 goto jleave;
2662 } else
2663 fp = stdout;
2665 imaplock = 1;
2666 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2667 safe_signal(SIGINT, &_imap_maincatch);
2668 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2669 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2670 goto junroll;
2671 if (savepipe != SIG_IGN)
2672 safe_signal(SIGPIPE, imapcatch);
2674 if (mb.mb_type == MB_CACHE)
2675 cache_list(&mb, fold, strip, fp);
2676 else
2677 imap_list(&mb, fold, strip, fp);
2679 imaplock = 0;
2680 if (interrupts) {
2681 if (options & OPT_TTYOUT)
2682 Fclose(fp);
2683 goto jleave;
2685 fflush(fp);
2687 if (options & OPT_TTYOUT) {
2688 rewind(fp);
2689 if (fsize(fp) > 0)
2690 dopr(fp);
2691 else
2692 fprintf(stderr, "Folder not found.\n");
2694 junroll:
2695 safe_signal(SIGINT, saveint);
2696 safe_signal(SIGPIPE, savepipe);
2697 if (options & OPT_TTYOUT)
2698 Fclose(fp);
2699 jleave:
2700 NYD_LEAVE;
2701 if (interrupts)
2702 onintr(0);
2705 static void
2706 dopr(FILE *fp)
2708 char o[LINESIZE];
2709 int c;
2710 long n = 0, mx = 0, columns, width;
2711 FILE *out;
2712 NYD_ENTER;
2714 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2715 == NULL) {
2716 perror("tmpfile");
2717 goto jleave;
2720 while ((c = getc(fp)) != EOF) {
2721 if (c == '\n') {
2722 if (n > mx)
2723 mx = n;
2724 n = 0;
2725 } else
2726 ++n;
2728 rewind(fp);
2730 width = scrnwidth;
2731 if (mx < width / 2) {
2732 columns = width / (mx+2);
2733 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2734 } else
2735 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2736 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2737 try_pager(out);
2738 Fclose(out);
2739 jleave:
2740 NYD_LEAVE;
2743 static enum okay
2744 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2746 char o[LINESIZE];
2747 const char *qname;
2748 int twice = 0, stored = 0;
2749 FILE *queuefp = NULL;
2750 enum okay ok = STOP;
2751 NYD_X;
2753 if (mp->mb_type == MB_CACHE) {
2754 if ((queuefp = cache_queue(mp)) == NULL)
2755 return STOP;
2756 ok = OKAY;
2758 qname = imap_quotestr(name = imap_fileof(name));
2759 /* Since it is not possible to set flags on the copy, recently
2760 * set flags must be set on the original to include it in the copy */
2761 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2762 imap_store(mp, m, n, '+', "\\Seen", 0);
2763 if (m->m_flag&MFLAG)
2764 imap_store(mp, m, n, '+', "\\Flagged", 0);
2765 if (m->m_flag&MUNFLAG)
2766 imap_store(mp, m, n, '-', "\\Flagged", 0);
2767 if (m->m_flag&MANSWER)
2768 imap_store(mp, m, n, '+', "\\Answered", 0);
2769 if (m->m_flag&MUNANSWER)
2770 imap_store(mp, m, n, '-', "\\Flagged", 0);
2771 if (m->m_flag&MDRAFT)
2772 imap_store(mp, m, n, '+', "\\Draft", 0);
2773 if (m->m_flag&MUNDRAFT)
2774 imap_store(mp, m, n, '-', "\\Draft", 0);
2775 again:
2776 if (m->m_uid)
2777 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2778 else {
2779 if (check_expunged() == STOP)
2780 goto out;
2781 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2783 IMAP_OUT(o, MB_COMD, goto out)
2784 while (mp->mb_active & MB_COMD)
2785 ok = imap_answer(mp, twice);
2787 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2788 response_status == RESPONSE_OK)
2789 imap_copyuid(mp, m, name);
2791 if (response_status == RESPONSE_NO && twice++ == 0) {
2792 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2793 IMAP_OUT(o, MB_COMD, goto out)
2794 while (mp->mb_active & MB_COMD)
2795 ok = imap_answer(mp, 1);
2796 if (ok == OKAY) {
2797 imap_created_mailbox++;
2798 goto again;
2802 if (queuefp != NULL)
2803 Fclose(queuefp);
2805 /* ... and reset the flag to its initial value so that the 'exit'
2806 * command still leaves the message unread */
2807 out:
2808 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2809 imap_store(mp, m, n, '-', "\\Seen", 0);
2810 stored++;
2812 if (m->m_flag & MFLAG) {
2813 imap_store(mp, m, n, '-', "\\Flagged", 0);
2814 stored++;
2816 if (m->m_flag & MUNFLAG) {
2817 imap_store(mp, m, n, '+', "\\Flagged", 0);
2818 stored++;
2820 if (m->m_flag & MANSWER) {
2821 imap_store(mp, m, n, '-', "\\Answered", 0);
2822 stored++;
2824 if (m->m_flag & MUNANSWER) {
2825 imap_store(mp, m, n, '+', "\\Answered", 0);
2826 stored++;
2828 if (m->m_flag & MDRAFT) {
2829 imap_store(mp, m, n, '-', "\\Draft", 0);
2830 stored++;
2832 if (m->m_flag & MUNDRAFT) {
2833 imap_store(mp, m, n, '+', "\\Draft", 0);
2834 stored++;
2836 if (stored) {
2837 mp->mb_active |= MB_COMD;
2838 (void)imap_finish(mp);
2840 return ok;
2843 FL enum okay
2844 imap_copy(struct message *m, int n, const char *name)
2846 sighandler_type saveint, savepipe;
2847 enum okay rv = STOP;
2848 NYD_ENTER;
2850 imaplock = 1;
2851 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2852 safe_signal(SIGINT, &_imap_maincatch);
2853 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2854 if (sigsetjmp(imapjmp, 1) == 0) {
2855 if (savepipe != SIG_IGN)
2856 safe_signal(SIGPIPE, imapcatch);
2858 rv = imap_copy1(&mb, m, n, name);
2860 safe_signal(SIGINT, saveint);
2861 safe_signal(SIGPIPE, savepipe);
2862 imaplock = 0;
2864 NYD_LEAVE;
2865 if (interrupts)
2866 onintr(0);
2867 return rv;
2870 static enum okay
2871 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2872 unsigned long *olduid, unsigned long *newuid)
2874 char *xp, *yp, *zp;
2875 enum okay rv;
2876 NYD_ENTER;
2878 *uidvalidity = strtoul(cp, &xp, 10);
2879 *olduid = strtoul(xp, &yp, 10);
2880 *newuid = strtoul(yp, &zp, 10);
2881 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2882 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2883 NYD_LEAVE;
2884 return rv;
2887 static enum okay
2888 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2889 unsigned long *uid)
2891 char *xp, *yp;
2892 enum okay rv;
2893 NYD_ENTER;
2895 *uidvalidity = strtoul(cp, &xp, 10);
2896 *uid = strtoul(xp, &yp, 10);
2897 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2898 *yp == ']');
2899 NYD_LEAVE;
2900 return rv;
2903 static enum okay
2904 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2906 struct mailbox xmb;
2907 struct message xm;
2908 const char *cp;
2909 unsigned long uidvalidity, olduid, newuid;
2910 enum okay rv;
2911 NYD_ENTER;
2913 rv = STOP;
2914 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2915 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2916 goto jleave;
2917 rv = OKAY;
2919 xmb = *mp;
2920 xmb.mb_cache_directory = NULL;
2921 xmb.mb_imap_mailbox = savestr(name);
2922 xmb.mb_uidvalidity = uidvalidity;
2923 initcache(&xmb);
2924 if (m == NULL) {
2925 memset(&xm, 0, sizeof xm);
2926 xm.m_uid = olduid;
2927 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2928 goto jleave;
2929 getcache(mp, &xm, NEED_HEADER);
2930 getcache(mp, &xm, NEED_BODY);
2931 } else {
2932 if ((m->m_flag & HAVE_HEADER) == 0)
2933 getcache(mp, m, NEED_HEADER);
2934 if ((m->m_flag & HAVE_BODY) == 0)
2935 getcache(mp, m, NEED_BODY);
2936 xm = *m;
2938 xm.m_uid = newuid;
2939 xm.m_flag &= ~MFULLYCACHED;
2940 putcache(&xmb, &xm);
2941 jleave:
2942 NYD_LEAVE;
2943 return rv;
2946 static enum okay
2947 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2948 long size, long lines, int flag, const char *name)
2950 struct mailbox xmb;
2951 struct message xm;
2952 const char *cp;
2953 unsigned long uidvalidity, uid;
2954 enum okay rv;
2955 NYD_ENTER;
2957 rv = STOP;
2958 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2959 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2960 goto jleave;
2961 rv = OKAY;
2963 xmb = *mp;
2964 xmb.mb_cache_directory = NULL;
2965 xmb.mb_imap_mailbox = savestr(name);
2966 xmb.mb_uidvalidity = uidvalidity;
2967 xmb.mb_otf = xmb.mb_itf = fp;
2968 initcache(&xmb);
2969 memset(&xm, 0, sizeof xm);
2970 xm.m_flag = (flag & MREAD) | MNEW;
2971 xm.m_time = t;
2972 xm.m_block = mailx_blockof(off1);
2973 xm.m_offset = mailx_offsetof(off1);
2974 xm.m_size = size;
2975 xm.m_xsize = xsize;
2976 xm.m_lines = xm.m_xlines = lines;
2977 xm.m_uid = uid;
2978 xm.m_have = HAVE_HEADER | HAVE_BODY;
2979 putcache(&xmb, &xm);
2980 jleave:
2981 NYD_LEAVE;
2982 return rv;
2985 static enum okay
2986 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2988 FILE *tp = NULL;
2989 time_t t;
2990 long size, xsize, ysize, lines;
2991 enum mflag flag = MNEW;
2992 char *name, *buf, *bp;
2993 char const *cp;
2994 size_t bufsize, buflen, cnt;
2995 enum okay rv = STOP;
2996 NYD_ENTER;
2998 buf = smalloc(bufsize = LINESIZE);
2999 buflen = 0;
3000 cnt = fsize(fp);
3001 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3002 goto jstop;
3004 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3006 while (*bp == ' ')
3007 ++bp;
3009 if ((cp = strrchr(bp, '{')) == NULL)
3010 goto jstop;
3012 xsize = atol(&cp[1]) + 2;
3013 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3014 goto jstop;
3015 while (*cp == ' ')
3016 cp++;
3018 if (*cp == '(') {
3019 imap_getflags(cp, &cp, &flag);
3020 while (*++cp == ' ')
3023 t = imap_read_date_time(cp);
3025 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3026 NULL)
3027 goto jstop;
3029 size = xsize;
3030 ysize = lines = 0;
3031 while (size > 0) {
3032 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3033 goto jstop;
3034 size -= buflen;
3035 buf[--buflen] = '\0';
3036 buf[buflen-1] = '\n';
3037 fwrite(buf, 1, buflen, tp);
3038 ysize += buflen;
3039 ++lines;
3041 fflush(tp);
3042 rewind(tp);
3044 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3045 imap_unquotestr(name));
3046 rv = OKAY;
3047 jstop:
3048 free(buf);
3049 if (tp)
3050 Fclose(tp);
3051 NYD_LEAVE;
3052 return rv;
3055 #ifdef HAVE_IMAP_SEARCH
3056 static enum okay
3057 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3058 int f)
3060 char *o, *xp, *cs, c;
3061 size_t osize;
3062 FILE *queuefp = NULL;
3063 int i;
3064 unsigned long n;
3065 const char *cp;
3066 enum okay ok = STOP;
3067 NYD_X;
3069 c = 0;
3070 for (cp = spec; *cp; cp++)
3071 c |= *cp;
3072 if (c & 0200) {
3073 cp = charset_get_lc();
3074 # ifdef HAVE_ICONV
3075 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3076 iconv_t it;
3077 char *nsp, *nspec;
3078 size_t sz, nsz;
3080 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3081 sz = strlen(spec) + 1;
3082 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3083 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3084 sz == 0) {
3085 spec = nspec;
3086 cp = "utf-8";
3088 n_iconv_close(it);
3091 # endif
3092 cp = imap_quotestr(cp);
3093 cs = salloc(n = strlen(cp) + 10);
3094 snprintf(cs, n, "CHARSET %s ", cp);
3095 } else
3096 cs = UNCONST("");
3098 o = ac_alloc(osize = strlen(spec) + 60);
3099 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3100 IMAP_OUT(o, MB_COMD, goto out)
3101 while (mp->mb_active & MB_COMD) {
3102 ok = imap_answer(mp, 0);
3103 if (response_status == RESPONSE_OTHER &&
3104 response_other == MAILBOX_DATA_SEARCH) {
3105 xp = responded_other_text;
3106 while (*xp && *xp != '\r') {
3107 n = strtoul(xp, &xp, 10);
3108 for (i = 0; i < cnt; i++)
3109 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3110 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3111 mark(i+1, f);
3115 out:
3116 ac_free(o);
3117 return ok;
3120 FL enum okay
3121 imap_search1(const char * volatile spec, int f)
3123 sighandler_type saveint, savepipe;
3124 enum okay rv = STOP;
3125 NYD_ENTER;
3127 if (mb.mb_type != MB_IMAP)
3128 goto jleave;
3130 imaplock = 1;
3131 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3132 safe_signal(SIGINT, &_imap_maincatch);
3133 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3134 if (sigsetjmp(imapjmp, 1) == 0) {
3135 if (savepipe != SIG_IGN)
3136 safe_signal(SIGPIPE, imapcatch);
3138 rv = imap_search2(&mb, message, msgCount, spec, f);
3140 safe_signal(SIGINT, saveint);
3141 safe_signal(SIGPIPE, savepipe);
3142 imaplock = 0;
3143 jleave:
3144 NYD_LEAVE;
3145 if (interrupts)
3146 onintr(0);
3147 return rv;
3149 #endif /* HAVE_IMAP_SEARCH */
3151 FL int
3152 imap_thisaccount(const char *cp)
3154 int rv;
3155 NYD_ENTER;
3157 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3158 rv = 0;
3159 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3160 mb.mb_imap_account == NULL)
3161 rv = 0;
3162 else
3163 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3164 NYD_LEAVE;
3165 return rv;
3168 FL enum okay
3169 imap_remove(const char * volatile name)
3171 sighandler_type volatile saveint, savepipe;
3172 enum okay rv = STOP;
3173 NYD_ENTER;
3175 if (mb.mb_type != MB_IMAP) {
3176 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3177 name);
3178 goto jleave;
3181 if (!imap_thisaccount(name)) {
3182 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3183 "server: \"%s\" not removed.\n", name);
3184 goto jleave;
3187 imaplock = 1;
3188 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3189 safe_signal(SIGINT, &_imap_maincatch);
3190 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3191 if (sigsetjmp(imapjmp, 1) == 0) {
3192 if (savepipe != SIG_IGN)
3193 safe_signal(SIGPIPE, imapcatch);
3195 rv = imap_remove1(&mb, imap_fileof(name));
3197 safe_signal(SIGINT, saveint);
3198 safe_signal(SIGPIPE, savepipe);
3199 imaplock = 0;
3201 if (rv == OKAY)
3202 rv = cache_remove(name);
3203 jleave:
3204 NYD_LEAVE;
3205 if (interrupts)
3206 onintr(0);
3207 return rv;
3210 static enum okay
3211 imap_remove1(struct mailbox *mp, const char *name)
3213 FILE *queuefp = NULL;
3214 char *o;
3215 int os;
3216 enum okay ok = STOP;
3217 NYD_X;
3219 o = ac_alloc(os = 2*strlen(name) + 100);
3220 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3221 IMAP_OUT(o, MB_COMD, goto out)
3222 while (mp->mb_active & MB_COMD)
3223 ok = imap_answer(mp, 1);
3224 out:
3225 ac_free(o);
3226 return ok;
3229 FL enum okay
3230 imap_rename(const char *old, const char *new)
3232 sighandler_type saveint, savepipe;
3233 enum okay rv = STOP;
3234 NYD_ENTER;
3236 if (mb.mb_type != MB_IMAP) {
3237 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3238 goto jleave;
3241 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3242 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3243 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3244 goto jleave;
3247 imaplock = 1;
3248 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3249 safe_signal(SIGINT, &_imap_maincatch);
3250 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3251 if (sigsetjmp(imapjmp, 1) == 0) {
3252 if (savepipe != SIG_IGN)
3253 safe_signal(SIGPIPE, imapcatch);
3255 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3257 safe_signal(SIGINT, saveint);
3258 safe_signal(SIGPIPE, savepipe);
3259 imaplock = 0;
3261 if (rv == OKAY)
3262 rv = cache_rename(old, new);
3263 jleave:
3264 NYD_LEAVE;
3265 if (interrupts)
3266 onintr(0);
3267 return rv;
3270 static enum okay
3271 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3273 FILE *queuefp = NULL;
3274 char *o;
3275 int os;
3276 enum okay ok = STOP;
3277 NYD_X;
3279 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3280 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3281 imap_quotestr(new));
3282 IMAP_OUT(o, MB_COMD, goto out)
3283 while (mp->mb_active & MB_COMD)
3284 ok = imap_answer(mp, 1);
3285 out:
3286 ac_free(o);
3287 return ok;
3290 FL enum okay
3291 imap_dequeue(struct mailbox *mp, FILE *fp)
3293 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3294 size_t bufsize, buflen, cnt;
3295 long offs, offs1, offs2, octets;
3296 int twice, gotcha = 0;
3297 FILE *queuefp = NULL;
3298 enum okay ok = OKAY, rok = OKAY;
3299 NYD_X;
3301 buf = smalloc(bufsize = LINESIZE);
3302 buflen = 0;
3303 cnt = fsize(fp);
3304 while ((offs1 = ftell(fp)) >= 0 &&
3305 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3306 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3308 while (*bp == ' ')
3309 ++bp;
3310 twice = 0;
3311 if ((offs = ftell(fp)) < 0)
3312 goto fail;
3313 again:
3314 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3315 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3316 cp = &bp[9];
3317 while (digitchar(*cp))
3318 cp++;
3319 if (*cp != ' ')
3320 goto fail;
3321 while (*cp == ' ')
3322 cp++;
3323 if ((newname = imap_strex(cp, NULL)) == NULL)
3324 goto fail;
3325 IMAP_OUT(o, MB_COMD, continue)
3326 while (mp->mb_active & MB_COMD)
3327 ok = imap_answer(mp, twice);
3328 if (response_status == RESPONSE_NO && twice++ == 0)
3329 goto trycreate;
3330 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3331 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3333 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3334 IMAP_OUT(o, MB_COMD, continue)
3335 while (mp->mb_active & MB_COMD)
3336 ok = imap_answer(mp, 1);
3337 if (ok == OKAY)
3338 gotcha++;
3339 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3340 if ((cp = strrchr(bp, '{')) == NULL)
3341 goto fail;
3342 octets = atol(&cp[1]) + 2;
3343 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3344 goto fail;
3345 IMAP_OUT(o, MB_COMD, continue)
3346 while (mp->mb_active & MB_COMD) {
3347 ok = imap_answer(mp, twice);
3348 if (response_type == RESPONSE_CONT)
3349 break;
3351 if (ok == STOP) {
3352 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3353 goto trycreate;
3354 goto fail;
3356 while (octets > 0) {
3357 size_t n = (UICMP(z, octets, >, sizeof iob)
3358 ? sizeof iob : (size_t)octets);
3359 octets -= n;
3360 if (n != fread(iob, 1, n, fp))
3361 goto fail;
3362 swrite1(&mp->mb_sock, iob, n, 1);
3364 swrite(&mp->mb_sock, "");
3365 while (mp->mb_active & MB_COMD) {
3366 ok = imap_answer(mp, 0);
3367 if (response_status == RESPONSE_NO && twice++ == 0) {
3368 if (fseek(fp, offs, SEEK_SET) < 0)
3369 goto fail;
3370 goto trycreate;
3373 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3374 if ((offs2 = ftell(fp)) < 0)
3375 goto fail;
3376 fseek(fp, offs1, SEEK_SET);
3377 if (imap_appenduid_cached(mp, fp) == STOP) {
3378 (void)fseek(fp, offs2, SEEK_SET);
3379 goto fail;
3382 } else {
3383 fail:
3384 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3385 rok = STOP;
3387 continue;
3388 trycreate:
3389 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3390 IMAP_OUT(o, MB_COMD, continue)
3391 while (mp->mb_active & MB_COMD)
3392 ok = imap_answer(mp, 1);
3393 if (ok == OKAY)
3394 goto again;
3396 fflush(fp);
3397 rewind(fp);
3398 ftruncate(fileno(fp), 0);
3399 if (gotcha)
3400 imap_close(mp);
3401 free(buf);
3402 return rok;
3405 static char *
3406 imap_strex(char const *cp, char const **xp)
3408 char const *cq;
3409 char *n = NULL;
3410 NYD_ENTER;
3412 if (*cp != '"')
3413 goto jleave;
3415 for (cq = cp + 1; *cq != '\0'; ++cq) {
3416 if (*cq == '\\')
3417 cq++;
3418 else if (*cq == '"')
3419 break;
3421 if (*cq != '"')
3422 goto jleave;
3424 n = salloc(cq - cp + 2);
3425 memcpy(n, cp, cq - cp +1);
3426 n[cq - cp + 1] = '\0';
3427 if (xp != NULL)
3428 *xp = cq + 1;
3429 jleave:
3430 NYD_LEAVE;
3431 return n;
3434 static enum okay
3435 check_expunged(void)
3437 enum okay rv;
3438 NYD_ENTER;
3440 if (expunged_messages > 0) {
3441 fprintf(stderr, "Command not executed - messages have been expunged\n");
3442 rv = STOP;
3443 } else
3444 rv = OKAY;
3445 NYD_LEAVE;
3446 return rv;
3449 FL int
3450 c_connect(void *vp)
3452 char *cp, *cq;
3453 int rv, omsgCount = msgCount;
3454 NYD_ENTER;
3455 UNUSED(vp);
3457 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3458 fprintf(stderr, "Already connected.\n");
3459 rv = 1;
3460 goto jleave;
3463 var_clear_allow_undefined = TRU1;
3464 ok_bclear(disconnected);
3466 cp = protbase(mailname);
3467 if (strncmp(cp, "imap://", 7) == 0)
3468 cp += 7;
3469 else if (strncmp(cp, "imaps://", 8) == 0)
3470 cp += 8;
3471 if ((cq = strchr(cp, ':')) != NULL)
3472 *cq = '\0';
3474 vok_bclear(savecat("disconnected-", cp));
3475 var_clear_allow_undefined = FAL0;
3477 if (mb.mb_type == MB_CACHE) {
3478 imap_setfile1(mailname, 0, edit, 1);
3479 if (msgCount > omsgCount)
3480 newmailinfo(omsgCount);
3482 rv = 0;
3483 jleave:
3484 NYD_LEAVE;
3485 return rv;
3488 FL int
3489 c_disconnect(void *vp)
3491 int rv = 1, *msgvec = vp;
3492 NYD_ENTER;
3494 if (mb.mb_type == MB_CACHE) {
3495 fprintf(stderr, "Not connected.\n");
3496 goto jleave;
3498 if (mb.mb_type == MB_IMAP) {
3499 if (cached_uidvalidity(&mb) == 0) {
3500 fprintf(stderr, "The current mailbox is not cached.\n");
3501 goto jleave;
3505 if (*msgvec)
3506 c_cache(vp);
3507 ok_bset(disconnected, TRU1);
3508 if (mb.mb_type == MB_IMAP) {
3509 sclose(&mb.mb_sock);
3510 imap_setfile1(mailname, 0, edit, 1);
3512 rv = 0;
3513 jleave:
3514 NYD_LEAVE;
3515 return rv;
3518 FL int
3519 c_cache(void *vp)
3521 int rv = 1, *msgvec = vp, *ip;
3522 struct message *mp;
3523 NYD_ENTER;
3525 if (mb.mb_type != MB_IMAP) {
3526 fprintf(stderr, "Not connected to an IMAP server.\n");
3527 goto jleave;
3529 if (cached_uidvalidity(&mb) == 0) {
3530 fprintf(stderr, "The current mailbox is not cached.\n");
3531 goto jleave;
3534 for (ip = msgvec; *ip; ++ip) {
3535 mp = &message[*ip - 1];
3536 if (!(mp->m_have & HAVE_BODY))
3537 get_body(mp);
3539 rv = 0;
3540 jleave:
3541 NYD_LEAVE;
3542 return rv;
3545 FL int
3546 disconnected(const char *file)
3548 char *cp, *cq, *vp;
3549 int vs, r = 1;
3550 NYD_ENTER;
3552 if (ok_blook(disconnected))
3553 goto jleave;
3555 cp = protbase(file);
3556 if (strncmp(cp, "imap://", 7) == 0)
3557 cp += 7;
3558 else if (strncmp(cp, "imaps://", 8) == 0)
3559 cp += 8;
3560 else {
3561 r = 0;
3562 goto jleave;
3565 if ((cq = strchr(cp, ':')) != NULL)
3566 *cq = '\0';
3567 vp = ac_alloc(vs = strlen(cp) + 14);
3568 snprintf(vp, vs, "disconnected-%s", cp);
3569 r = vok_blook(vp);
3570 ac_free(vp);
3571 jleave:
3572 NYD_LEAVE;
3573 return r;
3576 FL void
3577 transflags(struct message *omessage, long omsgCount, int transparent)
3579 struct message *omp, *nmp, *newdot, *newprevdot;
3580 int hf;
3581 NYD_ENTER;
3583 omp = omessage;
3584 nmp = message;
3585 newdot = message;
3586 newprevdot = NULL;
3587 while (PTRCMP(omp, <, omessage + omsgCount) &&
3588 PTRCMP(nmp, <, message + msgCount)) {
3589 if (dot && nmp->m_uid == dot->m_uid)
3590 newdot = nmp;
3591 if (prevdot && nmp->m_uid == prevdot->m_uid)
3592 newprevdot = nmp;
3593 if (omp->m_uid == nmp->m_uid) {
3594 hf = nmp->m_flag & MHIDDEN;
3595 if (transparent && mb.mb_type == MB_IMAP)
3596 omp->m_flag &= ~MHIDDEN;
3597 *nmp++ = *omp++;
3598 if (transparent && mb.mb_type == MB_CACHE)
3599 nmp[-1].m_flag |= hf;
3600 } else if (omp->m_uid < nmp->m_uid)
3601 ++omp;
3602 else
3603 ++nmp;
3605 dot = newdot;
3606 setdot(newdot);
3607 prevdot = newprevdot;
3608 free(omessage);
3609 NYD_LEAVE;
3612 FL time_t
3613 imap_read_date_time(const char *cp)
3615 char buf[3];
3616 time_t t;
3617 int i, year, month, day, hour, minute, second, sign = -1;
3618 NYD_ENTER;
3620 /* "25-Jul-2004 15:33:44 +0200"
3621 * | | | | | |
3622 * 0 5 10 15 20 25 */
3623 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3624 goto jinvalid;
3625 day = strtol(&cp[1], NULL, 10);
3626 for (i = 0;;) {
3627 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3628 break;
3629 if (month_names[++i][0] == '\0')
3630 goto jinvalid;
3632 month = i + 1;
3633 year = strtol(&cp[8], NULL, 10);
3634 hour = strtol(&cp[13], NULL, 10);
3635 minute = strtol(&cp[16], NULL, 10);
3636 second = strtol(&cp[19], NULL, 10);
3637 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3638 goto jinvalid;
3639 switch (cp[22]) {
3640 case '-':
3641 sign = 1;
3642 break;
3643 case '+':
3644 break;
3645 default:
3646 goto jinvalid;
3648 buf[2] = '\0';
3649 buf[0] = cp[23];
3650 buf[1] = cp[24];
3651 t += strtol(buf, NULL, 10) * sign * 3600;
3652 buf[0] = cp[25];
3653 buf[1] = cp[26];
3654 t += strtol(buf, NULL, 10) * sign * 60;
3655 jleave:
3656 NYD_LEAVE;
3657 return t;
3658 jinvalid:
3659 time(&t);
3660 goto jleave;
3663 FL const char *
3664 imap_make_date_time(time_t t)
3666 static char s[30];
3667 struct tm *tmptr;
3668 int tzdiff, tzdiff_hour, tzdiff_min;
3669 NYD_ENTER;
3671 tzdiff = t - mktime(gmtime(&t));
3672 tzdiff_hour = (int)(tzdiff / 60);
3673 tzdiff_min = tzdiff_hour % 60;
3674 tzdiff_hour /= 60;
3675 tmptr = localtime(&t);
3676 if (tmptr->tm_isdst > 0)
3677 tzdiff_hour++;
3678 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3679 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3680 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3681 NYD_LEAVE;
3682 return s;
3684 #endif /* HAVE_IMAP */
3686 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3687 FL char *
3688 imap_quotestr(char const *s)
3690 char *n, *np;
3691 NYD_ENTER;
3693 np = n = salloc(2 * strlen(s) + 3);
3694 *np++ = '"';
3695 while (*s) {
3696 if (*s == '"' || *s == '\\')
3697 *np++ = '\\';
3698 *np++ = *s++;
3700 *np++ = '"';
3701 *np = '\0';
3702 NYD_LEAVE;
3703 return n;
3706 FL char *
3707 imap_unquotestr(char const *s)
3709 char *n, *np;
3710 NYD_ENTER;
3712 if (*s != '"') {
3713 n = savestr(s);
3714 goto jleave;
3717 np = n = salloc(strlen(s) + 1);
3718 while (*++s) {
3719 if (*s == '\\')
3720 s++;
3721 else if (*s == '"')
3722 break;
3723 *np++ = *s;
3725 *np = '\0';
3726 jleave:
3727 NYD_LEAVE;
3728 return n;
3730 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3732 /* vim:set fenc=utf-8:s-it-mode */