Fix reverso in [0cd5c9e]
[s-mailx.git] / imap.c
blob39bf3286cdea9704c2d49167c0da3c650e4451e5
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ CRAM-MD5 as of RFC 2195.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 #ifndef HAVE_AMALGAMATION
42 # include "nail.h"
43 #endif
45 #ifdef HAVE_IMAP
46 # include <sys/socket.h>
48 # include <netdb.h>
50 # include <netinet/in.h>
52 # ifdef HAVE_ARPA_INET_H
53 # include <arpa/inet.h>
54 # endif
55 #endif
57 #ifdef HAVE_IMAP
58 #define IMAP_ANSWER() \
60 if (mp->mb_type != MB_CACHE) {\
61 enum okay ok = OKAY;\
62 while (mp->mb_active & MB_COMD)\
63 ok = imap_answer(mp, 1);\
64 if (ok == STOP)\
65 return STOP;\
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
77 do {\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
80 ACTIONBAIL;\
82 if (options & OPT_VERBVERB)\
83 fprintf(stderr, ">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static int volatile imaplock;
171 static int same_imap_account;
172 static bool_t _imap_rdonly;
174 static void imap_other_get(char *pp);
175 static void imap_response_get(const char **cp);
176 static void imap_response_parse(void);
177 static enum okay imap_answer(struct mailbox *mp, int errprnt);
178 static enum okay imap_parse_list(void);
179 static enum okay imap_finish(struct mailbox *mp);
180 static void imap_timer_off(void);
181 static void imapcatch(int s);
182 static void _imap_maincatch(int s);
183 static enum okay imap_noop1(struct mailbox *mp);
184 static void rec_queue(enum rec_type type, unsigned long cnt);
185 static enum okay rec_dequeue(void);
186 static void rec_rmqueue(void);
187 static void imapalarm(int s);
188 static int imap_use_starttls(const char *uhp);
189 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
190 const char *uhp);
191 static enum okay imap_capability(struct mailbox *mp);
192 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
193 #ifdef HAVE_MD5
194 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
195 #endif
196 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
197 #ifdef HAVE_GSSAPI
198 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
199 #endif
200 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
201 static void imap_init(struct mailbox *mp, int n);
202 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
203 int *prevcount);
204 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
205 struct url *urlp);
206 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
207 int transparent);
208 static int imap_fetchdata(struct mailbox *mp, struct message *m,
209 size_t expected, int need, const char *head,
210 size_t headsize, long headlines);
211 static void imap_putstr(struct mailbox *mp, struct message *m,
212 const char *str, const char *head, size_t headsize,
213 long headlines);
214 static enum okay imap_get(struct mailbox *mp, struct message *m,
215 enum needspec need);
216 static void commitmsg(struct mailbox *mp, struct message *to,
217 struct message *from, enum havespec have);
218 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
219 int bot, int top);
220 static enum okay imap_exit(struct mailbox *mp);
221 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
222 int needstat);
223 static enum okay imap_close(struct mailbox *mp);
224 static enum okay imap_update(struct mailbox *mp);
225 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
226 int c, const char *sp, int needstat);
227 static enum okay imap_unstore(struct message *m, int n, const char *flag);
228 static const char *tag(int new);
229 static char * imap_putflags(int f);
230 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
231 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
232 off_t off1, long xsize, enum mflag flag, time_t t);
233 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
234 static enum okay imap_list1(struct mailbox *mp, const char *base,
235 struct list_item **list, struct list_item **lend,
236 int level);
237 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
238 FILE *fp);
239 static void dopr(FILE *fp);
240 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
241 const char *name);
242 static enum okay imap_copyuid_parse(const char *cp,
243 unsigned long *uidvalidity, unsigned long *olduid,
244 unsigned long *newuid);
245 static enum okay imap_appenduid_parse(const char *cp,
246 unsigned long *uidvalidity, unsigned long *uid);
247 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
248 const char *name);
249 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
250 long off1, long xsize, long size, long lines, int flag,
251 const char *name);
252 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
253 #ifdef HAVE_IMAP_SEARCH
254 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
255 const char *spec, int f);
256 #endif
257 static enum okay imap_remove1(struct mailbox *mp, const char *name);
258 static enum okay imap_rename1(struct mailbox *mp, const char *old,
259 const char *new);
260 static char * imap_strex(char const *cp, char const **xp);
261 static enum okay check_expunged(void);
263 static void
264 imap_other_get(char *pp)
266 char *xp;
267 NYD2_ENTER;
269 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
270 pp += 6;
271 response_other = MAILBOX_DATA_FLAGS;
272 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
273 pp += 5;
274 response_other = MAILBOX_DATA_LIST;
275 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
276 pp += 5;
277 response_other = MAILBOX_DATA_LSUB;
278 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
279 pp += 8;
280 response_other = MAILBOX_DATA_MAILBOX;
281 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
282 pp += 7;
283 response_other = MAILBOX_DATA_SEARCH;
284 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
285 pp += 7;
286 response_other = MAILBOX_DATA_STATUS;
287 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
288 pp += 11;
289 response_other = CAPABILITY_DATA;
290 } else {
291 responded_other_number = strtol(pp, &xp, 10);
292 while (*xp == ' ')
293 ++xp;
294 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
295 response_other = MAILBOX_DATA_EXISTS;
296 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
297 response_other = MAILBOX_DATA_RECENT;
298 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
299 response_other = MESSAGE_DATA_EXPUNGE;
300 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
301 pp = &xp[6];
302 response_other = MESSAGE_DATA_FETCH;
303 } else
304 response_other = RESPONSE_OTHER_UNKNOWN;
306 responded_other_text = pp;
307 NYD2_LEAVE;
310 static void
311 imap_response_get(const char **cp)
313 NYD2_ENTER;
314 if (ascncasecmp(*cp, "OK ", 3) == 0) {
315 *cp += 3;
316 response_status = RESPONSE_OK;
317 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
318 *cp += 3;
319 response_status = RESPONSE_NO;
320 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
321 *cp += 4;
322 response_status = RESPONSE_BAD;
323 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
324 *cp += 8;
325 response_status = RESPONSE_PREAUTH;
326 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
327 *cp += 4;
328 response_status = RESPONSE_BYE;
329 } else
330 response_status = RESPONSE_OTHER;
331 NYD2_LEAVE;
334 static void
335 imap_response_parse(void)
337 static char *parsebuf; /* TODO Use pool */
338 static size_t parsebufsize;
340 const char *ip = imapbuf;
341 char *pp;
342 NYD2_ENTER;
344 if (parsebufsize < imapbufsize)
345 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
346 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
347 pp = parsebuf;
348 switch (*ip) {
349 case '+':
350 response_type = RESPONSE_CONT;
351 ip++;
352 pp++;
353 while (*ip == ' ') {
354 ip++;
355 pp++;
357 break;
358 case '*':
359 ip++;
360 pp++;
361 while (*ip == ' ') {
362 ip++;
363 pp++;
365 imap_response_get(&ip);
366 pp = &parsebuf[ip - imapbuf];
367 switch (response_status) {
368 case RESPONSE_BYE:
369 response_type = RESPONSE_FATAL;
370 break;
371 default:
372 response_type = RESPONSE_DATA;
374 break;
375 default:
376 responded_tag = parsebuf;
377 while (*pp && *pp != ' ')
378 pp++;
379 if (*pp == '\0') {
380 response_type = RESPONSE_ILLEGAL;
381 break;
383 *pp++ = '\0';
384 while (*pp && *pp == ' ')
385 pp++;
386 if (*pp == '\0') {
387 response_type = RESPONSE_ILLEGAL;
388 break;
390 ip = &imapbuf[pp - parsebuf];
391 response_type = RESPONSE_TAGGED;
392 imap_response_get(&ip);
393 pp = &parsebuf[ip - imapbuf];
395 responded_text = pp;
396 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
397 response_status == RESPONSE_OTHER)
398 imap_other_get(pp);
399 NYD2_LEAVE;
402 static enum okay
403 imap_answer(struct mailbox *mp, int errprnt)
405 int i, complete;
406 enum okay rv;
407 NYD2_ENTER;
409 rv = OKAY;
410 if (mp->mb_type == MB_CACHE)
411 goto jleave;
412 rv = STOP;
413 jagain:
414 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
415 if (options & OPT_VERBVERB)
416 fputs(imapbuf, stderr);
417 imap_response_parse();
418 if (response_type == RESPONSE_ILLEGAL)
419 goto jagain;
420 if (response_type == RESPONSE_CONT) {
421 rv = OKAY;
422 goto jleave;
424 if (response_status == RESPONSE_OTHER) {
425 if (response_other == MAILBOX_DATA_EXISTS) {
426 had_exists = responded_other_number;
427 rec_queue(REC_EXISTS, responded_other_number);
428 if (had_expunge > 0)
429 had_expunge = 0;
430 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
431 rec_queue(REC_EXPUNGE, responded_other_number);
432 if (had_expunge < 0)
433 had_expunge = 0;
434 had_expunge++;
435 expunged_messages++;
438 complete = 0;
439 if (response_type == RESPONSE_TAGGED) {
440 if (asccasecmp(responded_tag, tag(0)) == 0)
441 complete |= 1;
442 else
443 goto jagain;
445 switch (response_status) {
446 case RESPONSE_PREAUTH:
447 mp->mb_active &= ~MB_PREAUTH;
448 /*FALLTHRU*/
449 case RESPONSE_OK:
450 jokay:
451 rv = OKAY;
452 complete |= 2;
453 break;
454 case RESPONSE_NO:
455 case RESPONSE_BAD:
456 jstop:
457 rv = STOP;
458 complete |= 2;
459 if (errprnt)
460 fprintf(stderr, _("IMAP error: %s"), responded_text);
461 break;
462 case RESPONSE_UNKNOWN: /* does not happen */
463 case RESPONSE_BYE:
464 i = mp->mb_active;
465 mp->mb_active = MB_NONE;
466 if (i & MB_BYE)
467 goto jokay;
468 goto jstop;
469 case RESPONSE_OTHER:
470 rv = OKAY;
471 break;
473 if (response_status != RESPONSE_OTHER &&
474 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
475 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
476 if (complete == 3)
477 mp->mb_active &= ~MB_COMD;
478 } else {
479 rv = STOP;
480 mp->mb_active = MB_NONE;
482 jleave:
483 NYD2_LEAVE;
484 return rv;
487 static enum okay
488 imap_parse_list(void)
490 char *cp;
491 enum okay rv;
492 NYD2_ENTER;
494 rv = STOP;
496 cp = responded_other_text;
497 list_attributes = LIST_NONE;
498 if (*cp == '(') {
499 while (*cp && *cp != ')') {
500 if (*cp == '\\') {
501 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
502 list_attributes |= LIST_NOINFERIORS;
503 cp += 12;
504 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
505 list_attributes |= LIST_NOSELECT;
506 cp += 9;
507 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
508 list_attributes |= LIST_MARKED;
509 cp += 7;
510 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
511 list_attributes |= LIST_UNMARKED;
512 cp += 9;
515 cp++;
517 if (*++cp != ' ')
518 goto jleave;
519 while (*cp == ' ')
520 cp++;
523 list_hierarchy_delimiter = EOF;
524 if (*cp == '"') {
525 if (*++cp == '\\')
526 cp++;
527 list_hierarchy_delimiter = *cp++ & 0377;
528 if (cp[0] != '"' || cp[1] != ' ')
529 goto jleave;
530 cp++;
531 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
532 list_hierarchy_delimiter = EOF;
533 cp += 3;
536 while (*cp == ' ')
537 cp++;
538 list_name = cp;
539 while (*cp && *cp != '\r')
540 cp++;
541 *cp = '\0';
542 rv = OKAY;
543 jleave:
544 NYD2_LEAVE;
545 return rv;
548 static enum okay
549 imap_finish(struct mailbox *mp)
551 NYD_ENTER;
552 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
553 imap_answer(mp, 1);
554 NYD_LEAVE;
555 return OKAY;
558 static void
559 imap_timer_off(void)
561 NYD_ENTER;
562 if (imapkeepalive > 0) {
563 alarm(0);
564 safe_signal(SIGALRM, savealrm);
566 NYD_LEAVE;
569 static void
570 imapcatch(int s)
572 NYD_X; /* Signal handler */
573 switch (s) {
574 case SIGINT:
575 fprintf(stderr, _("Interrupt\n"));
576 siglongjmp(imapjmp, 1);
577 /*NOTREACHED*/
578 case SIGPIPE:
579 fprintf(stderr, _("Received SIGPIPE during IMAP operation\n"));
580 break;
584 static void
585 _imap_maincatch(int s)
587 NYD_X; /* Signal handler */
588 UNUSED(s);
589 if (interrupts++ == 0) {
590 fprintf(stderr, _("Interrupt\n"));
591 return;
593 onintr(0);
596 static enum okay
597 imap_noop1(struct mailbox *mp)
599 char o[LINESIZE];
600 FILE *queuefp = NULL;
601 NYD_X;
603 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
604 IMAP_OUT(o, MB_COMD, return STOP)
605 IMAP_ANSWER()
606 return OKAY;
609 FL char const *
610 imap_fileof(char const *xcp)
612 char const *cp = xcp;
613 int state = 0;
614 NYD_ENTER;
616 while (*cp) {
617 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
618 cp += 3;
619 state = 1;
621 if (cp[0] == '/' && state == 1) {
622 ++cp;
623 goto jleave;
625 if (cp[0] == '/') {
626 cp = xcp;
627 goto jleave;
629 ++cp;
631 jleave:
632 NYD_LEAVE;
633 return cp;
636 FL enum okay
637 imap_noop(void)
639 sighandler_type volatile oldint, oldpipe;
640 enum okay rv = STOP;
641 NYD_ENTER;
643 if (mb.mb_type != MB_IMAP)
644 goto jleave;
646 imaplock = 1;
647 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
648 safe_signal(SIGINT, &_imap_maincatch);
649 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
650 if (sigsetjmp(imapjmp, 1) == 0) {
651 if (oldpipe != SIG_IGN)
652 safe_signal(SIGPIPE, imapcatch);
654 rv = imap_noop1(&mb);
656 safe_signal(SIGINT, oldint);
657 safe_signal(SIGPIPE, oldpipe);
658 imaplock = 0;
659 jleave:
660 NYD_LEAVE;
661 if (interrupts)
662 onintr(0);
663 return rv;
666 static void
667 rec_queue(enum rec_type rt, unsigned long cnt)
669 struct record *rp;
670 NYD_ENTER;
672 rp = scalloc(1, sizeof *rp);
673 rp->rec_type = rt;
674 rp->rec_count = cnt;
675 if (record && recend) {
676 recend->rec_next = rp;
677 recend = rp;
678 } else
679 record = recend = rp;
680 NYD_LEAVE;
683 static enum okay
684 rec_dequeue(void)
686 struct message *omessage;
687 struct record *rp, *rq;
688 uiz_t exists = 0, i;
689 enum okay rv = STOP;
690 NYD_ENTER;
692 if (record == NULL)
693 goto jleave;
695 omessage = message;
696 message = smalloc((msgCount+1) * sizeof *message);
697 if (msgCount)
698 memcpy(message, omessage, msgCount * sizeof *message);
699 memset(&message[msgCount], 0, sizeof *message);
701 rp = record, rq = NULL;
702 rv = OKAY;
703 while (rp != NULL) {
704 switch (rp->rec_type) {
705 case REC_EXISTS:
706 exists = rp->rec_count;
707 break;
708 case REC_EXPUNGE:
709 if (rp->rec_count == 0) {
710 rv = STOP;
711 break;
713 if (rp->rec_count > (unsigned long)msgCount) {
714 if (exists == 0 || rp->rec_count > exists--)
715 rv = STOP;
716 break;
718 if (exists > 0)
719 exists--;
720 delcache(&mb, &message[rp->rec_count-1]);
721 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
722 ((msgCount - rp->rec_count + 1) * sizeof *message));
723 --msgCount;
724 /* If the message was part of a collapsed thread,
725 * the m_collapsed field of one of its ancestors
726 * should be incremented. It seems hardly possible
727 * to do this with the current message structure,
728 * though. The result is that a '+' may be shown
729 * in the header summary even if no collapsed
730 * children exists */
731 break;
733 if (rq != NULL)
734 free(rq);
735 rq = rp;
736 rp = rp->rec_next;
738 if (rq != NULL)
739 free(rq);
741 record = recend = NULL;
742 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
743 message = srealloc(message, (exists + 1) * sizeof *message);
744 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
745 for (i = msgCount; i < exists; ++i)
746 imap_init(&mb, i);
747 imap_flags(&mb, msgCount+1, exists);
748 msgCount = exists;
751 if (rv == STOP) {
752 free(message);
753 message = omessage;
755 jleave:
756 NYD_LEAVE;
757 return rv;
760 static void
761 rec_rmqueue(void)
763 struct record *rp;
764 NYD_ENTER;
766 for (rp = record; rp != NULL;) {
767 struct record *tmp = rp;
768 rp = rp->rec_next;
769 free(tmp);
771 record = recend = NULL;
772 NYD_LEAVE;
775 /*ARGSUSED*/
776 static void
777 imapalarm(int s)
779 sighandler_type volatile saveint, savepipe;
780 NYD_X; /* Signal handler */
781 UNUSED(s);
783 if (imaplock++ == 0) {
784 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
785 safe_signal(SIGINT, &_imap_maincatch);
786 savepipe = safe_signal(SIGPIPE, SIG_IGN);
787 if (sigsetjmp(imapjmp, 1)) {
788 safe_signal(SIGINT, saveint);
789 safe_signal(SIGPIPE, savepipe);
790 goto jbrk;
792 if (savepipe != SIG_IGN)
793 safe_signal(SIGPIPE, imapcatch);
794 if (imap_noop1(&mb) != OKAY) {
795 safe_signal(SIGINT, saveint);
796 safe_signal(SIGPIPE, savepipe);
797 goto jleave;
799 safe_signal(SIGINT, saveint);
800 safe_signal(SIGPIPE, savepipe);
802 jbrk:
803 alarm(imapkeepalive);
804 jleave:
805 --imaplock;
808 static int
809 imap_use_starttls(const char *uhp)
811 int rv;
812 NYD_ENTER;
814 if (ok_blook(imap_use_starttls))
815 rv = 1;
816 else {
817 char *var = savecat("imap-use-starttls-", uhp);
818 rv = vok_blook(var);
820 NYD_LEAVE;
821 return rv;
824 static enum okay
825 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
827 char *cp;
828 NYD_X;
830 mp->mb_active |= MB_PREAUTH;
831 imap_answer(mp, 1);
832 if ((cp = strchr(xserver, ':')) != NULL) {
833 char *x = salloc(cp - xserver + 1);
834 memcpy(x, xserver, cp - xserver);
835 x[cp - xserver] = '\0';
836 xserver = x;
838 #ifdef HAVE_SSL
839 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
840 FILE *queuefp = NULL;
841 char o[LINESIZE];
843 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
844 IMAP_OUT(o, MB_COMD, return STOP)
845 IMAP_ANSWER()
846 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
847 return STOP;
849 #else
850 if (imap_use_starttls(uhp)) {
851 fprintf(stderr, "No SSL support compiled in.\n");
852 return STOP;
854 #endif
855 imap_capability(mp);
856 return OKAY;
859 static enum okay
860 imap_capability(struct mailbox *mp)
862 char o[LINESIZE];
863 FILE *queuefp = NULL;
864 enum okay ok = STOP;
865 const char *cp;
866 NYD_X;
868 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
869 IMAP_OUT(o, MB_COMD, return STOP)
870 while (mp->mb_active & MB_COMD) {
871 ok = imap_answer(mp, 0);
872 if (response_status == RESPONSE_OTHER &&
873 response_other == CAPABILITY_DATA) {
874 cp = responded_other_text;
875 while (*cp) {
876 while (spacechar(*cp))
877 ++cp;
878 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
879 /* RFC 2359 */
880 mp->mb_flags |= MB_UIDPLUS;
881 while (*cp && !spacechar(*cp))
882 ++cp;
886 return ok;
889 static enum okay
890 imap_auth(struct mailbox *mp, struct ccred *ccred)
892 enum okay rv;
893 NYD_ENTER;
895 if (!(mp->mb_active & MB_PREAUTH)) {
896 rv = OKAY;
897 goto jleave;
900 switch (ccred->cc_authtype) {
901 case AUTHTYPE_LOGIN:
902 rv = imap_login(mp, ccred);
903 break;
904 #ifdef HAVE_MD5
905 case AUTHTYPE_CRAM_MD5:
906 rv = imap_cram_md5(mp, ccred);
907 break;
908 #endif
909 #ifdef HAVE_GSSAPI
910 case AUTHTYPE_GSSAPI:
911 rv = _imap_gssapi(mp, ccred);
912 break;
913 #endif
914 default:
915 rv = STOP;
916 break;
918 jleave:
919 NYD_LEAVE;
920 return rv;
923 #ifdef HAVE_MD5
924 static enum okay
925 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
927 char o[LINESIZE], *cp;
928 FILE *queuefp = NULL;
929 enum okay rv = STOP;
930 NYD_ENTER;
932 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
933 IMAP_XOUT(o, 0, goto jleave, goto jleave);
934 imap_answer(mp, 1);
935 if (response_type != RESPONSE_CONT)
936 goto jleave;
938 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
939 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
940 while (mp->mb_active & MB_COMD)
941 rv = imap_answer(mp, 1);
942 jleave:
943 NYD_LEAVE;
944 return rv;
946 #endif /* HAVE_MD5 */
948 static enum okay
949 imap_login(struct mailbox *mp, struct ccred *ccred)
951 char o[LINESIZE];
952 FILE *queuefp = NULL;
953 enum okay rv = STOP;
954 NYD_ENTER;
956 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
957 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
958 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
959 while (mp->mb_active & MB_COMD)
960 rv = imap_answer(mp, 1);
961 jleave:
962 NYD_LEAVE;
963 return rv;
966 #ifdef HAVE_GSSAPI
967 # include "imap_gssapi.h"
968 #endif
970 FL enum okay
971 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
973 enum okay ok = OKAY;
974 char const *cp;
975 char o[LINESIZE];
976 FILE *queuefp = NULL;
977 NYD_X;
978 UNUSED(size);
980 mp->mb_uidvalidity = 0;
981 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
982 IMAP_OUT(o, MB_COMD, return STOP)
983 while (mp->mb_active & MB_COMD) {
984 ok = imap_answer(mp, 1);
985 if (response_status != RESPONSE_OTHER &&
986 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
987 mp->mb_uidvalidity = atol(&cp[13]);
989 *cnt = (had_exists > 0) ? had_exists : 0;
990 if (response_status != RESPONSE_OTHER &&
991 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
992 mp->mb_perm = 0;
993 return ok;
996 static enum okay
997 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
999 char o[LINESIZE];
1000 FILE *queuefp = NULL;
1001 char const *cp;
1002 struct message *m;
1003 unsigned x = X, y = Y, n;
1004 NYD_X;
1006 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1007 IMAP_OUT(o, MB_COMD, return STOP)
1008 while (mp->mb_active & MB_COMD) {
1009 imap_answer(mp, 1);
1010 if (response_status == RESPONSE_OTHER &&
1011 response_other == MESSAGE_DATA_FETCH) {
1012 n = responded_other_number;
1013 if (n < x || n > y)
1014 continue;
1015 m = &message[n-1];
1016 m->m_xsize = 0;
1017 } else
1018 continue;
1020 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1021 cp += 5;
1022 while (*cp == ' ')
1023 cp++;
1024 if (*cp == '(')
1025 imap_getflags(cp, &cp, &m->m_flag);
1028 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1029 m->m_uid = strtoul(&cp[4], NULL, 10);
1030 getcache1(mp, m, NEED_UNSPEC, 1);
1031 m->m_flag &= ~MHIDDEN;
1034 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1035 x++;
1036 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1037 y--;
1038 if (x <= y) {
1039 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1040 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 } else
1051 continue;
1052 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1053 m->m_xsize = strtol(&cp[12], NULL, 10);
1054 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1055 m->m_time = imap_read_date_time(&cp[13]);
1059 srelax_hold();
1060 for (n = X; n <= Y; ++n) {
1061 putcache(mp, &message[n-1]);
1062 srelax();
1064 srelax_rele();
1065 return OKAY;
1068 static void
1069 imap_init(struct mailbox *mp, int n)
1071 struct message *m;
1072 NYD_ENTER;
1073 UNUSED(mp);
1075 m = message + n;
1076 m->m_flag = MUSED | MNOFROM;
1077 m->m_block = 0;
1078 m->m_offset = 0;
1079 NYD_LEAVE;
1082 static void
1083 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1085 struct message *omessage = 0;
1086 int i, omsgCount = 0;
1087 enum okay dequeued = STOP;
1088 NYD_ENTER;
1090 if (nmail || transparent) {
1091 omessage = message;
1092 omsgCount = msgCount;
1094 if (nmail)
1095 dequeued = rec_dequeue();
1097 if (had_exists >= 0) {
1098 if (dequeued != OKAY)
1099 msgCount = had_exists;
1100 had_exists = -1;
1102 if (had_expunge >= 0) {
1103 if (dequeued != OKAY)
1104 msgCount -= had_expunge;
1105 had_expunge = -1;
1108 if (nmail && expunged_messages)
1109 printf("Expunged %ld message%s.\n", expunged_messages,
1110 (expunged_messages != 1 ? "s" : ""));
1111 *prevcount = omsgCount - expunged_messages;
1112 expunged_messages = 0;
1113 if (msgCount < 0) {
1114 fputs("IMAP error: Negative message count\n", stderr);
1115 msgCount = 0;
1118 if (dequeued != OKAY) {
1119 message = scalloc(msgCount + 1, sizeof *message);
1120 for (i = 0; i < msgCount; i++)
1121 imap_init(mp, i);
1122 if (!nmail && mp->mb_type == MB_IMAP)
1123 initcache(mp);
1124 if (msgCount > 0)
1125 imap_flags(mp, 1, msgCount);
1126 message[msgCount].m_size = 0;
1127 message[msgCount].m_lines = 0;
1128 rec_rmqueue();
1130 if (nmail || transparent)
1131 transflags(omessage, omsgCount, transparent);
1132 else
1133 setdot(message);
1134 NYD_LEAVE;
1137 FL int
1138 imap_setfile(const char *xserver, enum fedit_mode fm)
1140 struct url url;
1141 int rv;
1142 NYD_ENTER;
1144 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1145 rv = 1;
1146 goto jleave;
1148 if (!ok_blook(v15_compat) &&
1149 (!url.url_had_user || url.url_pass.s != NULL))
1150 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
1152 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1153 rv = _imap_setfile1(&url, fm, 0);
1154 jleave:
1155 NYD_LEAVE;
1156 return rv;
1159 static bool_t
1160 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1162 bool_t rv = FAL0;
1163 NYD_ENTER;
1165 if (ok_blook(v15_compat))
1166 rv = ccred_lookup(ccredp, urlp);
1167 else {
1168 char *var, *old,
1169 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1171 if ((var = mbp->mb_imap_pass) != NULL) {
1172 var = savecat("password-", xuhp);
1173 old = vok_vlook(var);
1174 vok_vset(var, mbp->mb_imap_pass);
1176 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1177 if (var != NULL) {
1178 if (old != NULL)
1179 vok_vset(var, old);
1180 else
1181 vok_vclear(var);
1185 NYD_LEAVE;
1186 return rv;
1189 static int
1190 _imap_setfile1(struct url *urlp, enum fedit_mode fm, int volatile transparent)
1192 struct sock so;
1193 struct ccred ccred;
1194 sighandler_type volatile saveint, savepipe;
1195 char const *cp;
1196 int rv;
1197 int volatile prevcount = 0;
1198 enum mbflags same_flags;
1199 NYD_ENTER;
1201 if (fm & FEDIT_NEWMAIL) {
1202 saveint = safe_signal(SIGINT, SIG_IGN);
1203 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1204 if (saveint != SIG_IGN)
1205 safe_signal(SIGINT, imapcatch);
1206 if (savepipe != SIG_IGN)
1207 safe_signal(SIGPIPE, imapcatch);
1208 imaplock = 1;
1209 goto jnmail;
1212 same_flags = mb.mb_flags;
1213 same_imap_account = 0;
1214 if (mb.mb_imap_account != NULL &&
1215 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1216 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1217 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1218 disconnected(mb.mb_imap_account) == 0) {
1219 same_imap_account = 1;
1220 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1221 goto jduppass;
1222 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1223 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1224 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1225 jduppass:
1226 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1229 if (!_imap_getcred(&mb, &ccred, urlp)) {
1230 rv = -1;
1231 goto jleave;
1234 so.s_fd = -1;
1235 if (!same_imap_account) {
1236 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1237 rv = -1;
1238 goto jleave;
1240 } else
1241 so = mb.mb_sock;
1242 if (!transparent)
1243 quit();
1245 edit = !(fm & FEDIT_SYSBOX);
1246 if (mb.mb_imap_account != NULL)
1247 free(mb.mb_imap_account);
1248 if (mb.mb_imap_pass != NULL)
1249 free(mb.mb_imap_pass);
1250 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1251 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1252 * TODO object, and mailbox will naturally have an URL and credentials */
1253 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1255 if (!same_imap_account) {
1256 if (mb.mb_sock.s_fd >= 0)
1257 sclose(&mb.mb_sock);
1259 same_imap_account = 0;
1261 if (!transparent) {
1262 if (mb.mb_itf) {
1263 fclose(mb.mb_itf);
1264 mb.mb_itf = NULL;
1266 if (mb.mb_otf) {
1267 fclose(mb.mb_otf);
1268 mb.mb_otf = NULL;
1270 if (mb.mb_imap_mailbox != NULL)
1271 free(mb.mb_imap_mailbox);
1272 mb.mb_imap_mailbox = sstrdup((urlp->url_path.s != NULL)
1273 ? urlp->url_path.s : "INBOX");
1274 initbox(urlp->url_p_eu_h_p_p);
1276 mb.mb_type = MB_VOID;
1277 mb.mb_active = MB_NONE;
1279 imaplock = 1;
1280 saveint = safe_signal(SIGINT, SIG_IGN);
1281 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1282 if (sigsetjmp(imapjmp, 1)) {
1283 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1284 sclose(&mb.mb_sock);
1285 safe_signal(SIGINT, saveint);
1286 safe_signal(SIGPIPE, savepipe);
1287 imaplock = 0;
1289 mb.mb_type = MB_VOID;
1290 mb.mb_active = MB_NONE;
1291 rv = -1;
1292 goto jleave;
1294 if (saveint != SIG_IGN)
1295 safe_signal(SIGINT, imapcatch);
1296 if (savepipe != SIG_IGN)
1297 safe_signal(SIGPIPE, imapcatch);
1299 if (mb.mb_sock.s_fd < 0) {
1300 if (disconnected(mb.mb_imap_account)) {
1301 if (cache_setptr(fm, transparent) == STOP)
1302 fprintf(stderr, "Mailbox \"%s\" is not cached.\n",
1303 urlp->url_p_eu_h_p_p);
1304 goto jdone;
1306 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1307 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1308 savealrm = safe_signal(SIGALRM, imapalarm);
1309 alarm(imapkeepalive);
1313 mb.mb_sock = so;
1314 mb.mb_sock.s_desc = "IMAP";
1315 mb.mb_sock.s_onclose = imap_timer_off;
1316 if (imap_preauth(&mb, urlp->url_h_p.s, urlp->url_u_h_p.s) != OKAY ||
1317 imap_auth(&mb, &ccred) != OKAY) {
1318 sclose(&mb.mb_sock);
1319 imap_timer_off();
1320 safe_signal(SIGINT, saveint);
1321 safe_signal(SIGPIPE, savepipe);
1322 imaplock = 0;
1323 rv = -1;
1324 goto jleave;
1326 } else /* same account */
1327 mb.mb_flags |= same_flags;
1329 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
1330 mb.mb_type = MB_IMAP;
1331 cache_dequeue(&mb);
1332 if (imap_select(&mb, &mailsize, &msgCount,
1333 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX")) != OKAY) {
1334 /*sclose(&mb.mb_sock);
1335 imap_timer_off();*/
1336 safe_signal(SIGINT, saveint);
1337 safe_signal(SIGPIPE, savepipe);
1338 imaplock = 0;
1339 mb.mb_type = MB_VOID;
1340 rv = -1;
1341 goto jleave;
1344 jnmail:
1345 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1346 UNVOLATILE(&prevcount));
1347 jdone:
1348 setmsize(msgCount);
1349 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1350 sawcom = FAL0;
1351 safe_signal(SIGINT, saveint);
1352 safe_signal(SIGPIPE, savepipe);
1353 imaplock = 0;
1355 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1356 purgecache(&mb, message, msgCount);
1357 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1358 mb.mb_threaded = 0;
1359 c_sort((void*)-1);
1362 if (!(fm & FEDIT_NEWMAIL) && !edit && msgCount == 0) {
1363 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1364 !ok_blook(emptystart))
1365 fprintf(stderr, _("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1366 rv = 1;
1367 goto jleave;
1369 if (fm & FEDIT_NEWMAIL)
1370 newmailinfo(prevcount);
1371 rv = 0;
1372 jleave:
1373 NYD_LEAVE;
1374 return rv;
1377 static int
1378 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1379 int need, const char *head, size_t headsize, long headlines)
1381 char *line = NULL, *lp;
1382 size_t linesize = 0, linelen, size = 0;
1383 int emptyline = 0, lines = 0, excess = 0;
1384 off_t offset;
1385 NYD_ENTER;
1387 fseek(mp->mb_otf, 0L, SEEK_END);
1388 offset = ftell(mp->mb_otf);
1390 if (head)
1391 fwrite(head, 1, headsize, mp->mb_otf);
1393 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1394 lp = line;
1395 if (linelen > expected) {
1396 excess = linelen - expected;
1397 linelen = expected;
1399 /* TODO >>
1400 * Need to mask 'From ' lines. This cannot be done properly
1401 * since some servers pass them as 'From ' and others as
1402 * '>From '. Although one could identify the first kind of
1403 * server in principle, it is not possible to identify the
1404 * second as '>From ' may also come from a server of the
1405 * first type as actual data. So do what is absolutely
1406 * necessary only - mask 'From '.
1408 * If the line is the first line of the message header, it
1409 * is likely a real 'From ' line. In this case, it is just
1410 * ignored since it violates all standards.
1411 * TODO can the latter *really* happen??
1412 * TODO <<
1414 /* Since we simply copy over data without doing any transfer
1415 * encoding reclassification/adjustment we *have* to perform
1416 * RFC 4155 compliant From_ quoting here */
1417 if (is_head(lp, linelen)) {
1418 if (lines + headlines == 0)
1419 goto jskip;
1420 fputc('>', mp->mb_otf);
1421 ++size;
1423 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1424 emptyline = linelen <= 2;
1425 if (linelen > 2) {
1426 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1427 size += linelen - 1;
1428 } else
1429 ++size;
1430 fputc('\n', mp->mb_otf);
1431 } else {
1432 emptyline = 0;
1433 fwrite(lp, 1, linelen, mp->mb_otf);
1434 size += linelen;
1436 ++lines;
1437 jskip:
1438 if ((expected -= linelen) <= 0)
1439 break;
1441 if (!emptyline) {
1442 /* This is very ugly; but some IMAP daemons don't end a
1443 * message with \r\n\r\n, and we need \n\n for mbox format */
1444 fputc('\n', mp->mb_otf);
1445 ++lines;
1446 ++size;
1448 fflush(mp->mb_otf);
1450 if (m != NULL) {
1451 m->m_size = size + headsize;
1452 m->m_lines = lines + headlines;
1453 m->m_block = mailx_blockof(offset);
1454 m->m_offset = mailx_offsetof(offset);
1455 switch (need) {
1456 case NEED_HEADER:
1457 m->m_have |= HAVE_HEADER;
1458 break;
1459 case NEED_BODY:
1460 m->m_have |= HAVE_HEADER | HAVE_BODY;
1461 m->m_xlines = m->m_lines;
1462 m->m_xsize = m->m_size;
1463 break;
1466 free(line);
1467 NYD_LEAVE;
1468 return excess;
1471 static void
1472 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1473 const char *head, size_t headsize, long headlines)
1475 off_t offset;
1476 size_t len;
1477 NYD_ENTER;
1479 len = strlen(str);
1480 fseek(mp->mb_otf, 0L, SEEK_END);
1481 offset = ftell(mp->mb_otf);
1482 if (head)
1483 fwrite(head, 1, headsize, mp->mb_otf);
1484 if (len > 0) {
1485 fwrite(str, 1, len, mp->mb_otf);
1486 fputc('\n', mp->mb_otf);
1487 ++len;
1489 fflush(mp->mb_otf);
1491 if (m != NULL) {
1492 m->m_size = headsize + len;
1493 m->m_lines = headlines + 1;
1494 m->m_block = mailx_blockof(offset);
1495 m->m_offset = mailx_offsetof(offset);
1496 m->m_have |= HAVE_HEADER | HAVE_BODY;
1497 m->m_xlines = m->m_lines;
1498 m->m_xsize = m->m_size;
1500 NYD_LEAVE;
1503 static enum okay
1504 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1506 char o[LINESIZE];
1507 struct message mt;
1508 sighandler_type volatile saveint, savepipe;
1509 char * volatile head;
1510 char const *cp, *loc, * volatile item, * volatile resp;
1511 size_t expected;
1512 size_t volatile headsize;
1513 int number;
1514 FILE *queuefp;
1515 long volatile headlines;
1516 long n;
1517 ul_it volatile u;
1518 enum okay ok;
1519 NYD_X;
1521 saveint = savepipe = SIG_IGN;
1522 head = NULL;
1523 cp = loc = item = resp = NULL;
1524 headsize = 0;
1525 number = (int)PTR2SIZE(m - message + 1);
1526 queuefp = NULL;
1527 headlines = 0;
1528 n = -1;
1529 u = 0;
1530 ok = STOP;
1532 if (getcache(mp, m, need) == OKAY)
1533 return OKAY;
1534 if (mp->mb_type == MB_CACHE) {
1535 fprintf(stderr, "Message %u not available.\n", number);
1536 return STOP;
1539 if (mp->mb_sock.s_fd < 0) {
1540 fprintf(stderr, "IMAP connection closed.\n");
1541 return STOP;
1544 switch (need) {
1545 case NEED_HEADER:
1546 resp = item = "RFC822.HEADER";
1547 break;
1548 case NEED_BODY:
1549 item = "BODY.PEEK[]";
1550 resp = "BODY[]";
1551 if (m->m_flag & HAVE_HEADER && m->m_size) {
1552 char *hdr = smalloc(m->m_size);
1553 fflush(mp->mb_otf);
1554 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1555 SEEK_SET) < 0 ||
1556 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1557 free(hdr);
1558 break;
1560 head = hdr;
1561 headsize = m->m_size;
1562 headlines = m->m_lines;
1563 item = "BODY.PEEK[TEXT]";
1564 resp = "BODY[TEXT]";
1566 break;
1567 case NEED_UNSPEC:
1568 return STOP;
1571 imaplock = 1;
1572 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1573 if (sigsetjmp(imapjmp, 1)) {
1574 safe_signal(SIGINT, saveint);
1575 safe_signal(SIGPIPE, savepipe);
1576 imaplock = 0;
1577 return STOP;
1579 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1580 safe_signal(SIGINT, &_imap_maincatch);
1581 if (savepipe != SIG_IGN)
1582 safe_signal(SIGPIPE, imapcatch);
1584 if (m->m_uid)
1585 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1586 tag(1), m->m_uid, item);
1587 else {
1588 if (check_expunged() == STOP)
1589 goto out;
1590 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1592 IMAP_OUT(o, MB_COMD, goto out)
1593 for (;;) {
1594 ok = imap_answer(mp, 1);
1595 if (ok == STOP)
1596 break;
1597 if (response_status != RESPONSE_OTHER ||
1598 response_other != MESSAGE_DATA_FETCH)
1599 continue;
1600 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1601 continue;
1602 if (m->m_uid) {
1603 if ((cp = asccasestr(responded_other_text, "UID "))) {
1604 u = atol(&cp[4]);
1605 n = 0;
1606 } else {
1607 n = -1;
1608 u = 0;
1610 } else
1611 n = responded_other_number;
1612 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1613 if (m->m_uid ? m->m_uid != u : n != number)
1614 continue;
1615 if ((cp = strchr(loc, '"')) != NULL) {
1616 cp = imap_unquotestr(cp);
1617 imap_putstr(mp, m, cp, head, headsize, headlines);
1618 } else {
1619 m->m_have |= HAVE_HEADER|HAVE_BODY;
1620 m->m_xlines = m->m_lines;
1621 m->m_xsize = m->m_size;
1623 goto out;
1625 expected = atol(&cp[1]);
1626 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1627 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1628 continue;
1630 mt = *m;
1631 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1632 if (n >= 0) {
1633 commitmsg(mp, m, &mt, mt.m_have);
1634 break;
1636 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1637 if (options & OPT_VERBVERB)
1638 fputs(imapbuf, stderr);
1639 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1640 u = atol(&cp[4]);
1641 if (u == m->m_uid) {
1642 commitmsg(mp, m, &mt, mt.m_have);
1643 break;
1648 out:
1649 while (mp->mb_active & MB_COMD)
1650 ok = imap_answer(mp, 1);
1652 if (saveint != SIG_IGN)
1653 safe_signal(SIGINT, saveint);
1654 if (savepipe != SIG_IGN)
1655 safe_signal(SIGPIPE, savepipe);
1656 imaplock--;
1658 if (ok == OKAY)
1659 putcache(mp, m);
1660 if (head != NULL)
1661 free(head);
1662 if (interrupts)
1663 onintr(0);
1664 return ok;
1667 FL enum okay
1668 imap_header(struct message *m)
1670 enum okay rv;
1671 NYD_ENTER;
1673 rv = imap_get(&mb, m, NEED_HEADER);
1674 NYD_LEAVE;
1675 return rv;
1679 FL enum okay
1680 imap_body(struct message *m)
1682 enum okay rv;
1683 NYD_ENTER;
1685 rv = imap_get(&mb, m, NEED_BODY);
1686 NYD_LEAVE;
1687 return rv;
1690 static void
1691 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1692 enum havespec have)
1694 NYD_ENTER;
1695 tomp->m_size = frommp->m_size;
1696 tomp->m_lines = frommp->m_lines;
1697 tomp->m_block = frommp->m_block;
1698 tomp->m_offset = frommp->m_offset;
1699 tomp->m_have = have;
1700 if (have & HAVE_BODY) {
1701 tomp->m_xlines = frommp->m_lines;
1702 tomp->m_xsize = frommp->m_size;
1704 putcache(mp, tomp);
1705 NYD_LEAVE;
1708 static enum okay
1709 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1711 /* bot > topp */
1712 char o[LINESIZE];
1713 char const *cp;
1714 struct message mt;
1715 size_t expected;
1716 int n = 0, u;
1717 FILE *queuefp = NULL;
1718 enum okay ok;
1719 NYD_X;
1721 if (m[bot].m_uid)
1722 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1723 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1724 else {
1725 if (check_expunged() == STOP)
1726 return STOP;
1727 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1728 tag(1), bot, topp);
1730 IMAP_OUT(o, MB_COMD, return STOP)
1732 srelax_hold();
1733 for (;;) {
1734 ok = imap_answer(mp, 1);
1735 if (response_status != RESPONSE_OTHER)
1736 break;
1737 if (response_other != MESSAGE_DATA_FETCH)
1738 continue;
1739 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
1740 srelax_rele();
1741 return STOP;
1743 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1744 continue;
1745 expected = atol(&cp[1]);
1746 if (m[bot-1].m_uid) {
1747 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1748 u = atoi(&cp[4]);
1749 for (n = bot; n <= topp; n++)
1750 if ((unsigned long)u == m[n-1].m_uid)
1751 break;
1752 if (n > topp) {
1753 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1754 continue;
1756 } else
1757 n = -1;
1758 } else {
1759 n = responded_other_number;
1760 if (n <= 0 || n > msgCount) {
1761 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1762 continue;
1765 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1766 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1767 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1768 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1769 if (options & OPT_VERBVERB)
1770 fputs(imapbuf, stderr);
1771 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1772 u = atoi(&cp[4]);
1773 for (n = bot; n <= topp; n++)
1774 if ((unsigned long)u == m[n-1].m_uid)
1775 break;
1776 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1777 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1778 n = 0;
1781 srelax();
1783 srelax_rele();
1785 while (mp->mb_active & MB_COMD)
1786 ok = imap_answer(mp, 1);
1787 return ok;
1790 FL void
1791 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1793 sighandler_type saveint, savepipe;
1794 /*enum okay ok = STOP;*/
1795 int i, chunk = 256;
1796 NYD_X;
1798 if (mb.mb_type == MB_CACHE)
1799 return;
1800 if (bot < 1)
1801 bot = 1;
1802 if (topp > msgCount)
1803 topp = msgCount;
1804 for (i = bot; i < topp; i++) {
1805 if (message[i-1].m_have & HAVE_HEADER ||
1806 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1807 bot = i+1;
1808 else
1809 break;
1811 for (i = topp; i > bot; i--) {
1812 if (message[i-1].m_have & HAVE_HEADER ||
1813 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1814 topp = i-1;
1815 else
1816 break;
1818 if (bot >= topp)
1819 return;
1821 imaplock = 1;
1822 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1823 safe_signal(SIGINT, &_imap_maincatch);
1824 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1825 if (sigsetjmp(imapjmp, 1) == 0) {
1826 if (savepipe != SIG_IGN)
1827 safe_signal(SIGPIPE, imapcatch);
1829 for (i = bot; i <= topp; i += chunk) {
1830 int j = i + chunk - 1;
1831 j = MIN(j, topp);
1832 if (visible(message + j))
1833 /*ok = */imap_fetchheaders(&mb, message, i, j);
1834 if (interrupts)
1835 onintr(0); /* XXX imaplock? */
1838 safe_signal(SIGINT, saveint);
1839 safe_signal(SIGPIPE, savepipe);
1840 imaplock = 0;
1843 static enum okay
1844 __imap_exit(struct mailbox *mp)
1846 char o[LINESIZE];
1847 FILE *queuefp = NULL;
1848 NYD_X;
1850 mp->mb_active |= MB_BYE;
1851 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1852 IMAP_OUT(o, MB_COMD, return STOP)
1853 IMAP_ANSWER()
1854 return OKAY;
1857 static enum okay
1858 imap_exit(struct mailbox *mp)
1860 enum okay rv;
1861 NYD_ENTER;
1863 rv = __imap_exit(mp);
1864 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1865 free(mp->mb_imap_pass);
1866 free(mp->mb_imap_account);
1867 free(mp->mb_imap_mailbox);
1868 if (mp->mb_cache_directory != NULL)
1869 free(mp->mb_cache_directory);
1870 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1871 mp->mb_imap_account =
1872 mp->mb_imap_mailbox =
1873 mp->mb_cache_directory = "";
1874 #else
1875 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1876 mp->mb_imap_mailbox = NULL;
1877 mp->mb_cache_directory = NULL;
1878 #endif
1879 #endif
1880 sclose(&mp->mb_sock);
1881 NYD_LEAVE;
1882 return rv;
1885 static enum okay
1886 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1888 NYD_ENTER;
1889 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1890 if (mp->mb_type == MB_IMAP)
1891 delcache(mp, m);
1892 NYD_LEAVE;
1893 return OKAY;
1896 static enum okay
1897 imap_close(struct mailbox *mp)
1899 char o[LINESIZE];
1900 FILE *queuefp = NULL;
1901 NYD_X;
1903 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1904 IMAP_OUT(o, MB_COMD, return STOP)
1905 IMAP_ANSWER()
1906 return OKAY;
1909 static enum okay
1910 imap_update(struct mailbox *mp)
1912 struct message *m;
1913 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1914 NYD_ENTER;
1916 if (!edit && mp->mb_perm != 0) {
1917 holdbits();
1918 c = 0;
1919 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1920 if (m->m_flag & MBOX)
1921 ++c;
1922 if (c > 0)
1923 if (makembox() == STOP)
1924 goto jbypass;
1927 gotcha = held = 0;
1928 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1929 if (mp->mb_perm == 0)
1930 dodel = 0;
1931 else if (edit)
1932 dodel = ((m->m_flag & MDELETED) != 0);
1933 else
1934 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1936 /* Fetch the result after around each 800 STORE commands
1937 * sent (approx. 32k data sent). Otherwise, servers will
1938 * try to flush the return queue at some point, leading
1939 * to a deadlock if we are still writing commands but not
1940 * reading their results */
1941 needstat = stored > 0 && stored % 800 == 0;
1942 /* Even if this message has been deleted, continue
1943 * to set further flags. This is necessary to support
1944 * Gmail semantics, where "delete" actually means
1945 * "archive", and the flags are applied to the copy
1946 * in "All Mail" */
1947 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1948 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1949 stored++;
1951 if (m->m_flag & MFLAG) {
1952 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1953 stored++;
1955 if (m->m_flag & MUNFLAG) {
1956 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1957 stored++;
1959 if (m->m_flag & MANSWER) {
1960 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1961 stored++;
1963 if (m->m_flag & MUNANSWER) {
1964 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1965 stored++;
1967 if (m->m_flag & MDRAFT) {
1968 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1969 stored++;
1971 if (m->m_flag & MUNDRAFT) {
1972 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1973 stored++;
1976 if (dodel) {
1977 imap_delete(mp, m-message+1, m, needstat);
1978 stored++;
1979 gotcha++;
1980 } else if (mp->mb_type != MB_CACHE ||
1981 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1982 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1983 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1984 held++;
1985 if (m->m_flag & MNEW) {
1986 m->m_flag &= ~MNEW;
1987 m->m_flag |= MSTATUS;
1990 jbypass:
1991 if (gotcha)
1992 imap_close(mp);
1994 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1995 if (!(m->m_flag & MUNLINKED) &&
1996 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1997 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1998 putcache(mp, m);
1999 modflags++;
2002 if ((gotcha || modflags) && edit) {
2003 printf(_("\"%s\" "), displayname);
2004 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2005 ? _("complete\n") : _("updated.\n"));
2006 } else if (held && !edit && mp->mb_perm != 0) {
2007 if (held == 1)
2008 printf(_("Held 1 message in %s\n"), displayname);
2009 else
2010 printf(_("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 NYD2_ENTER;
2142 if (new)
2143 ++n;
2144 snprintf(ts, sizeof ts, "T%lu", n);
2145 NYD2_LEAVE;
2146 return ts;
2149 FL int
2150 c_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 volatile 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 NYD2_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 NYD2_LEAVE;
2248 return buf;
2251 static void
2252 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2254 NYD2_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 NYD2_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, _("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 struct url url;
2461 struct ccred ccred;
2462 char const * volatile mbx;
2463 enum okay rv = STOP;
2464 NYD_ENTER;
2466 if (!url_parse(&url, CPROTO_IMAP, xserver))
2467 goto j_leave;
2468 if (!ok_blook(v15_compat) &&
2469 (!url.url_had_user || url.url_pass.s != NULL))
2470 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
2471 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2473 imaplock = 1;
2474 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2475 safe_signal(SIGINT, &_imap_maincatch);
2476 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2477 if (sigsetjmp(imapjmp, 1))
2478 goto jleave;
2479 if (savepipe != SIG_IGN)
2480 safe_signal(SIGPIPE, imapcatch);
2482 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2483 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
2484 rv = imap_append0(&mb, mbx, fp);
2485 } else {
2486 struct mailbox mx;
2488 memset(&mx, 0, sizeof mx);
2490 if (!_imap_getcred(&mx, &ccred, &url))
2491 goto jleave;
2493 if (disconnected(url.url_p_eu_h_p) == 0) {
2494 if (!sopen(&mx.mb_sock, &url))
2495 goto jfail;
2496 mx.mb_sock.s_desc = "IMAP";
2497 mx.mb_type = MB_IMAP;
2498 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2499 /* TODO the code now did
2500 * TODO mx.mb_imap_mailbox = mbx;
2501 * TODO though imap_mailbox is sfree()d and mbx
2502 * TODO is possibly even a constant
2503 * TODO i changed this to sstrdup() sofar, as is used
2504 * TODO somewhere else in this file for this! */
2505 mx.mb_imap_mailbox = sstrdup(mbx);
2506 if (imap_preauth(&mx, url.url_h_p.s, url.url_u_h_p.s) != OKAY ||
2507 imap_auth(&mx, &ccred) != OKAY) {
2508 sclose(&mx.mb_sock);
2509 goto jfail;
2511 rv = imap_append0(&mx, mbx, fp);
2512 imap_exit(&mx);
2513 } else {
2514 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2515 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2516 mx.mb_type = MB_CACHE;
2517 rv = imap_append0(&mx, mbx, fp);
2519 jfail:
2523 jleave:
2524 safe_signal(SIGINT, saveint);
2525 safe_signal(SIGPIPE, savepipe);
2526 imaplock = 0;
2527 j_leave:
2528 NYD_LEAVE;
2529 if (interrupts)
2530 onintr(0);
2531 return rv;
2534 static enum okay
2535 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2536 struct list_item **lend, int level)
2538 char o[LINESIZE], *cp;
2539 const char *bp;
2540 FILE *queuefp = NULL;
2541 struct list_item *lp;
2542 enum okay ok = STOP;
2543 NYD_X;
2545 *list = *lend = NULL;
2546 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2547 IMAP_OUT(o, MB_COMD, return STOP)
2548 while (mp->mb_active & MB_COMD) {
2549 ok = imap_answer(mp, 1);
2550 if (response_status == RESPONSE_OTHER &&
2551 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2552 cp = imap_unquotestr(list_name);
2553 lp = csalloc(1, sizeof *lp);
2554 lp->l_name = cp;
2555 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2556 ++cp;
2557 lp->l_base = *cp ? cp : savestr(base);
2558 lp->l_attr = list_attributes;
2559 lp->l_level = level+1;
2560 lp->l_delim = list_hierarchy_delimiter;
2561 if (*list && *lend) {
2562 (*lend)->l_next = lp;
2563 *lend = lp;
2564 } else
2565 *list = *lend = lp;
2568 return ok;
2571 static enum okay
2572 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2574 struct list_item *list, *lend, *lp, *lx, *ly;
2575 int n, depth;
2576 const char *bp;
2577 char *cp;
2578 enum okay rv;
2579 NYD_ENTER;
2581 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2582 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2583 goto jleave;
2584 rv = OKAY;
2585 if (list == NULL || lend == NULL)
2586 goto jleave;
2588 for (lp = list; lp; lp = lp->l_next)
2589 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2590 !(lp->l_attr & LIST_NOINFERIORS)) {
2591 cp = salloc((n = strlen(lp->l_name)) + 2);
2592 memcpy(cp, lp->l_name, n);
2593 cp[n] = lp->l_delim;
2594 cp[n+1] = '\0';
2595 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2596 lp->l_has_children = 1;
2597 if (strcmp(cp, lx->l_name) == 0)
2598 lx = lx->l_next;
2599 if (lx) {
2600 lend->l_next = lx;
2601 lend = ly;
2606 for (lp = list; lp; lp = lp->l_next) {
2607 if (strip) {
2608 cp = lp->l_name;
2609 for (bp = base; *bp && *bp == *cp; bp++)
2610 cp++;
2611 } else
2612 cp = lp->l_name;
2613 if (!(lp->l_attr & LIST_NOSELECT))
2614 fprintf(fp, "%s\n", *cp ? cp : base);
2615 else if (lp->l_has_children == 0)
2616 fprintf(fp, "%s%c\n", *cp ? cp : base,
2617 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2619 jleave:
2620 NYD_LEAVE;
2621 return rv;
2624 FL void
2625 imap_folders(const char * volatile name, int strip)
2627 sighandler_type saveint, savepipe;
2628 const char *fold, *cp, *sp;
2629 FILE * volatile fp;
2630 NYD_ENTER;
2632 cp = protbase(name);
2633 sp = mb.mb_imap_account;
2634 if (sp == NULL || strcmp(cp, sp)) {
2635 fprintf(stderr, _(
2636 "Cannot perform `folders' but when on the very IMAP "
2637 "account; the current one is\n `%s' -- "
2638 "try `folders @'.\n"),
2639 (sp != NULL ? sp : _("[NONE]")));
2640 goto jleave;
2643 fold = imap_fileof(name);
2644 if (options & OPT_TTYOUT) {
2645 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2646 0600)) == NULL) {
2647 perror("tmpfile");
2648 goto jleave;
2650 } else
2651 fp = stdout;
2653 imaplock = 1;
2654 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2655 safe_signal(SIGINT, &_imap_maincatch);
2656 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2657 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2658 goto junroll;
2659 if (savepipe != SIG_IGN)
2660 safe_signal(SIGPIPE, imapcatch);
2662 if (mb.mb_type == MB_CACHE)
2663 cache_list(&mb, fold, strip, fp);
2664 else
2665 imap_list(&mb, fold, strip, fp);
2667 imaplock = 0;
2668 if (interrupts) {
2669 if (options & OPT_TTYOUT)
2670 Fclose(fp);
2671 goto jleave;
2673 fflush(fp);
2675 if (options & OPT_TTYOUT) {
2676 rewind(fp);
2677 if (fsize(fp) > 0)
2678 dopr(fp);
2679 else
2680 fprintf(stderr, "Folder not found.\n");
2682 junroll:
2683 safe_signal(SIGINT, saveint);
2684 safe_signal(SIGPIPE, savepipe);
2685 if (options & OPT_TTYOUT)
2686 Fclose(fp);
2687 jleave:
2688 NYD_LEAVE;
2689 if (interrupts)
2690 onintr(0);
2693 static void
2694 dopr(FILE *fp)
2696 char o[LINESIZE];
2697 int c;
2698 long n = 0, mx = 0, columns, width;
2699 FILE *out;
2700 NYD_ENTER;
2702 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2703 == NULL) {
2704 perror("tmpfile");
2705 goto jleave;
2708 while ((c = getc(fp)) != EOF) {
2709 if (c == '\n') {
2710 if (n > mx)
2711 mx = n;
2712 n = 0;
2713 } else
2714 ++n;
2716 rewind(fp);
2718 width = scrnwidth;
2719 if (mx < width / 2) {
2720 columns = width / (mx+2);
2721 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2722 } else
2723 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2724 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2725 page_or_print(out, 0);
2726 Fclose(out);
2727 jleave:
2728 NYD_LEAVE;
2731 static enum okay
2732 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2734 char o[LINESIZE];
2735 const char *qname;
2736 int twice = 0, stored = 0;
2737 FILE *queuefp = NULL;
2738 enum okay ok = STOP;
2739 NYD_X;
2741 if (mp->mb_type == MB_CACHE) {
2742 if ((queuefp = cache_queue(mp)) == NULL)
2743 return STOP;
2744 ok = OKAY;
2746 qname = imap_quotestr(name = imap_fileof(name));
2747 /* Since it is not possible to set flags on the copy, recently
2748 * set flags must be set on the original to include it in the copy */
2749 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2750 imap_store(mp, m, n, '+', "\\Seen", 0);
2751 if (m->m_flag&MFLAG)
2752 imap_store(mp, m, n, '+', "\\Flagged", 0);
2753 if (m->m_flag&MUNFLAG)
2754 imap_store(mp, m, n, '-', "\\Flagged", 0);
2755 if (m->m_flag&MANSWER)
2756 imap_store(mp, m, n, '+', "\\Answered", 0);
2757 if (m->m_flag&MUNANSWER)
2758 imap_store(mp, m, n, '-', "\\Flagged", 0);
2759 if (m->m_flag&MDRAFT)
2760 imap_store(mp, m, n, '+', "\\Draft", 0);
2761 if (m->m_flag&MUNDRAFT)
2762 imap_store(mp, m, n, '-', "\\Draft", 0);
2763 again:
2764 if (m->m_uid)
2765 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2766 else {
2767 if (check_expunged() == STOP)
2768 goto out;
2769 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2771 IMAP_OUT(o, MB_COMD, goto out)
2772 while (mp->mb_active & MB_COMD)
2773 ok = imap_answer(mp, twice);
2775 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2776 response_status == RESPONSE_OK)
2777 imap_copyuid(mp, m, name);
2779 if (response_status == RESPONSE_NO && twice++ == 0) {
2780 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2781 IMAP_OUT(o, MB_COMD, goto out)
2782 while (mp->mb_active & MB_COMD)
2783 ok = imap_answer(mp, 1);
2784 if (ok == OKAY) {
2785 imap_created_mailbox++;
2786 goto again;
2790 if (queuefp != NULL)
2791 Fclose(queuefp);
2793 /* ... and reset the flag to its initial value so that the 'exit'
2794 * command still leaves the message unread */
2795 out:
2796 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2797 imap_store(mp, m, n, '-', "\\Seen", 0);
2798 stored++;
2800 if (m->m_flag & MFLAG) {
2801 imap_store(mp, m, n, '-', "\\Flagged", 0);
2802 stored++;
2804 if (m->m_flag & MUNFLAG) {
2805 imap_store(mp, m, n, '+', "\\Flagged", 0);
2806 stored++;
2808 if (m->m_flag & MANSWER) {
2809 imap_store(mp, m, n, '-', "\\Answered", 0);
2810 stored++;
2812 if (m->m_flag & MUNANSWER) {
2813 imap_store(mp, m, n, '+', "\\Answered", 0);
2814 stored++;
2816 if (m->m_flag & MDRAFT) {
2817 imap_store(mp, m, n, '-', "\\Draft", 0);
2818 stored++;
2820 if (m->m_flag & MUNDRAFT) {
2821 imap_store(mp, m, n, '+', "\\Draft", 0);
2822 stored++;
2824 if (stored) {
2825 mp->mb_active |= MB_COMD;
2826 (void)imap_finish(mp);
2828 return ok;
2831 FL enum okay
2832 imap_copy(struct message *m, int n, const char *name)
2834 sighandler_type saveint, savepipe;
2835 enum okay rv = STOP;
2836 NYD_ENTER;
2838 imaplock = 1;
2839 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2840 safe_signal(SIGINT, &_imap_maincatch);
2841 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2842 if (sigsetjmp(imapjmp, 1) == 0) {
2843 if (savepipe != SIG_IGN)
2844 safe_signal(SIGPIPE, imapcatch);
2846 rv = imap_copy1(&mb, m, n, name);
2848 safe_signal(SIGINT, saveint);
2849 safe_signal(SIGPIPE, savepipe);
2850 imaplock = 0;
2852 NYD_LEAVE;
2853 if (interrupts)
2854 onintr(0);
2855 return rv;
2858 static enum okay
2859 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2860 unsigned long *olduid, unsigned long *newuid)
2862 char *xp, *yp, *zp;
2863 enum okay rv;
2864 NYD_ENTER;
2866 *uidvalidity = strtoul(cp, &xp, 10);
2867 *olduid = strtoul(xp, &yp, 10);
2868 *newuid = strtoul(yp, &zp, 10);
2869 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2870 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2871 NYD_LEAVE;
2872 return rv;
2875 static enum okay
2876 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2877 unsigned long *uid)
2879 char *xp, *yp;
2880 enum okay rv;
2881 NYD_ENTER;
2883 *uidvalidity = strtoul(cp, &xp, 10);
2884 *uid = strtoul(xp, &yp, 10);
2885 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2886 *yp == ']');
2887 NYD_LEAVE;
2888 return rv;
2891 static enum okay
2892 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2894 struct mailbox xmb;
2895 struct message xm;
2896 const char *cp;
2897 unsigned long uidvalidity, olduid, newuid;
2898 enum okay rv;
2899 NYD_ENTER;
2901 xmb.mb_imap_mailbox = NULL;
2902 rv = STOP;
2903 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2904 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2905 goto jleave;
2906 rv = OKAY;
2908 xmb = *mp;
2909 xmb.mb_cache_directory = NULL;
2910 xmb.mb_imap_mailbox = sstrdup(name);
2911 xmb.mb_uidvalidity = uidvalidity;
2912 initcache(&xmb);
2913 if (m == NULL) {
2914 memset(&xm, 0, sizeof xm);
2915 xm.m_uid = olduid;
2916 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2917 goto jleave;
2918 getcache(mp, &xm, NEED_HEADER);
2919 getcache(mp, &xm, NEED_BODY);
2920 } else {
2921 if ((m->m_flag & HAVE_HEADER) == 0)
2922 getcache(mp, m, NEED_HEADER);
2923 if ((m->m_flag & HAVE_BODY) == 0)
2924 getcache(mp, m, NEED_BODY);
2925 xm = *m;
2927 xm.m_uid = newuid;
2928 xm.m_flag &= ~MFULLYCACHED;
2929 putcache(&xmb, &xm);
2930 jleave:
2931 if (xmb.mb_imap_mailbox != NULL)
2932 free(xmb.mb_imap_mailbox);
2933 NYD_LEAVE;
2934 return rv;
2937 static enum okay
2938 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2939 long size, long lines, int flag, const char *name)
2941 struct mailbox xmb;
2942 struct message xm;
2943 const char *cp;
2944 unsigned long uidvalidity, uid;
2945 enum okay rv;
2946 NYD_ENTER;
2948 xmb.mb_imap_mailbox = NULL;
2949 rv = STOP;
2950 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2951 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2952 goto jleave;
2953 rv = OKAY;
2955 xmb = *mp;
2956 xmb.mb_cache_directory = NULL;
2957 xmb.mb_imap_mailbox = sstrdup(name);
2958 xmb.mb_uidvalidity = uidvalidity;
2959 xmb.mb_otf = xmb.mb_itf = fp;
2960 initcache(&xmb);
2961 memset(&xm, 0, sizeof xm);
2962 xm.m_flag = (flag & MREAD) | MNEW;
2963 xm.m_time = t;
2964 xm.m_block = mailx_blockof(off1);
2965 xm.m_offset = mailx_offsetof(off1);
2966 xm.m_size = size;
2967 xm.m_xsize = xsize;
2968 xm.m_lines = xm.m_xlines = lines;
2969 xm.m_uid = uid;
2970 xm.m_have = HAVE_HEADER | HAVE_BODY;
2971 putcache(&xmb, &xm);
2972 jleave:
2973 if (xmb.mb_imap_mailbox != NULL)
2974 free(xmb.mb_imap_mailbox);
2975 NYD_LEAVE;
2976 return rv;
2979 static enum okay
2980 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2982 FILE *tp = NULL;
2983 time_t t;
2984 long size, xsize, ysize, lines;
2985 enum mflag flag = MNEW;
2986 char *name, *buf, *bp;
2987 char const *cp;
2988 size_t bufsize, buflen, cnt;
2989 enum okay rv = STOP;
2990 NYD_ENTER;
2992 buf = smalloc(bufsize = LINESIZE);
2993 buflen = 0;
2994 cnt = fsize(fp);
2995 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2996 goto jstop;
2998 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3000 while (*bp == ' ')
3001 ++bp;
3003 if ((cp = strrchr(bp, '{')) == NULL)
3004 goto jstop;
3006 xsize = atol(&cp[1]) + 2;
3007 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3008 goto jstop;
3009 while (*cp == ' ')
3010 cp++;
3012 if (*cp == '(') {
3013 imap_getflags(cp, &cp, &flag);
3014 while (*++cp == ' ')
3017 t = imap_read_date_time(cp);
3019 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3020 NULL)
3021 goto jstop;
3023 size = xsize;
3024 ysize = lines = 0;
3025 while (size > 0) {
3026 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3027 goto jstop;
3028 size -= buflen;
3029 buf[--buflen] = '\0';
3030 buf[buflen-1] = '\n';
3031 fwrite(buf, 1, buflen, tp);
3032 ysize += buflen;
3033 ++lines;
3035 fflush(tp);
3036 rewind(tp);
3038 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3039 imap_unquotestr(name));
3040 rv = OKAY;
3041 jstop:
3042 free(buf);
3043 if (tp)
3044 Fclose(tp);
3045 NYD_LEAVE;
3046 return rv;
3049 #ifdef HAVE_IMAP_SEARCH
3050 static enum okay
3051 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3052 int f)
3054 char *o, *xp, *cs, c;
3055 size_t osize;
3056 FILE *queuefp = NULL;
3057 int i;
3058 unsigned long n;
3059 const char *cp;
3060 enum okay ok = STOP;
3061 NYD_X;
3063 c = 0;
3064 for (cp = spec; *cp; cp++)
3065 c |= *cp;
3066 if (c & 0200) {
3067 cp = charset_get_lc();
3068 # ifdef HAVE_ICONV
3069 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3070 iconv_t it;
3071 char *nsp, *nspec;
3072 size_t sz, nsz;
3074 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3075 sz = strlen(spec) + 1;
3076 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3077 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3078 sz == 0) {
3079 spec = nspec;
3080 cp = "utf-8";
3082 n_iconv_close(it);
3085 # endif
3086 cp = imap_quotestr(cp);
3087 cs = salloc(n = strlen(cp) + 10);
3088 snprintf(cs, n, "CHARSET %s ", cp);
3089 } else
3090 cs = UNCONST("");
3092 o = ac_alloc(osize = strlen(spec) + 60);
3093 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3094 IMAP_OUT(o, MB_COMD, goto out)
3095 while (mp->mb_active & MB_COMD) {
3096 ok = imap_answer(mp, 0);
3097 if (response_status == RESPONSE_OTHER &&
3098 response_other == MAILBOX_DATA_SEARCH) {
3099 xp = responded_other_text;
3100 while (*xp && *xp != '\r') {
3101 n = strtoul(xp, &xp, 10);
3102 for (i = 0; i < cnt; i++)
3103 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3104 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3105 mark(i+1, f);
3109 out:
3110 ac_free(o);
3111 return ok;
3114 FL enum okay
3115 imap_search1(const char * volatile spec, int f)
3117 sighandler_type saveint, savepipe;
3118 enum okay volatile rv = STOP;
3119 NYD_ENTER;
3121 if (mb.mb_type != MB_IMAP)
3122 goto jleave;
3124 imaplock = 1;
3125 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3126 safe_signal(SIGINT, &_imap_maincatch);
3127 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3128 if (sigsetjmp(imapjmp, 1) == 0) {
3129 if (savepipe != SIG_IGN)
3130 safe_signal(SIGPIPE, imapcatch);
3132 rv = imap_search2(&mb, message, msgCount, spec, f);
3134 safe_signal(SIGINT, saveint);
3135 safe_signal(SIGPIPE, savepipe);
3136 imaplock = 0;
3137 jleave:
3138 NYD_LEAVE;
3139 if (interrupts)
3140 onintr(0);
3141 return rv;
3143 #endif /* HAVE_IMAP_SEARCH */
3145 FL int
3146 imap_thisaccount(const char *cp)
3148 int rv;
3149 NYD_ENTER;
3151 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3152 rv = 0;
3153 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3154 mb.mb_imap_account == NULL)
3155 rv = 0;
3156 else
3157 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3158 NYD_LEAVE;
3159 return rv;
3162 FL enum okay
3163 imap_remove(const char * volatile name)
3165 sighandler_type volatile saveint, savepipe;
3166 enum okay rv = STOP;
3167 NYD_ENTER;
3169 if (mb.mb_type != MB_IMAP) {
3170 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3171 name);
3172 goto jleave;
3175 if (!imap_thisaccount(name)) {
3176 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3177 "server: \"%s\" not removed.\n", name);
3178 goto jleave;
3181 imaplock = 1;
3182 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3183 safe_signal(SIGINT, &_imap_maincatch);
3184 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3185 if (sigsetjmp(imapjmp, 1) == 0) {
3186 if (savepipe != SIG_IGN)
3187 safe_signal(SIGPIPE, imapcatch);
3189 rv = imap_remove1(&mb, imap_fileof(name));
3191 safe_signal(SIGINT, saveint);
3192 safe_signal(SIGPIPE, savepipe);
3193 imaplock = 0;
3195 if (rv == OKAY)
3196 rv = cache_remove(name);
3197 jleave:
3198 NYD_LEAVE;
3199 if (interrupts)
3200 onintr(0);
3201 return rv;
3204 static enum okay
3205 imap_remove1(struct mailbox *mp, const char *name)
3207 FILE *queuefp = NULL;
3208 char *o;
3209 int os;
3210 enum okay ok = STOP;
3211 NYD_X;
3213 o = ac_alloc(os = 2*strlen(name) + 100);
3214 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3215 IMAP_OUT(o, MB_COMD, goto out)
3216 while (mp->mb_active & MB_COMD)
3217 ok = imap_answer(mp, 1);
3218 out:
3219 ac_free(o);
3220 return ok;
3223 FL enum okay
3224 imap_rename(const char *old, const char *new)
3226 sighandler_type saveint, savepipe;
3227 enum okay rv = STOP;
3228 NYD_ENTER;
3230 if (mb.mb_type != MB_IMAP) {
3231 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3232 goto jleave;
3235 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3236 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3237 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3238 goto jleave;
3241 imaplock = 1;
3242 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3243 safe_signal(SIGINT, &_imap_maincatch);
3244 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3245 if (sigsetjmp(imapjmp, 1) == 0) {
3246 if (savepipe != SIG_IGN)
3247 safe_signal(SIGPIPE, imapcatch);
3249 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3251 safe_signal(SIGINT, saveint);
3252 safe_signal(SIGPIPE, savepipe);
3253 imaplock = 0;
3255 if (rv == OKAY)
3256 rv = cache_rename(old, new);
3257 jleave:
3258 NYD_LEAVE;
3259 if (interrupts)
3260 onintr(0);
3261 return rv;
3264 static enum okay
3265 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3267 FILE *queuefp = NULL;
3268 char *o;
3269 int os;
3270 enum okay ok = STOP;
3271 NYD_X;
3273 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3274 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3275 imap_quotestr(new));
3276 IMAP_OUT(o, MB_COMD, goto out)
3277 while (mp->mb_active & MB_COMD)
3278 ok = imap_answer(mp, 1);
3279 out:
3280 ac_free(o);
3281 return ok;
3284 FL enum okay
3285 imap_dequeue(struct mailbox *mp, FILE *fp)
3287 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3288 size_t bufsize, buflen, cnt;
3289 long offs, offs1, offs2, octets;
3290 int twice, gotcha = 0;
3291 FILE *queuefp = NULL;
3292 enum okay ok = OKAY, rok = OKAY;
3293 NYD_X;
3295 buf = smalloc(bufsize = LINESIZE);
3296 buflen = 0;
3297 cnt = fsize(fp);
3298 while ((offs1 = ftell(fp)) >= 0 &&
3299 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3300 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3302 while (*bp == ' ')
3303 ++bp;
3304 twice = 0;
3305 if ((offs = ftell(fp)) < 0)
3306 goto fail;
3307 again:
3308 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3309 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3310 cp = &bp[9];
3311 while (digitchar(*cp))
3312 cp++;
3313 if (*cp != ' ')
3314 goto fail;
3315 while (*cp == ' ')
3316 cp++;
3317 if ((newname = imap_strex(cp, NULL)) == NULL)
3318 goto fail;
3319 IMAP_OUT(o, MB_COMD, continue)
3320 while (mp->mb_active & MB_COMD)
3321 ok = imap_answer(mp, twice);
3322 if (response_status == RESPONSE_NO && twice++ == 0)
3323 goto trycreate;
3324 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3325 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3327 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3328 IMAP_OUT(o, MB_COMD, continue)
3329 while (mp->mb_active & MB_COMD)
3330 ok = imap_answer(mp, 1);
3331 if (ok == OKAY)
3332 gotcha++;
3333 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3334 if ((cp = strrchr(bp, '{')) == NULL)
3335 goto fail;
3336 octets = atol(&cp[1]) + 2;
3337 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3338 goto fail;
3339 IMAP_OUT(o, MB_COMD, continue)
3340 while (mp->mb_active & MB_COMD) {
3341 ok = imap_answer(mp, twice);
3342 if (response_type == RESPONSE_CONT)
3343 break;
3345 if (ok == STOP) {
3346 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3347 goto trycreate;
3348 goto fail;
3350 while (octets > 0) {
3351 size_t n = (UICMP(z, octets, >, sizeof iob)
3352 ? sizeof iob : (size_t)octets);
3353 octets -= n;
3354 if (n != fread(iob, 1, n, fp))
3355 goto fail;
3356 swrite1(&mp->mb_sock, iob, n, 1);
3358 swrite(&mp->mb_sock, "");
3359 while (mp->mb_active & MB_COMD) {
3360 ok = imap_answer(mp, 0);
3361 if (response_status == RESPONSE_NO && twice++ == 0) {
3362 if (fseek(fp, offs, SEEK_SET) < 0)
3363 goto fail;
3364 goto trycreate;
3367 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3368 if ((offs2 = ftell(fp)) < 0)
3369 goto fail;
3370 fseek(fp, offs1, SEEK_SET);
3371 if (imap_appenduid_cached(mp, fp) == STOP) {
3372 (void)fseek(fp, offs2, SEEK_SET);
3373 goto fail;
3376 } else {
3377 fail:
3378 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3379 rok = STOP;
3381 continue;
3382 trycreate:
3383 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3384 IMAP_OUT(o, MB_COMD, continue)
3385 while (mp->mb_active & MB_COMD)
3386 ok = imap_answer(mp, 1);
3387 if (ok == OKAY)
3388 goto again;
3390 fflush(fp);
3391 rewind(fp);
3392 ftruncate(fileno(fp), 0);
3393 if (gotcha)
3394 imap_close(mp);
3395 free(buf);
3396 return rok;
3399 static char *
3400 imap_strex(char const *cp, char const **xp)
3402 char const *cq;
3403 char *n = NULL;
3404 NYD_ENTER;
3406 if (*cp != '"')
3407 goto jleave;
3409 for (cq = cp + 1; *cq != '\0'; ++cq) {
3410 if (*cq == '\\')
3411 cq++;
3412 else if (*cq == '"')
3413 break;
3415 if (*cq != '"')
3416 goto jleave;
3418 n = salloc(cq - cp + 2);
3419 memcpy(n, cp, cq - cp +1);
3420 n[cq - cp + 1] = '\0';
3421 if (xp != NULL)
3422 *xp = cq + 1;
3423 jleave:
3424 NYD_LEAVE;
3425 return n;
3428 static enum okay
3429 check_expunged(void)
3431 enum okay rv;
3432 NYD_ENTER;
3434 if (expunged_messages > 0) {
3435 fprintf(stderr, "Command not executed - messages have been expunged\n");
3436 rv = STOP;
3437 } else
3438 rv = OKAY;
3439 NYD_LEAVE;
3440 return rv;
3443 FL int
3444 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3446 struct url url;
3447 int rv, omsgCount = msgCount;
3448 NYD_ENTER;
3449 UNUSED(vp);
3451 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3452 fprintf(stderr, "Already connected.\n");
3453 rv = 1;
3454 goto jleave;
3457 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3458 rv = 1;
3459 goto jleave;
3461 var_clear_allow_undefined = TRU1;
3462 ok_bclear(disconnected);
3463 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
3464 var_clear_allow_undefined = FAL0;
3466 if (mb.mb_type == MB_CACHE) {
3467 enum fedit_mode fm = FEDIT_NONE;
3468 if (_imap_rdonly)
3469 fm |= FEDIT_RDONLY;
3470 if (!edit)
3471 fm |= FEDIT_SYSBOX;
3472 _imap_setfile1(&url, fm, 1);
3473 if (msgCount > omsgCount)
3474 newmailinfo(omsgCount);
3476 rv = 0;
3477 jleave:
3478 NYD_LEAVE;
3479 return rv;
3482 FL int
3483 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3485 struct url url;
3486 int rv = 1, *msgvec = vp;
3487 NYD_ENTER;
3489 if (mb.mb_type == MB_CACHE) {
3490 fprintf(stderr, "Not connected.\n");
3491 goto jleave;
3493 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3494 fprintf(stderr, "The current mailbox is not cached.\n");
3495 goto jleave;
3498 if (!url_parse(&url, CPROTO_IMAP, mailname))
3499 goto jleave;
3501 if (*msgvec)
3502 c_cache(vp);
3503 ok_bset(disconnected, TRU1);
3504 if (mb.mb_type == MB_IMAP) {
3505 enum fedit_mode fm = FEDIT_NONE;
3506 if (_imap_rdonly)
3507 fm |= FEDIT_RDONLY;
3508 if (!edit)
3509 fm |= FEDIT_SYSBOX;
3510 sclose(&mb.mb_sock);
3511 _imap_setfile1(&url, fm, 1);
3513 rv = 0;
3514 jleave:
3515 NYD_LEAVE;
3516 return rv;
3519 FL int
3520 c_cache(void *vp)
3522 int rv = 1, *msgvec = vp, *ip;
3523 struct message *mp;
3524 NYD_ENTER;
3526 if (mb.mb_type != MB_IMAP) {
3527 fprintf(stderr, "Not connected to an IMAP server.\n");
3528 goto jleave;
3530 if (cached_uidvalidity(&mb) == 0) {
3531 fprintf(stderr, "The current mailbox is not cached.\n");
3532 goto jleave;
3535 srelax_hold();
3536 for (ip = msgvec; *ip; ++ip) {
3537 mp = &message[*ip - 1];
3538 if (!(mp->m_have & HAVE_BODY)) {
3539 get_body(mp);
3540 srelax();
3543 srelax_rele();
3544 rv = 0;
3545 jleave:
3546 NYD_LEAVE;
3547 return rv;
3550 FL int
3551 disconnected(const char *file)
3553 struct url url;
3554 int rv = 1;
3555 NYD_ENTER;
3557 if (ok_blook(disconnected)) {
3558 rv = 1;
3559 goto jleave;
3562 if (!url_parse(&url, CPROTO_IMAP, file)) {
3563 rv = 0;
3564 goto jleave;
3566 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
3568 jleave:
3569 NYD_LEAVE;
3570 return rv;
3573 FL void
3574 transflags(struct message *omessage, long omsgCount, int transparent)
3576 struct message *omp, *nmp, *newdot, *newprevdot;
3577 int hf;
3578 NYD_ENTER;
3580 omp = omessage;
3581 nmp = message;
3582 newdot = message;
3583 newprevdot = NULL;
3584 while (PTRCMP(omp, <, omessage + omsgCount) &&
3585 PTRCMP(nmp, <, message + msgCount)) {
3586 if (dot && nmp->m_uid == dot->m_uid)
3587 newdot = nmp;
3588 if (prevdot && nmp->m_uid == prevdot->m_uid)
3589 newprevdot = nmp;
3590 if (omp->m_uid == nmp->m_uid) {
3591 hf = nmp->m_flag & MHIDDEN;
3592 if (transparent && mb.mb_type == MB_IMAP)
3593 omp->m_flag &= ~MHIDDEN;
3594 *nmp++ = *omp++;
3595 if (transparent && mb.mb_type == MB_CACHE)
3596 nmp[-1].m_flag |= hf;
3597 } else if (omp->m_uid < nmp->m_uid)
3598 ++omp;
3599 else
3600 ++nmp;
3602 dot = newdot;
3603 setdot(newdot);
3604 prevdot = newprevdot;
3605 free(omessage);
3606 NYD_LEAVE;
3609 FL time_t
3610 imap_read_date_time(const char *cp)
3612 char buf[3];
3613 time_t t;
3614 int i, year, month, day, hour, minute, second, sign = -1;
3615 NYD2_ENTER;
3617 /* "25-Jul-2004 15:33:44 +0200"
3618 * | | | | | |
3619 * 0 5 10 15 20 25 */
3620 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3621 goto jinvalid;
3622 day = strtol(&cp[1], NULL, 10);
3623 for (i = 0;;) {
3624 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3625 break;
3626 if (month_names[++i][0] == '\0')
3627 goto jinvalid;
3629 month = i + 1;
3630 year = strtol(&cp[8], NULL, 10);
3631 hour = strtol(&cp[13], NULL, 10);
3632 minute = strtol(&cp[16], NULL, 10);
3633 second = strtol(&cp[19], NULL, 10);
3634 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3635 goto jinvalid;
3636 switch (cp[22]) {
3637 case '-':
3638 sign = 1;
3639 break;
3640 case '+':
3641 break;
3642 default:
3643 goto jinvalid;
3645 buf[2] = '\0';
3646 buf[0] = cp[23];
3647 buf[1] = cp[24];
3648 t += strtol(buf, NULL, 10) * sign * 3600;
3649 buf[0] = cp[25];
3650 buf[1] = cp[26];
3651 t += strtol(buf, NULL, 10) * sign * 60;
3652 jleave:
3653 NYD2_LEAVE;
3654 return t;
3655 jinvalid:
3656 time(&t);
3657 goto jleave;
3660 FL const char *
3661 imap_make_date_time(time_t t)
3663 static char s[30];
3664 struct tm *tmptr;
3665 int tzdiff, tzdiff_hour, tzdiff_min;
3666 NYD2_ENTER;
3668 tzdiff = t - mktime(gmtime(&t));
3669 tzdiff_hour = (int)(tzdiff / 60);
3670 tzdiff_min = tzdiff_hour % 60;
3671 tzdiff_hour /= 60;
3672 tmptr = localtime(&t);
3673 if (tmptr->tm_isdst > 0)
3674 tzdiff_hour++;
3675 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3676 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3677 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3678 NYD2_LEAVE;
3679 return s;
3681 #endif /* HAVE_IMAP */
3683 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3684 FL char *
3685 imap_quotestr(char const *s)
3687 char *n, *np;
3688 NYD2_ENTER;
3690 np = n = salloc(2 * strlen(s) + 3);
3691 *np++ = '"';
3692 while (*s) {
3693 if (*s == '"' || *s == '\\')
3694 *np++ = '\\';
3695 *np++ = *s++;
3697 *np++ = '"';
3698 *np = '\0';
3699 NYD2_LEAVE;
3700 return n;
3703 FL char *
3704 imap_unquotestr(char const *s)
3706 char *n, *np;
3707 NYD2_ENTER;
3709 if (*s != '"') {
3710 n = savestr(s);
3711 goto jleave;
3714 np = n = salloc(strlen(s) + 1);
3715 while (*++s) {
3716 if (*s == '\\')
3717 s++;
3718 else if (*s == '"')
3719 break;
3720 *np++ = *s;
3722 *np = '\0';
3723 jleave:
3724 NYD2_LEAVE;
3725 return n;
3727 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3729 /* s-it-mode */