colour_table_create(): auto-enable $TERMs named "color" (Gavin Troy)
[s-mailx.git] / imap.c
blob1b6771a3cdcd290d37c5e1dced43d3f92574a5df
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ CRAM-MD5 as of RFC 2195.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 #ifndef HAVE_AMALGAMATION
42 # include "nail.h"
43 #endif
45 #ifdef HAVE_IMAP
46 # include <sys/socket.h>
48 # include <netdb.h>
50 # include <netinet/in.h>
52 # ifdef HAVE_ARPA_INET_H
53 # include <arpa/inet.h>
54 # endif
55 #endif
57 #ifdef HAVE_IMAP
58 #define IMAP_ANSWER() \
60 if (mp->mb_type != MB_CACHE) {\
61 enum okay ok = OKAY;\
62 while (mp->mb_active & MB_COMD)\
63 ok = imap_answer(mp, 1);\
64 if (ok == STOP)\
65 return STOP;\
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
77 do {\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
80 ACTIONBAIL;\
82 if (options & OPT_VERBVERB)\
83 fprintf(stderr, ">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static int volatile imaplock;
171 static int same_imap_account;
173 static void imap_other_get(char *pp);
174 static void imap_response_get(const char **cp);
175 static void imap_response_parse(void);
176 static enum okay imap_answer(struct mailbox *mp, int errprnt);
177 static enum okay imap_parse_list(void);
178 static enum okay imap_finish(struct mailbox *mp);
179 static void imap_timer_off(void);
180 static void imapcatch(int s);
181 static void _imap_maincatch(int s);
182 static enum okay imap_noop1(struct mailbox *mp);
183 static void rec_queue(enum rec_type type, unsigned long cnt);
184 static enum okay rec_dequeue(void);
185 static void rec_rmqueue(void);
186 static void imapalarm(int s);
187 static int imap_use_starttls(const char *uhp);
188 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
189 const char *uhp);
190 static enum okay imap_capability(struct mailbox *mp);
191 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
192 #ifdef HAVE_MD5
193 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
194 #endif
195 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
196 #ifdef HAVE_GSSAPI
197 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
198 #endif
199 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
200 static void imap_init(struct mailbox *mp, int n);
201 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
202 int *prevcount);
203 static int _imap_setfile1(struct url *urlp, int nmail, int isedit,
204 int transparent);
205 static int imap_fetchdata(struct mailbox *mp, struct message *m,
206 size_t expected, int need, const char *head,
207 size_t headsize, long headlines);
208 static void imap_putstr(struct mailbox *mp, struct message *m,
209 const char *str, const char *head, size_t headsize,
210 long headlines);
211 static enum okay imap_get(struct mailbox *mp, struct message *m,
212 enum needspec need);
213 static void commitmsg(struct mailbox *mp, struct message *to,
214 struct message *from, enum havespec have);
215 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
216 int bot, int top);
217 static enum okay imap_exit(struct mailbox *mp);
218 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
219 int needstat);
220 static enum okay imap_close(struct mailbox *mp);
221 static enum okay imap_update(struct mailbox *mp);
222 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
223 int c, const char *sp, int needstat);
224 static enum okay imap_unstore(struct message *m, int n, const char *flag);
225 static const char *tag(int new);
226 static char * imap_putflags(int f);
227 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
228 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
229 off_t off1, long xsize, enum mflag flag, time_t t);
230 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
231 static enum okay imap_list1(struct mailbox *mp, const char *base,
232 struct list_item **list, struct list_item **lend,
233 int level);
234 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
235 FILE *fp);
236 static void dopr(FILE *fp);
237 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
238 const char *name);
239 static enum okay imap_copyuid_parse(const char *cp,
240 unsigned long *uidvalidity, unsigned long *olduid,
241 unsigned long *newuid);
242 static enum okay imap_appenduid_parse(const char *cp,
243 unsigned long *uidvalidity, unsigned long *uid);
244 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
245 const char *name);
246 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
247 long off1, long xsize, long size, long lines, int flag,
248 const char *name);
249 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
250 #ifdef HAVE_IMAP_SEARCH
251 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
252 const char *spec, int f);
253 #endif
254 static enum okay imap_remove1(struct mailbox *mp, const char *name);
255 static enum okay imap_rename1(struct mailbox *mp, const char *old,
256 const char *new);
257 static char * imap_strex(char const *cp, char const **xp);
258 static enum okay check_expunged(void);
260 static void
261 imap_other_get(char *pp)
263 char *xp;
264 NYD_ENTER;
266 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
267 pp += 6;
268 response_other = MAILBOX_DATA_FLAGS;
269 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
270 pp += 5;
271 response_other = MAILBOX_DATA_LIST;
272 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
273 pp += 5;
274 response_other = MAILBOX_DATA_LSUB;
275 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
276 pp += 8;
277 response_other = MAILBOX_DATA_MAILBOX;
278 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
279 pp += 7;
280 response_other = MAILBOX_DATA_SEARCH;
281 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
282 pp += 7;
283 response_other = MAILBOX_DATA_STATUS;
284 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
285 pp += 11;
286 response_other = CAPABILITY_DATA;
287 } else {
288 responded_other_number = strtol(pp, &xp, 10);
289 while (*xp == ' ')
290 ++xp;
291 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
292 response_other = MAILBOX_DATA_EXISTS;
293 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
294 response_other = MAILBOX_DATA_RECENT;
295 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
296 response_other = MESSAGE_DATA_EXPUNGE;
297 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
298 pp = &xp[6];
299 response_other = MESSAGE_DATA_FETCH;
300 } else
301 response_other = RESPONSE_OTHER_UNKNOWN;
303 responded_other_text = pp;
304 NYD_LEAVE;
307 static void
308 imap_response_get(const char **cp)
310 NYD_ENTER;
311 if (ascncasecmp(*cp, "OK ", 3) == 0) {
312 *cp += 3;
313 response_status = RESPONSE_OK;
314 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
315 *cp += 3;
316 response_status = RESPONSE_NO;
317 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
318 *cp += 4;
319 response_status = RESPONSE_BAD;
320 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
321 *cp += 8;
322 response_status = RESPONSE_PREAUTH;
323 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
324 *cp += 4;
325 response_status = RESPONSE_BYE;
326 } else
327 response_status = RESPONSE_OTHER;
328 NYD_LEAVE;
331 static void
332 imap_response_parse(void)
334 static char *parsebuf; /* TODO Use pool */
335 static size_t parsebufsize;
337 const char *ip = imapbuf;
338 char *pp;
339 NYD_ENTER;
341 if (parsebufsize < imapbufsize)
342 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
343 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
344 pp = parsebuf;
345 switch (*ip) {
346 case '+':
347 response_type = RESPONSE_CONT;
348 ip++;
349 pp++;
350 while (*ip == ' ') {
351 ip++;
352 pp++;
354 break;
355 case '*':
356 ip++;
357 pp++;
358 while (*ip == ' ') {
359 ip++;
360 pp++;
362 imap_response_get(&ip);
363 pp = &parsebuf[ip - imapbuf];
364 switch (response_status) {
365 case RESPONSE_BYE:
366 response_type = RESPONSE_FATAL;
367 break;
368 default:
369 response_type = RESPONSE_DATA;
371 break;
372 default:
373 responded_tag = parsebuf;
374 while (*pp && *pp != ' ')
375 pp++;
376 if (*pp == '\0') {
377 response_type = RESPONSE_ILLEGAL;
378 break;
380 *pp++ = '\0';
381 while (*pp && *pp == ' ')
382 pp++;
383 if (*pp == '\0') {
384 response_type = RESPONSE_ILLEGAL;
385 break;
387 ip = &imapbuf[pp - parsebuf];
388 response_type = RESPONSE_TAGGED;
389 imap_response_get(&ip);
390 pp = &parsebuf[ip - imapbuf];
392 responded_text = pp;
393 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
394 response_status == RESPONSE_OTHER)
395 imap_other_get(pp);
396 NYD_LEAVE;
399 static enum okay
400 imap_answer(struct mailbox *mp, int errprnt)
402 int i, complete;
403 enum okay rv;
404 NYD_ENTER;
406 rv = OKAY;
407 if (mp->mb_type == MB_CACHE)
408 goto jleave;
409 rv = STOP;
410 jagain:
411 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
412 if (options & OPT_VERBVERB)
413 fputs(imapbuf, stderr);
414 imap_response_parse();
415 if (response_type == RESPONSE_ILLEGAL)
416 goto jagain;
417 if (response_type == RESPONSE_CONT) {
418 rv = OKAY;
419 goto jleave;
421 if (response_status == RESPONSE_OTHER) {
422 if (response_other == MAILBOX_DATA_EXISTS) {
423 had_exists = responded_other_number;
424 rec_queue(REC_EXISTS, responded_other_number);
425 if (had_expunge > 0)
426 had_expunge = 0;
427 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
428 rec_queue(REC_EXPUNGE, responded_other_number);
429 if (had_expunge < 0)
430 had_expunge = 0;
431 had_expunge++;
432 expunged_messages++;
435 complete = 0;
436 if (response_type == RESPONSE_TAGGED) {
437 if (asccasecmp(responded_tag, tag(0)) == 0)
438 complete |= 1;
439 else
440 goto jagain;
442 switch (response_status) {
443 case RESPONSE_PREAUTH:
444 mp->mb_active &= ~MB_PREAUTH;
445 /*FALLTHRU*/
446 case RESPONSE_OK:
447 jokay:
448 rv = OKAY;
449 complete |= 2;
450 break;
451 case RESPONSE_NO:
452 case RESPONSE_BAD:
453 jstop:
454 rv = STOP;
455 complete |= 2;
456 if (errprnt)
457 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
458 break;
459 case RESPONSE_UNKNOWN: /* does not happen */
460 case RESPONSE_BYE:
461 i = mp->mb_active;
462 mp->mb_active = MB_NONE;
463 if (i & MB_BYE)
464 goto jokay;
465 goto jstop;
466 case RESPONSE_OTHER:
467 rv = OKAY;
468 break;
470 if (response_status != RESPONSE_OTHER &&
471 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
472 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
473 if (complete == 3)
474 mp->mb_active &= ~MB_COMD;
475 } else {
476 rv = STOP;
477 mp->mb_active = MB_NONE;
479 jleave:
480 NYD_LEAVE;
481 return rv;
484 static enum okay
485 imap_parse_list(void)
487 char *cp;
488 enum okay rv;
489 NYD_ENTER;
491 rv = STOP;
493 cp = responded_other_text;
494 list_attributes = LIST_NONE;
495 if (*cp == '(') {
496 while (*cp && *cp != ')') {
497 if (*cp == '\\') {
498 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
499 list_attributes |= LIST_NOINFERIORS;
500 cp += 12;
501 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
502 list_attributes |= LIST_NOSELECT;
503 cp += 9;
504 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
505 list_attributes |= LIST_MARKED;
506 cp += 7;
507 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
508 list_attributes |= LIST_UNMARKED;
509 cp += 9;
512 cp++;
514 if (*++cp != ' ')
515 goto jleave;
516 while (*cp == ' ')
517 cp++;
520 list_hierarchy_delimiter = EOF;
521 if (*cp == '"') {
522 if (*++cp == '\\')
523 cp++;
524 list_hierarchy_delimiter = *cp++ & 0377;
525 if (cp[0] != '"' || cp[1] != ' ')
526 goto jleave;
527 cp++;
528 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
529 list_hierarchy_delimiter = EOF;
530 cp += 3;
533 while (*cp == ' ')
534 cp++;
535 list_name = cp;
536 while (*cp && *cp != '\r')
537 cp++;
538 *cp = '\0';
539 rv = OKAY;
540 jleave:
541 NYD_LEAVE;
542 return rv;
545 static enum okay
546 imap_finish(struct mailbox *mp)
548 NYD_ENTER;
549 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
550 imap_answer(mp, 1);
551 NYD_LEAVE;
552 return OKAY;
555 static void
556 imap_timer_off(void)
558 NYD_ENTER;
559 if (imapkeepalive > 0) {
560 alarm(0);
561 safe_signal(SIGALRM, savealrm);
563 NYD_LEAVE;
566 static void
567 imapcatch(int s)
569 NYD_X; /* Signal handler */
570 switch (s) {
571 case SIGINT:
572 fprintf(stderr, tr(102, "Interrupt\n"));
573 siglongjmp(imapjmp, 1);
574 /*NOTREACHED*/
575 case SIGPIPE:
576 fprintf(stderr, tr(98, "Received SIGPIPE during IMAP operation\n"));
577 break;
581 static void
582 _imap_maincatch(int s)
584 NYD_X; /* Signal handler */
585 UNUSED(s);
586 if (interrupts++ == 0) {
587 fprintf(stderr, tr(102, "Interrupt\n"));
588 return;
590 onintr(0);
593 static enum okay
594 imap_noop1(struct mailbox *mp)
596 char o[LINESIZE];
597 FILE *queuefp = NULL;
598 NYD_X;
600 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
601 IMAP_OUT(o, MB_COMD, return STOP)
602 IMAP_ANSWER()
603 return OKAY;
606 FL char const *
607 imap_fileof(char const *xcp)
609 char const *cp = xcp;
610 int state = 0;
611 NYD_ENTER;
613 while (*cp) {
614 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
615 cp += 3;
616 state = 1;
618 if (cp[0] == '/' && state == 1) {
619 ++cp;
620 goto jleave;
622 if (cp[0] == '/') {
623 cp = xcp;
624 goto jleave;
626 ++cp;
628 jleave:
629 NYD_LEAVE;
630 return cp;
633 FL enum okay
634 imap_noop(void)
636 sighandler_type volatile oldint, oldpipe;
637 enum okay rv = STOP;
638 NYD_ENTER;
640 if (mb.mb_type != MB_IMAP)
641 goto jleave;
643 imaplock = 1;
644 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
645 safe_signal(SIGINT, &_imap_maincatch);
646 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
647 if (sigsetjmp(imapjmp, 1) == 0) {
648 if (oldpipe != SIG_IGN)
649 safe_signal(SIGPIPE, imapcatch);
651 rv = imap_noop1(&mb);
653 safe_signal(SIGINT, oldint);
654 safe_signal(SIGPIPE, oldpipe);
655 imaplock = 0;
656 jleave:
657 NYD_LEAVE;
658 if (interrupts)
659 onintr(0);
660 return rv;
663 static void
664 rec_queue(enum rec_type rt, unsigned long cnt)
666 struct record *rp;
667 NYD_ENTER;
669 rp = scalloc(1, sizeof *rp);
670 rp->rec_type = rt;
671 rp->rec_count = cnt;
672 if (record && recend) {
673 recend->rec_next = rp;
674 recend = rp;
675 } else
676 record = recend = rp;
677 NYD_LEAVE;
680 static enum okay
681 rec_dequeue(void)
683 struct message *omessage;
684 struct record *rp, *rq;
685 uiz_t exists = 0, i;
686 enum okay rv = STOP;
687 NYD_ENTER;
689 if (record == NULL)
690 goto jleave;
692 omessage = message;
693 message = smalloc((msgCount+1) * sizeof *message);
694 if (msgCount)
695 memcpy(message, omessage, msgCount * sizeof *message);
696 memset(&message[msgCount], 0, sizeof *message);
698 rp = record, rq = NULL;
699 rv = OKAY;
700 while (rp != NULL) {
701 switch (rp->rec_type) {
702 case REC_EXISTS:
703 exists = rp->rec_count;
704 break;
705 case REC_EXPUNGE:
706 if (rp->rec_count == 0) {
707 rv = STOP;
708 break;
710 if (rp->rec_count > (unsigned long)msgCount) {
711 if (exists == 0 || rp->rec_count > exists--)
712 rv = STOP;
713 break;
715 if (exists > 0)
716 exists--;
717 delcache(&mb, &message[rp->rec_count-1]);
718 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
719 ((msgCount - rp->rec_count + 1) * sizeof *message));
720 --msgCount;
721 /* If the message was part of a collapsed thread,
722 * the m_collapsed field of one of its ancestors
723 * should be incremented. It seems hardly possible
724 * to do this with the current message structure,
725 * though. The result is that a '+' may be shown
726 * in the header summary even if no collapsed
727 * children exists */
728 break;
730 if (rq != NULL)
731 free(rq);
732 rq = rp;
733 rp = rp->rec_next;
735 if (rq != NULL)
736 free(rq);
738 record = recend = NULL;
739 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
740 message = srealloc(message, (exists + 1) * sizeof *message);
741 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
742 for (i = msgCount; i < exists; ++i)
743 imap_init(&mb, i);
744 imap_flags(&mb, msgCount+1, exists);
745 msgCount = exists;
748 if (rv == STOP) {
749 free(message);
750 message = omessage;
752 jleave:
753 NYD_LEAVE;
754 return rv;
757 static void
758 rec_rmqueue(void)
760 struct record *rp;
761 NYD_ENTER;
763 for (rp = record; rp != NULL;) {
764 struct record *tmp = rp;
765 rp = rp->rec_next;
766 free(tmp);
768 record = recend = NULL;
769 NYD_LEAVE;
772 /*ARGSUSED*/
773 static void
774 imapalarm(int s)
776 sighandler_type volatile saveint, savepipe;
777 NYD_X; /* Signal handler */
778 UNUSED(s);
780 if (imaplock++ == 0) {
781 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
782 safe_signal(SIGINT, &_imap_maincatch);
783 savepipe = safe_signal(SIGPIPE, SIG_IGN);
784 if (sigsetjmp(imapjmp, 1)) {
785 safe_signal(SIGINT, saveint);
786 safe_signal(SIGPIPE, savepipe);
787 goto jbrk;
789 if (savepipe != SIG_IGN)
790 safe_signal(SIGPIPE, imapcatch);
791 if (imap_noop1(&mb) != OKAY) {
792 safe_signal(SIGINT, saveint);
793 safe_signal(SIGPIPE, savepipe);
794 goto jleave;
796 safe_signal(SIGINT, saveint);
797 safe_signal(SIGPIPE, savepipe);
799 jbrk:
800 alarm(imapkeepalive);
801 jleave:
802 --imaplock;
805 static int
806 imap_use_starttls(const char *uhp)
808 int rv;
809 NYD_ENTER;
811 if (ok_blook(imap_use_starttls))
812 rv = 1;
813 else {
814 char *var = savecat("imap-use-starttls-", uhp);
815 rv = vok_blook(var);
817 NYD_LEAVE;
818 return rv;
821 static enum okay
822 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
824 char *cp;
825 NYD_X;
827 mp->mb_active |= MB_PREAUTH;
828 imap_answer(mp, 1);
829 if ((cp = strchr(xserver, ':')) != NULL) {
830 char *x = salloc(cp - xserver + 1);
831 memcpy(x, xserver, cp - xserver);
832 x[cp - xserver] = '\0';
833 xserver = x;
835 #ifdef HAVE_SSL
836 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
837 FILE *queuefp = NULL;
838 char o[LINESIZE];
840 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
841 IMAP_OUT(o, MB_COMD, return STOP)
842 IMAP_ANSWER()
843 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
844 return STOP;
846 #else
847 if (imap_use_starttls(uhp)) {
848 fprintf(stderr, "No SSL support compiled in.\n");
849 return STOP;
851 #endif
852 imap_capability(mp);
853 return OKAY;
856 static enum okay
857 imap_capability(struct mailbox *mp)
859 char o[LINESIZE];
860 FILE *queuefp = NULL;
861 enum okay ok = STOP;
862 const char *cp;
863 NYD_X;
865 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
866 IMAP_OUT(o, MB_COMD, return STOP)
867 while (mp->mb_active & MB_COMD) {
868 ok = imap_answer(mp, 0);
869 if (response_status == RESPONSE_OTHER &&
870 response_other == CAPABILITY_DATA) {
871 cp = responded_other_text;
872 while (*cp) {
873 while (spacechar(*cp))
874 ++cp;
875 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
876 /* RFC 2359 */
877 mp->mb_flags |= MB_UIDPLUS;
878 while (*cp && !spacechar(*cp))
879 ++cp;
883 return ok;
886 static enum okay
887 imap_auth(struct mailbox *mp, struct ccred *ccred)
889 enum okay rv;
890 NYD_ENTER;
892 if (!(mp->mb_active & MB_PREAUTH)) {
893 rv = OKAY;
894 goto jleave;
897 switch (ccred->cc_authtype) {
898 case AUTHTYPE_LOGIN:
899 rv = imap_login(mp, ccred);
900 break;
901 #ifdef HAVE_MD5
902 case AUTHTYPE_CRAM_MD5:
903 rv = imap_cram_md5(mp, ccred);
904 break;
905 #endif
906 #ifdef HAVE_GSSAPI
907 case AUTHTYPE_GSSAPI:
908 rv = _imap_gssapi(mp, ccred);
909 break;
910 #endif
911 default:
912 rv = STOP;
913 break;
915 jleave:
916 NYD_LEAVE;
917 return rv;
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, &ccred->cc_pass, 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 goto jduppass;
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 jduppass:
1189 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1192 if (!(ok_blook(v15_compat) ? ccred_lookup(&ccred, urlp)
1193 : ccred_lookup_old(&ccred, CPROTO_IMAP, urlp->url_uhp.s))) {
1194 rv = -1;
1195 goto jleave;
1198 so.s_fd = -1;
1199 if (!same_imap_account) {
1200 if (!disconnected(urlp->url_puhp) && !sopen(&so, urlp)) {
1201 rv = -1;
1202 goto jleave;
1204 } else
1205 so = mb.mb_sock;
1206 if (!transparent)
1207 quit();
1209 edit = (isedit != 0);
1210 if (mb.mb_imap_account != NULL)
1211 free(mb.mb_imap_account);
1212 if (mb.mb_imap_pass != NULL)
1213 free(mb.mb_imap_pass);
1214 mb.mb_imap_account = sstrdup(urlp->url_puhp);
1215 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1216 * TODO object, and mailbox will naturally have an URL and credentials */
1217 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1219 if (!same_imap_account) {
1220 if (mb.mb_sock.s_fd >= 0)
1221 sclose(&mb.mb_sock);
1223 same_imap_account = 0;
1225 if (!transparent) {
1226 if (mb.mb_itf) {
1227 fclose(mb.mb_itf);
1228 mb.mb_itf = NULL;
1230 if (mb.mb_otf) {
1231 fclose(mb.mb_otf);
1232 mb.mb_otf = NULL;
1234 if (mb.mb_imap_mailbox != NULL)
1235 free(mb.mb_imap_mailbox);
1236 mb.mb_imap_mailbox = sstrdup((urlp->url_path.s != NULL)
1237 ? urlp->url_path.s : "INBOX");
1238 initbox(urlp->url_puhpp);
1240 mb.mb_type = MB_VOID;
1241 mb.mb_active = MB_NONE;
1243 imaplock = 1;
1244 saveint = safe_signal(SIGINT, SIG_IGN);
1245 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1246 if (sigsetjmp(imapjmp, 1)) {
1247 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1248 sclose(&mb.mb_sock);
1249 safe_signal(SIGINT, saveint);
1250 safe_signal(SIGPIPE, savepipe);
1251 imaplock = 0;
1253 mb.mb_type = MB_VOID;
1254 mb.mb_active = MB_NONE;
1255 rv = -1;
1256 goto jleave;
1258 if (saveint != SIG_IGN)
1259 safe_signal(SIGINT, imapcatch);
1260 if (savepipe != SIG_IGN)
1261 safe_signal(SIGPIPE, imapcatch);
1263 if (mb.mb_sock.s_fd < 0) {
1264 if (disconnected(mb.mb_imap_account)) {
1265 if (cache_setptr(transparent) == STOP)
1266 fprintf(stderr, "Mailbox \"%s\" is not cached.\n",
1267 urlp->url_puhpp);
1268 goto jdone;
1270 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1271 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1272 savealrm = safe_signal(SIGALRM, imapalarm);
1273 alarm(imapkeepalive);
1277 mb.mb_sock = so;
1278 mb.mb_sock.s_desc = "IMAP";
1279 mb.mb_sock.s_onclose = imap_timer_off;
1280 if (imap_preauth(&mb, urlp->url_hp.s, urlp->url_uhp.s) != OKAY ||
1281 imap_auth(&mb, &ccred) != OKAY) {
1282 sclose(&mb.mb_sock);
1283 imap_timer_off();
1284 safe_signal(SIGINT, saveint);
1285 safe_signal(SIGPIPE, savepipe);
1286 imaplock = 0;
1287 rv = -1;
1288 goto jleave;
1290 } else /* same account */
1291 mb.mb_flags |= same_flags;
1293 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1294 mb.mb_type = MB_IMAP;
1295 cache_dequeue(&mb);
1296 if (imap_select(&mb, &mailsize, &msgCount,
1297 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX")) != OKAY) {
1298 /*sclose(&mb.mb_sock);
1299 imap_timer_off();*/
1300 safe_signal(SIGINT, saveint);
1301 safe_signal(SIGPIPE, savepipe);
1302 imaplock = 0;
1303 mb.mb_type = MB_VOID;
1304 rv = -1;
1305 goto jleave;
1308 jnmail:
1309 imap_setptr(&mb, nmail, transparent, UNVOLATILE(&prevcount));
1310 jdone:
1311 setmsize(msgCount);
1312 if (!nmail && !transparent)
1313 sawcom = FAL0;
1314 safe_signal(SIGINT, saveint);
1315 safe_signal(SIGPIPE, savepipe);
1316 imaplock = 0;
1318 if (!nmail && mb.mb_type == MB_IMAP)
1319 purgecache(&mb, message, msgCount);
1320 if ((nmail || transparent) && mb.mb_sorted) {
1321 mb.mb_threaded = 0;
1322 c_sort((void*)-1);
1325 if (!nmail && !edit && msgCount == 0) {
1326 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1327 !ok_blook(emptystart))
1328 fprintf(stderr, tr(258, "No mail at %s\n"), urlp->url_puhpp);
1329 rv = 1;
1330 goto jleave;
1332 if (nmail)
1333 newmailinfo(prevcount);
1334 rv = 0;
1335 jleave:
1336 NYD_LEAVE;
1337 return rv;
1340 static int
1341 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1342 int need, const char *head, size_t headsize, long headlines)
1344 char *line = NULL, *lp;
1345 size_t linesize = 0, linelen, size = 0;
1346 int emptyline = 0, lines = 0, excess = 0;
1347 off_t offset;
1348 NYD_ENTER;
1350 fseek(mp->mb_otf, 0L, SEEK_END);
1351 offset = ftell(mp->mb_otf);
1353 if (head)
1354 fwrite(head, 1, headsize, mp->mb_otf);
1356 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1357 lp = line;
1358 if (linelen > expected) {
1359 excess = linelen - expected;
1360 linelen = expected;
1362 /* TODO >>
1363 * Need to mask 'From ' lines. This cannot be done properly
1364 * since some servers pass them as 'From ' and others as
1365 * '>From '. Although one could identify the first kind of
1366 * server in principle, it is not possible to identify the
1367 * second as '>From ' may also come from a server of the
1368 * first type as actual data. So do what is absolutely
1369 * necessary only - mask 'From '.
1371 * If the line is the first line of the message header, it
1372 * is likely a real 'From ' line. In this case, it is just
1373 * ignored since it violates all standards.
1374 * TODO can the latter *really* happen??
1375 * TODO <<
1377 /* Since we simply copy over data without doing any transfer
1378 * encoding reclassification/adjustment we *have* to perform
1379 * RFC 4155 compliant From_ quoting here */
1380 if (is_head(lp, linelen)) {
1381 if (lines + headlines == 0)
1382 goto jskip;
1383 fputc('>', mp->mb_otf);
1384 ++size;
1386 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1387 emptyline = linelen <= 2;
1388 if (linelen > 2) {
1389 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1390 size += linelen - 1;
1391 } else
1392 ++size;
1393 fputc('\n', mp->mb_otf);
1394 } else {
1395 emptyline = 0;
1396 fwrite(lp, 1, linelen, mp->mb_otf);
1397 size += linelen;
1399 ++lines;
1400 jskip:
1401 if ((expected -= linelen) <= 0)
1402 break;
1404 if (!emptyline) {
1405 /* This is very ugly; but some IMAP daemons don't end a
1406 * message with \r\n\r\n, and we need \n\n for mbox format */
1407 fputc('\n', mp->mb_otf);
1408 ++lines;
1409 ++size;
1411 fflush(mp->mb_otf);
1413 if (m != NULL) {
1414 m->m_size = size + headsize;
1415 m->m_lines = lines + headlines;
1416 m->m_block = mailx_blockof(offset);
1417 m->m_offset = mailx_offsetof(offset);
1418 switch (need) {
1419 case NEED_HEADER:
1420 m->m_have |= HAVE_HEADER;
1421 break;
1422 case NEED_BODY:
1423 m->m_have |= HAVE_HEADER | HAVE_BODY;
1424 m->m_xlines = m->m_lines;
1425 m->m_xsize = m->m_size;
1426 break;
1429 free(line);
1430 NYD_LEAVE;
1431 return excess;
1434 static void
1435 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1436 const char *head, size_t headsize, long headlines)
1438 off_t offset;
1439 size_t len;
1440 NYD_ENTER;
1442 len = strlen(str);
1443 fseek(mp->mb_otf, 0L, SEEK_END);
1444 offset = ftell(mp->mb_otf);
1445 if (head)
1446 fwrite(head, 1, headsize, mp->mb_otf);
1447 if (len > 0) {
1448 fwrite(str, 1, len, mp->mb_otf);
1449 fputc('\n', mp->mb_otf);
1450 ++len;
1452 fflush(mp->mb_otf);
1454 if (m != NULL) {
1455 m->m_size = headsize + len;
1456 m->m_lines = headlines + 1;
1457 m->m_block = mailx_blockof(offset);
1458 m->m_offset = mailx_offsetof(offset);
1459 m->m_have |= HAVE_HEADER | HAVE_BODY;
1460 m->m_xlines = m->m_lines;
1461 m->m_xsize = m->m_size;
1463 NYD_LEAVE;
1466 static enum okay
1467 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1469 char o[LINESIZE];
1470 struct message mt;
1471 sighandler_type volatile saveint, savepipe;
1472 char * volatile head;
1473 char const *cp, *loc, * volatile item, * volatile resp;
1474 size_t expected;
1475 size_t volatile headsize;
1476 int number;
1477 FILE *queuefp;
1478 long volatile headlines;
1479 long n;
1480 ul_it volatile u;
1481 enum okay ok;
1482 NYD_X;
1484 saveint = savepipe = SIG_IGN;
1485 head = NULL;
1486 cp = loc = item = resp = NULL;
1487 headsize = 0;
1488 number = (int)PTR2SIZE(m - message + 1);
1489 queuefp = NULL;
1490 headlines = 0;
1491 n = -1;
1492 u = 0;
1493 ok = STOP;
1495 if (getcache(mp, m, need) == OKAY)
1496 return OKAY;
1497 if (mp->mb_type == MB_CACHE) {
1498 fprintf(stderr, "Message %u not available.\n", number);
1499 return STOP;
1502 if (mp->mb_sock.s_fd < 0) {
1503 fprintf(stderr, "IMAP connection closed.\n");
1504 return STOP;
1507 switch (need) {
1508 case NEED_HEADER:
1509 resp = item = "RFC822.HEADER";
1510 break;
1511 case NEED_BODY:
1512 item = "BODY.PEEK[]";
1513 resp = "BODY[]";
1514 if (m->m_flag & HAVE_HEADER && m->m_size) {
1515 char *hdr = smalloc(m->m_size);
1516 fflush(mp->mb_otf);
1517 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1518 SEEK_SET) < 0 ||
1519 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1520 free(hdr);
1521 break;
1523 head = hdr;
1524 headsize = m->m_size;
1525 headlines = m->m_lines;
1526 item = "BODY.PEEK[TEXT]";
1527 resp = "BODY[TEXT]";
1529 break;
1530 case NEED_UNSPEC:
1531 return STOP;
1534 imaplock = 1;
1535 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1536 if (sigsetjmp(imapjmp, 1)) {
1537 safe_signal(SIGINT, saveint);
1538 safe_signal(SIGPIPE, savepipe);
1539 imaplock = 0;
1540 return STOP;
1542 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1543 safe_signal(SIGINT, &_imap_maincatch);
1544 if (savepipe != SIG_IGN)
1545 safe_signal(SIGPIPE, imapcatch);
1547 if (m->m_uid)
1548 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1549 tag(1), m->m_uid, item);
1550 else {
1551 if (check_expunged() == STOP)
1552 goto out;
1553 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1555 IMAP_OUT(o, MB_COMD, goto out)
1556 for (;;) {
1557 ok = imap_answer(mp, 1);
1558 if (ok == STOP)
1559 break;
1560 if (response_status != RESPONSE_OTHER ||
1561 response_other != MESSAGE_DATA_FETCH)
1562 continue;
1563 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1564 continue;
1565 if (m->m_uid) {
1566 if ((cp = asccasestr(responded_other_text, "UID "))) {
1567 u = atol(&cp[4]);
1568 n = 0;
1569 } else {
1570 n = -1;
1571 u = 0;
1573 } else
1574 n = responded_other_number;
1575 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1576 if (m->m_uid ? m->m_uid != u : n != number)
1577 continue;
1578 if ((cp = strchr(loc, '"')) != NULL) {
1579 cp = imap_unquotestr(cp);
1580 imap_putstr(mp, m, cp, head, headsize, headlines);
1581 } else {
1582 m->m_have |= HAVE_HEADER|HAVE_BODY;
1583 m->m_xlines = m->m_lines;
1584 m->m_xsize = m->m_size;
1586 goto out;
1588 expected = atol(&cp[1]);
1589 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1590 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1591 continue;
1593 mt = *m;
1594 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1595 if (n >= 0) {
1596 commitmsg(mp, m, &mt, mt.m_have);
1597 break;
1599 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1600 if (options & OPT_VERBVERB)
1601 fputs(imapbuf, stderr);
1602 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1603 u = atol(&cp[4]);
1604 if (u == m->m_uid) {
1605 commitmsg(mp, m, &mt, mt.m_have);
1606 break;
1611 out:
1612 while (mp->mb_active & MB_COMD)
1613 ok = imap_answer(mp, 1);
1615 if (saveint != SIG_IGN)
1616 safe_signal(SIGINT, saveint);
1617 if (savepipe != SIG_IGN)
1618 safe_signal(SIGPIPE, savepipe);
1619 imaplock--;
1621 if (ok == OKAY)
1622 putcache(mp, m);
1623 if (head != NULL)
1624 free(head);
1625 if (interrupts)
1626 onintr(0);
1627 return ok;
1630 FL enum okay
1631 imap_header(struct message *m)
1633 enum okay rv;
1634 NYD_ENTER;
1636 rv = imap_get(&mb, m, NEED_HEADER);
1637 NYD_LEAVE;
1638 return rv;
1642 FL enum okay
1643 imap_body(struct message *m)
1645 enum okay rv;
1646 NYD_ENTER;
1648 rv = imap_get(&mb, m, NEED_BODY);
1649 NYD_LEAVE;
1650 return rv;
1653 static void
1654 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1655 enum havespec have)
1657 NYD_ENTER;
1658 tomp->m_size = frommp->m_size;
1659 tomp->m_lines = frommp->m_lines;
1660 tomp->m_block = frommp->m_block;
1661 tomp->m_offset = frommp->m_offset;
1662 tomp->m_have = have;
1663 if (have & HAVE_BODY) {
1664 tomp->m_xlines = frommp->m_lines;
1665 tomp->m_xsize = frommp->m_size;
1667 putcache(mp, tomp);
1668 NYD_LEAVE;
1671 static enum okay
1672 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1674 /* bot > topp */
1675 char o[LINESIZE];
1676 char const *cp;
1677 struct message mt;
1678 size_t expected;
1679 int n = 0, u;
1680 FILE *queuefp = NULL;
1681 enum okay ok;
1682 NYD_X;
1684 if (m[bot].m_uid)
1685 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1686 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1687 else {
1688 if (check_expunged() == STOP)
1689 return STOP;
1690 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1691 tag(1), bot, topp);
1693 IMAP_OUT(o, MB_COMD, return STOP)
1694 for (;;) {
1695 ok = imap_answer(mp, 1);
1696 if (response_status != RESPONSE_OTHER)
1697 break;
1698 if (response_other != MESSAGE_DATA_FETCH)
1699 continue;
1700 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1701 return STOP;
1702 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1703 continue;
1704 expected = atol(&cp[1]);
1705 if (m[bot-1].m_uid) {
1706 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1707 u = atoi(&cp[4]);
1708 for (n = bot; n <= topp; n++)
1709 if ((unsigned long)u == m[n-1].m_uid)
1710 break;
1711 if (n > topp) {
1712 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1713 continue;
1715 } else
1716 n = -1;
1717 } else {
1718 n = responded_other_number;
1719 if (n <= 0 || n > msgCount) {
1720 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1721 continue;
1724 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1725 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1726 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1727 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1728 if (options & OPT_VERBVERB)
1729 fputs(imapbuf, stderr);
1730 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1731 u = atoi(&cp[4]);
1732 for (n = bot; n <= topp; n++)
1733 if ((unsigned long)u == m[n-1].m_uid)
1734 break;
1735 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1736 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1737 n = 0;
1741 while (mp->mb_active & MB_COMD)
1742 ok = imap_answer(mp, 1);
1743 return ok;
1746 FL void
1747 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1749 sighandler_type saveint, savepipe;
1750 /* enum okay ok = STOP;*/
1751 int i, chunk = 256;
1752 NYD_X;
1754 if (mb.mb_type == MB_CACHE)
1755 return;
1756 if (bot < 1)
1757 bot = 1;
1758 if (topp > msgCount)
1759 topp = msgCount;
1760 for (i = bot; i < topp; i++) {
1761 if (message[i-1].m_have & HAVE_HEADER ||
1762 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1763 bot = i+1;
1764 else
1765 break;
1767 for (i = topp; i > bot; i--) {
1768 if (message[i-1].m_have & HAVE_HEADER ||
1769 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1770 topp = i-1;
1771 else
1772 break;
1774 if (bot >= topp)
1775 return;
1777 imaplock = 1;
1778 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1779 safe_signal(SIGINT, &_imap_maincatch);
1780 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1781 if (sigsetjmp(imapjmp, 1) == 0) {
1782 if (savepipe != SIG_IGN)
1783 safe_signal(SIGPIPE, imapcatch);
1785 for (i = bot; i <= topp; i += chunk) {
1786 int j = i + chunk - 1;
1787 j = MIN(j, topp);
1788 if (visible(message + j))
1789 /*ok = */imap_fetchheaders(&mb, message, i, j);
1790 if (interrupts)
1791 onintr(0); /* XXX imaplock? */
1794 safe_signal(SIGINT, saveint);
1795 safe_signal(SIGPIPE, savepipe);
1796 imaplock = 0;
1799 static enum okay
1800 __imap_exit(struct mailbox *mp)
1802 char o[LINESIZE];
1803 FILE *queuefp = NULL;
1804 NYD_X;
1806 mp->mb_active |= MB_BYE;
1807 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1808 IMAP_OUT(o, MB_COMD, return STOP)
1809 IMAP_ANSWER()
1810 return OKAY;
1813 static enum okay
1814 imap_exit(struct mailbox *mp)
1816 enum okay rv;
1817 NYD_ENTER;
1819 rv = __imap_exit(mp);
1820 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1821 free(mp->mb_imap_pass);
1822 free(mp->mb_imap_account);
1823 free(mp->mb_imap_mailbox);
1824 if (mp->mb_cache_directory != NULL)
1825 free(mp->mb_cache_directory);
1826 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1827 mp->mb_imap_account =
1828 mp->mb_imap_mailbox =
1829 mp->mb_cache_directory = "";
1830 #else
1831 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1832 mp->mb_imap_mailbox = NULL;
1833 mp->mb_cache_directory = NULL;
1834 #endif
1835 #endif
1836 sclose(&mp->mb_sock);
1837 NYD_LEAVE;
1838 return rv;
1841 static enum okay
1842 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1844 NYD_ENTER;
1845 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1846 if (mp->mb_type == MB_IMAP)
1847 delcache(mp, m);
1848 NYD_LEAVE;
1849 return OKAY;
1852 static enum okay
1853 imap_close(struct mailbox *mp)
1855 char o[LINESIZE];
1856 FILE *queuefp = NULL;
1857 NYD_X;
1859 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1860 IMAP_OUT(o, MB_COMD, return STOP)
1861 IMAP_ANSWER()
1862 return OKAY;
1865 static enum okay
1866 imap_update(struct mailbox *mp)
1868 struct message *m;
1869 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1870 NYD_ENTER;
1872 if (!edit && mp->mb_perm != 0) {
1873 holdbits();
1874 c = 0;
1875 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1876 if (m->m_flag & MBOX)
1877 ++c;
1878 if (c > 0)
1879 if (makembox() == STOP)
1880 goto jbypass;
1883 gotcha = held = 0;
1884 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1885 if (mp->mb_perm == 0)
1886 dodel = 0;
1887 else if (edit)
1888 dodel = ((m->m_flag & MDELETED) != 0);
1889 else
1890 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1892 /* Fetch the result after around each 800 STORE commands
1893 * sent (approx. 32k data sent). Otherwise, servers will
1894 * try to flush the return queue at some point, leading
1895 * to a deadlock if we are still writing commands but not
1896 * reading their results */
1897 needstat = stored > 0 && stored % 800 == 0;
1898 /* Even if this message has been deleted, continue
1899 * to set further flags. This is necessary to support
1900 * Gmail semantics, where "delete" actually means
1901 * "archive", and the flags are applied to the copy
1902 * in "All Mail" */
1903 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1904 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1905 stored++;
1907 if (m->m_flag & MFLAG) {
1908 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1909 stored++;
1911 if (m->m_flag & MUNFLAG) {
1912 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1913 stored++;
1915 if (m->m_flag & MANSWER) {
1916 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1917 stored++;
1919 if (m->m_flag & MUNANSWER) {
1920 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1921 stored++;
1923 if (m->m_flag & MDRAFT) {
1924 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1925 stored++;
1927 if (m->m_flag & MUNDRAFT) {
1928 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1929 stored++;
1932 if (dodel) {
1933 imap_delete(mp, m-message+1, m, needstat);
1934 stored++;
1935 gotcha++;
1936 } else if (mp->mb_type != MB_CACHE ||
1937 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1938 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1939 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1940 held++;
1941 if (m->m_flag & MNEW) {
1942 m->m_flag &= ~MNEW;
1943 m->m_flag |= MSTATUS;
1946 jbypass:
1947 if (gotcha)
1948 imap_close(mp);
1950 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1951 if (!(m->m_flag & MUNLINKED) &&
1952 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1953 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1954 putcache(mp, m);
1955 modflags++;
1957 if ((gotcha || modflags) && edit) {
1958 printf(tr(168, "\"%s\" "), displayname);
1959 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1960 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1961 } else if (held && !edit && mp->mb_perm != 0) {
1962 if (held == 1)
1963 printf(tr(155, "Held 1 message in %s\n"), displayname);
1964 else
1965 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
1967 fflush(stdout);
1968 NYD_LEAVE;
1969 return OKAY;
1972 FL void
1973 imap_quit(void)
1975 sighandler_type volatile saveint, savepipe;
1976 NYD_ENTER;
1978 if (mb.mb_type == MB_CACHE) {
1979 imap_update(&mb);
1980 goto jleave;
1983 if (mb.mb_sock.s_fd < 0) {
1984 fprintf(stderr, "IMAP connection closed.\n");
1985 goto jleave;
1988 imaplock = 1;
1989 saveint = safe_signal(SIGINT, SIG_IGN);
1990 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1991 if (sigsetjmp(imapjmp, 1)) {
1992 safe_signal(SIGINT, saveint);
1993 safe_signal(SIGPIPE, saveint);
1994 imaplock = 0;
1995 goto jleave;
1997 if (saveint != SIG_IGN)
1998 safe_signal(SIGINT, imapcatch);
1999 if (savepipe != SIG_IGN)
2000 safe_signal(SIGPIPE, imapcatch);
2002 imap_update(&mb);
2003 if (!same_imap_account)
2004 imap_exit(&mb);
2006 safe_signal(SIGINT, saveint);
2007 safe_signal(SIGPIPE, savepipe);
2008 imaplock = 0;
2009 jleave:
2010 NYD_LEAVE;
2013 static enum okay
2014 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2015 int needstat)
2017 char o[LINESIZE];
2018 FILE *queuefp = NULL;
2019 NYD_X;
2021 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2022 return STOP;
2023 if (m->m_uid)
2024 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2025 tag(1), m->m_uid, c, sp);
2026 else {
2027 if (check_expunged() == STOP)
2028 return STOP;
2029 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2031 IMAP_OUT(o, MB_COMD, return STOP)
2032 if (needstat)
2033 IMAP_ANSWER()
2034 else
2035 mb.mb_active &= ~MB_COMD;
2036 if (queuefp != NULL)
2037 Fclose(queuefp);
2038 return OKAY;
2041 FL enum okay
2042 imap_undelete(struct message *m, int n)
2044 enum okay rv;
2045 NYD_ENTER;
2047 rv = imap_unstore(m, n, "\\Deleted");
2048 NYD_LEAVE;
2049 return rv;
2052 FL enum okay
2053 imap_unread(struct message *m, int n)
2055 enum okay rv;
2056 NYD_ENTER;
2058 rv = imap_unstore(m, n, "\\Seen");
2059 NYD_LEAVE;
2060 return rv;
2063 static enum okay
2064 imap_unstore(struct message *m, int n, const char *flag)
2066 sighandler_type saveint, savepipe;
2067 enum okay rv = STOP;
2068 NYD_ENTER;
2070 imaplock = 1;
2071 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2072 safe_signal(SIGINT, &_imap_maincatch);
2073 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2074 if (sigsetjmp(imapjmp, 1) == 0) {
2075 if (savepipe != SIG_IGN)
2076 safe_signal(SIGPIPE, imapcatch);
2078 rv = imap_store(&mb, m, n, '-', flag, 1);
2080 safe_signal(SIGINT, saveint);
2081 safe_signal(SIGPIPE, savepipe);
2082 imaplock = 0;
2084 NYD_LEAVE;
2085 if (interrupts)
2086 onintr(0);
2087 return rv;
2090 static const char *
2091 tag(int new)
2093 static char ts[20];
2094 static long n;
2095 NYD_ENTER;
2097 if (new)
2098 ++n;
2099 snprintf(ts, sizeof ts, "T%lu", n);
2100 NYD_LEAVE;
2101 return ts;
2104 FL int
2105 c_imap_imap(void *vp)
2107 char o[LINESIZE];
2108 sighandler_type saveint, savepipe;
2109 struct mailbox *mp = &mb;
2110 FILE *queuefp = NULL;
2111 enum okay volatile ok = STOP;
2112 NYD_X;
2114 if (mp->mb_type != MB_IMAP) {
2115 printf("Not operating on an IMAP mailbox.\n");
2116 return 1;
2118 imaplock = 1;
2119 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2120 safe_signal(SIGINT, &_imap_maincatch);
2121 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2122 if (sigsetjmp(imapjmp, 1) == 0) {
2123 if (savepipe != SIG_IGN)
2124 safe_signal(SIGPIPE, imapcatch);
2126 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2127 IMAP_OUT(o, MB_COMD, goto out)
2128 while (mp->mb_active & MB_COMD) {
2129 ok = imap_answer(mp, 0);
2130 fputs(responded_text, stdout);
2133 out:
2134 safe_signal(SIGINT, saveint);
2135 safe_signal(SIGPIPE, savepipe);
2136 imaplock = 0;
2138 if (interrupts)
2139 onintr(0);
2140 return ok != OKAY;
2143 FL int
2144 imap_newmail(int nmail)
2146 NYD_ENTER;
2148 if (nmail && had_exists < 0 && had_expunge < 0) {
2149 imaplock = 1;
2150 imap_noop();
2151 imaplock = 0;
2154 if (had_exists == msgCount && had_expunge < 0)
2155 /* Some servers always respond with EXISTS to NOOP. If
2156 * the mailbox has been changed but the number of messages
2157 * has not, an EXPUNGE must also had been sent; otherwise,
2158 * nothing has changed */
2159 had_exists = -1;
2160 NYD_LEAVE;
2161 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2164 static char *
2165 imap_putflags(int f)
2167 const char *cp;
2168 char *buf, *bp;
2169 NYD_ENTER;
2171 bp = buf = salloc(100);
2172 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2173 *bp++ = '(';
2174 if (f & MREAD) {
2175 if (bp[-1] != '(')
2176 *bp++ = ' ';
2177 for (cp = "\\Seen"; *cp; cp++)
2178 *bp++ = *cp;
2180 if (f & MFLAGGED) {
2181 if (bp[-1] != '(')
2182 *bp++ = ' ';
2183 for (cp = "\\Flagged"; *cp; cp++)
2184 *bp++ = *cp;
2186 if (f & MANSWERED) {
2187 if (bp[-1] != '(')
2188 *bp++ = ' ';
2189 for (cp = "\\Answered"; *cp; cp++)
2190 *bp++ = *cp;
2192 if (f & MDRAFT) {
2193 if (bp[-1] != '(')
2194 *bp++ = ' ';
2195 for (cp = "\\Draft"; *cp; cp++)
2196 *bp++ = *cp;
2198 *bp++ = ')';
2199 *bp++ = ' ';
2201 *bp = '\0';
2202 NYD_LEAVE;
2203 return buf;
2206 static void
2207 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2209 NYD_ENTER;
2210 while (*cp != ')') {
2211 if (*cp == '\\') {
2212 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2213 *f |= MREAD;
2214 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2215 *f |= MNEW;
2216 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2217 *f |= MDELETED;
2218 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2219 *f |= MFLAGGED;
2220 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2221 *f |= MANSWERED;
2222 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2223 *f |= MDRAFTED;
2225 cp++;
2228 if (xp != NULL)
2229 *xp = cp;
2230 NYD_LEAVE;
2233 static enum okay
2234 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2235 long xsize, enum mflag flag, time_t t)
2237 char o[LINESIZE], *buf;
2238 size_t bufsize, buflen, cnt;
2239 long size, lines, ysize;
2240 int twice = 0;
2241 FILE *queuefp = NULL;
2242 enum okay rv;
2243 NYD_ENTER;
2245 if (mp->mb_type == MB_CACHE) {
2246 queuefp = cache_queue(mp);
2247 if (queuefp == NULL) {
2248 rv = STOP;
2249 buf = NULL;
2250 goto jleave;
2252 rv = OKAY;
2253 } else
2254 rv = STOP;
2256 buf = smalloc(bufsize = LINESIZE);
2257 buflen = 0;
2258 jagain:
2259 size = xsize;
2260 cnt = fsize(fp);
2261 if (fseek(fp, off1, SEEK_SET) < 0) {
2262 rv = STOP;
2263 goto jleave;
2266 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2267 tag(1), imap_quotestr(name), imap_putflags(flag),
2268 imap_make_date_time(t), size);
2269 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2270 while (mp->mb_active & MB_COMD) {
2271 rv = imap_answer(mp, twice);
2272 if (response_type == RESPONSE_CONT)
2273 break;
2276 if (mp->mb_type != MB_CACHE && rv == STOP) {
2277 if (twice == 0)
2278 goto jtrycreate;
2279 else
2280 goto jleave;
2283 lines = ysize = 0;
2284 while (size > 0) {
2285 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2286 lines++;
2287 ysize += buflen;
2288 buf[buflen - 1] = '\r';
2289 buf[buflen] = '\n';
2290 if (mp->mb_type != MB_CACHE)
2291 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2292 else if (queuefp)
2293 fwrite(buf, 1, buflen+1, queuefp);
2294 size -= buflen + 1;
2296 if (mp->mb_type != MB_CACHE)
2297 swrite(&mp->mb_sock, "\r\n");
2298 else if (queuefp)
2299 fputs("\r\n", queuefp);
2300 while (mp->mb_active & MB_COMD) {
2301 rv = imap_answer(mp, 0);
2302 if (response_status == RESPONSE_NO /*&&
2303 ascncasecmp(responded_text,
2304 "[TRYCREATE] ", 12) == 0*/) {
2305 jtrycreate:
2306 if (twice++) {
2307 rv = STOP;
2308 goto jleave;
2310 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2311 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2312 while (mp->mb_active & MB_COMD)
2313 rv = imap_answer(mp, 1);
2314 if (rv == STOP)
2315 goto jleave;
2316 imap_created_mailbox++;
2317 goto jagain;
2318 } else if (rv != OKAY)
2319 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
2320 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2321 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2323 jleave:
2324 if (queuefp != NULL)
2325 Fclose(queuefp);
2326 if (buf != NULL)
2327 free(buf);
2328 NYD_LEAVE;
2329 return rv;
2332 static enum okay
2333 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2335 char *buf, *bp, *lp;
2336 size_t bufsize, buflen, cnt;
2337 off_t off1 = -1, offs;
2338 int inhead = 1, flag = MNEW | MNEWEST;
2339 long size = 0;
2340 time_t tim;
2341 enum okay rv;
2342 NYD_ENTER;
2344 buf = smalloc(bufsize = LINESIZE);
2345 buflen = 0;
2346 cnt = fsize(fp);
2347 offs = ftell(fp);
2348 time(&tim);
2350 do {
2351 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2352 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2353 if (off1 != (off_t)-1) {
2354 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2355 if (rv == STOP)
2356 goto jleave;
2357 fseek(fp, offs+buflen, SEEK_SET);
2359 off1 = offs + buflen;
2360 size = 0;
2361 inhead = 1;
2362 flag = MNEW;
2363 if (bp != NULL)
2364 tim = unixtime(buf);
2365 } else
2366 size += buflen+1;
2367 offs += buflen;
2369 if (bp && buf[0] == '\n')
2370 inhead = 0;
2371 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2372 lp = &buf[6];
2373 while (whitechar(*lp))
2374 lp++;
2375 if (*lp == ':')
2376 while (*++lp != '\0')
2377 switch (*lp) {
2378 case 'R':
2379 flag |= MREAD;
2380 break;
2381 case 'O':
2382 flag &= ~MNEW;
2383 break;
2385 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2386 lp = &buf[8];
2387 while (whitechar(*lp))
2388 lp++;
2389 if (*lp == ':')
2390 while (*++lp != '\0')
2391 switch (*lp) {
2392 case 'F':
2393 flag |= MFLAGGED;
2394 break;
2395 case 'A':
2396 flag |= MANSWERED;
2397 break;
2398 case 'T':
2399 flag |= MDRAFTED;
2400 break;
2403 } while (bp != NULL);
2404 rv = OKAY;
2405 jleave:
2406 free(buf);
2407 NYD_LEAVE;
2408 return rv;
2411 FL enum okay
2412 imap_append(const char *xserver, FILE *fp)
2414 sighandler_type volatile saveint, savepipe;
2415 struct url url;
2416 struct ccred ccred;
2417 char const * volatile mbx;
2418 enum okay rv = STOP;
2419 NYD_ENTER;
2421 if (!url_parse(&url, CPROTO_IMAP, xserver))
2422 goto j_leave;
2423 if (!ok_blook(v15_compat) &&
2424 (!url.url_had_user || url.url_pass.s != NULL))
2425 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
2426 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2428 imaplock = 1;
2429 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2430 safe_signal(SIGINT, &_imap_maincatch);
2431 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2432 if (sigsetjmp(imapjmp, 1))
2433 goto jleave;
2434 if (savepipe != SIG_IGN)
2435 safe_signal(SIGPIPE, imapcatch);
2437 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2438 !strcmp(url.url_puhp, mb.mb_imap_account)) {
2439 rv = imap_append0(&mb, mbx, fp);
2440 } else {
2441 struct mailbox mx;
2443 if (!(ok_blook(v15_compat) ? ccred_lookup(&ccred, &url)
2444 : ccred_lookup_old(&ccred, CPROTO_IMAP, url.url_uhp.s)))
2445 goto jleave;
2447 memset(&mx, 0, sizeof mx);
2448 if (disconnected(url.url_puhp) == 0) {
2449 if (!sopen(&mx.mb_sock, &url))
2450 goto jfail;
2451 mx.mb_sock.s_desc = "IMAP";
2452 mx.mb_type = MB_IMAP;
2453 mx.mb_imap_account = UNCONST(url.url_puhp);
2454 /* TODO the code now did
2455 * TODO mx.mb_imap_mailbox = mbx;
2456 * TODO though imap_mailbox is sfree()d and mbx
2457 * TODO is possibly even a constant
2458 * TODO i changed this to sstrdup() sofar, as is used
2459 * TODO somewhere else in this file for this! */
2460 mx.mb_imap_mailbox = sstrdup(mbx);
2461 if (imap_preauth(&mx, url.url_hp.s, url.url_uhp.s) != OKAY ||
2462 imap_auth(&mx, &ccred) != OKAY) {
2463 sclose(&mx.mb_sock);
2464 goto jfail;
2466 rv = imap_append0(&mx, mbx, fp);
2467 imap_exit(&mx);
2468 } else {
2469 mx.mb_imap_account = UNCONST(url.url_puhp);
2470 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2471 mx.mb_type = MB_CACHE;
2472 rv = imap_append0(&mx, mbx, fp);
2474 jfail:
2478 jleave:
2479 safe_signal(SIGINT, saveint);
2480 safe_signal(SIGPIPE, savepipe);
2481 imaplock = 0;
2482 j_leave:
2483 NYD_LEAVE;
2484 if (interrupts)
2485 onintr(0);
2486 return rv;
2489 static enum okay
2490 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2491 struct list_item **lend, int level)
2493 char o[LINESIZE], *cp;
2494 const char *bp;
2495 FILE *queuefp = NULL;
2496 struct list_item *lp;
2497 enum okay ok = STOP;
2498 NYD_X;
2500 *list = *lend = NULL;
2501 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2502 IMAP_OUT(o, MB_COMD, return STOP)
2503 while (mp->mb_active & MB_COMD) {
2504 ok = imap_answer(mp, 1);
2505 if (response_status == RESPONSE_OTHER &&
2506 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2507 cp = imap_unquotestr(list_name);
2508 lp = csalloc(1, sizeof *lp);
2509 lp->l_name = cp;
2510 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2511 ++cp;
2512 lp->l_base = *cp ? cp : savestr(base);
2513 lp->l_attr = list_attributes;
2514 lp->l_level = level+1;
2515 lp->l_delim = list_hierarchy_delimiter;
2516 if (*list && *lend) {
2517 (*lend)->l_next = lp;
2518 *lend = lp;
2519 } else
2520 *list = *lend = lp;
2523 return ok;
2526 static enum okay
2527 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2529 struct list_item *list, *lend, *lp, *lx, *ly;
2530 int n, depth;
2531 const char *bp;
2532 char *cp;
2533 enum okay rv;
2534 NYD_ENTER;
2536 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2537 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2538 goto jleave;
2539 rv = OKAY;
2540 if (list == NULL || lend == NULL)
2541 goto jleave;
2543 for (lp = list; lp; lp = lp->l_next)
2544 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2545 !(lp->l_attr & LIST_NOINFERIORS)) {
2546 cp = salloc((n = strlen(lp->l_name)) + 2);
2547 memcpy(cp, lp->l_name, n);
2548 cp[n] = lp->l_delim;
2549 cp[n+1] = '\0';
2550 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2551 lp->l_has_children = 1;
2552 if (strcmp(cp, lx->l_name) == 0)
2553 lx = lx->l_next;
2554 if (lx) {
2555 lend->l_next = lx;
2556 lend = ly;
2561 for (lp = list; lp; lp = lp->l_next) {
2562 if (strip) {
2563 cp = lp->l_name;
2564 for (bp = base; *bp && *bp == *cp; bp++)
2565 cp++;
2566 } else
2567 cp = lp->l_name;
2568 if (!(lp->l_attr & LIST_NOSELECT))
2569 fprintf(fp, "%s\n", *cp ? cp : base);
2570 else if (lp->l_has_children == 0)
2571 fprintf(fp, "%s%c\n", *cp ? cp : base,
2572 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2574 jleave:
2575 NYD_LEAVE;
2576 return rv;
2579 FL void
2580 imap_folders(const char * volatile name, int strip)
2582 sighandler_type saveint, savepipe;
2583 const char *fold, *cp, *sp;
2584 FILE * volatile fp;
2585 NYD_ENTER;
2587 cp = protbase(name);
2588 sp = mb.mb_imap_account;
2589 if (sp == NULL || strcmp(cp, sp)) {
2590 fprintf(stderr, tr(502,
2591 "Cannot perform `folders' but when on the very IMAP "
2592 "account; the current one is\n `%s' -- "
2593 "try `folders @'.\n"),
2594 (sp != NULL ? sp : tr(503, "[NONE]")));
2595 goto jleave;
2598 fold = imap_fileof(name);
2599 if (options & OPT_TTYOUT) {
2600 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2601 0600)) == NULL) {
2602 perror("tmpfile");
2603 goto jleave;
2605 } else
2606 fp = stdout;
2608 imaplock = 1;
2609 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2610 safe_signal(SIGINT, &_imap_maincatch);
2611 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2612 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2613 goto junroll;
2614 if (savepipe != SIG_IGN)
2615 safe_signal(SIGPIPE, imapcatch);
2617 if (mb.mb_type == MB_CACHE)
2618 cache_list(&mb, fold, strip, fp);
2619 else
2620 imap_list(&mb, fold, strip, fp);
2622 imaplock = 0;
2623 if (interrupts) {
2624 if (options & OPT_TTYOUT)
2625 Fclose(fp);
2626 goto jleave;
2628 fflush(fp);
2630 if (options & OPT_TTYOUT) {
2631 rewind(fp);
2632 if (fsize(fp) > 0)
2633 dopr(fp);
2634 else
2635 fprintf(stderr, "Folder not found.\n");
2637 junroll:
2638 safe_signal(SIGINT, saveint);
2639 safe_signal(SIGPIPE, savepipe);
2640 if (options & OPT_TTYOUT)
2641 Fclose(fp);
2642 jleave:
2643 NYD_LEAVE;
2644 if (interrupts)
2645 onintr(0);
2648 static void
2649 dopr(FILE *fp)
2651 char o[LINESIZE];
2652 int c;
2653 long n = 0, mx = 0, columns, width;
2654 FILE *out;
2655 NYD_ENTER;
2657 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2658 == NULL) {
2659 perror("tmpfile");
2660 goto jleave;
2663 while ((c = getc(fp)) != EOF) {
2664 if (c == '\n') {
2665 if (n > mx)
2666 mx = n;
2667 n = 0;
2668 } else
2669 ++n;
2671 rewind(fp);
2673 width = scrnwidth;
2674 if (mx < width / 2) {
2675 columns = width / (mx+2);
2676 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2677 } else
2678 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2679 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2680 page_or_print(out, 0);
2681 Fclose(out);
2682 jleave:
2683 NYD_LEAVE;
2686 static enum okay
2687 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2689 char o[LINESIZE];
2690 const char *qname;
2691 int twice = 0, stored = 0;
2692 FILE *queuefp = NULL;
2693 enum okay ok = STOP;
2694 NYD_X;
2696 if (mp->mb_type == MB_CACHE) {
2697 if ((queuefp = cache_queue(mp)) == NULL)
2698 return STOP;
2699 ok = OKAY;
2701 qname = imap_quotestr(name = imap_fileof(name));
2702 /* Since it is not possible to set flags on the copy, recently
2703 * set flags must be set on the original to include it in the copy */
2704 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2705 imap_store(mp, m, n, '+', "\\Seen", 0);
2706 if (m->m_flag&MFLAG)
2707 imap_store(mp, m, n, '+', "\\Flagged", 0);
2708 if (m->m_flag&MUNFLAG)
2709 imap_store(mp, m, n, '-', "\\Flagged", 0);
2710 if (m->m_flag&MANSWER)
2711 imap_store(mp, m, n, '+', "\\Answered", 0);
2712 if (m->m_flag&MUNANSWER)
2713 imap_store(mp, m, n, '-', "\\Flagged", 0);
2714 if (m->m_flag&MDRAFT)
2715 imap_store(mp, m, n, '+', "\\Draft", 0);
2716 if (m->m_flag&MUNDRAFT)
2717 imap_store(mp, m, n, '-', "\\Draft", 0);
2718 again:
2719 if (m->m_uid)
2720 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2721 else {
2722 if (check_expunged() == STOP)
2723 goto out;
2724 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2726 IMAP_OUT(o, MB_COMD, goto out)
2727 while (mp->mb_active & MB_COMD)
2728 ok = imap_answer(mp, twice);
2730 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2731 response_status == RESPONSE_OK)
2732 imap_copyuid(mp, m, name);
2734 if (response_status == RESPONSE_NO && twice++ == 0) {
2735 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2736 IMAP_OUT(o, MB_COMD, goto out)
2737 while (mp->mb_active & MB_COMD)
2738 ok = imap_answer(mp, 1);
2739 if (ok == OKAY) {
2740 imap_created_mailbox++;
2741 goto again;
2745 if (queuefp != NULL)
2746 Fclose(queuefp);
2748 /* ... and reset the flag to its initial value so that the 'exit'
2749 * command still leaves the message unread */
2750 out:
2751 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2752 imap_store(mp, m, n, '-', "\\Seen", 0);
2753 stored++;
2755 if (m->m_flag & MFLAG) {
2756 imap_store(mp, m, n, '-', "\\Flagged", 0);
2757 stored++;
2759 if (m->m_flag & MUNFLAG) {
2760 imap_store(mp, m, n, '+', "\\Flagged", 0);
2761 stored++;
2763 if (m->m_flag & MANSWER) {
2764 imap_store(mp, m, n, '-', "\\Answered", 0);
2765 stored++;
2767 if (m->m_flag & MUNANSWER) {
2768 imap_store(mp, m, n, '+', "\\Answered", 0);
2769 stored++;
2771 if (m->m_flag & MDRAFT) {
2772 imap_store(mp, m, n, '-', "\\Draft", 0);
2773 stored++;
2775 if (m->m_flag & MUNDRAFT) {
2776 imap_store(mp, m, n, '+', "\\Draft", 0);
2777 stored++;
2779 if (stored) {
2780 mp->mb_active |= MB_COMD;
2781 (void)imap_finish(mp);
2783 return ok;
2786 FL enum okay
2787 imap_copy(struct message *m, int n, const char *name)
2789 sighandler_type saveint, savepipe;
2790 enum okay rv = STOP;
2791 NYD_ENTER;
2793 imaplock = 1;
2794 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2795 safe_signal(SIGINT, &_imap_maincatch);
2796 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2797 if (sigsetjmp(imapjmp, 1) == 0) {
2798 if (savepipe != SIG_IGN)
2799 safe_signal(SIGPIPE, imapcatch);
2801 rv = imap_copy1(&mb, m, n, name);
2803 safe_signal(SIGINT, saveint);
2804 safe_signal(SIGPIPE, savepipe);
2805 imaplock = 0;
2807 NYD_LEAVE;
2808 if (interrupts)
2809 onintr(0);
2810 return rv;
2813 static enum okay
2814 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2815 unsigned long *olduid, unsigned long *newuid)
2817 char *xp, *yp, *zp;
2818 enum okay rv;
2819 NYD_ENTER;
2821 *uidvalidity = strtoul(cp, &xp, 10);
2822 *olduid = strtoul(xp, &yp, 10);
2823 *newuid = strtoul(yp, &zp, 10);
2824 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2825 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2826 NYD_LEAVE;
2827 return rv;
2830 static enum okay
2831 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2832 unsigned long *uid)
2834 char *xp, *yp;
2835 enum okay rv;
2836 NYD_ENTER;
2838 *uidvalidity = strtoul(cp, &xp, 10);
2839 *uid = strtoul(xp, &yp, 10);
2840 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2841 *yp == ']');
2842 NYD_LEAVE;
2843 return rv;
2846 static enum okay
2847 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2849 struct mailbox xmb;
2850 struct message xm;
2851 const char *cp;
2852 unsigned long uidvalidity, olduid, newuid;
2853 enum okay rv;
2854 NYD_ENTER;
2856 rv = STOP;
2857 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2858 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2859 goto jleave;
2860 rv = OKAY;
2862 xmb = *mp;
2863 xmb.mb_cache_directory = NULL;
2864 xmb.mb_imap_mailbox = savestr(name);
2865 xmb.mb_uidvalidity = uidvalidity;
2866 initcache(&xmb);
2867 if (m == NULL) {
2868 memset(&xm, 0, sizeof xm);
2869 xm.m_uid = olduid;
2870 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2871 goto jleave;
2872 getcache(mp, &xm, NEED_HEADER);
2873 getcache(mp, &xm, NEED_BODY);
2874 } else {
2875 if ((m->m_flag & HAVE_HEADER) == 0)
2876 getcache(mp, m, NEED_HEADER);
2877 if ((m->m_flag & HAVE_BODY) == 0)
2878 getcache(mp, m, NEED_BODY);
2879 xm = *m;
2881 xm.m_uid = newuid;
2882 xm.m_flag &= ~MFULLYCACHED;
2883 putcache(&xmb, &xm);
2884 jleave:
2885 NYD_LEAVE;
2886 return rv;
2889 static enum okay
2890 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2891 long size, long lines, int flag, const char *name)
2893 struct mailbox xmb;
2894 struct message xm;
2895 const char *cp;
2896 unsigned long uidvalidity, uid;
2897 enum okay rv;
2898 NYD_ENTER;
2900 rv = STOP;
2901 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2902 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2903 goto jleave;
2904 rv = OKAY;
2906 xmb = *mp;
2907 xmb.mb_cache_directory = NULL;
2908 xmb.mb_imap_mailbox = savestr(name);
2909 xmb.mb_uidvalidity = uidvalidity;
2910 xmb.mb_otf = xmb.mb_itf = fp;
2911 initcache(&xmb);
2912 memset(&xm, 0, sizeof xm);
2913 xm.m_flag = (flag & MREAD) | MNEW;
2914 xm.m_time = t;
2915 xm.m_block = mailx_blockof(off1);
2916 xm.m_offset = mailx_offsetof(off1);
2917 xm.m_size = size;
2918 xm.m_xsize = xsize;
2919 xm.m_lines = xm.m_xlines = lines;
2920 xm.m_uid = uid;
2921 xm.m_have = HAVE_HEADER | HAVE_BODY;
2922 putcache(&xmb, &xm);
2923 jleave:
2924 NYD_LEAVE;
2925 return rv;
2928 static enum okay
2929 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2931 FILE *tp = NULL;
2932 time_t t;
2933 long size, xsize, ysize, lines;
2934 enum mflag flag = MNEW;
2935 char *name, *buf, *bp;
2936 char const *cp;
2937 size_t bufsize, buflen, cnt;
2938 enum okay rv = STOP;
2939 NYD_ENTER;
2941 buf = smalloc(bufsize = LINESIZE);
2942 buflen = 0;
2943 cnt = fsize(fp);
2944 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2945 goto jstop;
2947 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
2949 while (*bp == ' ')
2950 ++bp;
2952 if ((cp = strrchr(bp, '{')) == NULL)
2953 goto jstop;
2955 xsize = atol(&cp[1]) + 2;
2956 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2957 goto jstop;
2958 while (*cp == ' ')
2959 cp++;
2961 if (*cp == '(') {
2962 imap_getflags(cp, &cp, &flag);
2963 while (*++cp == ' ')
2966 t = imap_read_date_time(cp);
2968 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2969 NULL)
2970 goto jstop;
2972 size = xsize;
2973 ysize = lines = 0;
2974 while (size > 0) {
2975 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2976 goto jstop;
2977 size -= buflen;
2978 buf[--buflen] = '\0';
2979 buf[buflen-1] = '\n';
2980 fwrite(buf, 1, buflen, tp);
2981 ysize += buflen;
2982 ++lines;
2984 fflush(tp);
2985 rewind(tp);
2987 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
2988 imap_unquotestr(name));
2989 rv = OKAY;
2990 jstop:
2991 free(buf);
2992 if (tp)
2993 Fclose(tp);
2994 NYD_LEAVE;
2995 return rv;
2998 #ifdef HAVE_IMAP_SEARCH
2999 static enum okay
3000 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3001 int f)
3003 char *o, *xp, *cs, c;
3004 size_t osize;
3005 FILE *queuefp = NULL;
3006 int i;
3007 unsigned long n;
3008 const char *cp;
3009 enum okay ok = STOP;
3010 NYD_X;
3012 c = 0;
3013 for (cp = spec; *cp; cp++)
3014 c |= *cp;
3015 if (c & 0200) {
3016 cp = charset_get_lc();
3017 # ifdef HAVE_ICONV
3018 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3019 iconv_t it;
3020 char *nsp, *nspec;
3021 size_t sz, nsz;
3023 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3024 sz = strlen(spec) + 1;
3025 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3026 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3027 sz == 0) {
3028 spec = nspec;
3029 cp = "utf-8";
3031 n_iconv_close(it);
3034 # endif
3035 cp = imap_quotestr(cp);
3036 cs = salloc(n = strlen(cp) + 10);
3037 snprintf(cs, n, "CHARSET %s ", cp);
3038 } else
3039 cs = UNCONST("");
3041 o = ac_alloc(osize = strlen(spec) + 60);
3042 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3043 IMAP_OUT(o, MB_COMD, goto out)
3044 while (mp->mb_active & MB_COMD) {
3045 ok = imap_answer(mp, 0);
3046 if (response_status == RESPONSE_OTHER &&
3047 response_other == MAILBOX_DATA_SEARCH) {
3048 xp = responded_other_text;
3049 while (*xp && *xp != '\r') {
3050 n = strtoul(xp, &xp, 10);
3051 for (i = 0; i < cnt; i++)
3052 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3053 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3054 mark(i+1, f);
3058 out:
3059 ac_free(o);
3060 return ok;
3063 FL enum okay
3064 imap_search1(const char * volatile spec, int f)
3066 sighandler_type saveint, savepipe;
3067 enum okay volatile rv = STOP;
3068 NYD_ENTER;
3070 if (mb.mb_type != MB_IMAP)
3071 goto jleave;
3073 imaplock = 1;
3074 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3075 safe_signal(SIGINT, &_imap_maincatch);
3076 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3077 if (sigsetjmp(imapjmp, 1) == 0) {
3078 if (savepipe != SIG_IGN)
3079 safe_signal(SIGPIPE, imapcatch);
3081 rv = imap_search2(&mb, message, msgCount, spec, f);
3083 safe_signal(SIGINT, saveint);
3084 safe_signal(SIGPIPE, savepipe);
3085 imaplock = 0;
3086 jleave:
3087 NYD_LEAVE;
3088 if (interrupts)
3089 onintr(0);
3090 return rv;
3092 #endif /* HAVE_IMAP_SEARCH */
3094 FL int
3095 imap_thisaccount(const char *cp)
3097 int rv;
3098 NYD_ENTER;
3100 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3101 rv = 0;
3102 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3103 mb.mb_imap_account == NULL)
3104 rv = 0;
3105 else
3106 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3107 NYD_LEAVE;
3108 return rv;
3111 FL enum okay
3112 imap_remove(const char * volatile name)
3114 sighandler_type volatile saveint, savepipe;
3115 enum okay rv = STOP;
3116 NYD_ENTER;
3118 if (mb.mb_type != MB_IMAP) {
3119 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3120 name);
3121 goto jleave;
3124 if (!imap_thisaccount(name)) {
3125 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3126 "server: \"%s\" not removed.\n", name);
3127 goto jleave;
3130 imaplock = 1;
3131 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3132 safe_signal(SIGINT, &_imap_maincatch);
3133 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3134 if (sigsetjmp(imapjmp, 1) == 0) {
3135 if (savepipe != SIG_IGN)
3136 safe_signal(SIGPIPE, imapcatch);
3138 rv = imap_remove1(&mb, imap_fileof(name));
3140 safe_signal(SIGINT, saveint);
3141 safe_signal(SIGPIPE, savepipe);
3142 imaplock = 0;
3144 if (rv == OKAY)
3145 rv = cache_remove(name);
3146 jleave:
3147 NYD_LEAVE;
3148 if (interrupts)
3149 onintr(0);
3150 return rv;
3153 static enum okay
3154 imap_remove1(struct mailbox *mp, const char *name)
3156 FILE *queuefp = NULL;
3157 char *o;
3158 int os;
3159 enum okay ok = STOP;
3160 NYD_X;
3162 o = ac_alloc(os = 2*strlen(name) + 100);
3163 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3164 IMAP_OUT(o, MB_COMD, goto out)
3165 while (mp->mb_active & MB_COMD)
3166 ok = imap_answer(mp, 1);
3167 out:
3168 ac_free(o);
3169 return ok;
3172 FL enum okay
3173 imap_rename(const char *old, const char *new)
3175 sighandler_type saveint, savepipe;
3176 enum okay rv = STOP;
3177 NYD_ENTER;
3179 if (mb.mb_type != MB_IMAP) {
3180 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3181 goto jleave;
3184 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3185 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3186 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3187 goto jleave;
3190 imaplock = 1;
3191 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3192 safe_signal(SIGINT, &_imap_maincatch);
3193 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3194 if (sigsetjmp(imapjmp, 1) == 0) {
3195 if (savepipe != SIG_IGN)
3196 safe_signal(SIGPIPE, imapcatch);
3198 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3200 safe_signal(SIGINT, saveint);
3201 safe_signal(SIGPIPE, savepipe);
3202 imaplock = 0;
3204 if (rv == OKAY)
3205 rv = cache_rename(old, new);
3206 jleave:
3207 NYD_LEAVE;
3208 if (interrupts)
3209 onintr(0);
3210 return rv;
3213 static enum okay
3214 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3216 FILE *queuefp = NULL;
3217 char *o;
3218 int os;
3219 enum okay ok = STOP;
3220 NYD_X;
3222 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3223 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3224 imap_quotestr(new));
3225 IMAP_OUT(o, MB_COMD, goto out)
3226 while (mp->mb_active & MB_COMD)
3227 ok = imap_answer(mp, 1);
3228 out:
3229 ac_free(o);
3230 return ok;
3233 FL enum okay
3234 imap_dequeue(struct mailbox *mp, FILE *fp)
3236 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3237 size_t bufsize, buflen, cnt;
3238 long offs, offs1, offs2, octets;
3239 int twice, gotcha = 0;
3240 FILE *queuefp = NULL;
3241 enum okay ok = OKAY, rok = OKAY;
3242 NYD_X;
3244 buf = smalloc(bufsize = LINESIZE);
3245 buflen = 0;
3246 cnt = fsize(fp);
3247 while ((offs1 = ftell(fp)) >= 0 &&
3248 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3249 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3251 while (*bp == ' ')
3252 ++bp;
3253 twice = 0;
3254 if ((offs = ftell(fp)) < 0)
3255 goto fail;
3256 again:
3257 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3258 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3259 cp = &bp[9];
3260 while (digitchar(*cp))
3261 cp++;
3262 if (*cp != ' ')
3263 goto fail;
3264 while (*cp == ' ')
3265 cp++;
3266 if ((newname = imap_strex(cp, NULL)) == NULL)
3267 goto fail;
3268 IMAP_OUT(o, MB_COMD, continue)
3269 while (mp->mb_active & MB_COMD)
3270 ok = imap_answer(mp, twice);
3271 if (response_status == RESPONSE_NO && twice++ == 0)
3272 goto trycreate;
3273 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3274 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3276 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3277 IMAP_OUT(o, MB_COMD, continue)
3278 while (mp->mb_active & MB_COMD)
3279 ok = imap_answer(mp, 1);
3280 if (ok == OKAY)
3281 gotcha++;
3282 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3283 if ((cp = strrchr(bp, '{')) == NULL)
3284 goto fail;
3285 octets = atol(&cp[1]) + 2;
3286 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3287 goto fail;
3288 IMAP_OUT(o, MB_COMD, continue)
3289 while (mp->mb_active & MB_COMD) {
3290 ok = imap_answer(mp, twice);
3291 if (response_type == RESPONSE_CONT)
3292 break;
3294 if (ok == STOP) {
3295 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3296 goto trycreate;
3297 goto fail;
3299 while (octets > 0) {
3300 size_t n = (UICMP(z, octets, >, sizeof iob)
3301 ? sizeof iob : (size_t)octets);
3302 octets -= n;
3303 if (n != fread(iob, 1, n, fp))
3304 goto fail;
3305 swrite1(&mp->mb_sock, iob, n, 1);
3307 swrite(&mp->mb_sock, "");
3308 while (mp->mb_active & MB_COMD) {
3309 ok = imap_answer(mp, 0);
3310 if (response_status == RESPONSE_NO && twice++ == 0) {
3311 if (fseek(fp, offs, SEEK_SET) < 0)
3312 goto fail;
3313 goto trycreate;
3316 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3317 if ((offs2 = ftell(fp)) < 0)
3318 goto fail;
3319 fseek(fp, offs1, SEEK_SET);
3320 if (imap_appenduid_cached(mp, fp) == STOP) {
3321 (void)fseek(fp, offs2, SEEK_SET);
3322 goto fail;
3325 } else {
3326 fail:
3327 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3328 rok = STOP;
3330 continue;
3331 trycreate:
3332 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3333 IMAP_OUT(o, MB_COMD, continue)
3334 while (mp->mb_active & MB_COMD)
3335 ok = imap_answer(mp, 1);
3336 if (ok == OKAY)
3337 goto again;
3339 fflush(fp);
3340 rewind(fp);
3341 ftruncate(fileno(fp), 0);
3342 if (gotcha)
3343 imap_close(mp);
3344 free(buf);
3345 return rok;
3348 static char *
3349 imap_strex(char const *cp, char const **xp)
3351 char const *cq;
3352 char *n = NULL;
3353 NYD_ENTER;
3355 if (*cp != '"')
3356 goto jleave;
3358 for (cq = cp + 1; *cq != '\0'; ++cq) {
3359 if (*cq == '\\')
3360 cq++;
3361 else if (*cq == '"')
3362 break;
3364 if (*cq != '"')
3365 goto jleave;
3367 n = salloc(cq - cp + 2);
3368 memcpy(n, cp, cq - cp +1);
3369 n[cq - cp + 1] = '\0';
3370 if (xp != NULL)
3371 *xp = cq + 1;
3372 jleave:
3373 NYD_LEAVE;
3374 return n;
3377 static enum okay
3378 check_expunged(void)
3380 enum okay rv;
3381 NYD_ENTER;
3383 if (expunged_messages > 0) {
3384 fprintf(stderr, "Command not executed - messages have been expunged\n");
3385 rv = STOP;
3386 } else
3387 rv = OKAY;
3388 NYD_LEAVE;
3389 return rv;
3392 FL int
3393 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3395 struct url url;
3396 int rv, omsgCount = msgCount;
3397 NYD_ENTER;
3398 UNUSED(vp);
3400 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3401 fprintf(stderr, "Already connected.\n");
3402 rv = 1;
3403 goto jleave;
3406 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3407 rv = 1;
3408 goto jleave;
3410 var_clear_allow_undefined = TRU1;
3411 ok_bclear(disconnected);
3412 vok_bclear(savecat("disconnected-", url.url_uhp.s));
3413 var_clear_allow_undefined = FAL0;
3415 if (mb.mb_type == MB_CACHE) {
3416 _imap_setfile1(&url, 0, edit, 1);
3417 if (msgCount > omsgCount)
3418 newmailinfo(omsgCount);
3420 rv = 0;
3421 jleave:
3422 NYD_LEAVE;
3423 return rv;
3426 FL int
3427 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3429 struct url url;
3430 int rv = 1, *msgvec = vp;
3431 NYD_ENTER;
3433 if (mb.mb_type == MB_CACHE) {
3434 fprintf(stderr, "Not connected.\n");
3435 goto jleave;
3437 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3438 fprintf(stderr, "The current mailbox is not cached.\n");
3439 goto jleave;
3442 if (!url_parse(&url, CPROTO_IMAP, mailname))
3443 goto jleave;
3445 if (*msgvec)
3446 c_cache(vp);
3447 ok_bset(disconnected, TRU1);
3448 if (mb.mb_type == MB_IMAP) {
3449 sclose(&mb.mb_sock);
3450 _imap_setfile1(&url, 0, edit, 1);
3452 rv = 0;
3453 jleave:
3454 NYD_LEAVE;
3455 return rv;
3458 FL int
3459 c_cache(void *vp)
3461 int rv = 1, *msgvec = vp, *ip;
3462 struct message *mp;
3463 NYD_ENTER;
3465 if (mb.mb_type != MB_IMAP) {
3466 fprintf(stderr, "Not connected to an IMAP server.\n");
3467 goto jleave;
3469 if (cached_uidvalidity(&mb) == 0) {
3470 fprintf(stderr, "The current mailbox is not cached.\n");
3471 goto jleave;
3474 for (ip = msgvec; *ip; ++ip) {
3475 mp = &message[*ip - 1];
3476 if (!(mp->m_have & HAVE_BODY))
3477 get_body(mp);
3479 rv = 0;
3480 jleave:
3481 NYD_LEAVE;
3482 return rv;
3485 FL int
3486 disconnected(const char *file)
3488 struct url url;
3489 int rv = 1;
3490 NYD_ENTER;
3492 if (ok_blook(disconnected)) {
3493 rv = 1;
3494 goto jleave;
3497 if (!url_parse(&url, CPROTO_IMAP, file)) {
3498 rv = 0;
3499 goto jleave;
3501 rv = vok_blook(savecat("disconnected-", url.url_uhp.s));
3503 jleave:
3504 NYD_LEAVE;
3505 return rv;
3508 FL void
3509 transflags(struct message *omessage, long omsgCount, int transparent)
3511 struct message *omp, *nmp, *newdot, *newprevdot;
3512 int hf;
3513 NYD_ENTER;
3515 omp = omessage;
3516 nmp = message;
3517 newdot = message;
3518 newprevdot = NULL;
3519 while (PTRCMP(omp, <, omessage + omsgCount) &&
3520 PTRCMP(nmp, <, message + msgCount)) {
3521 if (dot && nmp->m_uid == dot->m_uid)
3522 newdot = nmp;
3523 if (prevdot && nmp->m_uid == prevdot->m_uid)
3524 newprevdot = nmp;
3525 if (omp->m_uid == nmp->m_uid) {
3526 hf = nmp->m_flag & MHIDDEN;
3527 if (transparent && mb.mb_type == MB_IMAP)
3528 omp->m_flag &= ~MHIDDEN;
3529 *nmp++ = *omp++;
3530 if (transparent && mb.mb_type == MB_CACHE)
3531 nmp[-1].m_flag |= hf;
3532 } else if (omp->m_uid < nmp->m_uid)
3533 ++omp;
3534 else
3535 ++nmp;
3537 dot = newdot;
3538 setdot(newdot);
3539 prevdot = newprevdot;
3540 free(omessage);
3541 NYD_LEAVE;
3544 FL time_t
3545 imap_read_date_time(const char *cp)
3547 char buf[3];
3548 time_t t;
3549 int i, year, month, day, hour, minute, second, sign = -1;
3550 NYD_ENTER;
3552 /* "25-Jul-2004 15:33:44 +0200"
3553 * | | | | | |
3554 * 0 5 10 15 20 25 */
3555 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3556 goto jinvalid;
3557 day = strtol(&cp[1], NULL, 10);
3558 for (i = 0;;) {
3559 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3560 break;
3561 if (month_names[++i][0] == '\0')
3562 goto jinvalid;
3564 month = i + 1;
3565 year = strtol(&cp[8], NULL, 10);
3566 hour = strtol(&cp[13], NULL, 10);
3567 minute = strtol(&cp[16], NULL, 10);
3568 second = strtol(&cp[19], NULL, 10);
3569 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3570 goto jinvalid;
3571 switch (cp[22]) {
3572 case '-':
3573 sign = 1;
3574 break;
3575 case '+':
3576 break;
3577 default:
3578 goto jinvalid;
3580 buf[2] = '\0';
3581 buf[0] = cp[23];
3582 buf[1] = cp[24];
3583 t += strtol(buf, NULL, 10) * sign * 3600;
3584 buf[0] = cp[25];
3585 buf[1] = cp[26];
3586 t += strtol(buf, NULL, 10) * sign * 60;
3587 jleave:
3588 NYD_LEAVE;
3589 return t;
3590 jinvalid:
3591 time(&t);
3592 goto jleave;
3595 FL const char *
3596 imap_make_date_time(time_t t)
3598 static char s[30];
3599 struct tm *tmptr;
3600 int tzdiff, tzdiff_hour, tzdiff_min;
3601 NYD_ENTER;
3603 tzdiff = t - mktime(gmtime(&t));
3604 tzdiff_hour = (int)(tzdiff / 60);
3605 tzdiff_min = tzdiff_hour % 60;
3606 tzdiff_hour /= 60;
3607 tmptr = localtime(&t);
3608 if (tmptr->tm_isdst > 0)
3609 tzdiff_hour++;
3610 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3611 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3612 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3613 NYD_LEAVE;
3614 return s;
3616 #endif /* HAVE_IMAP */
3618 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3619 FL char *
3620 imap_quotestr(char const *s)
3622 char *n, *np;
3623 NYD_ENTER;
3625 np = n = salloc(2 * strlen(s) + 3);
3626 *np++ = '"';
3627 while (*s) {
3628 if (*s == '"' || *s == '\\')
3629 *np++ = '\\';
3630 *np++ = *s++;
3632 *np++ = '"';
3633 *np = '\0';
3634 NYD_LEAVE;
3635 return n;
3638 FL char *
3639 imap_unquotestr(char const *s)
3641 char *n, *np;
3642 NYD_ENTER;
3644 if (*s != '"') {
3645 n = savestr(s);
3646 goto jleave;
3649 np = n = salloc(strlen(s) + 1);
3650 while (*++s) {
3651 if (*s == '\\')
3652 s++;
3653 else if (*s == '"')
3654 break;
3655 *np++ = *s;
3657 *np = '\0';
3658 jleave:
3659 NYD_LEAVE;
3660 return n;
3662 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3664 /* vim:set fenc=utf-8:s-it-mode */