nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / imap.c
blob13edc84fb73156c1965358d56cf004b48fe055a6
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #ifdef HAVE_IMAP
45 # include <sys/socket.h>
47 # include <netdb.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
53 # endif
54 #endif
56 #ifdef HAVE_IMAP
57 #define IMAP_ANSWER() \
59 if (mp->mb_type != MB_CACHE) {\
60 enum okay ok = OKAY;\
61 while (mp->mb_active & MB_COMD)\
62 ok = imap_answer(mp, 1);\
63 if (ok == STOP)\
64 return STOP;\
68 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
69 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
70 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
71 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
72 * TODO I.e., that should be a function, not a macro ... or so.
73 * TODO This entire module needs MASSIVE work! */
74 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
75 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
76 do {\
77 if (mp->mb_type != MB_CACHE) {\
78 if (imap_finish(mp) == STOP) {\
79 ACTIONBAIL;\
81 if (options & OPT_VERBOSE)\
82 fprintf(stderr, ">>> %s", X);\
83 mp->mb_active |= Y;\
84 if (swrite(&mp->mb_sock, X) == STOP) {\
85 ACTIONERR;\
87 } else {\
88 if (queuefp != NULL)\
89 fputs(X, queuefp);\
91 } while (0);
93 static struct record {
94 struct record *rec_next;
95 unsigned long rec_count;
96 enum rec_type {
97 REC_EXISTS,
98 REC_EXPUNGE
99 } rec_type;
100 } *record, *recend;
102 static enum {
103 RESPONSE_TAGGED,
104 RESPONSE_DATA,
105 RESPONSE_FATAL,
106 RESPONSE_CONT,
107 RESPONSE_ILLEGAL
108 } response_type;
110 static enum {
111 RESPONSE_OK,
112 RESPONSE_NO,
113 RESPONSE_BAD,
114 RESPONSE_PREAUTH,
115 RESPONSE_BYE,
116 RESPONSE_OTHER,
117 RESPONSE_UNKNOWN
118 } response_status;
120 static char *responded_tag;
121 static char *responded_text;
122 static char *responded_other_text;
123 static long responded_other_number;
125 static enum {
126 MAILBOX_DATA_FLAGS,
127 MAILBOX_DATA_LIST,
128 MAILBOX_DATA_LSUB,
129 MAILBOX_DATA_MAILBOX,
130 MAILBOX_DATA_SEARCH,
131 MAILBOX_DATA_STATUS,
132 MAILBOX_DATA_EXISTS,
133 MAILBOX_DATA_RECENT,
134 MESSAGE_DATA_EXPUNGE,
135 MESSAGE_DATA_FETCH,
136 CAPABILITY_DATA,
137 RESPONSE_OTHER_UNKNOWN
138 } response_other;
140 static enum list_attributes {
141 LIST_NONE = 000,
142 LIST_NOINFERIORS = 001,
143 LIST_NOSELECT = 002,
144 LIST_MARKED = 004,
145 LIST_UNMARKED = 010
146 } list_attributes;
148 static int list_hierarchy_delimiter;
149 static char *list_name;
151 struct list_item {
152 struct list_item *l_next;
153 char *l_name;
154 char *l_base;
155 enum list_attributes l_attr;
156 int l_delim;
157 int l_level;
158 int l_has_children;
161 static char *imapbuf; /* TODO not static, use pool */
162 static size_t imapbufsize;
163 static sigjmp_buf imapjmp;
164 static sighandler_type savealrm;
165 static int imapkeepalive;
166 static long had_exists = -1;
167 static long had_expunge = -1;
168 static long expunged_messages;
169 static int volatile imaplock;
170 static int same_imap_account;
172 static void imap_other_get(char *pp);
173 static void imap_response_get(const char **cp);
174 static void imap_response_parse(void);
175 static enum okay imap_answer(struct mailbox *mp, int errprnt);
176 static enum okay imap_parse_list(void);
177 static enum okay imap_finish(struct mailbox *mp);
178 static void imap_timer_off(void);
179 static void imapcatch(int s);
180 static void _imap_maincatch(int s);
181 static enum okay imap_noop1(struct mailbox *mp);
182 static void rec_queue(enum rec_type type, unsigned long cnt);
183 static enum okay rec_dequeue(void);
184 static void rec_rmqueue(void);
185 static void imapalarm(int s);
186 static int imap_use_starttls(const char *uhp);
187 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
188 const char *uhp);
189 static enum okay imap_capability(struct mailbox *mp);
190 static enum okay imap_auth(struct mailbox *mp, const char *uhp,
191 char *xuser, char *pass);
192 #ifdef HAVE_MD5
193 static enum okay imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass);
194 #endif
195 static enum okay imap_login(struct mailbox *mp, char *xuser, char *xpass);
196 #ifdef HAVE_GSSAPI
197 static enum okay imap_gss(struct mailbox *mp, char *user);
198 #endif
199 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
200 static void imap_init(struct mailbox *mp, int n);
201 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
202 int *prevcount);
203 static void imap_split(char **server, const char **sp, int *use_ssl,
204 const char **cp, char const **uhp, char const **mbx,
205 char **pass, char **user);
206 static int imap_setfile1(const char *xserver, int nmail, int isedit,
207 int transparent);
208 static int imap_fetchdata(struct mailbox *mp, struct message *m,
209 size_t expected, int need, const char *head,
210 size_t headsize, long headlines);
211 static void imap_putstr(struct mailbox *mp, struct message *m,
212 const char *str, const char *head, size_t headsize,
213 long headlines);
214 static enum okay imap_get(struct mailbox *mp, struct message *m,
215 enum needspec need);
216 static void commitmsg(struct mailbox *mp, struct message *to,
217 struct message *from, enum havespec have);
218 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
219 int bot, int top);
220 static enum okay imap_exit(struct mailbox *mp);
221 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
222 int needstat);
223 static enum okay imap_close(struct mailbox *mp);
224 static enum okay imap_update(struct mailbox *mp);
225 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
226 int c, const char *sp, int needstat);
227 static enum okay imap_unstore(struct message *m, int n, const char *flag);
228 static const char *tag(int new);
229 static char * imap_putflags(int f);
230 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
231 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
232 off_t off1, long xsize, enum mflag flag, time_t t);
233 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
234 static enum okay imap_list1(struct mailbox *mp, const char *base,
235 struct list_item **list, struct list_item **lend,
236 int level);
237 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
238 FILE *fp);
239 static void dopr(FILE *fp);
240 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
241 const char *name);
242 static enum okay imap_copyuid_parse(const char *cp,
243 unsigned long *uidvalidity, unsigned long *olduid,
244 unsigned long *newuid);
245 static enum okay imap_appenduid_parse(const char *cp,
246 unsigned long *uidvalidity, unsigned long *uid);
247 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
248 const char *name);
249 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
250 long off1, long xsize, long size, long lines, int flag,
251 const char *name);
252 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
253 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
254 const char *spec, int f);
255 static enum okay imap_remove1(struct mailbox *mp, const char *name);
256 static enum okay imap_rename1(struct mailbox *mp, const char *old,
257 const char *new);
258 static char * imap_strex(char const *cp, char const **xp);
259 static enum okay check_expunged(void);
261 static void
262 imap_other_get(char *pp)
264 char *xp;
265 NYD_ENTER;
267 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
268 pp += 6;
269 response_other = MAILBOX_DATA_FLAGS;
270 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
271 pp += 5;
272 response_other = MAILBOX_DATA_LIST;
273 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
274 pp += 5;
275 response_other = MAILBOX_DATA_LSUB;
276 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
277 pp += 8;
278 response_other = MAILBOX_DATA_MAILBOX;
279 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
280 pp += 7;
281 response_other = MAILBOX_DATA_SEARCH;
282 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
283 pp += 7;
284 response_other = MAILBOX_DATA_STATUS;
285 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
286 pp += 11;
287 response_other = CAPABILITY_DATA;
288 } else {
289 responded_other_number = strtol(pp, &xp, 10);
290 while (*xp == ' ')
291 ++xp;
292 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
293 response_other = MAILBOX_DATA_EXISTS;
294 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
295 response_other = MAILBOX_DATA_RECENT;
296 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
297 response_other = MESSAGE_DATA_EXPUNGE;
298 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
299 pp = &xp[6];
300 response_other = MESSAGE_DATA_FETCH;
301 } else
302 response_other = RESPONSE_OTHER_UNKNOWN;
304 responded_other_text = pp;
305 NYD_LEAVE;
308 static void
309 imap_response_get(const char **cp)
311 NYD_ENTER;
312 if (ascncasecmp(*cp, "OK ", 3) == 0) {
313 *cp += 3;
314 response_status = RESPONSE_OK;
315 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
316 *cp += 3;
317 response_status = RESPONSE_NO;
318 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
319 *cp += 4;
320 response_status = RESPONSE_BAD;
321 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
322 *cp += 8;
323 response_status = RESPONSE_PREAUTH;
324 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
325 *cp += 4;
326 response_status = RESPONSE_BYE;
327 } else
328 response_status = RESPONSE_OTHER;
329 NYD_LEAVE;
332 static void
333 imap_response_parse(void)
335 static char *parsebuf; /* TODO Use pool */
336 static size_t parsebufsize;
338 const char *ip = imapbuf;
339 char *pp;
340 NYD_ENTER;
342 if (parsebufsize < imapbufsize)
343 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
344 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
345 pp = parsebuf;
346 switch (*ip) {
347 case '+':
348 response_type = RESPONSE_CONT;
349 ip++;
350 pp++;
351 while (*ip == ' ') {
352 ip++;
353 pp++;
355 break;
356 case '*':
357 ip++;
358 pp++;
359 while (*ip == ' ') {
360 ip++;
361 pp++;
363 imap_response_get(&ip);
364 pp = &parsebuf[ip - imapbuf];
365 switch (response_status) {
366 case RESPONSE_BYE:
367 response_type = RESPONSE_FATAL;
368 break;
369 default:
370 response_type = RESPONSE_DATA;
372 break;
373 default:
374 responded_tag = parsebuf;
375 while (*pp && *pp != ' ')
376 pp++;
377 if (*pp == '\0') {
378 response_type = RESPONSE_ILLEGAL;
379 break;
381 *pp++ = '\0';
382 while (*pp && *pp == ' ')
383 pp++;
384 if (*pp == '\0') {
385 response_type = RESPONSE_ILLEGAL;
386 break;
388 ip = &imapbuf[pp - parsebuf];
389 response_type = RESPONSE_TAGGED;
390 imap_response_get(&ip);
391 pp = &parsebuf[ip - imapbuf];
393 responded_text = pp;
394 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
395 response_status == RESPONSE_OTHER)
396 imap_other_get(pp);
397 NYD_LEAVE;
400 static enum okay
401 imap_answer(struct mailbox *mp, int errprnt)
403 int i, complete;
404 enum okay rv;
405 NYD_ENTER;
407 rv = OKAY;
408 if (mp->mb_type == MB_CACHE)
409 goto jleave;
410 rv = STOP;
411 jagain:
412 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
413 if (options & OPT_VERBOSE)
414 fputs(imapbuf, stderr);
415 imap_response_parse();
416 if (response_type == RESPONSE_ILLEGAL)
417 goto jagain;
418 if (response_type == RESPONSE_CONT) {
419 rv = OKAY;
420 goto jleave;
422 if (response_status == RESPONSE_OTHER) {
423 if (response_other == MAILBOX_DATA_EXISTS) {
424 had_exists = responded_other_number;
425 rec_queue(REC_EXISTS, responded_other_number);
426 if (had_expunge > 0)
427 had_expunge = 0;
428 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
429 rec_queue(REC_EXPUNGE, responded_other_number);
430 if (had_expunge < 0)
431 had_expunge = 0;
432 had_expunge++;
433 expunged_messages++;
436 complete = 0;
437 if (response_type == RESPONSE_TAGGED) {
438 if (asccasecmp(responded_tag, tag(0)) == 0)
439 complete |= 1;
440 else
441 goto jagain;
443 switch (response_status) {
444 case RESPONSE_PREAUTH:
445 mp->mb_active &= ~MB_PREAUTH;
446 /*FALLTHRU*/
447 case RESPONSE_OK:
448 jokay:
449 rv = OKAY;
450 complete |= 2;
451 break;
452 case RESPONSE_NO:
453 case RESPONSE_BAD:
454 jstop:
455 rv = STOP;
456 complete |= 2;
457 if (errprnt)
458 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
459 break;
460 case RESPONSE_UNKNOWN: /* does not happen */
461 case RESPONSE_BYE:
462 i = mp->mb_active;
463 mp->mb_active = MB_NONE;
464 if (i & MB_BYE)
465 goto jokay;
466 goto jstop;
467 case RESPONSE_OTHER:
468 rv = OKAY;
469 break;
471 if (response_status != RESPONSE_OTHER &&
472 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
473 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
474 if (complete == 3)
475 mp->mb_active &= ~MB_COMD;
476 } else {
477 rv = STOP;
478 mp->mb_active = MB_NONE;
480 jleave:
481 NYD_LEAVE;
482 return rv;
485 static enum okay
486 imap_parse_list(void)
488 char *cp;
489 enum okay rv;
490 NYD_ENTER;
492 rv = STOP;
494 cp = responded_other_text;
495 list_attributes = LIST_NONE;
496 if (*cp == '(') {
497 while (*cp && *cp != ')') {
498 if (*cp == '\\') {
499 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
500 list_attributes |= LIST_NOINFERIORS;
501 cp += 12;
502 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
503 list_attributes |= LIST_NOSELECT;
504 cp += 9;
505 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
506 list_attributes |= LIST_MARKED;
507 cp += 7;
508 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
509 list_attributes |= LIST_UNMARKED;
510 cp += 9;
513 cp++;
515 if (*++cp != ' ')
516 goto jleave;
517 while (*cp == ' ')
518 cp++;
521 list_hierarchy_delimiter = EOF;
522 if (*cp == '"') {
523 if (*++cp == '\\')
524 cp++;
525 list_hierarchy_delimiter = *cp++ & 0377;
526 if (cp[0] != '"' || cp[1] != ' ')
527 goto jleave;
528 cp++;
529 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
530 list_hierarchy_delimiter = EOF;
531 cp += 3;
534 while (*cp == ' ')
535 cp++;
536 list_name = cp;
537 while (*cp && *cp != '\r')
538 cp++;
539 *cp = '\0';
540 rv = OKAY;
541 jleave:
542 NYD_LEAVE;
543 return rv;
546 static enum okay
547 imap_finish(struct mailbox *mp)
549 NYD_ENTER;
550 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
551 imap_answer(mp, 1);
552 NYD_LEAVE;
553 return OKAY;
556 static void
557 imap_timer_off(void)
559 NYD_ENTER;
560 if (imapkeepalive > 0) {
561 alarm(0);
562 safe_signal(SIGALRM, savealrm);
564 NYD_LEAVE;
567 static void
568 imapcatch(int s)
570 NYD_X; /* Signal handler */
571 termios_state_reset();
572 switch (s) {
573 case SIGINT:
574 fprintf(stderr, tr(102, "Interrupt\n"));
575 siglongjmp(imapjmp, 1);
576 /*NOTREACHED*/
577 case SIGPIPE:
578 fprintf(stderr, tr(98, "Received SIGPIPE during IMAP operation\n"));
579 break;
583 static void
584 _imap_maincatch(int s)
586 NYD_X; /* Signal handler */
587 UNUSED(s);
588 if (interrupts++ == 0) {
589 fprintf(stderr, tr(102, "Interrupt\n"));
590 return;
592 onintr(0);
595 static enum okay
596 imap_noop1(struct mailbox *mp)
598 char o[LINESIZE];
599 FILE *queuefp = NULL;
600 NYD_X;
602 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
603 IMAP_OUT(o, MB_COMD, return STOP)
604 IMAP_ANSWER()
605 return OKAY;
608 FL char const *
609 imap_fileof(char const *xcp)
611 char const *cp = xcp;
612 int state = 0;
613 NYD_ENTER;
615 while (*cp) {
616 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
617 cp += 3;
618 state = 1;
620 if (cp[0] == '/' && state == 1) {
621 ++cp;
622 goto jleave;
624 if (cp[0] == '/') {
625 cp = xcp;
626 goto jleave;
628 ++cp;
630 jleave:
631 NYD_LEAVE;
632 return cp;
635 FL enum okay
636 imap_noop(void)
638 sighandler_type volatile oldint, oldpipe;
639 enum okay rv = STOP;
640 NYD_ENTER;
642 if (mb.mb_type != MB_IMAP)
643 goto jleave;
645 imaplock = 1;
646 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
647 safe_signal(SIGINT, &_imap_maincatch);
648 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
649 if (sigsetjmp(imapjmp, 1) == 0) {
650 if (oldpipe != SIG_IGN)
651 safe_signal(SIGPIPE, imapcatch);
653 rv = imap_noop1(&mb);
655 safe_signal(SIGINT, oldint);
656 safe_signal(SIGPIPE, oldpipe);
657 imaplock = 0;
658 jleave:
659 NYD_LEAVE;
660 if (interrupts)
661 onintr(0);
662 return rv;
665 static void
666 rec_queue(enum rec_type rt, unsigned long cnt)
668 struct record *rp;
669 NYD_ENTER;
671 rp = scalloc(1, sizeof *rp);
672 rp->rec_type = rt;
673 rp->rec_count = cnt;
674 if (record && recend) {
675 recend->rec_next = rp;
676 recend = rp;
677 } else
678 record = recend = rp;
679 NYD_LEAVE;
682 static enum okay
683 rec_dequeue(void)
685 struct message *omessage;
686 struct record *rp, *rq;
687 uiz_t exists = 0, i;
688 enum okay rv = STOP;
689 NYD_ENTER;
691 if (record == NULL)
692 goto jleave;
694 omessage = message;
695 message = smalloc((msgCount+1) * sizeof *message);
696 if (msgCount)
697 memcpy(message, omessage, msgCount * sizeof *message);
698 memset(&message[msgCount], 0, sizeof *message);
700 rp = record, rq = NULL;
701 rv = OKAY;
702 while (rp != NULL) {
703 switch (rp->rec_type) {
704 case REC_EXISTS:
705 exists = rp->rec_count;
706 break;
707 case REC_EXPUNGE:
708 if (rp->rec_count == 0) {
709 rv = STOP;
710 break;
712 if (rp->rec_count > (unsigned long)msgCount) {
713 if (exists == 0 || rp->rec_count > exists--)
714 rv = STOP;
715 break;
717 if (exists > 0)
718 exists--;
719 delcache(&mb, &message[rp->rec_count-1]);
720 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
721 ((msgCount - rp->rec_count + 1) * sizeof *message));
722 --msgCount;
723 /* If the message was part of a collapsed thread,
724 * the m_collapsed field of one of its ancestors
725 * should be incremented. It seems hardly possible
726 * to do this with the current message structure,
727 * though. The result is that a '+' may be shown
728 * in the header summary even if no collapsed
729 * children exists */
730 break;
732 if (rq != NULL)
733 free(rq);
734 rq = rp;
735 rp = rp->rec_next;
737 if (rq != NULL)
738 free(rq);
740 record = recend = NULL;
741 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
742 message = srealloc(message, (exists + 1) * sizeof *message);
743 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
744 for (i = msgCount; i < exists; ++i)
745 imap_init(&mb, i);
746 imap_flags(&mb, msgCount+1, exists);
747 msgCount = exists;
750 if (rv == STOP) {
751 free(message);
752 message = omessage;
754 jleave:
755 NYD_LEAVE;
756 return rv;
759 static void
760 rec_rmqueue(void)
762 struct record *rp;
763 NYD_ENTER;
765 for (rp = record; rp != NULL;) {
766 struct record *tmp = rp;
767 rp = rp->rec_next;
768 free(tmp);
770 record = recend = NULL;
771 NYD_LEAVE;
774 /*ARGSUSED*/
775 static void
776 imapalarm(int s)
778 sighandler_type volatile saveint, savepipe;
779 NYD_X; /* Signal handler */
780 UNUSED(s);
782 if (imaplock++ == 0) {
783 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
784 safe_signal(SIGINT, &_imap_maincatch);
785 savepipe = safe_signal(SIGPIPE, SIG_IGN);
786 if (sigsetjmp(imapjmp, 1)) {
787 safe_signal(SIGINT, saveint);
788 safe_signal(SIGPIPE, savepipe);
789 goto jbrk;
791 if (savepipe != SIG_IGN)
792 safe_signal(SIGPIPE, imapcatch);
793 if (imap_noop1(&mb) != OKAY) {
794 safe_signal(SIGINT, saveint);
795 safe_signal(SIGPIPE, savepipe);
796 goto jleave;
798 safe_signal(SIGINT, saveint);
799 safe_signal(SIGPIPE, savepipe);
801 jbrk:
802 alarm(imapkeepalive);
803 jleave:
804 --imaplock;
807 static int
808 imap_use_starttls(const char *uhp)
810 int rv;
811 NYD_ENTER;
813 if (ok_blook(imap_use_starttls))
814 rv = 1;
815 else {
816 char *var = savecat("imap-use-starttls-", uhp);
817 rv = vok_blook(var);
819 NYD_LEAVE;
820 return rv;
823 static enum okay
824 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
826 char *cp;
827 NYD_X;
829 mp->mb_active |= MB_PREAUTH;
830 imap_answer(mp, 1);
831 if ((cp = strchr(xserver, ':')) != NULL) {
832 char *x = salloc(cp - xserver + 1);
833 memcpy(x, xserver, cp - xserver);
834 x[cp - xserver] = '\0';
835 xserver = x;
837 #ifdef HAVE_SSL
838 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
839 FILE *queuefp = NULL;
840 char o[LINESIZE];
842 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
843 IMAP_OUT(o, MB_COMD, return STOP)
844 IMAP_ANSWER()
845 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
846 return STOP;
848 #else
849 if (imap_use_starttls(uhp)) {
850 fprintf(stderr, "No SSL support compiled in.\n");
851 return STOP;
853 #endif
854 imap_capability(mp);
855 return OKAY;
858 static enum okay
859 imap_capability(struct mailbox *mp)
861 char o[LINESIZE];
862 FILE *queuefp = NULL;
863 enum okay ok = STOP;
864 const char *cp;
865 NYD_X;
867 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
868 IMAP_OUT(o, MB_COMD, return STOP)
869 while (mp->mb_active & MB_COMD) {
870 ok = imap_answer(mp, 0);
871 if (response_status == RESPONSE_OTHER &&
872 response_other == CAPABILITY_DATA) {
873 cp = responded_other_text;
874 while (*cp) {
875 while (spacechar(*cp))
876 ++cp;
877 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
878 /* RFC 2359 */
879 mp->mb_flags |= MB_UIDPLUS;
880 while (*cp && !spacechar(*cp))
881 ++cp;
885 return ok;
888 static enum okay
889 imap_auth(struct mailbox *mp, const char *uhp, char *xuser, char *pass)
891 char *var, *auth;
892 enum okay rv;
893 NYD_ENTER;
895 if (!(mp->mb_active & MB_PREAUTH)) {
896 rv = OKAY;
897 goto jleave;
900 if ((auth = ok_vlook(imap_auth)) == NULL) {
901 size_t i = strlen(uhp) + 1;
902 var = ac_alloc(i + 10);
903 memcpy(var, "imap-auth-", 10);
904 memcpy(var + 10, uhp, i);
905 auth = vok_vlook(var);
906 ac_free(var);
909 if (auth == NULL || !strcmp(auth, "login"))
910 rv = imap_login(mp, xuser, pass);
911 else if (!strcmp(auth, "cram-md5")) {
912 #ifdef HAVE_MD5
913 rv = imap_cram_md5(mp, xuser, pass);
914 #else
915 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
916 rv = STOP;
917 #endif
918 } else if (!strcmp(auth, "gssapi")) {
919 #ifdef HAVE_GSSAPI
920 rv = imap_gss(mp, xuser);
921 #else
922 fprintf(stderr, tr(272, "No GSSAPI support compiled in.\n"));
923 rv = STOP;
924 #endif
925 } else {
926 fprintf(stderr, tr(273,
927 "Unknown IMAP authentication method: %s\n"), auth);
928 rv = STOP;
930 jleave:
931 NYD_LEAVE;
932 return rv;
935 /* Implementation of RFC 2194 */
936 #ifdef HAVE_MD5
937 static enum okay
938 imap_cram_md5(struct mailbox *mp, char *xuser, char *xpass)
940 char o[LINESIZE], *user, *pass, *cp;
941 FILE *queuefp = NULL;
942 enum okay rv = STOP;
943 NYD_ENTER;
945 jretry:
946 user = xuser;
947 pass = xpass;
948 if (!getcredentials(&user, &pass))
949 goto jleave;
951 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
952 IMAP_XOUT(o, 0, goto jleave, goto jleave);
953 imap_answer(mp, 1);
954 if (response_type != RESPONSE_CONT)
955 goto jleave;
957 cp = cram_md5_string(user, pass, responded_text);
958 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
959 while (mp->mb_active & MB_COMD)
960 rv = imap_answer(mp, 1);
961 if (rv == STOP) {
962 xpass = NULL;
963 goto jretry;
965 jleave:
966 NYD_LEAVE;
967 return rv;
969 #endif /* HAVE_MD5 */
971 static enum okay
972 imap_login(struct mailbox *mp, char *xuser, char *xpass)
974 char o[LINESIZE];
975 char *user, *pass;
976 FILE *queuefp = NULL;
977 enum okay rv = STOP;
978 NYD_ENTER;
980 jretry:
981 user = xuser;
982 pass = xpass;
983 if (!getcredentials(&user, &pass))
984 goto jleave;
986 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
987 tag(1), imap_quotestr(user), imap_quotestr(pass));
988 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
989 while (mp->mb_active & MB_COMD)
990 rv = imap_answer(mp, 1);
991 if (rv == STOP) {
992 xpass = NULL;
993 goto jretry;
995 jleave:
996 NYD_LEAVE;
997 return rv;
1000 #ifdef HAVE_GSSAPI
1001 # include "imap_gssapi.h"
1002 #endif
1004 FL enum okay
1005 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
1007 enum okay ok = OKAY;
1008 char const*cp;
1009 char o[LINESIZE];
1010 FILE *queuefp = NULL;
1011 NYD_X;
1012 UNUSED(size);
1014 mp->mb_uidvalidity = 0;
1015 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
1016 IMAP_OUT(o, MB_COMD, return STOP)
1017 while (mp->mb_active & MB_COMD) {
1018 ok = imap_answer(mp, 1);
1019 if (response_status != RESPONSE_OTHER &&
1020 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
1021 mp->mb_uidvalidity = atol(&cp[13]);
1023 *cnt = (had_exists > 0) ? had_exists : 0;
1024 if (response_status != RESPONSE_OTHER &&
1025 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
1026 mp->mb_perm = 0;
1027 return ok;
1030 static enum okay
1031 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1033 char o[LINESIZE];
1034 FILE *queuefp = NULL;
1035 char const *cp;
1036 struct message *m;
1037 unsigned x = X, y = Y, n;
1038 NYD_X;
1040 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1041 IMAP_OUT(o, MB_COMD, return STOP)
1042 while (mp->mb_active & MB_COMD) {
1043 imap_answer(mp, 1);
1044 if (response_status == RESPONSE_OTHER &&
1045 response_other == MESSAGE_DATA_FETCH) {
1046 n = responded_other_number;
1047 if (n < x || n > y)
1048 continue;
1049 m = &message[n-1];
1050 m->m_xsize = 0;
1051 } else
1052 continue;
1054 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1055 cp += 5;
1056 while (*cp == ' ')
1057 cp++;
1058 if (*cp == '(')
1059 imap_getflags(cp, &cp, &m->m_flag);
1062 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1063 m->m_uid = strtoul(&cp[4], NULL, 10);
1064 getcache1(mp, m, NEED_UNSPEC, 1);
1065 m->m_flag &= ~MHIDDEN;
1068 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1069 x++;
1070 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1071 y--;
1072 if (x <= y) {
1073 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1074 tag(1), x, y);
1075 IMAP_OUT(o, MB_COMD, return STOP)
1076 while (mp->mb_active & MB_COMD) {
1077 imap_answer(mp, 1);
1078 if (response_status == RESPONSE_OTHER &&
1079 response_other == MESSAGE_DATA_FETCH) {
1080 n = responded_other_number;
1081 if (n < x || n > y)
1082 continue;
1083 m = &message[n-1];
1084 } else
1085 continue;
1086 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1087 m->m_xsize = strtol(&cp[12], NULL, 10);
1088 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1089 m->m_time = imap_read_date_time(&cp[13]);
1093 for (n = X; n <= Y; n++)
1094 putcache(mp, &message[n-1]);
1095 return OKAY;
1098 static void
1099 imap_init(struct mailbox *mp, int n)
1101 struct message *m;
1102 NYD_ENTER;
1103 UNUSED(mp);
1105 m = message + n;
1106 m->m_flag = MUSED | MNOFROM;
1107 m->m_block = 0;
1108 m->m_offset = 0;
1109 NYD_LEAVE;
1112 static void
1113 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1115 struct message *omessage = 0;
1116 int i, omsgCount = 0;
1117 enum okay dequeued = STOP;
1118 NYD_ENTER;
1120 if (nmail || transparent) {
1121 omessage = message;
1122 omsgCount = msgCount;
1124 if (nmail)
1125 dequeued = rec_dequeue();
1127 if (had_exists >= 0) {
1128 if (dequeued != OKAY)
1129 msgCount = had_exists;
1130 had_exists = -1;
1132 if (had_expunge >= 0) {
1133 if (dequeued != OKAY)
1134 msgCount -= had_expunge;
1135 had_expunge = -1;
1138 if (nmail && expunged_messages)
1139 printf("Expunged %ld message%s.\n", expunged_messages,
1140 (expunged_messages != 1 ? "s" : ""));
1141 *prevcount = omsgCount - expunged_messages;
1142 expunged_messages = 0;
1143 if (msgCount < 0) {
1144 fputs("IMAP error: Negative message count\n", stderr);
1145 msgCount = 0;
1148 if (dequeued != OKAY) {
1149 message = scalloc(msgCount + 1, sizeof *message);
1150 for (i = 0; i < msgCount; i++)
1151 imap_init(mp, i);
1152 if (!nmail && mp->mb_type == MB_IMAP)
1153 initcache(mp);
1154 if (msgCount > 0)
1155 imap_flags(mp, 1, msgCount);
1156 message[msgCount].m_size = 0;
1157 message[msgCount].m_lines = 0;
1158 rec_rmqueue();
1160 if (nmail || transparent)
1161 transflags(omessage, omsgCount, transparent);
1162 else
1163 setdot(message);
1164 NYD_LEAVE;
1167 static void
1168 imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
1169 char const **uhp, char const **mbx, char **pass, char **user)
1171 NYD_ENTER;
1172 *sp = *server;
1173 if (strncmp(*sp, "imap://", 7) == 0) {
1174 *sp = &(*sp)[7];
1175 *use_ssl = 0;
1176 #ifdef HAVE_SSL
1177 } else if (strncmp(*sp, "imaps://", 8) == 0) {
1178 *sp = &(*sp)[8];
1179 *use_ssl = 1;
1180 #endif
1181 } else
1182 *use_ssl = 0; /* (silence CC) */
1184 if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
1185 char *x = savestr(*sp);
1186 x[*cp - *sp] = '\0';
1187 *uhp = x;
1188 *mbx = &(*cp)[1];
1189 } else {
1190 if (*cp)
1191 (*server)[*cp - *server] = '\0';
1192 *uhp = *sp;
1193 *mbx = "INBOX";
1196 *pass = lookup_password_for_token(*uhp);
1197 if ((*cp = last_at_before_slash(*uhp)) != NULL) {
1198 *user = salloc(*cp - *uhp + 1);
1199 memcpy(*user, *uhp, *cp - *uhp);
1200 (*user)[*cp - *uhp] = '\0';
1201 *sp = &(*cp)[1];
1202 *user = urlxdec(*user);
1203 } else {
1204 *user = NULL;
1205 *sp = *uhp;
1207 NYD_LEAVE;
1210 FL int
1211 imap_setfile(const char *xserver, int nmail, int isedit)
1213 int rv;
1214 NYD_ENTER;
1216 rv = imap_setfile1(xserver, nmail, isedit, 0);
1217 NYD_LEAVE;
1218 return rv;
1221 static int
1222 imap_setfile1(const char *xserver, int nmail, int isedit,
1223 int volatile transparent)
1225 struct sock so;
1226 sighandler_type volatile saveint, savepipe;
1227 char *server, *user, *pass, *acc;
1228 char const *cp, *sp, * volatile mbx, *uhp;
1229 int rv, use_ssl = 0, prevcount = 0;
1230 enum mbflags same_flags;
1231 NYD_ENTER;
1233 server = savestr(xserver);
1234 if (nmail) {
1235 saveint = safe_signal(SIGINT, SIG_IGN);
1236 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1237 if (saveint != SIG_IGN)
1238 safe_signal(SIGINT, imapcatch);
1239 if (savepipe != SIG_IGN)
1240 safe_signal(SIGPIPE, imapcatch);
1241 imaplock = 1;
1242 goto jnmail;
1245 same_flags = mb.mb_flags;
1246 same_imap_account = 0;
1247 sp = protbase(server);
1248 if (mb.mb_imap_account && mb.mb_type == MB_IMAP) {
1249 if (mb.mb_sock.s_fd > 0 && strcmp(mb.mb_imap_account, sp) == 0 &&
1250 disconnected(mb.mb_imap_account) == 0)
1251 same_imap_account = 1;
1253 acc = sstrdup(sp);
1256 char const *xmbx;
1257 imap_split(&server, &sp, &use_ssl, &cp, &uhp, &xmbx, &pass, &user);
1258 mbx = xmbx;
1260 so.s_fd = -1;
1261 if (!same_imap_account) {
1262 if (!disconnected(acc) &&
1263 sopen(sp, &so, use_ssl, uhp, (use_ssl ? "imaps" : "imap")
1264 ) != OKAY) {
1265 free(acc);
1266 rv = -1;
1267 goto jleave;
1269 } else
1270 so = mb.mb_sock;
1271 if (!transparent)
1272 quit();
1274 edit = (isedit != 0);
1275 if (mb.mb_imap_account != NULL)
1276 free(mb.mb_imap_account);
1277 mb.mb_imap_account = acc;
1278 if (!same_imap_account) {
1279 if (mb.mb_sock.s_fd >= 0)
1280 sclose(&mb.mb_sock);
1282 same_imap_account = 0;
1284 if (!transparent) {
1285 if (mb.mb_itf) {
1286 fclose(mb.mb_itf);
1287 mb.mb_itf = NULL;
1289 if (mb.mb_otf) {
1290 fclose(mb.mb_otf);
1291 mb.mb_otf = NULL;
1293 if (mb.mb_imap_mailbox != NULL)
1294 free(mb.mb_imap_mailbox);
1295 mb.mb_imap_mailbox = sstrdup(mbx);
1296 initbox(server);
1298 mb.mb_type = MB_VOID;
1299 mb.mb_active = MB_NONE;
1301 imaplock = 1;
1302 saveint = safe_signal(SIGINT, SIG_IGN);
1303 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1304 if (sigsetjmp(imapjmp, 1)) {
1305 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1306 sclose(&mb.mb_sock);
1307 safe_signal(SIGINT, saveint);
1308 safe_signal(SIGPIPE, savepipe);
1309 imaplock = 0;
1311 mb.mb_type = MB_VOID;
1312 mb.mb_active = MB_NONE;
1313 rv = -1;
1314 goto jleave;
1316 if (saveint != SIG_IGN)
1317 safe_signal(SIGINT, imapcatch);
1318 if (savepipe != SIG_IGN)
1319 safe_signal(SIGPIPE, imapcatch);
1321 if (mb.mb_sock.s_fd < 0) {
1322 if (disconnected(mb.mb_imap_account)) {
1323 if (cache_setptr(transparent) == STOP)
1324 fprintf(stderr,
1325 "Mailbox \"%s\" is not cached.\n",
1326 server);
1327 goto jdone;
1329 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1330 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1331 savealrm = safe_signal(SIGALRM, imapalarm);
1332 alarm(imapkeepalive);
1336 mb.mb_sock = so;
1337 mb.mb_sock.s_desc = "IMAP";
1338 mb.mb_sock.s_onclose = imap_timer_off;
1339 if (imap_preauth(&mb, sp, uhp) != OKAY ||
1340 imap_auth(&mb, uhp, user, pass) != OKAY) {
1341 sclose(&mb.mb_sock);
1342 imap_timer_off();
1343 safe_signal(SIGINT, saveint);
1344 safe_signal(SIGPIPE, savepipe);
1345 imaplock = 0;
1346 rv = -1;
1347 goto jleave;
1349 } else /* same account */
1350 mb.mb_flags |= same_flags;
1352 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1353 mb.mb_type = MB_IMAP;
1354 cache_dequeue(&mb);
1355 if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
1356 /*sclose(&mb.mb_sock);
1357 imap_timer_off();*/
1358 safe_signal(SIGINT, saveint);
1359 safe_signal(SIGPIPE, savepipe);
1360 imaplock = 0;
1361 mb.mb_type = MB_VOID;
1362 rv = -1;
1363 goto jleave;
1366 jnmail:
1367 imap_setptr(&mb, nmail, transparent, &prevcount);
1368 jdone:
1369 setmsize(msgCount);
1370 if (!nmail && !transparent)
1371 sawcom = FAL0;
1372 safe_signal(SIGINT, saveint);
1373 safe_signal(SIGPIPE, savepipe);
1374 imaplock = 0;
1376 if (!nmail && mb.mb_type == MB_IMAP)
1377 purgecache(&mb, message, msgCount);
1378 if ((nmail || transparent) && mb.mb_sorted) {
1379 mb.mb_threaded = 0;
1380 sort((void*)-1);
1383 if (!nmail && !edit && msgCount == 0) {
1384 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1385 !ok_blook(emptystart))
1386 fprintf(stderr, tr(258, "No mail at %s\n"), server);
1387 rv = 1;
1388 goto jleave;
1390 if (nmail)
1391 newmailinfo(prevcount);
1392 rv = 0;
1393 jleave:
1394 NYD_LEAVE;
1395 return rv;
1398 static int
1399 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1400 int need, const char *head, size_t headsize, long headlines)
1402 char *line = NULL, *lp;
1403 size_t linesize = 0, linelen, size = 0;
1404 int emptyline = 0, lines = 0, excess = 0;
1405 off_t offset;
1406 NYD_ENTER;
1408 fseek(mp->mb_otf, 0L, SEEK_END);
1409 offset = ftell(mp->mb_otf);
1411 if (head)
1412 fwrite(head, 1, headsize, mp->mb_otf);
1414 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1415 lp = line;
1416 if (linelen > expected) {
1417 excess = linelen - expected;
1418 linelen = expected;
1420 /* TODO >>
1421 * Need to mask 'From ' lines. This cannot be done properly
1422 * since some servers pass them as 'From ' and others as
1423 * '>From '. Although one could identify the first kind of
1424 * server in principle, it is not possible to identify the
1425 * second as '>From ' may also come from a server of the
1426 * first type as actual data. So do what is absolutely
1427 * necessary only - mask 'From '.
1429 * If the line is the first line of the message header, it
1430 * is likely a real 'From ' line. In this case, it is just
1431 * ignored since it violates all standards.
1432 * TODO can the latter *really* happen??
1433 * TODO <<
1435 /* Since we simply copy over data without doing any transfer
1436 * encoding reclassification/adjustment we *have* to perform
1437 * RFC 4155 compliant From_ quoting here */
1438 if (is_head(lp, linelen)) {
1439 if (lines + headlines == 0)
1440 goto jskip;
1441 fputc('>', mp->mb_otf);
1442 ++size;
1444 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1445 emptyline = linelen <= 2;
1446 if (linelen > 2) {
1447 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1448 size += linelen - 1;
1449 } else
1450 ++size;
1451 fputc('\n', mp->mb_otf);
1452 } else {
1453 emptyline = 0;
1454 fwrite(lp, 1, linelen, mp->mb_otf);
1455 size += linelen;
1457 ++lines;
1458 jskip:
1459 if ((expected -= linelen) <= 0)
1460 break;
1462 if (!emptyline) {
1463 /* This is very ugly; but some IMAP daemons don't end a
1464 * message with \r\n\r\n, and we need \n\n for mbox format */
1465 fputc('\n', mp->mb_otf);
1466 ++lines;
1467 ++size;
1469 fflush(mp->mb_otf);
1471 if (m != NULL) {
1472 m->m_size = size + headsize;
1473 m->m_lines = lines + headlines;
1474 m->m_block = mailx_blockof(offset);
1475 m->m_offset = mailx_offsetof(offset);
1476 switch (need) {
1477 case NEED_HEADER:
1478 m->m_have |= HAVE_HEADER;
1479 break;
1480 case NEED_BODY:
1481 m->m_have |= HAVE_HEADER | HAVE_BODY;
1482 m->m_xlines = m->m_lines;
1483 m->m_xsize = m->m_size;
1484 break;
1487 free(line);
1488 NYD_LEAVE;
1489 return excess;
1492 static void
1493 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1494 const char *head, size_t headsize, long headlines)
1496 off_t offset;
1497 size_t len;
1498 NYD_ENTER;
1500 len = strlen(str);
1501 fseek(mp->mb_otf, 0L, SEEK_END);
1502 offset = ftell(mp->mb_otf);
1503 if (head)
1504 fwrite(head, 1, headsize, mp->mb_otf);
1505 if (len > 0) {
1506 fwrite(str, 1, len, mp->mb_otf);
1507 fputc('\n', mp->mb_otf);
1508 ++len;
1510 fflush(mp->mb_otf);
1512 if (m != NULL) {
1513 m->m_size = headsize + len;
1514 m->m_lines = headlines + 1;
1515 m->m_block = mailx_blockof(offset);
1516 m->m_offset = mailx_offsetof(offset);
1517 m->m_have |= HAVE_HEADER | HAVE_BODY;
1518 m->m_xlines = m->m_lines;
1519 m->m_xsize = m->m_size;
1521 NYD_LEAVE;
1524 static enum okay
1525 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1527 char o[LINESIZE];
1528 struct message mt;
1529 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
1530 char *volatile head = NULL;
1531 char const *cp = NULL, *loc = NULL, * volatile item = NULL,
1532 * volatile resp = NULL;
1533 size_t expected;
1534 size_t volatile headsize = 0;
1535 int number = m - message + 1;
1536 FILE *queuefp = NULL;
1537 long volatile headlines = 0;
1538 long n = -1;
1539 unsigned long u = 0;
1540 enum okay ok = STOP;
1541 NYD_X;
1543 if (getcache(mp, m, need) == OKAY)
1544 return OKAY;
1545 if (mp->mb_type == MB_CACHE) {
1546 fprintf(stderr, "Message %u not available.\n", number);
1547 return STOP;
1550 if (mp->mb_sock.s_fd < 0) {
1551 fprintf(stderr, "IMAP connection closed.\n");
1552 return STOP;
1555 switch (need) {
1556 case NEED_HEADER:
1557 resp = item = "RFC822.HEADER";
1558 break;
1559 case NEED_BODY:
1560 item = "BODY.PEEK[]";
1561 resp = "BODY[]";
1562 if (m->m_flag & HAVE_HEADER && m->m_size) {
1563 char *hdr = smalloc(m->m_size);
1564 fflush(mp->mb_otf);
1565 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1566 SEEK_SET) < 0 ||
1567 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1568 free(hdr);
1569 break;
1571 head = hdr;
1572 headsize = m->m_size;
1573 headlines = m->m_lines;
1574 item = "BODY.PEEK[TEXT]";
1575 resp = "BODY[TEXT]";
1577 break;
1578 case NEED_UNSPEC:
1579 return STOP;
1582 imaplock = 1;
1583 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1584 if (sigsetjmp(imapjmp, 1)) {
1585 safe_signal(SIGINT, saveint);
1586 safe_signal(SIGPIPE, savepipe);
1587 imaplock = 0;
1588 return STOP;
1590 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1591 safe_signal(SIGINT, &_imap_maincatch);
1592 if (savepipe != SIG_IGN)
1593 safe_signal(SIGPIPE, imapcatch);
1595 if (m->m_uid)
1596 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1597 tag(1), m->m_uid, item);
1598 else {
1599 if (check_expunged() == STOP)
1600 goto out;
1601 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1603 IMAP_OUT(o, MB_COMD, goto out)
1604 for (;;) {
1605 ok = imap_answer(mp, 1);
1606 if (ok == STOP)
1607 break;
1608 if (response_status != RESPONSE_OTHER ||
1609 response_other != MESSAGE_DATA_FETCH)
1610 continue;
1611 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1612 continue;
1613 if (m->m_uid) {
1614 if ((cp = asccasestr(responded_other_text, "UID "))) {
1615 u = atol(&cp[4]);
1616 n = 0;
1617 } else {
1618 n = -1;
1619 u = 0;
1621 } else
1622 n = responded_other_number;
1623 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1624 if (m->m_uid ? m->m_uid != u : n != number)
1625 continue;
1626 if ((cp = strchr(loc, '"')) != NULL) {
1627 cp = imap_unquotestr(cp);
1628 imap_putstr(mp, m, cp, head, headsize, headlines);
1629 } else {
1630 m->m_have |= HAVE_HEADER|HAVE_BODY;
1631 m->m_xlines = m->m_lines;
1632 m->m_xsize = m->m_size;
1634 goto out;
1636 expected = atol(&cp[1]);
1637 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1638 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1639 continue;
1641 mt = *m;
1642 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1643 if (n >= 0) {
1644 commitmsg(mp, m, &mt, mt.m_have);
1645 break;
1647 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1648 if (options & OPT_VERBOSE)
1649 fputs(imapbuf, stderr);
1650 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1651 u = atol(&cp[4]);
1652 if (u == m->m_uid) {
1653 commitmsg(mp, m, &mt, mt.m_have);
1654 break;
1659 out:
1660 while (mp->mb_active & MB_COMD)
1661 ok = imap_answer(mp, 1);
1663 if (saveint != SIG_IGN)
1664 safe_signal(SIGINT, saveint);
1665 if (savepipe != SIG_IGN)
1666 safe_signal(SIGPIPE, savepipe);
1667 imaplock--;
1669 if (ok == OKAY)
1670 putcache(mp, m);
1671 if (head != NULL)
1672 free(head);
1673 if (interrupts)
1674 onintr(0);
1675 return ok;
1678 FL enum okay
1679 imap_header(struct message *m)
1681 enum okay rv;
1682 NYD_ENTER;
1684 rv = imap_get(&mb, m, NEED_HEADER);
1685 NYD_LEAVE;
1686 return rv;
1690 FL enum okay
1691 imap_body(struct message *m)
1693 enum okay rv;
1694 NYD_ENTER;
1696 rv = imap_get(&mb, m, NEED_BODY);
1697 NYD_LEAVE;
1698 return rv;
1701 static void
1702 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1703 enum havespec have)
1705 NYD_ENTER;
1706 tomp->m_size = frommp->m_size;
1707 tomp->m_lines = frommp->m_lines;
1708 tomp->m_block = frommp->m_block;
1709 tomp->m_offset = frommp->m_offset;
1710 tomp->m_have = have;
1711 if (have & HAVE_BODY) {
1712 tomp->m_xlines = frommp->m_lines;
1713 tomp->m_xsize = frommp->m_size;
1715 putcache(mp, tomp);
1716 NYD_LEAVE;
1719 static enum okay
1720 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1722 /* bot > topp */
1723 char o[LINESIZE];
1724 char const *cp;
1725 struct message mt;
1726 size_t expected;
1727 int n = 0, u;
1728 FILE *queuefp = NULL;
1729 enum okay ok;
1730 NYD_X;
1732 if (m[bot].m_uid)
1733 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1734 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1735 else {
1736 if (check_expunged() == STOP)
1737 return STOP;
1738 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1739 tag(1), bot, topp);
1741 IMAP_OUT(o, MB_COMD, return STOP)
1742 for (;;) {
1743 ok = imap_answer(mp, 1);
1744 if (response_status != RESPONSE_OTHER)
1745 break;
1746 if (response_other != MESSAGE_DATA_FETCH)
1747 continue;
1748 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1749 return STOP;
1750 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1751 continue;
1752 expected = atol(&cp[1]);
1753 if (m[bot-1].m_uid) {
1754 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1755 u = atoi(&cp[4]);
1756 for (n = bot; n <= topp; n++)
1757 if ((unsigned long)u == m[n-1].m_uid)
1758 break;
1759 if (n > topp) {
1760 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1761 continue;
1763 } else
1764 n = -1;
1765 } else {
1766 n = responded_other_number;
1767 if (n <= 0 || n > msgCount) {
1768 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1769 continue;
1772 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1773 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1774 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1775 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1776 if (options & OPT_VERBOSE)
1777 fputs(imapbuf, stderr);
1778 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1779 u = atoi(&cp[4]);
1780 for (n = bot; n <= topp; n++)
1781 if ((unsigned long)u == m[n-1].m_uid)
1782 break;
1783 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1784 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1785 n = 0;
1789 while (mp->mb_active & MB_COMD)
1790 ok = imap_answer(mp, 1);
1791 return ok;
1794 FL void
1795 imap_getheaders(int volatile bot, int topp)
1797 sighandler_type saveint, savepipe;
1798 /* enum okay ok = STOP;*/
1799 int i, chunk = 256;
1800 NYD_X;
1802 if (mb.mb_type == MB_CACHE)
1803 return;
1804 if (bot < 1)
1805 bot = 1;
1806 if (topp > msgCount)
1807 topp = msgCount;
1808 for (i = bot; i < topp; i++) {
1809 if (message[i-1].m_have & HAVE_HEADER ||
1810 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1811 bot = i+1;
1812 else
1813 break;
1815 for (i = topp; i > bot; i--) {
1816 if (message[i-1].m_have & HAVE_HEADER ||
1817 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1818 topp = i-1;
1819 else
1820 break;
1822 if (bot >= topp)
1823 return;
1825 imaplock = 1;
1826 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1827 safe_signal(SIGINT, &_imap_maincatch);
1828 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1829 if (sigsetjmp(imapjmp, 1) == 0) {
1830 if (savepipe != SIG_IGN)
1831 safe_signal(SIGPIPE, imapcatch);
1833 for (i = bot; i <= topp; i += chunk) {
1834 int j = i + chunk - 1;
1835 /*ok = */imap_fetchheaders(&mb, message, i, (j < topp ? j : topp));
1836 if (interrupts)
1837 onintr(0); /* XXX imaplock? */
1840 safe_signal(SIGINT, saveint);
1841 safe_signal(SIGPIPE, savepipe);
1842 imaplock = 0;
1845 static enum okay
1846 __imap_exit(struct mailbox *mp)
1848 char o[LINESIZE];
1849 FILE *queuefp = NULL;
1850 NYD_X;
1852 mp->mb_active |= MB_BYE;
1853 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1854 IMAP_OUT(o, MB_COMD, return STOP)
1855 IMAP_ANSWER()
1856 return OKAY;
1859 static enum okay
1860 imap_exit(struct mailbox *mp)
1862 enum okay rv;
1863 NYD_ENTER;
1865 rv = __imap_exit(mp);
1866 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1867 free(mp->mb_imap_account);
1868 free(mp->mb_imap_mailbox);
1869 if (mp->mb_cache_directory != NULL)
1870 free(mp->mb_cache_directory);
1871 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1872 mp->mb_imap_account =
1873 mp->mb_imap_mailbox =
1874 mp->mb_cache_directory = "";
1875 #else
1876 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1877 mp->mb_imap_mailbox = NULL;
1878 mp->mb_cache_directory = NULL;
1879 #endif
1880 #endif
1881 sclose(&mp->mb_sock);
1882 NYD_LEAVE;
1883 return rv;
1886 static enum okay
1887 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1889 NYD_ENTER;
1890 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1891 if (mp->mb_type == MB_IMAP)
1892 delcache(mp, m);
1893 NYD_LEAVE;
1894 return OKAY;
1897 static enum okay
1898 imap_close(struct mailbox *mp)
1900 char o[LINESIZE];
1901 FILE *queuefp = NULL;
1902 NYD_X;
1904 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1905 IMAP_OUT(o, MB_COMD, return STOP)
1906 IMAP_ANSWER()
1907 return OKAY;
1910 static enum okay
1911 imap_update(struct mailbox *mp)
1913 struct message *m;
1914 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1915 NYD_ENTER;
1917 if (!edit && mp->mb_perm != 0) {
1918 holdbits();
1919 c = 0;
1920 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1921 if (m->m_flag & MBOX)
1922 ++c;
1923 if (c > 0)
1924 if (makembox() == STOP)
1925 goto jbypass;
1928 gotcha = held = 0;
1929 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1930 if (mp->mb_perm == 0)
1931 dodel = 0;
1932 else if (edit)
1933 dodel = ((m->m_flag & MDELETED) != 0);
1934 else
1935 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1937 /* Fetch the result after around each 800 STORE commands
1938 * sent (approx. 32k data sent). Otherwise, servers will
1939 * try to flush the return queue at some point, leading
1940 * to a deadlock if we are still writing commands but not
1941 * reading their results */
1942 needstat = stored > 0 && stored % 800 == 0;
1943 /* Even if this message has been deleted, continue
1944 * to set further flags. This is necessary to support
1945 * Gmail semantics, where "delete" actually means
1946 * "archive", and the flags are applied to the copy
1947 * in "All Mail" */
1948 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1949 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1950 stored++;
1952 if (m->m_flag & MFLAG) {
1953 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1954 stored++;
1956 if (m->m_flag & MUNFLAG) {
1957 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1958 stored++;
1960 if (m->m_flag & MANSWER) {
1961 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1962 stored++;
1964 if (m->m_flag & MUNANSWER) {
1965 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1966 stored++;
1968 if (m->m_flag & MDRAFT) {
1969 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1970 stored++;
1972 if (m->m_flag & MUNDRAFT) {
1973 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1974 stored++;
1977 if (dodel) {
1978 imap_delete(mp, m-message+1, m, needstat);
1979 stored++;
1980 gotcha++;
1981 } else if (mp->mb_type != MB_CACHE ||
1982 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1983 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1984 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1985 held++;
1986 if (m->m_flag & MNEW) {
1987 m->m_flag &= ~MNEW;
1988 m->m_flag |= MSTATUS;
1991 jbypass:
1992 if (gotcha)
1993 imap_close(mp);
1995 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1996 if (!(m->m_flag & MUNLINKED) &&
1997 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1998 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1999 putcache(mp, m);
2000 modflags++;
2002 if ((gotcha || modflags) && edit) {
2003 printf(tr(168, "\"%s\" "), displayname);
2004 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2005 ? tr(170, "complete\n") : tr(212, "updated.\n"));
2006 } else if (held && !edit && mp->mb_perm != 0) {
2007 if (held == 1)
2008 printf(tr(155, "Held 1 message in %s\n"), displayname);
2009 else
2010 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
2012 fflush(stdout);
2013 NYD_LEAVE;
2014 return OKAY;
2017 FL void
2018 imap_quit(void)
2020 sighandler_type volatile saveint, savepipe;
2021 NYD_ENTER;
2023 if (mb.mb_type == MB_CACHE) {
2024 imap_update(&mb);
2025 goto jleave;
2028 if (mb.mb_sock.s_fd < 0) {
2029 fprintf(stderr, "IMAP connection closed.\n");
2030 goto jleave;
2033 imaplock = 1;
2034 saveint = safe_signal(SIGINT, SIG_IGN);
2035 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2036 if (sigsetjmp(imapjmp, 1)) {
2037 safe_signal(SIGINT, saveint);
2038 safe_signal(SIGPIPE, saveint);
2039 imaplock = 0;
2040 goto jleave;
2042 if (saveint != SIG_IGN)
2043 safe_signal(SIGINT, imapcatch);
2044 if (savepipe != SIG_IGN)
2045 safe_signal(SIGPIPE, imapcatch);
2047 imap_update(&mb);
2048 if (!same_imap_account)
2049 imap_exit(&mb);
2051 safe_signal(SIGINT, saveint);
2052 safe_signal(SIGPIPE, savepipe);
2053 imaplock = 0;
2054 jleave:
2055 NYD_LEAVE;
2058 static enum okay
2059 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2060 int needstat)
2062 char o[LINESIZE];
2063 FILE *queuefp = NULL;
2064 NYD_X;
2066 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2067 return STOP;
2068 if (m->m_uid)
2069 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2070 tag(1), m->m_uid, c, sp);
2071 else {
2072 if (check_expunged() == STOP)
2073 return STOP;
2074 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2076 IMAP_OUT(o, MB_COMD, return STOP)
2077 if (needstat)
2078 IMAP_ANSWER()
2079 else
2080 mb.mb_active &= ~MB_COMD;
2081 if (queuefp != NULL)
2082 Fclose(queuefp);
2083 return OKAY;
2086 FL enum okay
2087 imap_undelete(struct message *m, int n)
2089 enum okay rv;
2090 NYD_ENTER;
2092 rv = imap_unstore(m, n, "\\Deleted");
2093 NYD_LEAVE;
2094 return rv;
2097 FL enum okay
2098 imap_unread(struct message *m, int n)
2100 enum okay rv;
2101 NYD_ENTER;
2103 rv = imap_unstore(m, n, "\\Seen");
2104 NYD_LEAVE;
2105 return rv;
2108 static enum okay
2109 imap_unstore(struct message *m, int n, const char *flag)
2111 sighandler_type saveint, savepipe;
2112 enum okay rv = STOP;
2113 NYD_ENTER;
2115 imaplock = 1;
2116 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2117 safe_signal(SIGINT, &_imap_maincatch);
2118 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2119 if (sigsetjmp(imapjmp, 1) == 0) {
2120 if (savepipe != SIG_IGN)
2121 safe_signal(SIGPIPE, imapcatch);
2123 rv = imap_store(&mb, m, n, '-', flag, 1);
2125 safe_signal(SIGINT, saveint);
2126 safe_signal(SIGPIPE, savepipe);
2127 imaplock = 0;
2129 NYD_LEAVE;
2130 if (interrupts)
2131 onintr(0);
2132 return rv;
2135 static const char *
2136 tag(int new)
2138 static char ts[20];
2139 static long n;
2140 NYD_ENTER;
2142 if (new)
2143 ++n;
2144 snprintf(ts, sizeof ts, "T%lu", n);
2145 NYD_LEAVE;
2146 return ts;
2149 FL int
2150 imap_imap(void *vp)
2152 char o[LINESIZE];
2153 sighandler_type saveint, savepipe;
2154 struct mailbox *mp = &mb;
2155 FILE *queuefp = NULL;
2156 enum okay ok = STOP;
2157 NYD_X;
2159 if (mp->mb_type != MB_IMAP) {
2160 printf("Not operating on an IMAP mailbox.\n");
2161 return 1;
2163 imaplock = 1;
2164 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2165 safe_signal(SIGINT, &_imap_maincatch);
2166 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2167 if (sigsetjmp(imapjmp, 1) == 0) {
2168 if (savepipe != SIG_IGN)
2169 safe_signal(SIGPIPE, imapcatch);
2171 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2172 IMAP_OUT(o, MB_COMD, goto out)
2173 while (mp->mb_active & MB_COMD) {
2174 ok = imap_answer(mp, 0);
2175 fputs(responded_text, stdout);
2178 out:
2179 safe_signal(SIGINT, saveint);
2180 safe_signal(SIGPIPE, savepipe);
2181 imaplock = 0;
2183 if (interrupts)
2184 onintr(0);
2185 return ok != OKAY;
2188 FL int
2189 imap_newmail(int nmail)
2191 NYD_ENTER;
2193 if (nmail && had_exists < 0 && had_expunge < 0) {
2194 imaplock = 1;
2195 imap_noop();
2196 imaplock = 0;
2199 if (had_exists == msgCount && had_expunge < 0)
2200 /* Some servers always respond with EXISTS to NOOP. If
2201 * the mailbox has been changed but the number of messages
2202 * has not, an EXPUNGE must also had been sent; otherwise,
2203 * nothing has changed */
2204 had_exists = -1;
2205 NYD_LEAVE;
2206 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2209 static char *
2210 imap_putflags(int f)
2212 const char *cp;
2213 char *buf, *bp;
2214 NYD_ENTER;
2216 bp = buf = salloc(100);
2217 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2218 *bp++ = '(';
2219 if (f & MREAD) {
2220 if (bp[-1] != '(')
2221 *bp++ = ' ';
2222 for (cp = "\\Seen"; *cp; cp++)
2223 *bp++ = *cp;
2225 if (f & MFLAGGED) {
2226 if (bp[-1] != '(')
2227 *bp++ = ' ';
2228 for (cp = "\\Flagged"; *cp; cp++)
2229 *bp++ = *cp;
2231 if (f & MANSWERED) {
2232 if (bp[-1] != '(')
2233 *bp++ = ' ';
2234 for (cp = "\\Answered"; *cp; cp++)
2235 *bp++ = *cp;
2237 if (f & MDRAFT) {
2238 if (bp[-1] != '(')
2239 *bp++ = ' ';
2240 for (cp = "\\Draft"; *cp; cp++)
2241 *bp++ = *cp;
2243 *bp++ = ')';
2244 *bp++ = ' ';
2246 *bp = '\0';
2247 NYD_LEAVE;
2248 return buf;
2251 static void
2252 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2254 NYD_ENTER;
2255 while (*cp != ')') {
2256 if (*cp == '\\') {
2257 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2258 *f |= MREAD;
2259 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2260 *f |= MNEW;
2261 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2262 *f |= MDELETED;
2263 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2264 *f |= MFLAGGED;
2265 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2266 *f |= MANSWERED;
2267 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2268 *f |= MDRAFTED;
2270 cp++;
2273 if (xp != NULL)
2274 *xp = cp;
2275 NYD_LEAVE;
2278 static enum okay
2279 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2280 long xsize, enum mflag flag, time_t t)
2282 char o[LINESIZE], *buf;
2283 size_t bufsize, buflen, cnt;
2284 long size, lines, ysize;
2285 int twice = 0;
2286 FILE *queuefp = NULL;
2287 enum okay rv;
2288 NYD_ENTER;
2290 if (mp->mb_type == MB_CACHE) {
2291 queuefp = cache_queue(mp);
2292 if (queuefp == NULL) {
2293 rv = STOP;
2294 buf = NULL;
2295 goto jleave;
2297 rv = OKAY;
2298 } else
2299 rv = STOP;
2301 buf = smalloc(bufsize = LINESIZE);
2302 buflen = 0;
2303 jagain:
2304 size = xsize;
2305 cnt = fsize(fp);
2306 if (fseek(fp, off1, SEEK_SET) < 0) {
2307 rv = STOP;
2308 goto jleave;
2311 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2312 tag(1), imap_quotestr(name), imap_putflags(flag),
2313 imap_make_date_time(t), size);
2314 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2315 while (mp->mb_active & MB_COMD) {
2316 rv = imap_answer(mp, twice);
2317 if (response_type == RESPONSE_CONT)
2318 break;
2321 if (mp->mb_type != MB_CACHE && rv == STOP) {
2322 if (twice == 0)
2323 goto jtrycreate;
2324 else
2325 goto jleave;
2328 lines = ysize = 0;
2329 while (size > 0) {
2330 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2331 lines++;
2332 ysize += buflen;
2333 buf[buflen - 1] = '\r';
2334 buf[buflen] = '\n';
2335 if (mp->mb_type != MB_CACHE)
2336 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2337 else if (queuefp)
2338 fwrite(buf, 1, buflen+1, queuefp);
2339 size -= buflen + 1;
2341 if (mp->mb_type != MB_CACHE)
2342 swrite(&mp->mb_sock, "\r\n");
2343 else if (queuefp)
2344 fputs("\r\n", queuefp);
2345 while (mp->mb_active & MB_COMD) {
2346 rv = imap_answer(mp, 0);
2347 if (response_status == RESPONSE_NO /*&&
2348 ascncasecmp(responded_text,
2349 "[TRYCREATE] ", 12) == 0*/) {
2350 jtrycreate:
2351 if (twice++) {
2352 rv = STOP;
2353 goto jleave;
2355 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2356 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2357 while (mp->mb_active & MB_COMD)
2358 rv = imap_answer(mp, 1);
2359 if (rv == STOP)
2360 goto jleave;
2361 imap_created_mailbox++;
2362 goto jagain;
2363 } else if (rv != OKAY)
2364 fprintf(stderr, tr(270, "IMAP error: %s"), responded_text);
2365 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2366 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2368 jleave:
2369 if (queuefp != NULL)
2370 Fclose(queuefp);
2371 if (buf != NULL)
2372 free(buf);
2373 NYD_LEAVE;
2374 return rv;
2377 static enum okay
2378 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2380 char *buf, *bp, *lp;
2381 size_t bufsize, buflen, cnt;
2382 off_t off1 = -1, offs;
2383 int inhead = 1, flag = MNEW | MNEWEST;
2384 long size = 0;
2385 time_t tim;
2386 enum okay rv;
2387 NYD_ENTER;
2389 buf = smalloc(bufsize = LINESIZE);
2390 buflen = 0;
2391 cnt = fsize(fp);
2392 offs = ftell(fp);
2393 time(&tim);
2395 do {
2396 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2397 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2398 if (off1 != (off_t)-1) {
2399 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2400 if (rv == STOP)
2401 goto jleave;
2402 fseek(fp, offs+buflen, SEEK_SET);
2404 off1 = offs + buflen;
2405 size = 0;
2406 inhead = 1;
2407 flag = MNEW;
2408 if (bp != NULL)
2409 tim = unixtime(buf);
2410 } else
2411 size += buflen+1;
2412 offs += buflen;
2414 if (bp && buf[0] == '\n')
2415 inhead = 0;
2416 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2417 lp = &buf[6];
2418 while (whitechar(*lp))
2419 lp++;
2420 if (*lp == ':')
2421 while (*++lp != '\0')
2422 switch (*lp) {
2423 case 'R':
2424 flag |= MREAD;
2425 break;
2426 case 'O':
2427 flag &= ~MNEW;
2428 break;
2430 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2431 lp = &buf[8];
2432 while (whitechar(*lp))
2433 lp++;
2434 if (*lp == ':')
2435 while (*++lp != '\0')
2436 switch (*lp) {
2437 case 'F':
2438 flag |= MFLAGGED;
2439 break;
2440 case 'A':
2441 flag |= MANSWERED;
2442 break;
2443 case 'T':
2444 flag |= MDRAFTED;
2445 break;
2448 } while (bp != NULL);
2449 rv = OKAY;
2450 jleave:
2451 free(buf);
2452 NYD_LEAVE;
2453 return rv;
2456 FL enum okay
2457 imap_append(const char *xserver, FILE *fp)
2459 sighandler_type volatile saveint, savepipe;
2460 char *server, *user, *pass;
2461 char const *sp, *cp, * volatile mbx, *uhp;
2462 int volatile use_ssl;
2463 enum okay rv = STOP;
2464 NYD_ENTER;
2466 server = savestr(xserver);
2468 int xus;
2469 char const *xmbx;
2470 imap_split(&server, &sp, &xus, &cp, &uhp, &xmbx, &pass, &user);
2471 use_ssl = xus;
2472 mbx = xmbx;
2475 imaplock = 1;
2476 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2477 safe_signal(SIGINT, &_imap_maincatch);
2478 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2479 if (sigsetjmp(imapjmp, 1))
2480 goto jleave;
2481 if (savepipe != SIG_IGN)
2482 safe_signal(SIGPIPE, imapcatch);
2484 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2485 strcmp(protbase(server), mb.mb_imap_account) == 0) {
2486 rv = imap_append0(&mb, mbx, fp);
2487 } else {
2488 struct mailbox mx;
2490 memset(&mx, 0, sizeof mx);
2491 if (disconnected(server) == 0) {
2492 if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
2493 (use_ssl ? "imaps" : "imap")) != OKAY)
2494 goto jfail;
2495 mx.mb_sock.s_desc = "IMAP";
2496 mx.mb_type = MB_IMAP;
2497 mx.mb_imap_account = (char *)protbase(server);
2498 /* TODO the code now did
2499 * TODO mx.mb_imap_mailbox = mbx;
2500 * TODO though imap_mailbox is sfree()d and mbx
2501 * TODO is possibly even a constant
2502 * TODO i changed this to sstrdup() sofar, as is used
2503 * TODO somewhere else in this file for this! */
2504 mx.mb_imap_mailbox = sstrdup(mbx);
2505 if (imap_preauth(&mx, sp, uhp) != OKAY ||
2506 imap_auth(&mx, uhp, user, pass)!=OKAY) {
2507 sclose(&mx.mb_sock);
2508 goto jfail;
2510 rv = imap_append0(&mx, mbx, fp);
2511 imap_exit(&mx);
2512 } else {
2513 mx.mb_imap_account = (char*)protbase(server);
2514 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2515 mx.mb_type = MB_CACHE;
2516 rv = imap_append0(&mx, mbx, fp);
2518 jfail:
2521 jleave:
2522 safe_signal(SIGINT, saveint);
2523 safe_signal(SIGPIPE, savepipe);
2524 imaplock = 0;
2526 NYD_LEAVE;
2527 if (interrupts)
2528 onintr(0);
2529 return rv;
2532 static enum okay
2533 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2534 struct list_item **lend, int level)
2536 char o[LINESIZE], *cp;
2537 const char *bp;
2538 FILE *queuefp = NULL;
2539 struct list_item *lp;
2540 enum okay ok = STOP;
2541 NYD_X;
2543 *list = *lend = NULL;
2544 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2545 IMAP_OUT(o, MB_COMD, return STOP)
2546 while (mp->mb_active & MB_COMD) {
2547 ok = imap_answer(mp, 1);
2548 if (response_status == RESPONSE_OTHER &&
2549 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2550 cp = imap_unquotestr(list_name);
2551 lp = csalloc(1, sizeof *lp);
2552 lp->l_name = cp;
2553 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2554 ++cp;
2555 lp->l_base = *cp ? cp : savestr(base);
2556 lp->l_attr = list_attributes;
2557 lp->l_level = level+1;
2558 lp->l_delim = list_hierarchy_delimiter;
2559 if (*list && *lend) {
2560 (*lend)->l_next = lp;
2561 *lend = lp;
2562 } else
2563 *list = *lend = lp;
2566 return ok;
2569 static enum okay
2570 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2572 struct list_item *list, *lend, *lp, *lx, *ly;
2573 int n, depth;
2574 const char *bp;
2575 char *cp;
2576 enum okay rv;
2577 NYD_ENTER;
2579 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2580 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2581 goto jleave;
2582 rv = OKAY;
2583 if (list == NULL || lend == NULL)
2584 goto jleave;
2586 for (lp = list; lp; lp = lp->l_next)
2587 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2588 !(lp->l_attr & LIST_NOINFERIORS)) {
2589 cp = salloc((n = strlen(lp->l_name)) + 2);
2590 memcpy(cp, lp->l_name, n);
2591 cp[n] = lp->l_delim;
2592 cp[n+1] = '\0';
2593 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2594 lp->l_has_children = 1;
2595 if (strcmp(cp, lx->l_name) == 0)
2596 lx = lx->l_next;
2597 if (lx) {
2598 lend->l_next = lx;
2599 lend = ly;
2604 for (lp = list; lp; lp = lp->l_next) {
2605 if (strip) {
2606 cp = lp->l_name;
2607 for (bp = base; *bp && *bp == *cp; bp++)
2608 cp++;
2609 } else
2610 cp = lp->l_name;
2611 if (!(lp->l_attr & LIST_NOSELECT))
2612 fprintf(fp, "%s\n", *cp ? cp : base);
2613 else if (lp->l_has_children == 0)
2614 fprintf(fp, "%s%c\n", *cp ? cp : base,
2615 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2617 jleave:
2618 NYD_LEAVE;
2619 return rv;
2622 FL void
2623 imap_folders(const char * volatile name, int strip)
2625 sighandler_type saveint, savepipe;
2626 const char *fold, *cp, *sp;
2627 FILE *volatile fp;
2628 NYD_ENTER;
2630 cp = protbase(name);
2631 sp = mb.mb_imap_account;
2632 if (sp == NULL || strcmp(cp, sp)) {
2633 fprintf(stderr, tr(502,
2634 "Cannot perform `folders' but when on the very IMAP "
2635 "account; the current one is\n `%s' -- "
2636 "try `folders @'.\n"),
2637 (sp != NULL ? sp : tr(503, "[NONE]")));
2638 goto jleave;
2641 fold = imap_fileof(name);
2642 if (options & OPT_TTYOUT) {
2643 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2644 0600)) == NULL) {
2645 perror("tmpfile");
2646 goto jleave;
2648 } else
2649 fp = stdout;
2651 imaplock = 1;
2652 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2653 safe_signal(SIGINT, &_imap_maincatch);
2654 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2655 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2656 goto junroll;
2657 if (savepipe != SIG_IGN)
2658 safe_signal(SIGPIPE, imapcatch);
2660 if (mb.mb_type == MB_CACHE)
2661 cache_list(&mb, fold, strip, fp);
2662 else
2663 imap_list(&mb, fold, strip, fp);
2665 imaplock = 0;
2666 if (interrupts) {
2667 if (options & OPT_TTYOUT)
2668 Fclose(fp);
2669 goto jleave;
2671 fflush(fp);
2673 if (options & OPT_TTYOUT) {
2674 rewind(fp);
2675 if (fsize(fp) > 0)
2676 dopr(fp);
2677 else
2678 fprintf(stderr, "Folder not found.\n");
2680 junroll:
2681 safe_signal(SIGINT, saveint);
2682 safe_signal(SIGPIPE, savepipe);
2683 if (options & OPT_TTYOUT)
2684 Fclose(fp);
2685 jleave:
2686 NYD_LEAVE;
2687 if (interrupts)
2688 onintr(0);
2691 static void
2692 dopr(FILE *fp)
2694 char o[LINESIZE];
2695 int c;
2696 long n = 0, mx = 0, columns, width;
2697 FILE *out;
2698 NYD_ENTER;
2700 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2701 == NULL) {
2702 perror("tmpfile");
2703 goto jleave;
2706 while ((c = getc(fp)) != EOF) {
2707 if (c == '\n') {
2708 if (n > mx)
2709 mx = n;
2710 n = 0;
2711 } else
2712 ++n;
2714 rewind(fp);
2716 width = scrnwidth;
2717 if (mx < width / 2) {
2718 columns = width / (mx+2);
2719 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2720 } else
2721 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2722 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2723 try_pager(out);
2724 Fclose(out);
2725 jleave:
2726 NYD_LEAVE;
2729 static enum okay
2730 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2732 char o[LINESIZE];
2733 const char *qname;
2734 int twice = 0, stored = 0;
2735 FILE *queuefp = NULL;
2736 enum okay ok = STOP;
2737 NYD_X;
2739 if (mp->mb_type == MB_CACHE) {
2740 if ((queuefp = cache_queue(mp)) == NULL)
2741 return STOP;
2742 ok = OKAY;
2744 qname = imap_quotestr(name = imap_fileof(name));
2745 /* Since it is not possible to set flags on the copy, recently
2746 * set flags must be set on the original to include it in the copy */
2747 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2748 imap_store(mp, m, n, '+', "\\Seen", 0);
2749 if (m->m_flag&MFLAG)
2750 imap_store(mp, m, n, '+', "\\Flagged", 0);
2751 if (m->m_flag&MUNFLAG)
2752 imap_store(mp, m, n, '-', "\\Flagged", 0);
2753 if (m->m_flag&MANSWER)
2754 imap_store(mp, m, n, '+', "\\Answered", 0);
2755 if (m->m_flag&MUNANSWER)
2756 imap_store(mp, m, n, '-', "\\Flagged", 0);
2757 if (m->m_flag&MDRAFT)
2758 imap_store(mp, m, n, '+', "\\Draft", 0);
2759 if (m->m_flag&MUNDRAFT)
2760 imap_store(mp, m, n, '-', "\\Draft", 0);
2761 again:
2762 if (m->m_uid)
2763 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2764 else {
2765 if (check_expunged() == STOP)
2766 goto out;
2767 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2769 IMAP_OUT(o, MB_COMD, goto out)
2770 while (mp->mb_active & MB_COMD)
2771 ok = imap_answer(mp, twice);
2773 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2774 response_status == RESPONSE_OK)
2775 imap_copyuid(mp, m, name);
2777 if (response_status == RESPONSE_NO && twice++ == 0) {
2778 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2779 IMAP_OUT(o, MB_COMD, goto out)
2780 while (mp->mb_active & MB_COMD)
2781 ok = imap_answer(mp, 1);
2782 if (ok == OKAY) {
2783 imap_created_mailbox++;
2784 goto again;
2788 if (queuefp != NULL)
2789 Fclose(queuefp);
2791 /* ... and reset the flag to its initial value so that the 'exit'
2792 * command still leaves the message unread */
2793 out:
2794 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2795 imap_store(mp, m, n, '-', "\\Seen", 0);
2796 stored++;
2798 if (m->m_flag & MFLAG) {
2799 imap_store(mp, m, n, '-', "\\Flagged", 0);
2800 stored++;
2802 if (m->m_flag & MUNFLAG) {
2803 imap_store(mp, m, n, '+', "\\Flagged", 0);
2804 stored++;
2806 if (m->m_flag & MANSWER) {
2807 imap_store(mp, m, n, '-', "\\Answered", 0);
2808 stored++;
2810 if (m->m_flag & MUNANSWER) {
2811 imap_store(mp, m, n, '+', "\\Answered", 0);
2812 stored++;
2814 if (m->m_flag & MDRAFT) {
2815 imap_store(mp, m, n, '-', "\\Draft", 0);
2816 stored++;
2818 if (m->m_flag & MUNDRAFT) {
2819 imap_store(mp, m, n, '+', "\\Draft", 0);
2820 stored++;
2822 if (stored) {
2823 mp->mb_active |= MB_COMD;
2824 (void)imap_finish(mp);
2826 return ok;
2829 FL enum okay
2830 imap_copy(struct message *m, int n, const char *name)
2832 sighandler_type saveint, savepipe;
2833 enum okay rv = STOP;
2834 NYD_ENTER;
2836 imaplock = 1;
2837 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2838 safe_signal(SIGINT, &_imap_maincatch);
2839 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2840 if (sigsetjmp(imapjmp, 1) == 0) {
2841 if (savepipe != SIG_IGN)
2842 safe_signal(SIGPIPE, imapcatch);
2844 rv = imap_copy1(&mb, m, n, name);
2846 safe_signal(SIGINT, saveint);
2847 safe_signal(SIGPIPE, savepipe);
2848 imaplock = 0;
2850 NYD_LEAVE;
2851 if (interrupts)
2852 onintr(0);
2853 return rv;
2856 static enum okay
2857 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2858 unsigned long *olduid, unsigned long *newuid)
2860 char *xp, *yp, *zp;
2861 enum okay rv;
2862 NYD_ENTER;
2864 *uidvalidity = strtoul(cp, &xp, 10);
2865 *olduid = strtoul(xp, &yp, 10);
2866 *newuid = strtoul(yp, &zp, 10);
2867 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2868 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2869 NYD_LEAVE;
2870 return rv;
2873 static enum okay
2874 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2875 unsigned long *uid)
2877 char *xp, *yp;
2878 enum okay rv;
2879 NYD_ENTER;
2881 *uidvalidity = strtoul(cp, &xp, 10);
2882 *uid = strtoul(xp, &yp, 10);
2883 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2884 *yp == ']');
2885 NYD_LEAVE;
2886 return rv;
2889 static enum okay
2890 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2892 struct mailbox xmb;
2893 struct message xm;
2894 const char *cp;
2895 unsigned long uidvalidity, olduid, newuid;
2896 enum okay rv;
2897 NYD_ENTER;
2899 rv = STOP;
2900 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2901 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2902 goto jleave;
2903 rv = OKAY;
2905 xmb = *mp;
2906 xmb.mb_cache_directory = NULL;
2907 xmb.mb_imap_mailbox = savestr(name);
2908 xmb.mb_uidvalidity = uidvalidity;
2909 initcache(&xmb);
2910 if (m == NULL) {
2911 memset(&xm, 0, sizeof xm);
2912 xm.m_uid = olduid;
2913 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2914 goto jleave;
2915 getcache(mp, &xm, NEED_HEADER);
2916 getcache(mp, &xm, NEED_BODY);
2917 } else {
2918 if ((m->m_flag & HAVE_HEADER) == 0)
2919 getcache(mp, m, NEED_HEADER);
2920 if ((m->m_flag & HAVE_BODY) == 0)
2921 getcache(mp, m, NEED_BODY);
2922 xm = *m;
2924 xm.m_uid = newuid;
2925 xm.m_flag &= ~MFULLYCACHED;
2926 putcache(&xmb, &xm);
2927 jleave:
2928 NYD_LEAVE;
2929 return rv;
2932 static enum okay
2933 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2934 long size, long lines, int flag, const char *name)
2936 struct mailbox xmb;
2937 struct message xm;
2938 const char *cp;
2939 unsigned long uidvalidity, uid;
2940 enum okay rv;
2941 NYD_ENTER;
2943 rv = STOP;
2944 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2945 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2946 goto jleave;
2947 rv = OKAY;
2949 xmb = *mp;
2950 xmb.mb_cache_directory = NULL;
2951 xmb.mb_imap_mailbox = savestr(name);
2952 xmb.mb_uidvalidity = uidvalidity;
2953 xmb.mb_otf = xmb.mb_itf = fp;
2954 initcache(&xmb);
2955 memset(&xm, 0, sizeof xm);
2956 xm.m_flag = (flag & MREAD) | MNEW;
2957 xm.m_time = t;
2958 xm.m_block = mailx_blockof(off1);
2959 xm.m_offset = mailx_offsetof(off1);
2960 xm.m_size = size;
2961 xm.m_xsize = xsize;
2962 xm.m_lines = xm.m_xlines = lines;
2963 xm.m_uid = uid;
2964 xm.m_have = HAVE_HEADER | HAVE_BODY;
2965 putcache(&xmb, &xm);
2966 jleave:
2967 NYD_LEAVE;
2968 return rv;
2971 static enum okay
2972 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2974 FILE *tp = NULL;
2975 time_t t;
2976 long size, xsize, ysize, lines;
2977 enum mflag flag = MNEW;
2978 char *name, *buf, *bp;
2979 char const *cp;
2980 size_t bufsize, buflen, cnt;
2981 enum okay rv = STOP;
2982 NYD_ENTER;
2984 buf = smalloc(bufsize = LINESIZE);
2985 buflen = 0;
2986 cnt = fsize(fp);
2987 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2988 goto jstop;
2990 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
2992 while (*bp == ' ')
2993 ++bp;
2995 if ((cp = strrchr(bp, '{')) == NULL)
2996 goto jstop;
2998 xsize = atol(&cp[1]) + 2;
2999 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3000 goto jstop;
3001 while (*cp == ' ')
3002 cp++;
3004 if (*cp == '(') {
3005 imap_getflags(cp, &cp, &flag);
3006 while (*++cp == ' ')
3009 t = imap_read_date_time(cp);
3011 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3012 NULL)
3013 goto jstop;
3015 size = xsize;
3016 ysize = lines = 0;
3017 while (size > 0) {
3018 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3019 goto jstop;
3020 size -= buflen;
3021 buf[--buflen] = '\0';
3022 buf[buflen-1] = '\n';
3023 fwrite(buf, 1, buflen, tp);
3024 ysize += buflen;
3025 ++lines;
3027 fflush(tp);
3028 rewind(tp);
3030 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3031 imap_unquotestr(name));
3032 rv = OKAY;
3033 jstop:
3034 free(buf);
3035 if (tp)
3036 Fclose(tp);
3037 NYD_LEAVE;
3038 return rv;
3041 static enum okay
3042 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3043 int f)
3045 char *o, *xp, *cs, c;
3046 size_t osize;
3047 FILE *queuefp = NULL;
3048 int i;
3049 unsigned long n;
3050 const char *cp;
3051 enum okay ok = STOP;
3052 NYD_X;
3054 c = 0;
3055 for (cp = spec; *cp; cp++)
3056 c |= *cp;
3057 if (c & 0200) {
3058 cp = charset_get_lc();
3059 #ifdef HAVE_ICONV
3060 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3061 iconv_t it;
3062 char *nsp, *nspec;
3063 size_t sz, nsz;
3065 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3066 sz = strlen(spec) + 1;
3067 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3068 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3069 sz == 0) {
3070 spec = nspec;
3071 cp = "utf-8";
3073 n_iconv_close(it);
3076 #endif
3077 cp = imap_quotestr(cp);
3078 cs = salloc(n = strlen(cp) + 10);
3079 snprintf(cs, n, "CHARSET %s ", cp);
3080 } else
3081 cs = UNCONST("");
3083 o = ac_alloc(osize = strlen(spec) + 60);
3084 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3085 IMAP_OUT(o, MB_COMD, goto out)
3086 while (mp->mb_active & MB_COMD) {
3087 ok = imap_answer(mp, 0);
3088 if (response_status == RESPONSE_OTHER &&
3089 response_other == MAILBOX_DATA_SEARCH) {
3090 xp = responded_other_text;
3091 while (*xp && *xp != '\r') {
3092 n = strtoul(xp, &xp, 10);
3093 for (i = 0; i < cnt; i++)
3094 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3095 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3096 mark(i+1, f);
3100 out:
3101 ac_free(o);
3102 return ok;
3105 FL enum okay
3106 imap_search1(const char *volatile spec, int f)
3108 sighandler_type saveint, savepipe;
3109 enum okay rv = STOP;
3110 NYD_ENTER;
3112 if (mb.mb_type != MB_IMAP)
3113 goto jleave;
3115 imaplock = 1;
3116 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3117 safe_signal(SIGINT, &_imap_maincatch);
3118 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3119 if (sigsetjmp(imapjmp, 1) == 0) {
3120 if (savepipe != SIG_IGN)
3121 safe_signal(SIGPIPE, imapcatch);
3123 rv = imap_search2(&mb, message, msgCount, spec, f);
3125 safe_signal(SIGINT, saveint);
3126 safe_signal(SIGPIPE, savepipe);
3127 imaplock = 0;
3128 jleave:
3129 NYD_LEAVE;
3130 if (interrupts)
3131 onintr(0);
3132 return rv;
3135 FL int
3136 imap_thisaccount(const char *cp)
3138 int rv;
3139 NYD_ENTER;
3141 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3142 rv = 0;
3143 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3144 mb.mb_imap_account == NULL)
3145 rv = 0;
3146 else
3147 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3148 NYD_LEAVE;
3149 return rv;
3152 FL enum okay
3153 imap_remove(const char * volatile name)
3155 sighandler_type volatile saveint, savepipe;
3156 enum okay rv = STOP;
3157 NYD_ENTER;
3159 if (mb.mb_type != MB_IMAP) {
3160 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3161 name);
3162 goto jleave;
3165 if (!imap_thisaccount(name)) {
3166 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3167 "server: \"%s\" not removed.\n", name);
3168 goto jleave;
3171 imaplock = 1;
3172 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3173 safe_signal(SIGINT, &_imap_maincatch);
3174 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3175 if (sigsetjmp(imapjmp, 1) == 0) {
3176 if (savepipe != SIG_IGN)
3177 safe_signal(SIGPIPE, imapcatch);
3179 rv = imap_remove1(&mb, imap_fileof(name));
3181 safe_signal(SIGINT, saveint);
3182 safe_signal(SIGPIPE, savepipe);
3183 imaplock = 0;
3185 if (rv == OKAY)
3186 rv = cache_remove(name);
3187 jleave:
3188 NYD_LEAVE;
3189 if (interrupts)
3190 onintr(0);
3191 return rv;
3194 static enum okay
3195 imap_remove1(struct mailbox *mp, const char *name)
3197 FILE *queuefp = NULL;
3198 char *o;
3199 int os;
3200 enum okay ok = STOP;
3201 NYD_X;
3203 o = ac_alloc(os = 2*strlen(name) + 100);
3204 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3205 IMAP_OUT(o, MB_COMD, goto out)
3206 while (mp->mb_active & MB_COMD)
3207 ok = imap_answer(mp, 1);
3208 out:
3209 ac_free(o);
3210 return ok;
3213 FL enum okay
3214 imap_rename(const char *old, const char *new)
3216 sighandler_type saveint, savepipe;
3217 enum okay rv = STOP;
3218 NYD_ENTER;
3220 if (mb.mb_type != MB_IMAP) {
3221 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3222 goto jleave;
3225 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3226 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3227 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3228 goto jleave;
3231 imaplock = 1;
3232 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3233 safe_signal(SIGINT, &_imap_maincatch);
3234 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3235 if (sigsetjmp(imapjmp, 1) == 0) {
3236 if (savepipe != SIG_IGN)
3237 safe_signal(SIGPIPE, imapcatch);
3239 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3241 safe_signal(SIGINT, saveint);
3242 safe_signal(SIGPIPE, savepipe);
3243 imaplock = 0;
3245 if (rv == OKAY)
3246 rv = cache_rename(old, new);
3247 jleave:
3248 NYD_LEAVE;
3249 if (interrupts)
3250 onintr(0);
3251 return rv;
3254 static enum okay
3255 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3257 FILE *queuefp = NULL;
3258 char *o;
3259 int os;
3260 enum okay ok = STOP;
3261 NYD_X;
3263 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3264 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3265 imap_quotestr(new));
3266 IMAP_OUT(o, MB_COMD, goto out)
3267 while (mp->mb_active & MB_COMD)
3268 ok = imap_answer(mp, 1);
3269 out:
3270 ac_free(o);
3271 return ok;
3274 FL enum okay
3275 imap_dequeue(struct mailbox *mp, FILE *fp)
3277 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3278 size_t bufsize, buflen, cnt;
3279 long offs, offs1, offs2, octets;
3280 int twice, gotcha = 0;
3281 FILE *queuefp = NULL;
3282 enum okay ok = OKAY, rok = OKAY;
3283 NYD_X;
3285 buf = smalloc(bufsize = LINESIZE);
3286 buflen = 0;
3287 cnt = fsize(fp);
3288 while ((offs1 = ftell(fp)) >= 0 &&
3289 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3290 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3292 while (*bp == ' ')
3293 ++bp;
3294 twice = 0;
3295 if ((offs = ftell(fp)) < 0)
3296 goto fail;
3297 again:
3298 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3299 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3300 cp = &bp[9];
3301 while (digitchar(*cp))
3302 cp++;
3303 if (*cp != ' ')
3304 goto fail;
3305 while (*cp == ' ')
3306 cp++;
3307 if ((newname = imap_strex(cp, NULL)) == NULL)
3308 goto fail;
3309 IMAP_OUT(o, MB_COMD, continue)
3310 while (mp->mb_active & MB_COMD)
3311 ok = imap_answer(mp, twice);
3312 if (response_status == RESPONSE_NO && twice++ == 0)
3313 goto trycreate;
3314 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3315 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3317 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3318 IMAP_OUT(o, MB_COMD, continue)
3319 while (mp->mb_active & MB_COMD)
3320 ok = imap_answer(mp, 1);
3321 if (ok == OKAY)
3322 gotcha++;
3323 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3324 if ((cp = strrchr(bp, '{')) == NULL)
3325 goto fail;
3326 octets = atol(&cp[1]) + 2;
3327 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3328 goto fail;
3329 IMAP_OUT(o, MB_COMD, continue)
3330 while (mp->mb_active & MB_COMD) {
3331 ok = imap_answer(mp, twice);
3332 if (response_type == RESPONSE_CONT)
3333 break;
3335 if (ok == STOP) {
3336 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3337 goto trycreate;
3338 goto fail;
3340 while (octets > 0) {
3341 size_t n = (UICMP(z, octets, >, sizeof iob)
3342 ? sizeof iob : (size_t)octets);
3343 octets -= n;
3344 if (n != fread(iob, 1, n, fp))
3345 goto fail;
3346 swrite1(&mp->mb_sock, iob, n, 1);
3348 swrite(&mp->mb_sock, "");
3349 while (mp->mb_active & MB_COMD) {
3350 ok = imap_answer(mp, 0);
3351 if (response_status == RESPONSE_NO && twice++ == 0) {
3352 if (fseek(fp, offs, SEEK_SET) < 0)
3353 goto fail;
3354 goto trycreate;
3357 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3358 if ((offs2 = ftell(fp)) < 0)
3359 goto fail;
3360 fseek(fp, offs1, SEEK_SET);
3361 if (imap_appenduid_cached(mp, fp) == STOP) {
3362 (void)fseek(fp, offs2, SEEK_SET);
3363 goto fail;
3366 } else {
3367 fail:
3368 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3369 rok = STOP;
3371 continue;
3372 trycreate:
3373 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3374 IMAP_OUT(o, MB_COMD, continue)
3375 while (mp->mb_active & MB_COMD)
3376 ok = imap_answer(mp, 1);
3377 if (ok == OKAY)
3378 goto again;
3380 fflush(fp);
3381 rewind(fp);
3382 ftruncate(fileno(fp), 0);
3383 if (gotcha)
3384 imap_close(mp);
3385 free(buf);
3386 return rok;
3389 static char *
3390 imap_strex(char const *cp, char const **xp)
3392 char const *cq;
3393 char *n = NULL;
3394 NYD_ENTER;
3396 if (*cp != '"')
3397 goto jleave;
3399 for (cq = cp + 1; *cq != '\0'; ++cq) {
3400 if (*cq == '\\')
3401 cq++;
3402 else if (*cq == '"')
3403 break;
3405 if (*cq != '"')
3406 goto jleave;
3408 n = salloc(cq - cp + 2);
3409 memcpy(n, cp, cq - cp +1);
3410 n[cq - cp + 1] = '\0';
3411 if (xp != NULL)
3412 *xp = cq + 1;
3413 jleave:
3414 NYD_LEAVE;
3415 return n;
3418 static enum okay
3419 check_expunged(void)
3421 enum okay rv;
3422 NYD_ENTER;
3424 if (expunged_messages > 0) {
3425 fprintf(stderr, "Command not executed - messages have been expunged\n");
3426 rv = STOP;
3427 } else
3428 rv = OKAY;
3429 NYD_LEAVE;
3430 return rv;
3433 /*ARGSUSED*/
3434 FL int
3435 cconnect(void *vp)
3437 char *cp, *cq;
3438 int rv, omsgCount = msgCount;
3439 NYD_ENTER;
3440 UNUSED(vp);
3442 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3443 fprintf(stderr, "Already connected.\n");
3444 rv = 1;
3445 goto jleave;
3448 var_clear_allow_undefined = TRU1;
3449 ok_bclear(disconnected);
3451 cp = protbase(mailname);
3452 if (strncmp(cp, "imap://", 7) == 0)
3453 cp += 7;
3454 else if (strncmp(cp, "imaps://", 8) == 0)
3455 cp += 8;
3456 if ((cq = strchr(cp, ':')) != NULL)
3457 *cq = '\0';
3459 vok_bclear(savecat("disconnected-", cp));
3460 var_clear_allow_undefined = FAL0;
3462 if (mb.mb_type == MB_CACHE) {
3463 imap_setfile1(mailname, 0, edit, 1);
3464 if (msgCount > omsgCount)
3465 newmailinfo(omsgCount);
3467 rv = 0;
3468 jleave:
3469 NYD_LEAVE;
3470 return rv;
3473 FL int
3474 cdisconnect(void *vp)
3476 int rv = 1, *msgvec = vp;
3477 NYD_ENTER;
3479 if (mb.mb_type == MB_CACHE) {
3480 fprintf(stderr, "Not connected.\n");
3481 goto jleave;
3483 if (mb.mb_type == MB_IMAP) {
3484 if (cached_uidvalidity(&mb) == 0) {
3485 fprintf(stderr, "The current mailbox is not cached.\n");
3486 goto jleave;
3490 if (*msgvec)
3491 ccache(vp);
3492 ok_bset(disconnected, TRU1);
3493 if (mb.mb_type == MB_IMAP) {
3494 sclose(&mb.mb_sock);
3495 imap_setfile1(mailname, 0, edit, 1);
3497 rv = 0;
3498 jleave:
3499 NYD_LEAVE;
3500 return rv;
3503 FL int
3504 ccache(void *vp)
3506 int rv = 1, *msgvec = vp, *ip;
3507 struct message *mp;
3508 NYD_ENTER;
3510 if (mb.mb_type != MB_IMAP) {
3511 fprintf(stderr, "Not connected to an IMAP server.\n");
3512 goto jleave;
3514 if (cached_uidvalidity(&mb) == 0) {
3515 fprintf(stderr, "The current mailbox is not cached.\n");
3516 goto jleave;
3519 for (ip = msgvec; *ip; ++ip) {
3520 mp = &message[*ip - 1];
3521 if (!(mp->m_have & HAVE_BODY))
3522 get_body(mp);
3524 rv = 0;
3525 jleave:
3526 NYD_LEAVE;
3527 return rv;
3530 FL int
3531 disconnected(const char *file)
3533 char *cp, *cq, *vp;
3534 int vs, r = 1;
3535 NYD_ENTER;
3537 if (ok_blook(disconnected))
3538 goto jleave;
3540 cp = protbase(file);
3541 if (strncmp(cp, "imap://", 7) == 0)
3542 cp += 7;
3543 else if (strncmp(cp, "imaps://", 8) == 0)
3544 cp += 8;
3545 else {
3546 r = 0;
3547 goto jleave;
3550 if ((cq = strchr(cp, ':')) != NULL)
3551 *cq = '\0';
3552 vp = ac_alloc(vs = strlen(cp) + 14);
3553 snprintf(vp, vs, "disconnected-%s", cp);
3554 r = vok_blook(vp);
3555 ac_free(vp);
3556 jleave:
3557 NYD_LEAVE;
3558 return r;
3561 FL void
3562 transflags(struct message *omessage, long omsgCount, int transparent)
3564 struct message *omp, *nmp, *newdot, *newprevdot;
3565 int hf;
3566 NYD_ENTER;
3568 omp = omessage;
3569 nmp = message;
3570 newdot = message;
3571 newprevdot = NULL;
3572 while (PTRCMP(omp, <, omessage + omsgCount) &&
3573 PTRCMP(nmp, <, message + msgCount)) {
3574 if (dot && nmp->m_uid == dot->m_uid)
3575 newdot = nmp;
3576 if (prevdot && nmp->m_uid == prevdot->m_uid)
3577 newprevdot = nmp;
3578 if (omp->m_uid == nmp->m_uid) {
3579 hf = nmp->m_flag & MHIDDEN;
3580 if (transparent && mb.mb_type == MB_IMAP)
3581 omp->m_flag &= ~MHIDDEN;
3582 *nmp++ = *omp++;
3583 if (transparent && mb.mb_type == MB_CACHE)
3584 nmp[-1].m_flag |= hf;
3585 } else if (omp->m_uid < nmp->m_uid)
3586 ++omp;
3587 else
3588 ++nmp;
3590 dot = newdot;
3591 setdot(newdot);
3592 prevdot = newprevdot;
3593 free(omessage);
3594 NYD_LEAVE;
3597 FL time_t
3598 imap_read_date_time(const char *cp)
3600 char buf[3];
3601 time_t t;
3602 int i, year, month, day, hour, minute, second, sign = -1;
3603 NYD_ENTER;
3605 /* "25-Jul-2004 15:33:44 +0200"
3606 * | | | | | |
3607 * 0 5 10 15 20 25 */
3608 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3609 goto jinvalid;
3610 day = strtol(&cp[1], NULL, 10);
3611 for (i = 0;;) {
3612 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3613 break;
3614 if (month_names[++i][0] == '\0')
3615 goto jinvalid;
3617 month = i + 1;
3618 year = strtol(&cp[8], NULL, 10);
3619 hour = strtol(&cp[13], NULL, 10);
3620 minute = strtol(&cp[16], NULL, 10);
3621 second = strtol(&cp[19], NULL, 10);
3622 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3623 goto jinvalid;
3624 switch (cp[22]) {
3625 case '-':
3626 sign = 1;
3627 break;
3628 case '+':
3629 break;
3630 default:
3631 goto jinvalid;
3633 buf[2] = '\0';
3634 buf[0] = cp[23];
3635 buf[1] = cp[24];
3636 t += strtol(buf, NULL, 10) * sign * 3600;
3637 buf[0] = cp[25];
3638 buf[1] = cp[26];
3639 t += strtol(buf, NULL, 10) * sign * 60;
3640 jleave:
3641 NYD_LEAVE;
3642 return t;
3643 jinvalid:
3644 time(&t);
3645 goto jleave;
3648 FL const char *
3649 imap_make_date_time(time_t t)
3651 static char s[30];
3652 struct tm *tmptr;
3653 int tzdiff, tzdiff_hour, tzdiff_min;
3654 NYD_ENTER;
3656 tzdiff = t - mktime(gmtime(&t));
3657 tzdiff_hour = (int)(tzdiff / 60);
3658 tzdiff_min = tzdiff_hour % 60;
3659 tzdiff_hour /= 60;
3660 tmptr = localtime(&t);
3661 if (tmptr->tm_isdst > 0)
3662 tzdiff_hour++;
3663 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3664 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3665 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3666 NYD_LEAVE;
3667 return s;
3669 #endif /* HAVE_IMAP */
3671 FL time_t
3672 imap_read_date(char const *cp)
3674 time_t t = (time_t)-1;
3675 int year, month, day, i, tzdiff;
3676 struct tm *tmptr;
3677 char *xp, *yp;
3678 NYD_ENTER;
3680 if (*cp == '"')
3681 ++cp;
3682 day = strtol(cp, &xp, 10);
3683 if (day <= 0 || day > 31 || *xp++ != '-')
3684 goto jleave;
3686 for (i = 0;;) {
3687 if (ascncasecmp(xp, month_names[i], 3) == 0)
3688 break;
3689 if (month_names[++i][0] == '\0')
3690 goto jleave;
3692 month = i+1;
3693 if (xp[3] != '-')
3694 goto jleave;
3695 year = strtol(&xp[4], &yp, 10);
3696 if (year < 1970 || year > 2037 || yp != &xp[8])
3697 goto jleave;
3698 if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
3699 goto jleave;
3700 if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
3701 goto jleave;
3702 tzdiff = t - mktime(gmtime(&t));
3703 tmptr = localtime(&t);
3704 if (tmptr->tm_isdst > 0)
3705 tzdiff += 3600;
3706 t -= tzdiff;
3707 jleave:
3708 NYD_LEAVE;
3709 return t;
3712 FL char *
3713 imap_quotestr(char const *s)
3715 char *n, *np;
3716 NYD_ENTER;
3718 np = n = salloc(2 * strlen(s) + 3);
3719 *np++ = '"';
3720 while (*s) {
3721 if (*s == '"' || *s == '\\')
3722 *np++ = '\\';
3723 *np++ = *s++;
3725 *np++ = '"';
3726 *np = '\0';
3727 NYD_LEAVE;
3728 return n;
3731 FL char *
3732 imap_unquotestr(char const *s)
3734 char *n, *np;
3735 NYD_ENTER;
3737 if (*s != '"') {
3738 n = savestr(s);
3739 goto jleave;
3742 np = n = salloc(strlen(s) + 1);
3743 while (*++s) {
3744 if (*s == '\\')
3745 s++;
3746 else if (*s == '"')
3747 break;
3748 *np++ = *s;
3750 *np = '\0';
3751 jleave:
3752 NYD_LEAVE;
3753 return n;
3756 /* vim:set fenc=utf-8:s-it-mode */