Merge branch 'topic/unibidi'
[s-mailx.git] / imap.c
blob8100278520f3f9fc64e2e2a6d4a3b00998cf0ef5
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_VERBVERB)\
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, struct ccred *ccred);
191 #ifdef HAVE_MD5
192 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
193 #endif
194 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
195 #ifdef HAVE_GSSAPI
196 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
197 #endif
198 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
199 static void imap_init(struct mailbox *mp, int n);
200 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
201 int *prevcount);
202 static int _imap_setfile1(struct url *urlp, int nmail, int isedit,
203 int transparent);
204 static int imap_fetchdata(struct mailbox *mp, struct message *m,
205 size_t expected, int need, const char *head,
206 size_t headsize, long headlines);
207 static void imap_putstr(struct mailbox *mp, struct message *m,
208 const char *str, const char *head, size_t headsize,
209 long headlines);
210 static enum okay imap_get(struct mailbox *mp, struct message *m,
211 enum needspec need);
212 static void commitmsg(struct mailbox *mp, struct message *to,
213 struct message *from, enum havespec have);
214 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
215 int bot, int top);
216 static enum okay imap_exit(struct mailbox *mp);
217 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
218 int needstat);
219 static enum okay imap_close(struct mailbox *mp);
220 static enum okay imap_update(struct mailbox *mp);
221 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
222 int c, const char *sp, int needstat);
223 static enum okay imap_unstore(struct message *m, int n, const char *flag);
224 static const char *tag(int new);
225 static char * imap_putflags(int f);
226 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
227 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
228 off_t off1, long xsize, enum mflag flag, time_t t);
229 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
230 static enum okay imap_list1(struct mailbox *mp, const char *base,
231 struct list_item **list, struct list_item **lend,
232 int level);
233 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
234 FILE *fp);
235 static void dopr(FILE *fp);
236 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
237 const char *name);
238 static enum okay imap_copyuid_parse(const char *cp,
239 unsigned long *uidvalidity, unsigned long *olduid,
240 unsigned long *newuid);
241 static enum okay imap_appenduid_parse(const char *cp,
242 unsigned long *uidvalidity, unsigned long *uid);
243 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
244 const char *name);
245 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
246 long off1, long xsize, long size, long lines, int flag,
247 const char *name);
248 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
249 #ifdef HAVE_IMAP_SEARCH
250 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
251 const char *spec, int f);
252 #endif
253 static enum okay imap_remove1(struct mailbox *mp, const char *name);
254 static enum okay imap_rename1(struct mailbox *mp, const char *old,
255 const char *new);
256 static char * imap_strex(char const *cp, char const **xp);
257 static enum okay check_expunged(void);
259 static void
260 imap_other_get(char *pp)
262 char *xp;
263 NYD_ENTER;
265 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
266 pp += 6;
267 response_other = MAILBOX_DATA_FLAGS;
268 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
269 pp += 5;
270 response_other = MAILBOX_DATA_LIST;
271 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
272 pp += 5;
273 response_other = MAILBOX_DATA_LSUB;
274 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
275 pp += 8;
276 response_other = MAILBOX_DATA_MAILBOX;
277 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
278 pp += 7;
279 response_other = MAILBOX_DATA_SEARCH;
280 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
281 pp += 7;
282 response_other = MAILBOX_DATA_STATUS;
283 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
284 pp += 11;
285 response_other = CAPABILITY_DATA;
286 } else {
287 responded_other_number = strtol(pp, &xp, 10);
288 while (*xp == ' ')
289 ++xp;
290 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
291 response_other = MAILBOX_DATA_EXISTS;
292 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
293 response_other = MAILBOX_DATA_RECENT;
294 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
295 response_other = MESSAGE_DATA_EXPUNGE;
296 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
297 pp = &xp[6];
298 response_other = MESSAGE_DATA_FETCH;
299 } else
300 response_other = RESPONSE_OTHER_UNKNOWN;
302 responded_other_text = pp;
303 NYD_LEAVE;
306 static void
307 imap_response_get(const char **cp)
309 NYD_ENTER;
310 if (ascncasecmp(*cp, "OK ", 3) == 0) {
311 *cp += 3;
312 response_status = RESPONSE_OK;
313 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
314 *cp += 3;
315 response_status = RESPONSE_NO;
316 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
317 *cp += 4;
318 response_status = RESPONSE_BAD;
319 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
320 *cp += 8;
321 response_status = RESPONSE_PREAUTH;
322 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
323 *cp += 4;
324 response_status = RESPONSE_BYE;
325 } else
326 response_status = RESPONSE_OTHER;
327 NYD_LEAVE;
330 static void
331 imap_response_parse(void)
333 static char *parsebuf; /* TODO Use pool */
334 static size_t parsebufsize;
336 const char *ip = imapbuf;
337 char *pp;
338 NYD_ENTER;
340 if (parsebufsize < imapbufsize)
341 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
342 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
343 pp = parsebuf;
344 switch (*ip) {
345 case '+':
346 response_type = RESPONSE_CONT;
347 ip++;
348 pp++;
349 while (*ip == ' ') {
350 ip++;
351 pp++;
353 break;
354 case '*':
355 ip++;
356 pp++;
357 while (*ip == ' ') {
358 ip++;
359 pp++;
361 imap_response_get(&ip);
362 pp = &parsebuf[ip - imapbuf];
363 switch (response_status) {
364 case RESPONSE_BYE:
365 response_type = RESPONSE_FATAL;
366 break;
367 default:
368 response_type = RESPONSE_DATA;
370 break;
371 default:
372 responded_tag = parsebuf;
373 while (*pp && *pp != ' ')
374 pp++;
375 if (*pp == '\0') {
376 response_type = RESPONSE_ILLEGAL;
377 break;
379 *pp++ = '\0';
380 while (*pp && *pp == ' ')
381 pp++;
382 if (*pp == '\0') {
383 response_type = RESPONSE_ILLEGAL;
384 break;
386 ip = &imapbuf[pp - parsebuf];
387 response_type = RESPONSE_TAGGED;
388 imap_response_get(&ip);
389 pp = &parsebuf[ip - imapbuf];
391 responded_text = pp;
392 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
393 response_status == RESPONSE_OTHER)
394 imap_other_get(pp);
395 NYD_LEAVE;
398 static enum okay
399 imap_answer(struct mailbox *mp, int errprnt)
401 int i, complete;
402 enum okay rv;
403 NYD_ENTER;
405 rv = OKAY;
406 if (mp->mb_type == MB_CACHE)
407 goto jleave;
408 rv = STOP;
409 jagain:
410 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
411 if (options & OPT_VERBVERB)
412 fputs(imapbuf, stderr);
413 imap_response_parse();
414 if (response_type == RESPONSE_ILLEGAL)
415 goto jagain;
416 if (response_type == RESPONSE_CONT) {
417 rv = OKAY;
418 goto jleave;
420 if (response_status == RESPONSE_OTHER) {
421 if (response_other == MAILBOX_DATA_EXISTS) {
422 had_exists = responded_other_number;
423 rec_queue(REC_EXISTS, responded_other_number);
424 if (had_expunge > 0)
425 had_expunge = 0;
426 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
427 rec_queue(REC_EXPUNGE, responded_other_number);
428 if (had_expunge < 0)
429 had_expunge = 0;
430 had_expunge++;
431 expunged_messages++;
434 complete = 0;
435 if (response_type == RESPONSE_TAGGED) {
436 if (asccasecmp(responded_tag, tag(0)) == 0)
437 complete |= 1;
438 else
439 goto jagain;
441 switch (response_status) {
442 case RESPONSE_PREAUTH:
443 mp->mb_active &= ~MB_PREAUTH;
444 /*FALLTHRU*/
445 case RESPONSE_OK:
446 jokay:
447 rv = OKAY;
448 complete |= 2;
449 break;
450 case RESPONSE_NO:
451 case RESPONSE_BAD:
452 jstop:
453 rv = STOP;
454 complete |= 2;
455 if (errprnt)
456 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
457 break;
458 case RESPONSE_UNKNOWN: /* does not happen */
459 case RESPONSE_BYE:
460 i = mp->mb_active;
461 mp->mb_active = MB_NONE;
462 if (i & MB_BYE)
463 goto jokay;
464 goto jstop;
465 case RESPONSE_OTHER:
466 rv = OKAY;
467 break;
469 if (response_status != RESPONSE_OTHER &&
470 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
471 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
472 if (complete == 3)
473 mp->mb_active &= ~MB_COMD;
474 } else {
475 rv = STOP;
476 mp->mb_active = MB_NONE;
478 jleave:
479 NYD_LEAVE;
480 return rv;
483 static enum okay
484 imap_parse_list(void)
486 char *cp;
487 enum okay rv;
488 NYD_ENTER;
490 rv = STOP;
492 cp = responded_other_text;
493 list_attributes = LIST_NONE;
494 if (*cp == '(') {
495 while (*cp && *cp != ')') {
496 if (*cp == '\\') {
497 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
498 list_attributes |= LIST_NOINFERIORS;
499 cp += 12;
500 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
501 list_attributes |= LIST_NOSELECT;
502 cp += 9;
503 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
504 list_attributes |= LIST_MARKED;
505 cp += 7;
506 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
507 list_attributes |= LIST_UNMARKED;
508 cp += 9;
511 cp++;
513 if (*++cp != ' ')
514 goto jleave;
515 while (*cp == ' ')
516 cp++;
519 list_hierarchy_delimiter = EOF;
520 if (*cp == '"') {
521 if (*++cp == '\\')
522 cp++;
523 list_hierarchy_delimiter = *cp++ & 0377;
524 if (cp[0] != '"' || cp[1] != ' ')
525 goto jleave;
526 cp++;
527 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
528 list_hierarchy_delimiter = EOF;
529 cp += 3;
532 while (*cp == ' ')
533 cp++;
534 list_name = cp;
535 while (*cp && *cp != '\r')
536 cp++;
537 *cp = '\0';
538 rv = OKAY;
539 jleave:
540 NYD_LEAVE;
541 return rv;
544 static enum okay
545 imap_finish(struct mailbox *mp)
547 NYD_ENTER;
548 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
549 imap_answer(mp, 1);
550 NYD_LEAVE;
551 return OKAY;
554 static void
555 imap_timer_off(void)
557 NYD_ENTER;
558 if (imapkeepalive > 0) {
559 alarm(0);
560 safe_signal(SIGALRM, savealrm);
562 NYD_LEAVE;
565 static void
566 imapcatch(int s)
568 NYD_X; /* Signal handler */
569 switch (s) {
570 case SIGINT:
571 fprintf(stderr, tr(102, "Interrupt\n"));
572 siglongjmp(imapjmp, 1);
573 /*NOTREACHED*/
574 case SIGPIPE:
575 fprintf(stderr, tr(98, "Received SIGPIPE during IMAP operation\n"));
576 break;
580 static void
581 _imap_maincatch(int s)
583 NYD_X; /* Signal handler */
584 UNUSED(s);
585 if (interrupts++ == 0) {
586 fprintf(stderr, tr(102, "Interrupt\n"));
587 return;
589 onintr(0);
592 static enum okay
593 imap_noop1(struct mailbox *mp)
595 char o[LINESIZE];
596 FILE *queuefp = NULL;
597 NYD_X;
599 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
600 IMAP_OUT(o, MB_COMD, return STOP)
601 IMAP_ANSWER()
602 return OKAY;
605 FL char const *
606 imap_fileof(char const *xcp)
608 char const *cp = xcp;
609 int state = 0;
610 NYD_ENTER;
612 while (*cp) {
613 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
614 cp += 3;
615 state = 1;
617 if (cp[0] == '/' && state == 1) {
618 ++cp;
619 goto jleave;
621 if (cp[0] == '/') {
622 cp = xcp;
623 goto jleave;
625 ++cp;
627 jleave:
628 NYD_LEAVE;
629 return cp;
632 FL enum okay
633 imap_noop(void)
635 sighandler_type volatile oldint, oldpipe;
636 enum okay rv = STOP;
637 NYD_ENTER;
639 if (mb.mb_type != MB_IMAP)
640 goto jleave;
642 imaplock = 1;
643 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
644 safe_signal(SIGINT, &_imap_maincatch);
645 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
646 if (sigsetjmp(imapjmp, 1) == 0) {
647 if (oldpipe != SIG_IGN)
648 safe_signal(SIGPIPE, imapcatch);
650 rv = imap_noop1(&mb);
652 safe_signal(SIGINT, oldint);
653 safe_signal(SIGPIPE, oldpipe);
654 imaplock = 0;
655 jleave:
656 NYD_LEAVE;
657 if (interrupts)
658 onintr(0);
659 return rv;
662 static void
663 rec_queue(enum rec_type rt, unsigned long cnt)
665 struct record *rp;
666 NYD_ENTER;
668 rp = scalloc(1, sizeof *rp);
669 rp->rec_type = rt;
670 rp->rec_count = cnt;
671 if (record && recend) {
672 recend->rec_next = rp;
673 recend = rp;
674 } else
675 record = recend = rp;
676 NYD_LEAVE;
679 static enum okay
680 rec_dequeue(void)
682 struct message *omessage;
683 struct record *rp, *rq;
684 uiz_t exists = 0, i;
685 enum okay rv = STOP;
686 NYD_ENTER;
688 if (record == NULL)
689 goto jleave;
691 omessage = message;
692 message = smalloc((msgCount+1) * sizeof *message);
693 if (msgCount)
694 memcpy(message, omessage, msgCount * sizeof *message);
695 memset(&message[msgCount], 0, sizeof *message);
697 rp = record, rq = NULL;
698 rv = OKAY;
699 while (rp != NULL) {
700 switch (rp->rec_type) {
701 case REC_EXISTS:
702 exists = rp->rec_count;
703 break;
704 case REC_EXPUNGE:
705 if (rp->rec_count == 0) {
706 rv = STOP;
707 break;
709 if (rp->rec_count > (unsigned long)msgCount) {
710 if (exists == 0 || rp->rec_count > exists--)
711 rv = STOP;
712 break;
714 if (exists > 0)
715 exists--;
716 delcache(&mb, &message[rp->rec_count-1]);
717 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
718 ((msgCount - rp->rec_count + 1) * sizeof *message));
719 --msgCount;
720 /* If the message was part of a collapsed thread,
721 * the m_collapsed field of one of its ancestors
722 * should be incremented. It seems hardly possible
723 * to do this with the current message structure,
724 * though. The result is that a '+' may be shown
725 * in the header summary even if no collapsed
726 * children exists */
727 break;
729 if (rq != NULL)
730 free(rq);
731 rq = rp;
732 rp = rp->rec_next;
734 if (rq != NULL)
735 free(rq);
737 record = recend = NULL;
738 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
739 message = srealloc(message, (exists + 1) * sizeof *message);
740 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
741 for (i = msgCount; i < exists; ++i)
742 imap_init(&mb, i);
743 imap_flags(&mb, msgCount+1, exists);
744 msgCount = exists;
747 if (rv == STOP) {
748 free(message);
749 message = omessage;
751 jleave:
752 NYD_LEAVE;
753 return rv;
756 static void
757 rec_rmqueue(void)
759 struct record *rp;
760 NYD_ENTER;
762 for (rp = record; rp != NULL;) {
763 struct record *tmp = rp;
764 rp = rp->rec_next;
765 free(tmp);
767 record = recend = NULL;
768 NYD_LEAVE;
771 /*ARGSUSED*/
772 static void
773 imapalarm(int s)
775 sighandler_type volatile saveint, savepipe;
776 NYD_X; /* Signal handler */
777 UNUSED(s);
779 if (imaplock++ == 0) {
780 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
781 safe_signal(SIGINT, &_imap_maincatch);
782 savepipe = safe_signal(SIGPIPE, SIG_IGN);
783 if (sigsetjmp(imapjmp, 1)) {
784 safe_signal(SIGINT, saveint);
785 safe_signal(SIGPIPE, savepipe);
786 goto jbrk;
788 if (savepipe != SIG_IGN)
789 safe_signal(SIGPIPE, imapcatch);
790 if (imap_noop1(&mb) != OKAY) {
791 safe_signal(SIGINT, saveint);
792 safe_signal(SIGPIPE, savepipe);
793 goto jleave;
795 safe_signal(SIGINT, saveint);
796 safe_signal(SIGPIPE, savepipe);
798 jbrk:
799 alarm(imapkeepalive);
800 jleave:
801 --imaplock;
804 static int
805 imap_use_starttls(const char *uhp)
807 int rv;
808 NYD_ENTER;
810 if (ok_blook(imap_use_starttls))
811 rv = 1;
812 else {
813 char *var = savecat("imap-use-starttls-", uhp);
814 rv = vok_blook(var);
816 NYD_LEAVE;
817 return rv;
820 static enum okay
821 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
823 char *cp;
824 NYD_X;
826 mp->mb_active |= MB_PREAUTH;
827 imap_answer(mp, 1);
828 if ((cp = strchr(xserver, ':')) != NULL) {
829 char *x = salloc(cp - xserver + 1);
830 memcpy(x, xserver, cp - xserver);
831 x[cp - xserver] = '\0';
832 xserver = x;
834 #ifdef HAVE_SSL
835 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
836 FILE *queuefp = NULL;
837 char o[LINESIZE];
839 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
840 IMAP_OUT(o, MB_COMD, return STOP)
841 IMAP_ANSWER()
842 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
843 return STOP;
845 #else
846 if (imap_use_starttls(uhp)) {
847 fprintf(stderr, "No SSL support compiled in.\n");
848 return STOP;
850 #endif
851 imap_capability(mp);
852 return OKAY;
855 static enum okay
856 imap_capability(struct mailbox *mp)
858 char o[LINESIZE];
859 FILE *queuefp = NULL;
860 enum okay ok = STOP;
861 const char *cp;
862 NYD_X;
864 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
865 IMAP_OUT(o, MB_COMD, return STOP)
866 while (mp->mb_active & MB_COMD) {
867 ok = imap_answer(mp, 0);
868 if (response_status == RESPONSE_OTHER &&
869 response_other == CAPABILITY_DATA) {
870 cp = responded_other_text;
871 while (*cp) {
872 while (spacechar(*cp))
873 ++cp;
874 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
875 /* RFC 2359 */
876 mp->mb_flags |= MB_UIDPLUS;
877 while (*cp && !spacechar(*cp))
878 ++cp;
882 return ok;
885 static enum okay
886 imap_auth(struct mailbox *mp, struct ccred *ccred)
888 enum okay rv;
889 NYD_ENTER;
891 if (!(mp->mb_active & MB_PREAUTH)) {
892 rv = OKAY;
893 goto jleave;
896 switch (ccred->cc_authtype) {
897 case AUTHTYPE_LOGIN:
898 rv = imap_login(mp, ccred);
899 break;
900 #ifdef HAVE_MD5
901 case AUTHTYPE_CRAM_MD5:
902 rv = imap_cram_md5(mp, ccred);
903 break;
904 #endif
905 #ifdef HAVE_GSSAPI
906 case AUTHTYPE_GSSAPI:
907 rv = _imap_gssapi(mp, ccred);
908 break;
909 #endif
910 default:
911 rv = STOP;
912 break;
914 jleave:
915 NYD_LEAVE;
916 return rv;
919 /* Implementation of RFC 2194 */
920 #ifdef HAVE_MD5
921 static enum okay
922 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
924 char o[LINESIZE], *cp;
925 FILE *queuefp = NULL;
926 enum okay rv = STOP;
927 NYD_ENTER;
929 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
930 IMAP_XOUT(o, 0, goto jleave, goto jleave);
931 imap_answer(mp, 1);
932 if (response_type != RESPONSE_CONT)
933 goto jleave;
935 cp = cram_md5_string(ccred->cc_user.s, ccred->cc_pass.s, responded_text);
936 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
937 while (mp->mb_active & MB_COMD)
938 rv = imap_answer(mp, 1);
939 jleave:
940 NYD_LEAVE;
941 return rv;
943 #endif /* HAVE_MD5 */
945 static enum okay
946 imap_login(struct mailbox *mp, struct ccred *ccred)
948 char o[LINESIZE];
949 FILE *queuefp = NULL;
950 enum okay rv = STOP;
951 NYD_ENTER;
953 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
954 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
955 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
956 while (mp->mb_active & MB_COMD)
957 rv = imap_answer(mp, 1);
958 jleave:
959 NYD_LEAVE;
960 return rv;
963 #ifdef HAVE_GSSAPI
964 # include "imap_gssapi.h"
965 #endif
967 FL enum okay
968 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
970 enum okay ok = OKAY;
971 char const *cp;
972 char o[LINESIZE];
973 FILE *queuefp = NULL;
974 NYD_X;
975 UNUSED(size);
977 mp->mb_uidvalidity = 0;
978 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
979 IMAP_OUT(o, MB_COMD, return STOP)
980 while (mp->mb_active & MB_COMD) {
981 ok = imap_answer(mp, 1);
982 if (response_status != RESPONSE_OTHER &&
983 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
984 mp->mb_uidvalidity = atol(&cp[13]);
986 *cnt = (had_exists > 0) ? had_exists : 0;
987 if (response_status != RESPONSE_OTHER &&
988 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
989 mp->mb_perm = 0;
990 return ok;
993 static enum okay
994 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
996 char o[LINESIZE];
997 FILE *queuefp = NULL;
998 char const *cp;
999 struct message *m;
1000 unsigned x = X, y = Y, n;
1001 NYD_X;
1003 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1004 IMAP_OUT(o, MB_COMD, return STOP)
1005 while (mp->mb_active & MB_COMD) {
1006 imap_answer(mp, 1);
1007 if (response_status == RESPONSE_OTHER &&
1008 response_other == MESSAGE_DATA_FETCH) {
1009 n = responded_other_number;
1010 if (n < x || n > y)
1011 continue;
1012 m = &message[n-1];
1013 m->m_xsize = 0;
1014 } else
1015 continue;
1017 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1018 cp += 5;
1019 while (*cp == ' ')
1020 cp++;
1021 if (*cp == '(')
1022 imap_getflags(cp, &cp, &m->m_flag);
1025 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1026 m->m_uid = strtoul(&cp[4], NULL, 10);
1027 getcache1(mp, m, NEED_UNSPEC, 1);
1028 m->m_flag &= ~MHIDDEN;
1031 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1032 x++;
1033 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1034 y--;
1035 if (x <= y) {
1036 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1037 tag(1), x, y);
1038 IMAP_OUT(o, MB_COMD, return STOP)
1039 while (mp->mb_active & MB_COMD) {
1040 imap_answer(mp, 1);
1041 if (response_status == RESPONSE_OTHER &&
1042 response_other == MESSAGE_DATA_FETCH) {
1043 n = responded_other_number;
1044 if (n < x || n > y)
1045 continue;
1046 m = &message[n-1];
1047 } else
1048 continue;
1049 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1050 m->m_xsize = strtol(&cp[12], NULL, 10);
1051 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1052 m->m_time = imap_read_date_time(&cp[13]);
1056 for (n = X; n <= Y; n++)
1057 putcache(mp, &message[n-1]);
1058 return OKAY;
1061 static void
1062 imap_init(struct mailbox *mp, int n)
1064 struct message *m;
1065 NYD_ENTER;
1066 UNUSED(mp);
1068 m = message + n;
1069 m->m_flag = MUSED | MNOFROM;
1070 m->m_block = 0;
1071 m->m_offset = 0;
1072 NYD_LEAVE;
1075 static void
1076 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1078 struct message *omessage = 0;
1079 int i, omsgCount = 0;
1080 enum okay dequeued = STOP;
1081 NYD_ENTER;
1083 if (nmail || transparent) {
1084 omessage = message;
1085 omsgCount = msgCount;
1087 if (nmail)
1088 dequeued = rec_dequeue();
1090 if (had_exists >= 0) {
1091 if (dequeued != OKAY)
1092 msgCount = had_exists;
1093 had_exists = -1;
1095 if (had_expunge >= 0) {
1096 if (dequeued != OKAY)
1097 msgCount -= had_expunge;
1098 had_expunge = -1;
1101 if (nmail && expunged_messages)
1102 printf("Expunged %ld message%s.\n", expunged_messages,
1103 (expunged_messages != 1 ? "s" : ""));
1104 *prevcount = omsgCount - expunged_messages;
1105 expunged_messages = 0;
1106 if (msgCount < 0) {
1107 fputs("IMAP error: Negative message count\n", stderr);
1108 msgCount = 0;
1111 if (dequeued != OKAY) {
1112 message = scalloc(msgCount + 1, sizeof *message);
1113 for (i = 0; i < msgCount; i++)
1114 imap_init(mp, i);
1115 if (!nmail && mp->mb_type == MB_IMAP)
1116 initcache(mp);
1117 if (msgCount > 0)
1118 imap_flags(mp, 1, msgCount);
1119 message[msgCount].m_size = 0;
1120 message[msgCount].m_lines = 0;
1121 rec_rmqueue();
1123 if (nmail || transparent)
1124 transflags(omessage, omsgCount, transparent);
1125 else
1126 setdot(message);
1127 NYD_LEAVE;
1130 FL int
1131 imap_setfile(const char *xserver, int nmail, int isedit)
1133 struct url url;
1134 int rv;
1135 NYD_ENTER;
1137 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1138 rv = 1;
1139 goto jleave;
1141 if (!ok_blook(v15_compat) &&
1142 (!url.url_had_user || url.url_pass.s != NULL))
1143 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
1145 rv = _imap_setfile1(&url, nmail, isedit, 0);
1146 jleave:
1147 NYD_LEAVE;
1148 return rv;
1151 static int
1152 _imap_setfile1(struct url *urlp, int nmail, int isedit,
1153 int volatile transparent)
1155 struct sock so;
1156 struct ccred ccred;
1157 sighandler_type volatile saveint, savepipe;
1158 char const *cp;
1159 int rv;
1160 int volatile prevcount = 0;
1161 enum mbflags same_flags;
1162 NYD_ENTER;
1164 if (nmail) {
1165 saveint = safe_signal(SIGINT, SIG_IGN);
1166 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1167 if (saveint != SIG_IGN)
1168 safe_signal(SIGINT, imapcatch);
1169 if (savepipe != SIG_IGN)
1170 safe_signal(SIGPIPE, imapcatch);
1171 imaplock = 1;
1172 goto jnmail;
1175 same_flags = mb.mb_flags;
1176 same_imap_account = 0;
1177 if (mb.mb_imap_account != NULL &&
1178 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1179 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1180 !strcmp(mb.mb_imap_account, urlp->url_puhp) &&
1181 disconnected(mb.mb_imap_account) == 0) {
1182 same_imap_account = 1;
1183 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1184 urlp->url_pass.s = mb.mb_imap_pass;
1185 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1186 !strcmp(mb.mb_imap_account, urlp->url_puhp) &&
1187 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1188 urlp->url_pass.s = mb.mb_imap_pass;
1191 if (!(ok_blook(v15_compat) ? ccred_lookup(&ccred, urlp)
1192 : ccred_lookup_old(&ccred, CPROTO_IMAP, urlp->url_uhp.s))) {
1193 rv = -1;
1194 goto jleave;
1197 so.s_fd = -1;
1198 if (!same_imap_account) {
1199 if (!disconnected(urlp->url_puhp) && !sopen(&so, urlp)) {
1200 rv = -1;
1201 goto jleave;
1203 } else
1204 so = mb.mb_sock;
1205 if (!transparent)
1206 quit();
1208 edit = (isedit != 0);
1209 if (mb.mb_imap_account != NULL)
1210 free(mb.mb_imap_account);
1211 if (mb.mb_imap_pass != NULL)
1212 free(mb.mb_imap_pass);
1213 mb.mb_imap_account = sstrdup(urlp->url_puhp);
1214 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1215 * TODO object, and mailbox will naturally have an URL and credentials */
1216 mb.mb_imap_pass = sstrdup(ccred.cc_pass.s);
1218 if (!same_imap_account) {
1219 if (mb.mb_sock.s_fd >= 0)
1220 sclose(&mb.mb_sock);
1222 same_imap_account = 0;
1224 if (!transparent) {
1225 if (mb.mb_itf) {
1226 fclose(mb.mb_itf);
1227 mb.mb_itf = NULL;
1229 if (mb.mb_otf) {
1230 fclose(mb.mb_otf);
1231 mb.mb_otf = NULL;
1233 if (mb.mb_imap_mailbox != NULL)
1234 free(mb.mb_imap_mailbox);
1235 mb.mb_imap_mailbox = sstrdup((urlp->url_path.s != NULL)
1236 ? urlp->url_path.s : "INBOX");
1237 initbox(urlp->url_puhpp);
1239 mb.mb_type = MB_VOID;
1240 mb.mb_active = MB_NONE;
1242 imaplock = 1;
1243 saveint = safe_signal(SIGINT, SIG_IGN);
1244 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1245 if (sigsetjmp(imapjmp, 1)) {
1246 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1247 sclose(&mb.mb_sock);
1248 safe_signal(SIGINT, saveint);
1249 safe_signal(SIGPIPE, savepipe);
1250 imaplock = 0;
1252 mb.mb_type = MB_VOID;
1253 mb.mb_active = MB_NONE;
1254 rv = -1;
1255 goto jleave;
1257 if (saveint != SIG_IGN)
1258 safe_signal(SIGINT, imapcatch);
1259 if (savepipe != SIG_IGN)
1260 safe_signal(SIGPIPE, imapcatch);
1262 if (mb.mb_sock.s_fd < 0) {
1263 if (disconnected(mb.mb_imap_account)) {
1264 if (cache_setptr(transparent) == STOP)
1265 fprintf(stderr, "Mailbox \"%s\" is not cached.\n",
1266 urlp->url_puhpp);
1267 goto jdone;
1269 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1270 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1271 savealrm = safe_signal(SIGALRM, imapalarm);
1272 alarm(imapkeepalive);
1276 mb.mb_sock = so;
1277 mb.mb_sock.s_desc = "IMAP";
1278 mb.mb_sock.s_onclose = imap_timer_off;
1279 if (imap_preauth(&mb, urlp->url_hp.s, urlp->url_uhp.s) != OKAY ||
1280 imap_auth(&mb, &ccred) != OKAY) {
1281 sclose(&mb.mb_sock);
1282 imap_timer_off();
1283 safe_signal(SIGINT, saveint);
1284 safe_signal(SIGPIPE, savepipe);
1285 imaplock = 0;
1286 rv = -1;
1287 goto jleave;
1289 } else /* same account */
1290 mb.mb_flags |= same_flags;
1292 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1293 mb.mb_type = MB_IMAP;
1294 cache_dequeue(&mb);
1295 if (imap_select(&mb, &mailsize, &msgCount,
1296 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX")) != OKAY) {
1297 /*sclose(&mb.mb_sock);
1298 imap_timer_off();*/
1299 safe_signal(SIGINT, saveint);
1300 safe_signal(SIGPIPE, savepipe);
1301 imaplock = 0;
1302 mb.mb_type = MB_VOID;
1303 rv = -1;
1304 goto jleave;
1307 jnmail:
1308 imap_setptr(&mb, nmail, transparent, UNVOLATILE(&prevcount));
1309 jdone:
1310 setmsize(msgCount);
1311 if (!nmail && !transparent)
1312 sawcom = FAL0;
1313 safe_signal(SIGINT, saveint);
1314 safe_signal(SIGPIPE, savepipe);
1315 imaplock = 0;
1317 if (!nmail && mb.mb_type == MB_IMAP)
1318 purgecache(&mb, message, msgCount);
1319 if ((nmail || transparent) && mb.mb_sorted) {
1320 mb.mb_threaded = 0;
1321 c_sort((void*)-1);
1324 if (!nmail && !edit && msgCount == 0) {
1325 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1326 !ok_blook(emptystart))
1327 fprintf(stderr, tr(258, "No mail at %s\n"), urlp->url_puhpp);
1328 rv = 1;
1329 goto jleave;
1331 if (nmail)
1332 newmailinfo(prevcount);
1333 rv = 0;
1334 jleave:
1335 NYD_LEAVE;
1336 return rv;
1339 static int
1340 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1341 int need, const char *head, size_t headsize, long headlines)
1343 char *line = NULL, *lp;
1344 size_t linesize = 0, linelen, size = 0;
1345 int emptyline = 0, lines = 0, excess = 0;
1346 off_t offset;
1347 NYD_ENTER;
1349 fseek(mp->mb_otf, 0L, SEEK_END);
1350 offset = ftell(mp->mb_otf);
1352 if (head)
1353 fwrite(head, 1, headsize, mp->mb_otf);
1355 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1356 lp = line;
1357 if (linelen > expected) {
1358 excess = linelen - expected;
1359 linelen = expected;
1361 /* TODO >>
1362 * Need to mask 'From ' lines. This cannot be done properly
1363 * since some servers pass them as 'From ' and others as
1364 * '>From '. Although one could identify the first kind of
1365 * server in principle, it is not possible to identify the
1366 * second as '>From ' may also come from a server of the
1367 * first type as actual data. So do what is absolutely
1368 * necessary only - mask 'From '.
1370 * If the line is the first line of the message header, it
1371 * is likely a real 'From ' line. In this case, it is just
1372 * ignored since it violates all standards.
1373 * TODO can the latter *really* happen??
1374 * TODO <<
1376 /* Since we simply copy over data without doing any transfer
1377 * encoding reclassification/adjustment we *have* to perform
1378 * RFC 4155 compliant From_ quoting here */
1379 if (is_head(lp, linelen)) {
1380 if (lines + headlines == 0)
1381 goto jskip;
1382 fputc('>', mp->mb_otf);
1383 ++size;
1385 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1386 emptyline = linelen <= 2;
1387 if (linelen > 2) {
1388 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1389 size += linelen - 1;
1390 } else
1391 ++size;
1392 fputc('\n', mp->mb_otf);
1393 } else {
1394 emptyline = 0;
1395 fwrite(lp, 1, linelen, mp->mb_otf);
1396 size += linelen;
1398 ++lines;
1399 jskip:
1400 if ((expected -= linelen) <= 0)
1401 break;
1403 if (!emptyline) {
1404 /* This is very ugly; but some IMAP daemons don't end a
1405 * message with \r\n\r\n, and we need \n\n for mbox format */
1406 fputc('\n', mp->mb_otf);
1407 ++lines;
1408 ++size;
1410 fflush(mp->mb_otf);
1412 if (m != NULL) {
1413 m->m_size = size + headsize;
1414 m->m_lines = lines + headlines;
1415 m->m_block = mailx_blockof(offset);
1416 m->m_offset = mailx_offsetof(offset);
1417 switch (need) {
1418 case NEED_HEADER:
1419 m->m_have |= HAVE_HEADER;
1420 break;
1421 case NEED_BODY:
1422 m->m_have |= HAVE_HEADER | HAVE_BODY;
1423 m->m_xlines = m->m_lines;
1424 m->m_xsize = m->m_size;
1425 break;
1428 free(line);
1429 NYD_LEAVE;
1430 return excess;
1433 static void
1434 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1435 const char *head, size_t headsize, long headlines)
1437 off_t offset;
1438 size_t len;
1439 NYD_ENTER;
1441 len = strlen(str);
1442 fseek(mp->mb_otf, 0L, SEEK_END);
1443 offset = ftell(mp->mb_otf);
1444 if (head)
1445 fwrite(head, 1, headsize, mp->mb_otf);
1446 if (len > 0) {
1447 fwrite(str, 1, len, mp->mb_otf);
1448 fputc('\n', mp->mb_otf);
1449 ++len;
1451 fflush(mp->mb_otf);
1453 if (m != NULL) {
1454 m->m_size = headsize + len;
1455 m->m_lines = headlines + 1;
1456 m->m_block = mailx_blockof(offset);
1457 m->m_offset = mailx_offsetof(offset);
1458 m->m_have |= HAVE_HEADER | HAVE_BODY;
1459 m->m_xlines = m->m_lines;
1460 m->m_xsize = m->m_size;
1462 NYD_LEAVE;
1465 static enum okay
1466 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1468 char o[LINESIZE];
1469 struct message mt;
1470 sighandler_type volatile saveint, savepipe;
1471 char * volatile head;
1472 char const *cp, *loc, * volatile item, * volatile resp;
1473 size_t expected;
1474 size_t volatile headsize;
1475 int number;
1476 FILE *queuefp;
1477 long volatile headlines;
1478 long n;
1479 ul_it volatile u;
1480 enum okay ok;
1481 NYD_X;
1483 saveint = savepipe = SIG_IGN;
1484 head = NULL;
1485 cp = loc = item = resp = NULL;
1486 headsize = 0;
1487 number = (int)PTR2SIZE(m - message + 1);
1488 queuefp = NULL;
1489 headlines = 0;
1490 n = -1;
1491 u = 0;
1492 ok = STOP;
1494 if (getcache(mp, m, need) == OKAY)
1495 return OKAY;
1496 if (mp->mb_type == MB_CACHE) {
1497 fprintf(stderr, "Message %u not available.\n", number);
1498 return STOP;
1501 if (mp->mb_sock.s_fd < 0) {
1502 fprintf(stderr, "IMAP connection closed.\n");
1503 return STOP;
1506 switch (need) {
1507 case NEED_HEADER:
1508 resp = item = "RFC822.HEADER";
1509 break;
1510 case NEED_BODY:
1511 item = "BODY.PEEK[]";
1512 resp = "BODY[]";
1513 if (m->m_flag & HAVE_HEADER && m->m_size) {
1514 char *hdr = smalloc(m->m_size);
1515 fflush(mp->mb_otf);
1516 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1517 SEEK_SET) < 0 ||
1518 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1519 free(hdr);
1520 break;
1522 head = hdr;
1523 headsize = m->m_size;
1524 headlines = m->m_lines;
1525 item = "BODY.PEEK[TEXT]";
1526 resp = "BODY[TEXT]";
1528 break;
1529 case NEED_UNSPEC:
1530 return STOP;
1533 imaplock = 1;
1534 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1535 if (sigsetjmp(imapjmp, 1)) {
1536 safe_signal(SIGINT, saveint);
1537 safe_signal(SIGPIPE, savepipe);
1538 imaplock = 0;
1539 return STOP;
1541 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1542 safe_signal(SIGINT, &_imap_maincatch);
1543 if (savepipe != SIG_IGN)
1544 safe_signal(SIGPIPE, imapcatch);
1546 if (m->m_uid)
1547 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1548 tag(1), m->m_uid, item);
1549 else {
1550 if (check_expunged() == STOP)
1551 goto out;
1552 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1554 IMAP_OUT(o, MB_COMD, goto out)
1555 for (;;) {
1556 ok = imap_answer(mp, 1);
1557 if (ok == STOP)
1558 break;
1559 if (response_status != RESPONSE_OTHER ||
1560 response_other != MESSAGE_DATA_FETCH)
1561 continue;
1562 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1563 continue;
1564 if (m->m_uid) {
1565 if ((cp = asccasestr(responded_other_text, "UID "))) {
1566 u = atol(&cp[4]);
1567 n = 0;
1568 } else {
1569 n = -1;
1570 u = 0;
1572 } else
1573 n = responded_other_number;
1574 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1575 if (m->m_uid ? m->m_uid != u : n != number)
1576 continue;
1577 if ((cp = strchr(loc, '"')) != NULL) {
1578 cp = imap_unquotestr(cp);
1579 imap_putstr(mp, m, cp, head, headsize, headlines);
1580 } else {
1581 m->m_have |= HAVE_HEADER|HAVE_BODY;
1582 m->m_xlines = m->m_lines;
1583 m->m_xsize = m->m_size;
1585 goto out;
1587 expected = atol(&cp[1]);
1588 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1589 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1590 continue;
1592 mt = *m;
1593 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1594 if (n >= 0) {
1595 commitmsg(mp, m, &mt, mt.m_have);
1596 break;
1598 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1599 if (options & OPT_VERBVERB)
1600 fputs(imapbuf, stderr);
1601 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1602 u = atol(&cp[4]);
1603 if (u == m->m_uid) {
1604 commitmsg(mp, m, &mt, mt.m_have);
1605 break;
1610 out:
1611 while (mp->mb_active & MB_COMD)
1612 ok = imap_answer(mp, 1);
1614 if (saveint != SIG_IGN)
1615 safe_signal(SIGINT, saveint);
1616 if (savepipe != SIG_IGN)
1617 safe_signal(SIGPIPE, savepipe);
1618 imaplock--;
1620 if (ok == OKAY)
1621 putcache(mp, m);
1622 if (head != NULL)
1623 free(head);
1624 if (interrupts)
1625 onintr(0);
1626 return ok;
1629 FL enum okay
1630 imap_header(struct message *m)
1632 enum okay rv;
1633 NYD_ENTER;
1635 rv = imap_get(&mb, m, NEED_HEADER);
1636 NYD_LEAVE;
1637 return rv;
1641 FL enum okay
1642 imap_body(struct message *m)
1644 enum okay rv;
1645 NYD_ENTER;
1647 rv = imap_get(&mb, m, NEED_BODY);
1648 NYD_LEAVE;
1649 return rv;
1652 static void
1653 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1654 enum havespec have)
1656 NYD_ENTER;
1657 tomp->m_size = frommp->m_size;
1658 tomp->m_lines = frommp->m_lines;
1659 tomp->m_block = frommp->m_block;
1660 tomp->m_offset = frommp->m_offset;
1661 tomp->m_have = have;
1662 if (have & HAVE_BODY) {
1663 tomp->m_xlines = frommp->m_lines;
1664 tomp->m_xsize = frommp->m_size;
1666 putcache(mp, tomp);
1667 NYD_LEAVE;
1670 static enum okay
1671 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1673 /* bot > topp */
1674 char o[LINESIZE];
1675 char const *cp;
1676 struct message mt;
1677 size_t expected;
1678 int n = 0, u;
1679 FILE *queuefp = NULL;
1680 enum okay ok;
1681 NYD_X;
1683 if (m[bot].m_uid)
1684 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1685 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1686 else {
1687 if (check_expunged() == STOP)
1688 return STOP;
1689 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1690 tag(1), bot, topp);
1692 IMAP_OUT(o, MB_COMD, return STOP)
1693 for (;;) {
1694 ok = imap_answer(mp, 1);
1695 if (response_status != RESPONSE_OTHER)
1696 break;
1697 if (response_other != MESSAGE_DATA_FETCH)
1698 continue;
1699 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1700 return STOP;
1701 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1702 continue;
1703 expected = atol(&cp[1]);
1704 if (m[bot-1].m_uid) {
1705 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1706 u = atoi(&cp[4]);
1707 for (n = bot; n <= topp; n++)
1708 if ((unsigned long)u == m[n-1].m_uid)
1709 break;
1710 if (n > topp) {
1711 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1712 continue;
1714 } else
1715 n = -1;
1716 } else {
1717 n = responded_other_number;
1718 if (n <= 0 || n > msgCount) {
1719 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1720 continue;
1723 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1724 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1725 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1726 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1727 if (options & OPT_VERBVERB)
1728 fputs(imapbuf, stderr);
1729 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1730 u = atoi(&cp[4]);
1731 for (n = bot; n <= topp; n++)
1732 if ((unsigned long)u == m[n-1].m_uid)
1733 break;
1734 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1735 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1736 n = 0;
1740 while (mp->mb_active & MB_COMD)
1741 ok = imap_answer(mp, 1);
1742 return ok;
1745 FL void
1746 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1748 sighandler_type saveint, savepipe;
1749 /* enum okay ok = STOP;*/
1750 int i, chunk = 256;
1751 NYD_X;
1753 if (mb.mb_type == MB_CACHE)
1754 return;
1755 if (bot < 1)
1756 bot = 1;
1757 if (topp > msgCount)
1758 topp = msgCount;
1759 for (i = bot; i < topp; i++) {
1760 if (message[i-1].m_have & HAVE_HEADER ||
1761 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1762 bot = i+1;
1763 else
1764 break;
1766 for (i = topp; i > bot; i--) {
1767 if (message[i-1].m_have & HAVE_HEADER ||
1768 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1769 topp = i-1;
1770 else
1771 break;
1773 if (bot >= topp)
1774 return;
1776 imaplock = 1;
1777 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1778 safe_signal(SIGINT, &_imap_maincatch);
1779 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1780 if (sigsetjmp(imapjmp, 1) == 0) {
1781 if (savepipe != SIG_IGN)
1782 safe_signal(SIGPIPE, imapcatch);
1784 for (i = bot; i <= topp; i += chunk) {
1785 int j = i + chunk - 1;
1786 j = MIN(j, topp);
1787 if (visible(message + j))
1788 /*ok = */imap_fetchheaders(&mb, message, i, j);
1789 if (interrupts)
1790 onintr(0); /* XXX imaplock? */
1793 safe_signal(SIGINT, saveint);
1794 safe_signal(SIGPIPE, savepipe);
1795 imaplock = 0;
1798 static enum okay
1799 __imap_exit(struct mailbox *mp)
1801 char o[LINESIZE];
1802 FILE *queuefp = NULL;
1803 NYD_X;
1805 mp->mb_active |= MB_BYE;
1806 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1807 IMAP_OUT(o, MB_COMD, return STOP)
1808 IMAP_ANSWER()
1809 return OKAY;
1812 static enum okay
1813 imap_exit(struct mailbox *mp)
1815 enum okay rv;
1816 NYD_ENTER;
1818 rv = __imap_exit(mp);
1819 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1820 free(mp->mb_imap_pass);
1821 free(mp->mb_imap_account);
1822 free(mp->mb_imap_mailbox);
1823 if (mp->mb_cache_directory != NULL)
1824 free(mp->mb_cache_directory);
1825 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1826 mp->mb_imap_account =
1827 mp->mb_imap_mailbox =
1828 mp->mb_cache_directory = "";
1829 #else
1830 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1831 mp->mb_imap_mailbox = NULL;
1832 mp->mb_cache_directory = NULL;
1833 #endif
1834 #endif
1835 sclose(&mp->mb_sock);
1836 NYD_LEAVE;
1837 return rv;
1840 static enum okay
1841 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1843 NYD_ENTER;
1844 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1845 if (mp->mb_type == MB_IMAP)
1846 delcache(mp, m);
1847 NYD_LEAVE;
1848 return OKAY;
1851 static enum okay
1852 imap_close(struct mailbox *mp)
1854 char o[LINESIZE];
1855 FILE *queuefp = NULL;
1856 NYD_X;
1858 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1859 IMAP_OUT(o, MB_COMD, return STOP)
1860 IMAP_ANSWER()
1861 return OKAY;
1864 static enum okay
1865 imap_update(struct mailbox *mp)
1867 struct message *m;
1868 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1869 NYD_ENTER;
1871 if (!edit && mp->mb_perm != 0) {
1872 holdbits();
1873 c = 0;
1874 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1875 if (m->m_flag & MBOX)
1876 ++c;
1877 if (c > 0)
1878 if (makembox() == STOP)
1879 goto jbypass;
1882 gotcha = held = 0;
1883 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1884 if (mp->mb_perm == 0)
1885 dodel = 0;
1886 else if (edit)
1887 dodel = ((m->m_flag & MDELETED) != 0);
1888 else
1889 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1891 /* Fetch the result after around each 800 STORE commands
1892 * sent (approx. 32k data sent). Otherwise, servers will
1893 * try to flush the return queue at some point, leading
1894 * to a deadlock if we are still writing commands but not
1895 * reading their results */
1896 needstat = stored > 0 && stored % 800 == 0;
1897 /* Even if this message has been deleted, continue
1898 * to set further flags. This is necessary to support
1899 * Gmail semantics, where "delete" actually means
1900 * "archive", and the flags are applied to the copy
1901 * in "All Mail" */
1902 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1903 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1904 stored++;
1906 if (m->m_flag & MFLAG) {
1907 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1908 stored++;
1910 if (m->m_flag & MUNFLAG) {
1911 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1912 stored++;
1914 if (m->m_flag & MANSWER) {
1915 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1916 stored++;
1918 if (m->m_flag & MUNANSWER) {
1919 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1920 stored++;
1922 if (m->m_flag & MDRAFT) {
1923 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1924 stored++;
1926 if (m->m_flag & MUNDRAFT) {
1927 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1928 stored++;
1931 if (dodel) {
1932 imap_delete(mp, m-message+1, m, needstat);
1933 stored++;
1934 gotcha++;
1935 } else if (mp->mb_type != MB_CACHE ||
1936 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1937 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1938 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1939 held++;
1940 if (m->m_flag & MNEW) {
1941 m->m_flag &= ~MNEW;
1942 m->m_flag |= MSTATUS;
1945 jbypass:
1946 if (gotcha)
1947 imap_close(mp);
1949 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1950 if (!(m->m_flag & MUNLINKED) &&
1951 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1952 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1953 putcache(mp, m);
1954 modflags++;
1956 if ((gotcha || modflags) && edit) {
1957 printf(tr(168, "\"%s\" "), displayname);
1958 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1959 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1960 } else if (held && !edit && mp->mb_perm != 0) {
1961 if (held == 1)
1962 printf(tr(155, "Held 1 message in %s\n"), displayname);
1963 else
1964 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
1966 fflush(stdout);
1967 NYD_LEAVE;
1968 return OKAY;
1971 FL void
1972 imap_quit(void)
1974 sighandler_type volatile saveint, savepipe;
1975 NYD_ENTER;
1977 if (mb.mb_type == MB_CACHE) {
1978 imap_update(&mb);
1979 goto jleave;
1982 if (mb.mb_sock.s_fd < 0) {
1983 fprintf(stderr, "IMAP connection closed.\n");
1984 goto jleave;
1987 imaplock = 1;
1988 saveint = safe_signal(SIGINT, SIG_IGN);
1989 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1990 if (sigsetjmp(imapjmp, 1)) {
1991 safe_signal(SIGINT, saveint);
1992 safe_signal(SIGPIPE, saveint);
1993 imaplock = 0;
1994 goto jleave;
1996 if (saveint != SIG_IGN)
1997 safe_signal(SIGINT, imapcatch);
1998 if (savepipe != SIG_IGN)
1999 safe_signal(SIGPIPE, imapcatch);
2001 imap_update(&mb);
2002 if (!same_imap_account)
2003 imap_exit(&mb);
2005 safe_signal(SIGINT, saveint);
2006 safe_signal(SIGPIPE, savepipe);
2007 imaplock = 0;
2008 jleave:
2009 NYD_LEAVE;
2012 static enum okay
2013 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2014 int needstat)
2016 char o[LINESIZE];
2017 FILE *queuefp = NULL;
2018 NYD_X;
2020 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2021 return STOP;
2022 if (m->m_uid)
2023 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2024 tag(1), m->m_uid, c, sp);
2025 else {
2026 if (check_expunged() == STOP)
2027 return STOP;
2028 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2030 IMAP_OUT(o, MB_COMD, return STOP)
2031 if (needstat)
2032 IMAP_ANSWER()
2033 else
2034 mb.mb_active &= ~MB_COMD;
2035 if (queuefp != NULL)
2036 Fclose(queuefp);
2037 return OKAY;
2040 FL enum okay
2041 imap_undelete(struct message *m, int n)
2043 enum okay rv;
2044 NYD_ENTER;
2046 rv = imap_unstore(m, n, "\\Deleted");
2047 NYD_LEAVE;
2048 return rv;
2051 FL enum okay
2052 imap_unread(struct message *m, int n)
2054 enum okay rv;
2055 NYD_ENTER;
2057 rv = imap_unstore(m, n, "\\Seen");
2058 NYD_LEAVE;
2059 return rv;
2062 static enum okay
2063 imap_unstore(struct message *m, int n, const char *flag)
2065 sighandler_type saveint, savepipe;
2066 enum okay rv = STOP;
2067 NYD_ENTER;
2069 imaplock = 1;
2070 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2071 safe_signal(SIGINT, &_imap_maincatch);
2072 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2073 if (sigsetjmp(imapjmp, 1) == 0) {
2074 if (savepipe != SIG_IGN)
2075 safe_signal(SIGPIPE, imapcatch);
2077 rv = imap_store(&mb, m, n, '-', flag, 1);
2079 safe_signal(SIGINT, saveint);
2080 safe_signal(SIGPIPE, savepipe);
2081 imaplock = 0;
2083 NYD_LEAVE;
2084 if (interrupts)
2085 onintr(0);
2086 return rv;
2089 static const char *
2090 tag(int new)
2092 static char ts[20];
2093 static long n;
2094 NYD_ENTER;
2096 if (new)
2097 ++n;
2098 snprintf(ts, sizeof ts, "T%lu", n);
2099 NYD_LEAVE;
2100 return ts;
2103 FL int
2104 c_imap_imap(void *vp)
2106 char o[LINESIZE];
2107 sighandler_type saveint, savepipe;
2108 struct mailbox *mp = &mb;
2109 FILE *queuefp = NULL;
2110 enum okay volatile ok = STOP;
2111 NYD_X;
2113 if (mp->mb_type != MB_IMAP) {
2114 printf("Not operating on an IMAP mailbox.\n");
2115 return 1;
2117 imaplock = 1;
2118 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2119 safe_signal(SIGINT, &_imap_maincatch);
2120 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2121 if (sigsetjmp(imapjmp, 1) == 0) {
2122 if (savepipe != SIG_IGN)
2123 safe_signal(SIGPIPE, imapcatch);
2125 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2126 IMAP_OUT(o, MB_COMD, goto out)
2127 while (mp->mb_active & MB_COMD) {
2128 ok = imap_answer(mp, 0);
2129 fputs(responded_text, stdout);
2132 out:
2133 safe_signal(SIGINT, saveint);
2134 safe_signal(SIGPIPE, savepipe);
2135 imaplock = 0;
2137 if (interrupts)
2138 onintr(0);
2139 return ok != OKAY;
2142 FL int
2143 imap_newmail(int nmail)
2145 NYD_ENTER;
2147 if (nmail && had_exists < 0 && had_expunge < 0) {
2148 imaplock = 1;
2149 imap_noop();
2150 imaplock = 0;
2153 if (had_exists == msgCount && had_expunge < 0)
2154 /* Some servers always respond with EXISTS to NOOP. If
2155 * the mailbox has been changed but the number of messages
2156 * has not, an EXPUNGE must also had been sent; otherwise,
2157 * nothing has changed */
2158 had_exists = -1;
2159 NYD_LEAVE;
2160 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2163 static char *
2164 imap_putflags(int f)
2166 const char *cp;
2167 char *buf, *bp;
2168 NYD_ENTER;
2170 bp = buf = salloc(100);
2171 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2172 *bp++ = '(';
2173 if (f & MREAD) {
2174 if (bp[-1] != '(')
2175 *bp++ = ' ';
2176 for (cp = "\\Seen"; *cp; cp++)
2177 *bp++ = *cp;
2179 if (f & MFLAGGED) {
2180 if (bp[-1] != '(')
2181 *bp++ = ' ';
2182 for (cp = "\\Flagged"; *cp; cp++)
2183 *bp++ = *cp;
2185 if (f & MANSWERED) {
2186 if (bp[-1] != '(')
2187 *bp++ = ' ';
2188 for (cp = "\\Answered"; *cp; cp++)
2189 *bp++ = *cp;
2191 if (f & MDRAFT) {
2192 if (bp[-1] != '(')
2193 *bp++ = ' ';
2194 for (cp = "\\Draft"; *cp; cp++)
2195 *bp++ = *cp;
2197 *bp++ = ')';
2198 *bp++ = ' ';
2200 *bp = '\0';
2201 NYD_LEAVE;
2202 return buf;
2205 static void
2206 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2208 NYD_ENTER;
2209 while (*cp != ')') {
2210 if (*cp == '\\') {
2211 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2212 *f |= MREAD;
2213 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2214 *f |= MNEW;
2215 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2216 *f |= MDELETED;
2217 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2218 *f |= MFLAGGED;
2219 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2220 *f |= MANSWERED;
2221 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2222 *f |= MDRAFTED;
2224 cp++;
2227 if (xp != NULL)
2228 *xp = cp;
2229 NYD_LEAVE;
2232 static enum okay
2233 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2234 long xsize, enum mflag flag, time_t t)
2236 char o[LINESIZE], *buf;
2237 size_t bufsize, buflen, cnt;
2238 long size, lines, ysize;
2239 int twice = 0;
2240 FILE *queuefp = NULL;
2241 enum okay rv;
2242 NYD_ENTER;
2244 if (mp->mb_type == MB_CACHE) {
2245 queuefp = cache_queue(mp);
2246 if (queuefp == NULL) {
2247 rv = STOP;
2248 buf = NULL;
2249 goto jleave;
2251 rv = OKAY;
2252 } else
2253 rv = STOP;
2255 buf = smalloc(bufsize = LINESIZE);
2256 buflen = 0;
2257 jagain:
2258 size = xsize;
2259 cnt = fsize(fp);
2260 if (fseek(fp, off1, SEEK_SET) < 0) {
2261 rv = STOP;
2262 goto jleave;
2265 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2266 tag(1), imap_quotestr(name), imap_putflags(flag),
2267 imap_make_date_time(t), size);
2268 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2269 while (mp->mb_active & MB_COMD) {
2270 rv = imap_answer(mp, twice);
2271 if (response_type == RESPONSE_CONT)
2272 break;
2275 if (mp->mb_type != MB_CACHE && rv == STOP) {
2276 if (twice == 0)
2277 goto jtrycreate;
2278 else
2279 goto jleave;
2282 lines = ysize = 0;
2283 while (size > 0) {
2284 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2285 lines++;
2286 ysize += buflen;
2287 buf[buflen - 1] = '\r';
2288 buf[buflen] = '\n';
2289 if (mp->mb_type != MB_CACHE)
2290 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2291 else if (queuefp)
2292 fwrite(buf, 1, buflen+1, queuefp);
2293 size -= buflen + 1;
2295 if (mp->mb_type != MB_CACHE)
2296 swrite(&mp->mb_sock, "\r\n");
2297 else if (queuefp)
2298 fputs("\r\n", queuefp);
2299 while (mp->mb_active & MB_COMD) {
2300 rv = imap_answer(mp, 0);
2301 if (response_status == RESPONSE_NO /*&&
2302 ascncasecmp(responded_text,
2303 "[TRYCREATE] ", 12) == 0*/) {
2304 jtrycreate:
2305 if (twice++) {
2306 rv = STOP;
2307 goto jleave;
2309 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2310 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2311 while (mp->mb_active & MB_COMD)
2312 rv = imap_answer(mp, 1);
2313 if (rv == STOP)
2314 goto jleave;
2315 imap_created_mailbox++;
2316 goto jagain;
2317 } else if (rv != OKAY)
2318 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
2319 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2320 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2322 jleave:
2323 if (queuefp != NULL)
2324 Fclose(queuefp);
2325 if (buf != NULL)
2326 free(buf);
2327 NYD_LEAVE;
2328 return rv;
2331 static enum okay
2332 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2334 char *buf, *bp, *lp;
2335 size_t bufsize, buflen, cnt;
2336 off_t off1 = -1, offs;
2337 int inhead = 1, flag = MNEW | MNEWEST;
2338 long size = 0;
2339 time_t tim;
2340 enum okay rv;
2341 NYD_ENTER;
2343 buf = smalloc(bufsize = LINESIZE);
2344 buflen = 0;
2345 cnt = fsize(fp);
2346 offs = ftell(fp);
2347 time(&tim);
2349 do {
2350 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2351 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2352 if (off1 != (off_t)-1) {
2353 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2354 if (rv == STOP)
2355 goto jleave;
2356 fseek(fp, offs+buflen, SEEK_SET);
2358 off1 = offs + buflen;
2359 size = 0;
2360 inhead = 1;
2361 flag = MNEW;
2362 if (bp != NULL)
2363 tim = unixtime(buf);
2364 } else
2365 size += buflen+1;
2366 offs += buflen;
2368 if (bp && buf[0] == '\n')
2369 inhead = 0;
2370 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2371 lp = &buf[6];
2372 while (whitechar(*lp))
2373 lp++;
2374 if (*lp == ':')
2375 while (*++lp != '\0')
2376 switch (*lp) {
2377 case 'R':
2378 flag |= MREAD;
2379 break;
2380 case 'O':
2381 flag &= ~MNEW;
2382 break;
2384 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2385 lp = &buf[8];
2386 while (whitechar(*lp))
2387 lp++;
2388 if (*lp == ':')
2389 while (*++lp != '\0')
2390 switch (*lp) {
2391 case 'F':
2392 flag |= MFLAGGED;
2393 break;
2394 case 'A':
2395 flag |= MANSWERED;
2396 break;
2397 case 'T':
2398 flag |= MDRAFTED;
2399 break;
2402 } while (bp != NULL);
2403 rv = OKAY;
2404 jleave:
2405 free(buf);
2406 NYD_LEAVE;
2407 return rv;
2410 FL enum okay
2411 imap_append(const char *xserver, FILE *fp)
2413 sighandler_type volatile saveint, savepipe;
2414 struct url url;
2415 struct ccred ccred;
2416 char const * volatile mbx;
2417 enum okay rv = STOP;
2418 NYD_ENTER;
2420 if (!url_parse(&url, CPROTO_IMAP, xserver))
2421 goto j_leave;
2422 if (!ok_blook(v15_compat) &&
2423 (!url.url_had_user || url.url_pass.s != NULL))
2424 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
2425 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2427 imaplock = 1;
2428 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2429 safe_signal(SIGINT, &_imap_maincatch);
2430 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2431 if (sigsetjmp(imapjmp, 1))
2432 goto jleave;
2433 if (savepipe != SIG_IGN)
2434 safe_signal(SIGPIPE, imapcatch);
2436 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2437 !strcmp(url.url_puhp, mb.mb_imap_account)) {
2438 rv = imap_append0(&mb, mbx, fp);
2439 } else {
2440 struct mailbox mx;
2442 if (!(ok_blook(v15_compat) ? ccred_lookup(&ccred, &url)
2443 : ccred_lookup_old(&ccred, CPROTO_IMAP, url.url_uhp.s)))
2444 goto jleave;
2446 memset(&mx, 0, sizeof mx);
2447 if (disconnected(url.url_puhp) == 0) {
2448 if (!sopen(&mx.mb_sock, &url))
2449 goto jfail;
2450 mx.mb_sock.s_desc = "IMAP";
2451 mx.mb_type = MB_IMAP;
2452 mx.mb_imap_account = UNCONST(url.url_puhp);
2453 /* TODO the code now did
2454 * TODO mx.mb_imap_mailbox = mbx;
2455 * TODO though imap_mailbox is sfree()d and mbx
2456 * TODO is possibly even a constant
2457 * TODO i changed this to sstrdup() sofar, as is used
2458 * TODO somewhere else in this file for this! */
2459 mx.mb_imap_mailbox = sstrdup(mbx);
2460 if (imap_preauth(&mx, url.url_hp.s, url.url_uhp.s) != OKAY ||
2461 imap_auth(&mx, &ccred) != OKAY) {
2462 sclose(&mx.mb_sock);
2463 goto jfail;
2465 rv = imap_append0(&mx, mbx, fp);
2466 imap_exit(&mx);
2467 } else {
2468 mx.mb_imap_account = UNCONST(url.url_puhp);
2469 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2470 mx.mb_type = MB_CACHE;
2471 rv = imap_append0(&mx, mbx, fp);
2473 jfail:
2477 jleave:
2478 safe_signal(SIGINT, saveint);
2479 safe_signal(SIGPIPE, savepipe);
2480 imaplock = 0;
2481 j_leave:
2482 NYD_LEAVE;
2483 if (interrupts)
2484 onintr(0);
2485 return rv;
2488 static enum okay
2489 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2490 struct list_item **lend, int level)
2492 char o[LINESIZE], *cp;
2493 const char *bp;
2494 FILE *queuefp = NULL;
2495 struct list_item *lp;
2496 enum okay ok = STOP;
2497 NYD_X;
2499 *list = *lend = NULL;
2500 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2501 IMAP_OUT(o, MB_COMD, return STOP)
2502 while (mp->mb_active & MB_COMD) {
2503 ok = imap_answer(mp, 1);
2504 if (response_status == RESPONSE_OTHER &&
2505 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2506 cp = imap_unquotestr(list_name);
2507 lp = csalloc(1, sizeof *lp);
2508 lp->l_name = cp;
2509 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2510 ++cp;
2511 lp->l_base = *cp ? cp : savestr(base);
2512 lp->l_attr = list_attributes;
2513 lp->l_level = level+1;
2514 lp->l_delim = list_hierarchy_delimiter;
2515 if (*list && *lend) {
2516 (*lend)->l_next = lp;
2517 *lend = lp;
2518 } else
2519 *list = *lend = lp;
2522 return ok;
2525 static enum okay
2526 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2528 struct list_item *list, *lend, *lp, *lx, *ly;
2529 int n, depth;
2530 const char *bp;
2531 char *cp;
2532 enum okay rv;
2533 NYD_ENTER;
2535 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2536 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2537 goto jleave;
2538 rv = OKAY;
2539 if (list == NULL || lend == NULL)
2540 goto jleave;
2542 for (lp = list; lp; lp = lp->l_next)
2543 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2544 !(lp->l_attr & LIST_NOINFERIORS)) {
2545 cp = salloc((n = strlen(lp->l_name)) + 2);
2546 memcpy(cp, lp->l_name, n);
2547 cp[n] = lp->l_delim;
2548 cp[n+1] = '\0';
2549 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2550 lp->l_has_children = 1;
2551 if (strcmp(cp, lx->l_name) == 0)
2552 lx = lx->l_next;
2553 if (lx) {
2554 lend->l_next = lx;
2555 lend = ly;
2560 for (lp = list; lp; lp = lp->l_next) {
2561 if (strip) {
2562 cp = lp->l_name;
2563 for (bp = base; *bp && *bp == *cp; bp++)
2564 cp++;
2565 } else
2566 cp = lp->l_name;
2567 if (!(lp->l_attr & LIST_NOSELECT))
2568 fprintf(fp, "%s\n", *cp ? cp : base);
2569 else if (lp->l_has_children == 0)
2570 fprintf(fp, "%s%c\n", *cp ? cp : base,
2571 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2573 jleave:
2574 NYD_LEAVE;
2575 return rv;
2578 FL void
2579 imap_folders(const char * volatile name, int strip)
2581 sighandler_type saveint, savepipe;
2582 const char *fold, *cp, *sp;
2583 FILE * volatile fp;
2584 NYD_ENTER;
2586 cp = protbase(name);
2587 sp = mb.mb_imap_account;
2588 if (sp == NULL || strcmp(cp, sp)) {
2589 fprintf(stderr, tr(502,
2590 "Cannot perform `folders' but when on the very IMAP "
2591 "account; the current one is\n `%s' -- "
2592 "try `folders @'.\n"),
2593 (sp != NULL ? sp : tr(503, "[NONE]")));
2594 goto jleave;
2597 fold = imap_fileof(name);
2598 if (options & OPT_TTYOUT) {
2599 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2600 0600)) == NULL) {
2601 perror("tmpfile");
2602 goto jleave;
2604 } else
2605 fp = stdout;
2607 imaplock = 1;
2608 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2609 safe_signal(SIGINT, &_imap_maincatch);
2610 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2611 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2612 goto junroll;
2613 if (savepipe != SIG_IGN)
2614 safe_signal(SIGPIPE, imapcatch);
2616 if (mb.mb_type == MB_CACHE)
2617 cache_list(&mb, fold, strip, fp);
2618 else
2619 imap_list(&mb, fold, strip, fp);
2621 imaplock = 0;
2622 if (interrupts) {
2623 if (options & OPT_TTYOUT)
2624 Fclose(fp);
2625 goto jleave;
2627 fflush(fp);
2629 if (options & OPT_TTYOUT) {
2630 rewind(fp);
2631 if (fsize(fp) > 0)
2632 dopr(fp);
2633 else
2634 fprintf(stderr, "Folder not found.\n");
2636 junroll:
2637 safe_signal(SIGINT, saveint);
2638 safe_signal(SIGPIPE, savepipe);
2639 if (options & OPT_TTYOUT)
2640 Fclose(fp);
2641 jleave:
2642 NYD_LEAVE;
2643 if (interrupts)
2644 onintr(0);
2647 static void
2648 dopr(FILE *fp)
2650 char o[LINESIZE];
2651 int c;
2652 long n = 0, mx = 0, columns, width;
2653 FILE *out;
2654 NYD_ENTER;
2656 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2657 == NULL) {
2658 perror("tmpfile");
2659 goto jleave;
2662 while ((c = getc(fp)) != EOF) {
2663 if (c == '\n') {
2664 if (n > mx)
2665 mx = n;
2666 n = 0;
2667 } else
2668 ++n;
2670 rewind(fp);
2672 width = scrnwidth;
2673 if (mx < width / 2) {
2674 columns = width / (mx+2);
2675 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2676 } else
2677 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2678 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2679 page_or_print(out, 0);
2680 Fclose(out);
2681 jleave:
2682 NYD_LEAVE;
2685 static enum okay
2686 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2688 char o[LINESIZE];
2689 const char *qname;
2690 int twice = 0, stored = 0;
2691 FILE *queuefp = NULL;
2692 enum okay ok = STOP;
2693 NYD_X;
2695 if (mp->mb_type == MB_CACHE) {
2696 if ((queuefp = cache_queue(mp)) == NULL)
2697 return STOP;
2698 ok = OKAY;
2700 qname = imap_quotestr(name = imap_fileof(name));
2701 /* Since it is not possible to set flags on the copy, recently
2702 * set flags must be set on the original to include it in the copy */
2703 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2704 imap_store(mp, m, n, '+', "\\Seen", 0);
2705 if (m->m_flag&MFLAG)
2706 imap_store(mp, m, n, '+', "\\Flagged", 0);
2707 if (m->m_flag&MUNFLAG)
2708 imap_store(mp, m, n, '-', "\\Flagged", 0);
2709 if (m->m_flag&MANSWER)
2710 imap_store(mp, m, n, '+', "\\Answered", 0);
2711 if (m->m_flag&MUNANSWER)
2712 imap_store(mp, m, n, '-', "\\Flagged", 0);
2713 if (m->m_flag&MDRAFT)
2714 imap_store(mp, m, n, '+', "\\Draft", 0);
2715 if (m->m_flag&MUNDRAFT)
2716 imap_store(mp, m, n, '-', "\\Draft", 0);
2717 again:
2718 if (m->m_uid)
2719 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2720 else {
2721 if (check_expunged() == STOP)
2722 goto out;
2723 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2725 IMAP_OUT(o, MB_COMD, goto out)
2726 while (mp->mb_active & MB_COMD)
2727 ok = imap_answer(mp, twice);
2729 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2730 response_status == RESPONSE_OK)
2731 imap_copyuid(mp, m, name);
2733 if (response_status == RESPONSE_NO && twice++ == 0) {
2734 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2735 IMAP_OUT(o, MB_COMD, goto out)
2736 while (mp->mb_active & MB_COMD)
2737 ok = imap_answer(mp, 1);
2738 if (ok == OKAY) {
2739 imap_created_mailbox++;
2740 goto again;
2744 if (queuefp != NULL)
2745 Fclose(queuefp);
2747 /* ... and reset the flag to its initial value so that the 'exit'
2748 * command still leaves the message unread */
2749 out:
2750 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2751 imap_store(mp, m, n, '-', "\\Seen", 0);
2752 stored++;
2754 if (m->m_flag & MFLAG) {
2755 imap_store(mp, m, n, '-', "\\Flagged", 0);
2756 stored++;
2758 if (m->m_flag & MUNFLAG) {
2759 imap_store(mp, m, n, '+', "\\Flagged", 0);
2760 stored++;
2762 if (m->m_flag & MANSWER) {
2763 imap_store(mp, m, n, '-', "\\Answered", 0);
2764 stored++;
2766 if (m->m_flag & MUNANSWER) {
2767 imap_store(mp, m, n, '+', "\\Answered", 0);
2768 stored++;
2770 if (m->m_flag & MDRAFT) {
2771 imap_store(mp, m, n, '-', "\\Draft", 0);
2772 stored++;
2774 if (m->m_flag & MUNDRAFT) {
2775 imap_store(mp, m, n, '+', "\\Draft", 0);
2776 stored++;
2778 if (stored) {
2779 mp->mb_active |= MB_COMD;
2780 (void)imap_finish(mp);
2782 return ok;
2785 FL enum okay
2786 imap_copy(struct message *m, int n, const char *name)
2788 sighandler_type saveint, savepipe;
2789 enum okay rv = STOP;
2790 NYD_ENTER;
2792 imaplock = 1;
2793 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2794 safe_signal(SIGINT, &_imap_maincatch);
2795 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2796 if (sigsetjmp(imapjmp, 1) == 0) {
2797 if (savepipe != SIG_IGN)
2798 safe_signal(SIGPIPE, imapcatch);
2800 rv = imap_copy1(&mb, m, n, name);
2802 safe_signal(SIGINT, saveint);
2803 safe_signal(SIGPIPE, savepipe);
2804 imaplock = 0;
2806 NYD_LEAVE;
2807 if (interrupts)
2808 onintr(0);
2809 return rv;
2812 static enum okay
2813 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2814 unsigned long *olduid, unsigned long *newuid)
2816 char *xp, *yp, *zp;
2817 enum okay rv;
2818 NYD_ENTER;
2820 *uidvalidity = strtoul(cp, &xp, 10);
2821 *olduid = strtoul(xp, &yp, 10);
2822 *newuid = strtoul(yp, &zp, 10);
2823 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2824 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2825 NYD_LEAVE;
2826 return rv;
2829 static enum okay
2830 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2831 unsigned long *uid)
2833 char *xp, *yp;
2834 enum okay rv;
2835 NYD_ENTER;
2837 *uidvalidity = strtoul(cp, &xp, 10);
2838 *uid = strtoul(xp, &yp, 10);
2839 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2840 *yp == ']');
2841 NYD_LEAVE;
2842 return rv;
2845 static enum okay
2846 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2848 struct mailbox xmb;
2849 struct message xm;
2850 const char *cp;
2851 unsigned long uidvalidity, olduid, newuid;
2852 enum okay rv;
2853 NYD_ENTER;
2855 rv = STOP;
2856 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2857 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2858 goto jleave;
2859 rv = OKAY;
2861 xmb = *mp;
2862 xmb.mb_cache_directory = NULL;
2863 xmb.mb_imap_mailbox = savestr(name);
2864 xmb.mb_uidvalidity = uidvalidity;
2865 initcache(&xmb);
2866 if (m == NULL) {
2867 memset(&xm, 0, sizeof xm);
2868 xm.m_uid = olduid;
2869 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2870 goto jleave;
2871 getcache(mp, &xm, NEED_HEADER);
2872 getcache(mp, &xm, NEED_BODY);
2873 } else {
2874 if ((m->m_flag & HAVE_HEADER) == 0)
2875 getcache(mp, m, NEED_HEADER);
2876 if ((m->m_flag & HAVE_BODY) == 0)
2877 getcache(mp, m, NEED_BODY);
2878 xm = *m;
2880 xm.m_uid = newuid;
2881 xm.m_flag &= ~MFULLYCACHED;
2882 putcache(&xmb, &xm);
2883 jleave:
2884 NYD_LEAVE;
2885 return rv;
2888 static enum okay
2889 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2890 long size, long lines, int flag, const char *name)
2892 struct mailbox xmb;
2893 struct message xm;
2894 const char *cp;
2895 unsigned long uidvalidity, uid;
2896 enum okay rv;
2897 NYD_ENTER;
2899 rv = STOP;
2900 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2901 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2902 goto jleave;
2903 rv = OKAY;
2905 xmb = *mp;
2906 xmb.mb_cache_directory = NULL;
2907 xmb.mb_imap_mailbox = savestr(name);
2908 xmb.mb_uidvalidity = uidvalidity;
2909 xmb.mb_otf = xmb.mb_itf = fp;
2910 initcache(&xmb);
2911 memset(&xm, 0, sizeof xm);
2912 xm.m_flag = (flag & MREAD) | MNEW;
2913 xm.m_time = t;
2914 xm.m_block = mailx_blockof(off1);
2915 xm.m_offset = mailx_offsetof(off1);
2916 xm.m_size = size;
2917 xm.m_xsize = xsize;
2918 xm.m_lines = xm.m_xlines = lines;
2919 xm.m_uid = uid;
2920 xm.m_have = HAVE_HEADER | HAVE_BODY;
2921 putcache(&xmb, &xm);
2922 jleave:
2923 NYD_LEAVE;
2924 return rv;
2927 static enum okay
2928 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2930 FILE *tp = NULL;
2931 time_t t;
2932 long size, xsize, ysize, lines;
2933 enum mflag flag = MNEW;
2934 char *name, *buf, *bp;
2935 char const *cp;
2936 size_t bufsize, buflen, cnt;
2937 enum okay rv = STOP;
2938 NYD_ENTER;
2940 buf = smalloc(bufsize = LINESIZE);
2941 buflen = 0;
2942 cnt = fsize(fp);
2943 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2944 goto jstop;
2946 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
2948 while (*bp == ' ')
2949 ++bp;
2951 if ((cp = strrchr(bp, '{')) == NULL)
2952 goto jstop;
2954 xsize = atol(&cp[1]) + 2;
2955 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2956 goto jstop;
2957 while (*cp == ' ')
2958 cp++;
2960 if (*cp == '(') {
2961 imap_getflags(cp, &cp, &flag);
2962 while (*++cp == ' ')
2965 t = imap_read_date_time(cp);
2967 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2968 NULL)
2969 goto jstop;
2971 size = xsize;
2972 ysize = lines = 0;
2973 while (size > 0) {
2974 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2975 goto jstop;
2976 size -= buflen;
2977 buf[--buflen] = '\0';
2978 buf[buflen-1] = '\n';
2979 fwrite(buf, 1, buflen, tp);
2980 ysize += buflen;
2981 ++lines;
2983 fflush(tp);
2984 rewind(tp);
2986 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2987 imap_unquotestr(name));
2988 rv = OKAY;
2989 jstop:
2990 free(buf);
2991 if (tp)
2992 Fclose(tp);
2993 NYD_LEAVE;
2994 return rv;
2997 #ifdef HAVE_IMAP_SEARCH
2998 static enum okay
2999 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3000 int f)
3002 char *o, *xp, *cs, c;
3003 size_t osize;
3004 FILE *queuefp = NULL;
3005 int i;
3006 unsigned long n;
3007 const char *cp;
3008 enum okay ok = STOP;
3009 NYD_X;
3011 c = 0;
3012 for (cp = spec; *cp; cp++)
3013 c |= *cp;
3014 if (c & 0200) {
3015 cp = charset_get_lc();
3016 # ifdef HAVE_ICONV
3017 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3018 iconv_t it;
3019 char *nsp, *nspec;
3020 size_t sz, nsz;
3022 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3023 sz = strlen(spec) + 1;
3024 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3025 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3026 sz == 0) {
3027 spec = nspec;
3028 cp = "utf-8";
3030 n_iconv_close(it);
3033 # endif
3034 cp = imap_quotestr(cp);
3035 cs = salloc(n = strlen(cp) + 10);
3036 snprintf(cs, n, "CHARSET %s ", cp);
3037 } else
3038 cs = UNCONST("");
3040 o = ac_alloc(osize = strlen(spec) + 60);
3041 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3042 IMAP_OUT(o, MB_COMD, goto out)
3043 while (mp->mb_active & MB_COMD) {
3044 ok = imap_answer(mp, 0);
3045 if (response_status == RESPONSE_OTHER &&
3046 response_other == MAILBOX_DATA_SEARCH) {
3047 xp = responded_other_text;
3048 while (*xp && *xp != '\r') {
3049 n = strtoul(xp, &xp, 10);
3050 for (i = 0; i < cnt; i++)
3051 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3052 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3053 mark(i+1, f);
3057 out:
3058 ac_free(o);
3059 return ok;
3062 FL enum okay
3063 imap_search1(const char * volatile spec, int f)
3065 sighandler_type saveint, savepipe;
3066 enum okay volatile rv = STOP;
3067 NYD_ENTER;
3069 if (mb.mb_type != MB_IMAP)
3070 goto jleave;
3072 imaplock = 1;
3073 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3074 safe_signal(SIGINT, &_imap_maincatch);
3075 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3076 if (sigsetjmp(imapjmp, 1) == 0) {
3077 if (savepipe != SIG_IGN)
3078 safe_signal(SIGPIPE, imapcatch);
3080 rv = imap_search2(&mb, message, msgCount, spec, f);
3082 safe_signal(SIGINT, saveint);
3083 safe_signal(SIGPIPE, savepipe);
3084 imaplock = 0;
3085 jleave:
3086 NYD_LEAVE;
3087 if (interrupts)
3088 onintr(0);
3089 return rv;
3091 #endif /* HAVE_IMAP_SEARCH */
3093 FL int
3094 imap_thisaccount(const char *cp)
3096 int rv;
3097 NYD_ENTER;
3099 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3100 rv = 0;
3101 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3102 mb.mb_imap_account == NULL)
3103 rv = 0;
3104 else
3105 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3106 NYD_LEAVE;
3107 return rv;
3110 FL enum okay
3111 imap_remove(const char * volatile name)
3113 sighandler_type volatile saveint, savepipe;
3114 enum okay rv = STOP;
3115 NYD_ENTER;
3117 if (mb.mb_type != MB_IMAP) {
3118 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3119 name);
3120 goto jleave;
3123 if (!imap_thisaccount(name)) {
3124 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3125 "server: \"%s\" not removed.\n", name);
3126 goto jleave;
3129 imaplock = 1;
3130 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3131 safe_signal(SIGINT, &_imap_maincatch);
3132 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3133 if (sigsetjmp(imapjmp, 1) == 0) {
3134 if (savepipe != SIG_IGN)
3135 safe_signal(SIGPIPE, imapcatch);
3137 rv = imap_remove1(&mb, imap_fileof(name));
3139 safe_signal(SIGINT, saveint);
3140 safe_signal(SIGPIPE, savepipe);
3141 imaplock = 0;
3143 if (rv == OKAY)
3144 rv = cache_remove(name);
3145 jleave:
3146 NYD_LEAVE;
3147 if (interrupts)
3148 onintr(0);
3149 return rv;
3152 static enum okay
3153 imap_remove1(struct mailbox *mp, const char *name)
3155 FILE *queuefp = NULL;
3156 char *o;
3157 int os;
3158 enum okay ok = STOP;
3159 NYD_X;
3161 o = ac_alloc(os = 2*strlen(name) + 100);
3162 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3163 IMAP_OUT(o, MB_COMD, goto out)
3164 while (mp->mb_active & MB_COMD)
3165 ok = imap_answer(mp, 1);
3166 out:
3167 ac_free(o);
3168 return ok;
3171 FL enum okay
3172 imap_rename(const char *old, const char *new)
3174 sighandler_type saveint, savepipe;
3175 enum okay rv = STOP;
3176 NYD_ENTER;
3178 if (mb.mb_type != MB_IMAP) {
3179 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3180 goto jleave;
3183 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3184 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3185 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3186 goto jleave;
3189 imaplock = 1;
3190 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3191 safe_signal(SIGINT, &_imap_maincatch);
3192 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3193 if (sigsetjmp(imapjmp, 1) == 0) {
3194 if (savepipe != SIG_IGN)
3195 safe_signal(SIGPIPE, imapcatch);
3197 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3199 safe_signal(SIGINT, saveint);
3200 safe_signal(SIGPIPE, savepipe);
3201 imaplock = 0;
3203 if (rv == OKAY)
3204 rv = cache_rename(old, new);
3205 jleave:
3206 NYD_LEAVE;
3207 if (interrupts)
3208 onintr(0);
3209 return rv;
3212 static enum okay
3213 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3215 FILE *queuefp = NULL;
3216 char *o;
3217 int os;
3218 enum okay ok = STOP;
3219 NYD_X;
3221 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3222 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3223 imap_quotestr(new));
3224 IMAP_OUT(o, MB_COMD, goto out)
3225 while (mp->mb_active & MB_COMD)
3226 ok = imap_answer(mp, 1);
3227 out:
3228 ac_free(o);
3229 return ok;
3232 FL enum okay
3233 imap_dequeue(struct mailbox *mp, FILE *fp)
3235 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3236 size_t bufsize, buflen, cnt;
3237 long offs, offs1, offs2, octets;
3238 int twice, gotcha = 0;
3239 FILE *queuefp = NULL;
3240 enum okay ok = OKAY, rok = OKAY;
3241 NYD_X;
3243 buf = smalloc(bufsize = LINESIZE);
3244 buflen = 0;
3245 cnt = fsize(fp);
3246 while ((offs1 = ftell(fp)) >= 0 &&
3247 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3248 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3250 while (*bp == ' ')
3251 ++bp;
3252 twice = 0;
3253 if ((offs = ftell(fp)) < 0)
3254 goto fail;
3255 again:
3256 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3257 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3258 cp = &bp[9];
3259 while (digitchar(*cp))
3260 cp++;
3261 if (*cp != ' ')
3262 goto fail;
3263 while (*cp == ' ')
3264 cp++;
3265 if ((newname = imap_strex(cp, NULL)) == NULL)
3266 goto fail;
3267 IMAP_OUT(o, MB_COMD, continue)
3268 while (mp->mb_active & MB_COMD)
3269 ok = imap_answer(mp, twice);
3270 if (response_status == RESPONSE_NO && twice++ == 0)
3271 goto trycreate;
3272 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3273 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3275 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3276 IMAP_OUT(o, MB_COMD, continue)
3277 while (mp->mb_active & MB_COMD)
3278 ok = imap_answer(mp, 1);
3279 if (ok == OKAY)
3280 gotcha++;
3281 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3282 if ((cp = strrchr(bp, '{')) == NULL)
3283 goto fail;
3284 octets = atol(&cp[1]) + 2;
3285 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3286 goto fail;
3287 IMAP_OUT(o, MB_COMD, continue)
3288 while (mp->mb_active & MB_COMD) {
3289 ok = imap_answer(mp, twice);
3290 if (response_type == RESPONSE_CONT)
3291 break;
3293 if (ok == STOP) {
3294 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3295 goto trycreate;
3296 goto fail;
3298 while (octets > 0) {
3299 size_t n = (UICMP(z, octets, >, sizeof iob)
3300 ? sizeof iob : (size_t)octets);
3301 octets -= n;
3302 if (n != fread(iob, 1, n, fp))
3303 goto fail;
3304 swrite1(&mp->mb_sock, iob, n, 1);
3306 swrite(&mp->mb_sock, "");
3307 while (mp->mb_active & MB_COMD) {
3308 ok = imap_answer(mp, 0);
3309 if (response_status == RESPONSE_NO && twice++ == 0) {
3310 if (fseek(fp, offs, SEEK_SET) < 0)
3311 goto fail;
3312 goto trycreate;
3315 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3316 if ((offs2 = ftell(fp)) < 0)
3317 goto fail;
3318 fseek(fp, offs1, SEEK_SET);
3319 if (imap_appenduid_cached(mp, fp) == STOP) {
3320 (void)fseek(fp, offs2, SEEK_SET);
3321 goto fail;
3324 } else {
3325 fail:
3326 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3327 rok = STOP;
3329 continue;
3330 trycreate:
3331 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3332 IMAP_OUT(o, MB_COMD, continue)
3333 while (mp->mb_active & MB_COMD)
3334 ok = imap_answer(mp, 1);
3335 if (ok == OKAY)
3336 goto again;
3338 fflush(fp);
3339 rewind(fp);
3340 ftruncate(fileno(fp), 0);
3341 if (gotcha)
3342 imap_close(mp);
3343 free(buf);
3344 return rok;
3347 static char *
3348 imap_strex(char const *cp, char const **xp)
3350 char const *cq;
3351 char *n = NULL;
3352 NYD_ENTER;
3354 if (*cp != '"')
3355 goto jleave;
3357 for (cq = cp + 1; *cq != '\0'; ++cq) {
3358 if (*cq == '\\')
3359 cq++;
3360 else if (*cq == '"')
3361 break;
3363 if (*cq != '"')
3364 goto jleave;
3366 n = salloc(cq - cp + 2);
3367 memcpy(n, cp, cq - cp +1);
3368 n[cq - cp + 1] = '\0';
3369 if (xp != NULL)
3370 *xp = cq + 1;
3371 jleave:
3372 NYD_LEAVE;
3373 return n;
3376 static enum okay
3377 check_expunged(void)
3379 enum okay rv;
3380 NYD_ENTER;
3382 if (expunged_messages > 0) {
3383 fprintf(stderr, "Command not executed - messages have been expunged\n");
3384 rv = STOP;
3385 } else
3386 rv = OKAY;
3387 NYD_LEAVE;
3388 return rv;
3391 FL int
3392 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3394 struct url url;
3395 int rv, omsgCount = msgCount;
3396 NYD_ENTER;
3397 UNUSED(vp);
3399 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3400 fprintf(stderr, "Already connected.\n");
3401 rv = 1;
3402 goto jleave;
3405 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3406 rv = 1;
3407 goto jleave;
3409 var_clear_allow_undefined = TRU1;
3410 ok_bclear(disconnected);
3411 vok_bclear(savecat("disconnected-", url.url_uhp.s));
3412 var_clear_allow_undefined = FAL0;
3414 if (mb.mb_type == MB_CACHE) {
3415 _imap_setfile1(&url, 0, edit, 1);
3416 if (msgCount > omsgCount)
3417 newmailinfo(omsgCount);
3419 rv = 0;
3420 jleave:
3421 NYD_LEAVE;
3422 return rv;
3425 FL int
3426 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3428 struct url url;
3429 int rv = 1, *msgvec = vp;
3430 NYD_ENTER;
3432 if (mb.mb_type == MB_CACHE) {
3433 fprintf(stderr, "Not connected.\n");
3434 goto jleave;
3436 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3437 fprintf(stderr, "The current mailbox is not cached.\n");
3438 goto jleave;
3441 if (!url_parse(&url, CPROTO_IMAP, mailname))
3442 goto jleave;
3444 if (*msgvec)
3445 c_cache(vp);
3446 ok_bset(disconnected, TRU1);
3447 if (mb.mb_type == MB_IMAP) {
3448 sclose(&mb.mb_sock);
3449 _imap_setfile1(&url, 0, edit, 1);
3451 rv = 0;
3452 jleave:
3453 NYD_LEAVE;
3454 return rv;
3457 FL int
3458 c_cache(void *vp)
3460 int rv = 1, *msgvec = vp, *ip;
3461 struct message *mp;
3462 NYD_ENTER;
3464 if (mb.mb_type != MB_IMAP) {
3465 fprintf(stderr, "Not connected to an IMAP server.\n");
3466 goto jleave;
3468 if (cached_uidvalidity(&mb) == 0) {
3469 fprintf(stderr, "The current mailbox is not cached.\n");
3470 goto jleave;
3473 for (ip = msgvec; *ip; ++ip) {
3474 mp = &message[*ip - 1];
3475 if (!(mp->m_have & HAVE_BODY))
3476 get_body(mp);
3478 rv = 0;
3479 jleave:
3480 NYD_LEAVE;
3481 return rv;
3484 FL int
3485 disconnected(const char *file)
3487 struct url url;
3488 int rv = 1;
3489 NYD_ENTER;
3491 if (ok_blook(disconnected)) {
3492 rv = 1;
3493 goto jleave;
3496 if (!url_parse(&url, CPROTO_IMAP, file)) {
3497 rv = 0;
3498 goto jleave;
3500 rv = vok_blook(savecat("disconnected-", url.url_uhp.s));
3502 jleave:
3503 NYD_LEAVE;
3504 return rv;
3507 FL void
3508 transflags(struct message *omessage, long omsgCount, int transparent)
3510 struct message *omp, *nmp, *newdot, *newprevdot;
3511 int hf;
3512 NYD_ENTER;
3514 omp = omessage;
3515 nmp = message;
3516 newdot = message;
3517 newprevdot = NULL;
3518 while (PTRCMP(omp, <, omessage + omsgCount) &&
3519 PTRCMP(nmp, <, message + msgCount)) {
3520 if (dot && nmp->m_uid == dot->m_uid)
3521 newdot = nmp;
3522 if (prevdot && nmp->m_uid == prevdot->m_uid)
3523 newprevdot = nmp;
3524 if (omp->m_uid == nmp->m_uid) {
3525 hf = nmp->m_flag & MHIDDEN;
3526 if (transparent && mb.mb_type == MB_IMAP)
3527 omp->m_flag &= ~MHIDDEN;
3528 *nmp++ = *omp++;
3529 if (transparent && mb.mb_type == MB_CACHE)
3530 nmp[-1].m_flag |= hf;
3531 } else if (omp->m_uid < nmp->m_uid)
3532 ++omp;
3533 else
3534 ++nmp;
3536 dot = newdot;
3537 setdot(newdot);
3538 prevdot = newprevdot;
3539 free(omessage);
3540 NYD_LEAVE;
3543 FL time_t
3544 imap_read_date_time(const char *cp)
3546 char buf[3];
3547 time_t t;
3548 int i, year, month, day, hour, minute, second, sign = -1;
3549 NYD_ENTER;
3551 /* "25-Jul-2004 15:33:44 +0200"
3552 * | | | | | |
3553 * 0 5 10 15 20 25 */
3554 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3555 goto jinvalid;
3556 day = strtol(&cp[1], NULL, 10);
3557 for (i = 0;;) {
3558 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3559 break;
3560 if (month_names[++i][0] == '\0')
3561 goto jinvalid;
3563 month = i + 1;
3564 year = strtol(&cp[8], NULL, 10);
3565 hour = strtol(&cp[13], NULL, 10);
3566 minute = strtol(&cp[16], NULL, 10);
3567 second = strtol(&cp[19], NULL, 10);
3568 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3569 goto jinvalid;
3570 switch (cp[22]) {
3571 case '-':
3572 sign = 1;
3573 break;
3574 case '+':
3575 break;
3576 default:
3577 goto jinvalid;
3579 buf[2] = '\0';
3580 buf[0] = cp[23];
3581 buf[1] = cp[24];
3582 t += strtol(buf, NULL, 10) * sign * 3600;
3583 buf[0] = cp[25];
3584 buf[1] = cp[26];
3585 t += strtol(buf, NULL, 10) * sign * 60;
3586 jleave:
3587 NYD_LEAVE;
3588 return t;
3589 jinvalid:
3590 time(&t);
3591 goto jleave;
3594 FL const char *
3595 imap_make_date_time(time_t t)
3597 static char s[30];
3598 struct tm *tmptr;
3599 int tzdiff, tzdiff_hour, tzdiff_min;
3600 NYD_ENTER;
3602 tzdiff = t - mktime(gmtime(&t));
3603 tzdiff_hour = (int)(tzdiff / 60);
3604 tzdiff_min = tzdiff_hour % 60;
3605 tzdiff_hour /= 60;
3606 tmptr = localtime(&t);
3607 if (tmptr->tm_isdst > 0)
3608 tzdiff_hour++;
3609 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3610 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3611 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3612 NYD_LEAVE;
3613 return s;
3615 #endif /* HAVE_IMAP */
3617 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3618 FL char *
3619 imap_quotestr(char const *s)
3621 char *n, *np;
3622 NYD_ENTER;
3624 np = n = salloc(2 * strlen(s) + 3);
3625 *np++ = '"';
3626 while (*s) {
3627 if (*s == '"' || *s == '\\')
3628 *np++ = '\\';
3629 *np++ = *s++;
3631 *np++ = '"';
3632 *np = '\0';
3633 NYD_LEAVE;
3634 return n;
3637 FL char *
3638 imap_unquotestr(char const *s)
3640 char *n, *np;
3641 NYD_ENTER;
3643 if (*s != '"') {
3644 n = savestr(s);
3645 goto jleave;
3648 np = n = salloc(strlen(s) + 1);
3649 while (*++s) {
3650 if (*s == '\\')
3651 s++;
3652 else if (*s == '"')
3653 break;
3654 *np++ = *s;
3656 *np = '\0';
3657 jleave:
3658 NYD_LEAVE;
3659 return n;
3661 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3663 /* vim:set fenc=utf-8:s-it-mode */