nailfuns: add NYD2 series (only if HAVE_NYD2)
[s-mailx.git] / imap.c
blob0981a8365ec4e804c9148179854f19b45ac4b49c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ CRAM-MD5 as of RFC 2195.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 #ifndef HAVE_AMALGAMATION
42 # include "nail.h"
43 #endif
45 #ifdef HAVE_IMAP
46 # include <sys/socket.h>
48 # include <netdb.h>
50 # include <netinet/in.h>
52 # ifdef HAVE_ARPA_INET_H
53 # include <arpa/inet.h>
54 # endif
55 #endif
57 #ifdef HAVE_IMAP
58 #define IMAP_ANSWER() \
60 if (mp->mb_type != MB_CACHE) {\
61 enum okay ok = OKAY;\
62 while (mp->mb_active & MB_COMD)\
63 ok = imap_answer(mp, 1);\
64 if (ok == STOP)\
65 return STOP;\
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
77 do {\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
80 ACTIONBAIL;\
82 if (options & OPT_VERBVERB)\
83 fprintf(stderr, ">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static int volatile imaplock;
171 static int same_imap_account;
173 static void imap_other_get(char *pp);
174 static void imap_response_get(const char **cp);
175 static void imap_response_parse(void);
176 static enum okay imap_answer(struct mailbox *mp, int errprnt);
177 static enum okay imap_parse_list(void);
178 static enum okay imap_finish(struct mailbox *mp);
179 static void imap_timer_off(void);
180 static void imapcatch(int s);
181 static void _imap_maincatch(int s);
182 static enum okay imap_noop1(struct mailbox *mp);
183 static void rec_queue(enum rec_type type, unsigned long cnt);
184 static enum okay rec_dequeue(void);
185 static void rec_rmqueue(void);
186 static void imapalarm(int s);
187 static int imap_use_starttls(const char *uhp);
188 static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
189 const char *uhp);
190 static enum okay imap_capability(struct mailbox *mp);
191 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
192 #ifdef HAVE_MD5
193 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
194 #endif
195 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
196 #ifdef HAVE_GSSAPI
197 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
198 #endif
199 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
200 static void imap_init(struct mailbox *mp, int n);
201 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
202 int *prevcount);
203 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
204 struct url *urlp);
205 static int _imap_setfile1(struct url *urlp, int nmail, int isedit,
206 int transparent);
207 static int imap_fetchdata(struct mailbox *mp, struct message *m,
208 size_t expected, int need, const char *head,
209 size_t headsize, long headlines);
210 static void imap_putstr(struct mailbox *mp, struct message *m,
211 const char *str, const char *head, size_t headsize,
212 long headlines);
213 static enum okay imap_get(struct mailbox *mp, struct message *m,
214 enum needspec need);
215 static void commitmsg(struct mailbox *mp, struct message *to,
216 struct message *from, enum havespec have);
217 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
218 int bot, int top);
219 static enum okay imap_exit(struct mailbox *mp);
220 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
221 int needstat);
222 static enum okay imap_close(struct mailbox *mp);
223 static enum okay imap_update(struct mailbox *mp);
224 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
225 int c, const char *sp, int needstat);
226 static enum okay imap_unstore(struct message *m, int n, const char *flag);
227 static const char *tag(int new);
228 static char * imap_putflags(int f);
229 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
230 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
231 off_t off1, long xsize, enum mflag flag, time_t t);
232 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
233 static enum okay imap_list1(struct mailbox *mp, const char *base,
234 struct list_item **list, struct list_item **lend,
235 int level);
236 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
237 FILE *fp);
238 static void dopr(FILE *fp);
239 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
240 const char *name);
241 static enum okay imap_copyuid_parse(const char *cp,
242 unsigned long *uidvalidity, unsigned long *olduid,
243 unsigned long *newuid);
244 static enum okay imap_appenduid_parse(const char *cp,
245 unsigned long *uidvalidity, unsigned long *uid);
246 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
247 const char *name);
248 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
249 long off1, long xsize, long size, long lines, int flag,
250 const char *name);
251 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
252 #ifdef HAVE_IMAP_SEARCH
253 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
254 const char *spec, int f);
255 #endif
256 static enum okay imap_remove1(struct mailbox *mp, const char *name);
257 static enum okay imap_rename1(struct mailbox *mp, const char *old,
258 const char *new);
259 static char * imap_strex(char const *cp, char const **xp);
260 static enum okay check_expunged(void);
262 static void
263 imap_other_get(char *pp)
265 char *xp;
266 NYD_ENTER;
268 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
269 pp += 6;
270 response_other = MAILBOX_DATA_FLAGS;
271 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
272 pp += 5;
273 response_other = MAILBOX_DATA_LIST;
274 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
275 pp += 5;
276 response_other = MAILBOX_DATA_LSUB;
277 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
278 pp += 8;
279 response_other = MAILBOX_DATA_MAILBOX;
280 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
281 pp += 7;
282 response_other = MAILBOX_DATA_SEARCH;
283 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
284 pp += 7;
285 response_other = MAILBOX_DATA_STATUS;
286 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
287 pp += 11;
288 response_other = CAPABILITY_DATA;
289 } else {
290 responded_other_number = strtol(pp, &xp, 10);
291 while (*xp == ' ')
292 ++xp;
293 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
294 response_other = MAILBOX_DATA_EXISTS;
295 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
296 response_other = MAILBOX_DATA_RECENT;
297 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
298 response_other = MESSAGE_DATA_EXPUNGE;
299 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
300 pp = &xp[6];
301 response_other = MESSAGE_DATA_FETCH;
302 } else
303 response_other = RESPONSE_OTHER_UNKNOWN;
305 responded_other_text = pp;
306 NYD_LEAVE;
309 static void
310 imap_response_get(const char **cp)
312 NYD_ENTER;
313 if (ascncasecmp(*cp, "OK ", 3) == 0) {
314 *cp += 3;
315 response_status = RESPONSE_OK;
316 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
317 *cp += 3;
318 response_status = RESPONSE_NO;
319 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
320 *cp += 4;
321 response_status = RESPONSE_BAD;
322 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
323 *cp += 8;
324 response_status = RESPONSE_PREAUTH;
325 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
326 *cp += 4;
327 response_status = RESPONSE_BYE;
328 } else
329 response_status = RESPONSE_OTHER;
330 NYD_LEAVE;
333 static void
334 imap_response_parse(void)
336 static char *parsebuf; /* TODO Use pool */
337 static size_t parsebufsize;
339 const char *ip = imapbuf;
340 char *pp;
341 NYD_ENTER;
343 if (parsebufsize < imapbufsize)
344 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
345 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
346 pp = parsebuf;
347 switch (*ip) {
348 case '+':
349 response_type = RESPONSE_CONT;
350 ip++;
351 pp++;
352 while (*ip == ' ') {
353 ip++;
354 pp++;
356 break;
357 case '*':
358 ip++;
359 pp++;
360 while (*ip == ' ') {
361 ip++;
362 pp++;
364 imap_response_get(&ip);
365 pp = &parsebuf[ip - imapbuf];
366 switch (response_status) {
367 case RESPONSE_BYE:
368 response_type = RESPONSE_FATAL;
369 break;
370 default:
371 response_type = RESPONSE_DATA;
373 break;
374 default:
375 responded_tag = parsebuf;
376 while (*pp && *pp != ' ')
377 pp++;
378 if (*pp == '\0') {
379 response_type = RESPONSE_ILLEGAL;
380 break;
382 *pp++ = '\0';
383 while (*pp && *pp == ' ')
384 pp++;
385 if (*pp == '\0') {
386 response_type = RESPONSE_ILLEGAL;
387 break;
389 ip = &imapbuf[pp - parsebuf];
390 response_type = RESPONSE_TAGGED;
391 imap_response_get(&ip);
392 pp = &parsebuf[ip - imapbuf];
394 responded_text = pp;
395 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
396 response_status == RESPONSE_OTHER)
397 imap_other_get(pp);
398 NYD_LEAVE;
401 static enum okay
402 imap_answer(struct mailbox *mp, int errprnt)
404 int i, complete;
405 enum okay rv;
406 NYD_ENTER;
408 rv = OKAY;
409 if (mp->mb_type == MB_CACHE)
410 goto jleave;
411 rv = STOP;
412 jagain:
413 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
414 if (options & OPT_VERBVERB)
415 fputs(imapbuf, stderr);
416 imap_response_parse();
417 if (response_type == RESPONSE_ILLEGAL)
418 goto jagain;
419 if (response_type == RESPONSE_CONT) {
420 rv = OKAY;
421 goto jleave;
423 if (response_status == RESPONSE_OTHER) {
424 if (response_other == MAILBOX_DATA_EXISTS) {
425 had_exists = responded_other_number;
426 rec_queue(REC_EXISTS, responded_other_number);
427 if (had_expunge > 0)
428 had_expunge = 0;
429 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
430 rec_queue(REC_EXPUNGE, responded_other_number);
431 if (had_expunge < 0)
432 had_expunge = 0;
433 had_expunge++;
434 expunged_messages++;
437 complete = 0;
438 if (response_type == RESPONSE_TAGGED) {
439 if (asccasecmp(responded_tag, tag(0)) == 0)
440 complete |= 1;
441 else
442 goto jagain;
444 switch (response_status) {
445 case RESPONSE_PREAUTH:
446 mp->mb_active &= ~MB_PREAUTH;
447 /*FALLTHRU*/
448 case RESPONSE_OK:
449 jokay:
450 rv = OKAY;
451 complete |= 2;
452 break;
453 case RESPONSE_NO:
454 case RESPONSE_BAD:
455 jstop:
456 rv = STOP;
457 complete |= 2;
458 if (errprnt)
459 fprintf(stderr, _("IMAP error: %s"), responded_text);
460 break;
461 case RESPONSE_UNKNOWN: /* does not happen */
462 case RESPONSE_BYE:
463 i = mp->mb_active;
464 mp->mb_active = MB_NONE;
465 if (i & MB_BYE)
466 goto jokay;
467 goto jstop;
468 case RESPONSE_OTHER:
469 rv = OKAY;
470 break;
472 if (response_status != RESPONSE_OTHER &&
473 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
474 fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
475 if (complete == 3)
476 mp->mb_active &= ~MB_COMD;
477 } else {
478 rv = STOP;
479 mp->mb_active = MB_NONE;
481 jleave:
482 NYD_LEAVE;
483 return rv;
486 static enum okay
487 imap_parse_list(void)
489 char *cp;
490 enum okay rv;
491 NYD_ENTER;
493 rv = STOP;
495 cp = responded_other_text;
496 list_attributes = LIST_NONE;
497 if (*cp == '(') {
498 while (*cp && *cp != ')') {
499 if (*cp == '\\') {
500 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
501 list_attributes |= LIST_NOINFERIORS;
502 cp += 12;
503 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
504 list_attributes |= LIST_NOSELECT;
505 cp += 9;
506 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
507 list_attributes |= LIST_MARKED;
508 cp += 7;
509 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
510 list_attributes |= LIST_UNMARKED;
511 cp += 9;
514 cp++;
516 if (*++cp != ' ')
517 goto jleave;
518 while (*cp == ' ')
519 cp++;
522 list_hierarchy_delimiter = EOF;
523 if (*cp == '"') {
524 if (*++cp == '\\')
525 cp++;
526 list_hierarchy_delimiter = *cp++ & 0377;
527 if (cp[0] != '"' || cp[1] != ' ')
528 goto jleave;
529 cp++;
530 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
531 list_hierarchy_delimiter = EOF;
532 cp += 3;
535 while (*cp == ' ')
536 cp++;
537 list_name = cp;
538 while (*cp && *cp != '\r')
539 cp++;
540 *cp = '\0';
541 rv = OKAY;
542 jleave:
543 NYD_LEAVE;
544 return rv;
547 static enum okay
548 imap_finish(struct mailbox *mp)
550 NYD_ENTER;
551 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
552 imap_answer(mp, 1);
553 NYD_LEAVE;
554 return OKAY;
557 static void
558 imap_timer_off(void)
560 NYD_ENTER;
561 if (imapkeepalive > 0) {
562 alarm(0);
563 safe_signal(SIGALRM, savealrm);
565 NYD_LEAVE;
568 static void
569 imapcatch(int s)
571 NYD_X; /* Signal handler */
572 switch (s) {
573 case SIGINT:
574 fprintf(stderr, _("Interrupt\n"));
575 siglongjmp(imapjmp, 1);
576 /*NOTREACHED*/
577 case SIGPIPE:
578 fprintf(stderr, _("Received SIGPIPE during IMAP operation\n"));
579 break;
583 static void
584 _imap_maincatch(int s)
586 NYD_X; /* Signal handler */
587 UNUSED(s);
588 if (interrupts++ == 0) {
589 fprintf(stderr, _("Interrupt\n"));
590 return;
592 onintr(0);
595 static enum okay
596 imap_noop1(struct mailbox *mp)
598 char o[LINESIZE];
599 FILE *queuefp = NULL;
600 NYD_X;
602 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
603 IMAP_OUT(o, MB_COMD, return STOP)
604 IMAP_ANSWER()
605 return OKAY;
608 FL char const *
609 imap_fileof(char const *xcp)
611 char const *cp = xcp;
612 int state = 0;
613 NYD_ENTER;
615 while (*cp) {
616 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
617 cp += 3;
618 state = 1;
620 if (cp[0] == '/' && state == 1) {
621 ++cp;
622 goto jleave;
624 if (cp[0] == '/') {
625 cp = xcp;
626 goto jleave;
628 ++cp;
630 jleave:
631 NYD_LEAVE;
632 return cp;
635 FL enum okay
636 imap_noop(void)
638 sighandler_type volatile oldint, oldpipe;
639 enum okay rv = STOP;
640 NYD_ENTER;
642 if (mb.mb_type != MB_IMAP)
643 goto jleave;
645 imaplock = 1;
646 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
647 safe_signal(SIGINT, &_imap_maincatch);
648 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
649 if (sigsetjmp(imapjmp, 1) == 0) {
650 if (oldpipe != SIG_IGN)
651 safe_signal(SIGPIPE, imapcatch);
653 rv = imap_noop1(&mb);
655 safe_signal(SIGINT, oldint);
656 safe_signal(SIGPIPE, oldpipe);
657 imaplock = 0;
658 jleave:
659 NYD_LEAVE;
660 if (interrupts)
661 onintr(0);
662 return rv;
665 static void
666 rec_queue(enum rec_type rt, unsigned long cnt)
668 struct record *rp;
669 NYD_ENTER;
671 rp = scalloc(1, sizeof *rp);
672 rp->rec_type = rt;
673 rp->rec_count = cnt;
674 if (record && recend) {
675 recend->rec_next = rp;
676 recend = rp;
677 } else
678 record = recend = rp;
679 NYD_LEAVE;
682 static enum okay
683 rec_dequeue(void)
685 struct message *omessage;
686 struct record *rp, *rq;
687 uiz_t exists = 0, i;
688 enum okay rv = STOP;
689 NYD_ENTER;
691 if (record == NULL)
692 goto jleave;
694 omessage = message;
695 message = smalloc((msgCount+1) * sizeof *message);
696 if (msgCount)
697 memcpy(message, omessage, msgCount * sizeof *message);
698 memset(&message[msgCount], 0, sizeof *message);
700 rp = record, rq = NULL;
701 rv = OKAY;
702 while (rp != NULL) {
703 switch (rp->rec_type) {
704 case REC_EXISTS:
705 exists = rp->rec_count;
706 break;
707 case REC_EXPUNGE:
708 if (rp->rec_count == 0) {
709 rv = STOP;
710 break;
712 if (rp->rec_count > (unsigned long)msgCount) {
713 if (exists == 0 || rp->rec_count > exists--)
714 rv = STOP;
715 break;
717 if (exists > 0)
718 exists--;
719 delcache(&mb, &message[rp->rec_count-1]);
720 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
721 ((msgCount - rp->rec_count + 1) * sizeof *message));
722 --msgCount;
723 /* If the message was part of a collapsed thread,
724 * the m_collapsed field of one of its ancestors
725 * should be incremented. It seems hardly possible
726 * to do this with the current message structure,
727 * though. The result is that a '+' may be shown
728 * in the header summary even if no collapsed
729 * children exists */
730 break;
732 if (rq != NULL)
733 free(rq);
734 rq = rp;
735 rp = rp->rec_next;
737 if (rq != NULL)
738 free(rq);
740 record = recend = NULL;
741 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
742 message = srealloc(message, (exists + 1) * sizeof *message);
743 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
744 for (i = msgCount; i < exists; ++i)
745 imap_init(&mb, i);
746 imap_flags(&mb, msgCount+1, exists);
747 msgCount = exists;
750 if (rv == STOP) {
751 free(message);
752 message = omessage;
754 jleave:
755 NYD_LEAVE;
756 return rv;
759 static void
760 rec_rmqueue(void)
762 struct record *rp;
763 NYD_ENTER;
765 for (rp = record; rp != NULL;) {
766 struct record *tmp = rp;
767 rp = rp->rec_next;
768 free(tmp);
770 record = recend = NULL;
771 NYD_LEAVE;
774 /*ARGSUSED*/
775 static void
776 imapalarm(int s)
778 sighandler_type volatile saveint, savepipe;
779 NYD_X; /* Signal handler */
780 UNUSED(s);
782 if (imaplock++ == 0) {
783 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
784 safe_signal(SIGINT, &_imap_maincatch);
785 savepipe = safe_signal(SIGPIPE, SIG_IGN);
786 if (sigsetjmp(imapjmp, 1)) {
787 safe_signal(SIGINT, saveint);
788 safe_signal(SIGPIPE, savepipe);
789 goto jbrk;
791 if (savepipe != SIG_IGN)
792 safe_signal(SIGPIPE, imapcatch);
793 if (imap_noop1(&mb) != OKAY) {
794 safe_signal(SIGINT, saveint);
795 safe_signal(SIGPIPE, savepipe);
796 goto jleave;
798 safe_signal(SIGINT, saveint);
799 safe_signal(SIGPIPE, savepipe);
801 jbrk:
802 alarm(imapkeepalive);
803 jleave:
804 --imaplock;
807 static int
808 imap_use_starttls(const char *uhp)
810 int rv;
811 NYD_ENTER;
813 if (ok_blook(imap_use_starttls))
814 rv = 1;
815 else {
816 char *var = savecat("imap-use-starttls-", uhp);
817 rv = vok_blook(var);
819 NYD_LEAVE;
820 return rv;
823 static enum okay
824 imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
826 char *cp;
827 NYD_X;
829 mp->mb_active |= MB_PREAUTH;
830 imap_answer(mp, 1);
831 if ((cp = strchr(xserver, ':')) != NULL) {
832 char *x = salloc(cp - xserver + 1);
833 memcpy(x, xserver, cp - xserver);
834 x[cp - xserver] = '\0';
835 xserver = x;
837 #ifdef HAVE_SSL
838 if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
839 FILE *queuefp = NULL;
840 char o[LINESIZE];
842 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
843 IMAP_OUT(o, MB_COMD, return STOP)
844 IMAP_ANSWER()
845 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
846 return STOP;
848 #else
849 if (imap_use_starttls(uhp)) {
850 fprintf(stderr, "No SSL support compiled in.\n");
851 return STOP;
853 #endif
854 imap_capability(mp);
855 return OKAY;
858 static enum okay
859 imap_capability(struct mailbox *mp)
861 char o[LINESIZE];
862 FILE *queuefp = NULL;
863 enum okay ok = STOP;
864 const char *cp;
865 NYD_X;
867 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
868 IMAP_OUT(o, MB_COMD, return STOP)
869 while (mp->mb_active & MB_COMD) {
870 ok = imap_answer(mp, 0);
871 if (response_status == RESPONSE_OTHER &&
872 response_other == CAPABILITY_DATA) {
873 cp = responded_other_text;
874 while (*cp) {
875 while (spacechar(*cp))
876 ++cp;
877 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
878 /* RFC 2359 */
879 mp->mb_flags |= MB_UIDPLUS;
880 while (*cp && !spacechar(*cp))
881 ++cp;
885 return ok;
888 static enum okay
889 imap_auth(struct mailbox *mp, struct ccred *ccred)
891 enum okay rv;
892 NYD_ENTER;
894 if (!(mp->mb_active & MB_PREAUTH)) {
895 rv = OKAY;
896 goto jleave;
899 switch (ccred->cc_authtype) {
900 case AUTHTYPE_LOGIN:
901 rv = imap_login(mp, ccred);
902 break;
903 #ifdef HAVE_MD5
904 case AUTHTYPE_CRAM_MD5:
905 rv = imap_cram_md5(mp, ccred);
906 break;
907 #endif
908 #ifdef HAVE_GSSAPI
909 case AUTHTYPE_GSSAPI:
910 rv = _imap_gssapi(mp, ccred);
911 break;
912 #endif
913 default:
914 rv = STOP;
915 break;
917 jleave:
918 NYD_LEAVE;
919 return rv;
922 #ifdef HAVE_MD5
923 static enum okay
924 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
926 char o[LINESIZE], *cp;
927 FILE *queuefp = NULL;
928 enum okay rv = STOP;
929 NYD_ENTER;
931 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
932 IMAP_XOUT(o, 0, goto jleave, goto jleave);
933 imap_answer(mp, 1);
934 if (response_type != RESPONSE_CONT)
935 goto jleave;
937 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
938 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
939 while (mp->mb_active & MB_COMD)
940 rv = imap_answer(mp, 1);
941 jleave:
942 NYD_LEAVE;
943 return rv;
945 #endif /* HAVE_MD5 */
947 static enum okay
948 imap_login(struct mailbox *mp, struct ccred *ccred)
950 char o[LINESIZE];
951 FILE *queuefp = NULL;
952 enum okay rv = STOP;
953 NYD_ENTER;
955 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
956 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
957 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
958 while (mp->mb_active & MB_COMD)
959 rv = imap_answer(mp, 1);
960 jleave:
961 NYD_LEAVE;
962 return rv;
965 #ifdef HAVE_GSSAPI
966 # include "imap_gssapi.h"
967 #endif
969 FL enum okay
970 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx)
972 enum okay ok = OKAY;
973 char const *cp;
974 char o[LINESIZE];
975 FILE *queuefp = NULL;
976 NYD_X;
977 UNUSED(size);
979 mp->mb_uidvalidity = 0;
980 snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
981 IMAP_OUT(o, MB_COMD, return STOP)
982 while (mp->mb_active & MB_COMD) {
983 ok = imap_answer(mp, 1);
984 if (response_status != RESPONSE_OTHER &&
985 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
986 mp->mb_uidvalidity = atol(&cp[13]);
988 *cnt = (had_exists > 0) ? had_exists : 0;
989 if (response_status != RESPONSE_OTHER &&
990 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
991 mp->mb_perm = 0;
992 return ok;
995 static enum okay
996 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
998 char o[LINESIZE];
999 FILE *queuefp = NULL;
1000 char const *cp;
1001 struct message *m;
1002 unsigned x = X, y = Y, n;
1003 NYD_X;
1005 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1006 IMAP_OUT(o, MB_COMD, return STOP)
1007 while (mp->mb_active & MB_COMD) {
1008 imap_answer(mp, 1);
1009 if (response_status == RESPONSE_OTHER &&
1010 response_other == MESSAGE_DATA_FETCH) {
1011 n = responded_other_number;
1012 if (n < x || n > y)
1013 continue;
1014 m = &message[n-1];
1015 m->m_xsize = 0;
1016 } else
1017 continue;
1019 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1020 cp += 5;
1021 while (*cp == ' ')
1022 cp++;
1023 if (*cp == '(')
1024 imap_getflags(cp, &cp, &m->m_flag);
1027 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1028 m->m_uid = strtoul(&cp[4], NULL, 10);
1029 getcache1(mp, m, NEED_UNSPEC, 1);
1030 m->m_flag &= ~MHIDDEN;
1033 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1034 x++;
1035 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1036 y--;
1037 if (x <= y) {
1038 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1039 tag(1), x, y);
1040 IMAP_OUT(o, MB_COMD, return STOP)
1041 while (mp->mb_active & MB_COMD) {
1042 imap_answer(mp, 1);
1043 if (response_status == RESPONSE_OTHER &&
1044 response_other == MESSAGE_DATA_FETCH) {
1045 n = responded_other_number;
1046 if (n < x || n > y)
1047 continue;
1048 m = &message[n-1];
1049 } else
1050 continue;
1051 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1052 m->m_xsize = strtol(&cp[12], NULL, 10);
1053 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1054 m->m_time = imap_read_date_time(&cp[13]);
1058 for (n = X; n <= Y; n++)
1059 putcache(mp, &message[n-1]);
1060 return OKAY;
1063 static void
1064 imap_init(struct mailbox *mp, int n)
1066 struct message *m;
1067 NYD_ENTER;
1068 UNUSED(mp);
1070 m = message + n;
1071 m->m_flag = MUSED | MNOFROM;
1072 m->m_block = 0;
1073 m->m_offset = 0;
1074 NYD_LEAVE;
1077 static void
1078 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1080 struct message *omessage = 0;
1081 int i, omsgCount = 0;
1082 enum okay dequeued = STOP;
1083 NYD_ENTER;
1085 if (nmail || transparent) {
1086 omessage = message;
1087 omsgCount = msgCount;
1089 if (nmail)
1090 dequeued = rec_dequeue();
1092 if (had_exists >= 0) {
1093 if (dequeued != OKAY)
1094 msgCount = had_exists;
1095 had_exists = -1;
1097 if (had_expunge >= 0) {
1098 if (dequeued != OKAY)
1099 msgCount -= had_expunge;
1100 had_expunge = -1;
1103 if (nmail && expunged_messages)
1104 printf("Expunged %ld message%s.\n", expunged_messages,
1105 (expunged_messages != 1 ? "s" : ""));
1106 *prevcount = omsgCount - expunged_messages;
1107 expunged_messages = 0;
1108 if (msgCount < 0) {
1109 fputs("IMAP error: Negative message count\n", stderr);
1110 msgCount = 0;
1113 if (dequeued != OKAY) {
1114 message = scalloc(msgCount + 1, sizeof *message);
1115 for (i = 0; i < msgCount; i++)
1116 imap_init(mp, i);
1117 if (!nmail && mp->mb_type == MB_IMAP)
1118 initcache(mp);
1119 if (msgCount > 0)
1120 imap_flags(mp, 1, msgCount);
1121 message[msgCount].m_size = 0;
1122 message[msgCount].m_lines = 0;
1123 rec_rmqueue();
1125 if (nmail || transparent)
1126 transflags(omessage, omsgCount, transparent);
1127 else
1128 setdot(message);
1129 NYD_LEAVE;
1132 FL int
1133 imap_setfile(const char *xserver, int nmail, int isedit)
1135 struct url url;
1136 int rv;
1137 NYD_ENTER;
1139 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1140 rv = 1;
1141 goto jleave;
1143 if (!ok_blook(v15_compat) &&
1144 (!url.url_had_user || url.url_pass.s != NULL))
1145 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
1147 rv = _imap_setfile1(&url, nmail, isedit, 0);
1148 jleave:
1149 NYD_LEAVE;
1150 return rv;
1153 static bool_t
1154 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1156 bool_t rv = FAL0;
1157 NYD_ENTER;
1159 if (ok_blook(v15_compat))
1160 rv = ccred_lookup(ccredp, urlp);
1161 else {
1162 char *var, *old;
1164 if ((var = mbp->mb_imap_pass) != NULL) {
1165 var = savecat("password-", urlp->url_u_h_p.s);
1166 old = vok_vlook(var);
1167 vok_vset(var, mbp->mb_imap_pass);
1169 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, urlp->url_u_h_p.s);
1170 if (var != NULL) {
1171 if (old != NULL)
1172 vok_vset(var, old);
1173 else
1174 vok_vclear(var);
1178 NYD_LEAVE;
1179 return rv;
1182 static int
1183 _imap_setfile1(struct url *urlp, int nmail, int isedit,
1184 int volatile transparent)
1186 struct sock so;
1187 struct ccred ccred;
1188 sighandler_type volatile saveint, savepipe;
1189 char const *cp;
1190 int rv;
1191 int volatile prevcount = 0;
1192 enum mbflags same_flags;
1193 NYD_ENTER;
1195 if (nmail) {
1196 saveint = safe_signal(SIGINT, SIG_IGN);
1197 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1198 if (saveint != SIG_IGN)
1199 safe_signal(SIGINT, imapcatch);
1200 if (savepipe != SIG_IGN)
1201 safe_signal(SIGPIPE, imapcatch);
1202 imaplock = 1;
1203 goto jnmail;
1206 same_flags = mb.mb_flags;
1207 same_imap_account = 0;
1208 if (mb.mb_imap_account != NULL &&
1209 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1210 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1211 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1212 disconnected(mb.mb_imap_account) == 0) {
1213 same_imap_account = 1;
1214 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1215 goto jduppass;
1216 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1217 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1218 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1219 jduppass:
1220 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1223 if (!_imap_getcred(&mb, &ccred, urlp)) {
1224 rv = -1;
1225 goto jleave;
1228 so.s_fd = -1;
1229 if (!same_imap_account) {
1230 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1231 rv = -1;
1232 goto jleave;
1234 } else
1235 so = mb.mb_sock;
1236 if (!transparent)
1237 quit();
1239 edit = (isedit != 0);
1240 if (mb.mb_imap_account != NULL)
1241 free(mb.mb_imap_account);
1242 if (mb.mb_imap_pass != NULL)
1243 free(mb.mb_imap_pass);
1244 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1245 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1246 * TODO object, and mailbox will naturally have an URL and credentials */
1247 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1249 if (!same_imap_account) {
1250 if (mb.mb_sock.s_fd >= 0)
1251 sclose(&mb.mb_sock);
1253 same_imap_account = 0;
1255 if (!transparent) {
1256 if (mb.mb_itf) {
1257 fclose(mb.mb_itf);
1258 mb.mb_itf = NULL;
1260 if (mb.mb_otf) {
1261 fclose(mb.mb_otf);
1262 mb.mb_otf = NULL;
1264 if (mb.mb_imap_mailbox != NULL)
1265 free(mb.mb_imap_mailbox);
1266 mb.mb_imap_mailbox = sstrdup((urlp->url_path.s != NULL)
1267 ? urlp->url_path.s : "INBOX");
1268 initbox(urlp->url_p_eu_h_p_p);
1270 mb.mb_type = MB_VOID;
1271 mb.mb_active = MB_NONE;
1273 imaplock = 1;
1274 saveint = safe_signal(SIGINT, SIG_IGN);
1275 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1276 if (sigsetjmp(imapjmp, 1)) {
1277 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1278 sclose(&mb.mb_sock);
1279 safe_signal(SIGINT, saveint);
1280 safe_signal(SIGPIPE, savepipe);
1281 imaplock = 0;
1283 mb.mb_type = MB_VOID;
1284 mb.mb_active = MB_NONE;
1285 rv = -1;
1286 goto jleave;
1288 if (saveint != SIG_IGN)
1289 safe_signal(SIGINT, imapcatch);
1290 if (savepipe != SIG_IGN)
1291 safe_signal(SIGPIPE, imapcatch);
1293 if (mb.mb_sock.s_fd < 0) {
1294 if (disconnected(mb.mb_imap_account)) {
1295 if (cache_setptr(transparent) == STOP)
1296 fprintf(stderr, "Mailbox \"%s\" is not cached.\n",
1297 urlp->url_p_eu_h_p_p);
1298 goto jdone;
1300 if ((cp = ok_vlook(imap_keepalive)) != NULL) {
1301 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1302 savealrm = safe_signal(SIGALRM, imapalarm);
1303 alarm(imapkeepalive);
1307 mb.mb_sock = so;
1308 mb.mb_sock.s_desc = "IMAP";
1309 mb.mb_sock.s_onclose = imap_timer_off;
1310 if (imap_preauth(&mb, urlp->url_h_p.s, urlp->url_u_h_p.s) != OKAY ||
1311 imap_auth(&mb, &ccred) != OKAY) {
1312 sclose(&mb.mb_sock);
1313 imap_timer_off();
1314 safe_signal(SIGINT, saveint);
1315 safe_signal(SIGPIPE, savepipe);
1316 imaplock = 0;
1317 rv = -1;
1318 goto jleave;
1320 } else /* same account */
1321 mb.mb_flags |= same_flags;
1323 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
1324 mb.mb_type = MB_IMAP;
1325 cache_dequeue(&mb);
1326 if (imap_select(&mb, &mailsize, &msgCount,
1327 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX")) != OKAY) {
1328 /*sclose(&mb.mb_sock);
1329 imap_timer_off();*/
1330 safe_signal(SIGINT, saveint);
1331 safe_signal(SIGPIPE, savepipe);
1332 imaplock = 0;
1333 mb.mb_type = MB_VOID;
1334 rv = -1;
1335 goto jleave;
1338 jnmail:
1339 imap_setptr(&mb, nmail, transparent, UNVOLATILE(&prevcount));
1340 jdone:
1341 setmsize(msgCount);
1342 if (!nmail && !transparent)
1343 sawcom = FAL0;
1344 safe_signal(SIGINT, saveint);
1345 safe_signal(SIGPIPE, savepipe);
1346 imaplock = 0;
1348 if (!nmail && mb.mb_type == MB_IMAP)
1349 purgecache(&mb, message, msgCount);
1350 if ((nmail || transparent) && mb.mb_sorted) {
1351 mb.mb_threaded = 0;
1352 c_sort((void*)-1);
1355 if (!nmail && !edit && msgCount == 0) {
1356 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1357 !ok_blook(emptystart))
1358 fprintf(stderr, _("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1359 rv = 1;
1360 goto jleave;
1362 if (nmail)
1363 newmailinfo(prevcount);
1364 rv = 0;
1365 jleave:
1366 NYD_LEAVE;
1367 return rv;
1370 static int
1371 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1372 int need, const char *head, size_t headsize, long headlines)
1374 char *line = NULL, *lp;
1375 size_t linesize = 0, linelen, size = 0;
1376 int emptyline = 0, lines = 0, excess = 0;
1377 off_t offset;
1378 NYD_ENTER;
1380 fseek(mp->mb_otf, 0L, SEEK_END);
1381 offset = ftell(mp->mb_otf);
1383 if (head)
1384 fwrite(head, 1, headsize, mp->mb_otf);
1386 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1387 lp = line;
1388 if (linelen > expected) {
1389 excess = linelen - expected;
1390 linelen = expected;
1392 /* TODO >>
1393 * Need to mask 'From ' lines. This cannot be done properly
1394 * since some servers pass them as 'From ' and others as
1395 * '>From '. Although one could identify the first kind of
1396 * server in principle, it is not possible to identify the
1397 * second as '>From ' may also come from a server of the
1398 * first type as actual data. So do what is absolutely
1399 * necessary only - mask 'From '.
1401 * If the line is the first line of the message header, it
1402 * is likely a real 'From ' line. In this case, it is just
1403 * ignored since it violates all standards.
1404 * TODO can the latter *really* happen??
1405 * TODO <<
1407 /* Since we simply copy over data without doing any transfer
1408 * encoding reclassification/adjustment we *have* to perform
1409 * RFC 4155 compliant From_ quoting here */
1410 if (is_head(lp, linelen)) {
1411 if (lines + headlines == 0)
1412 goto jskip;
1413 fputc('>', mp->mb_otf);
1414 ++size;
1416 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1417 emptyline = linelen <= 2;
1418 if (linelen > 2) {
1419 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1420 size += linelen - 1;
1421 } else
1422 ++size;
1423 fputc('\n', mp->mb_otf);
1424 } else {
1425 emptyline = 0;
1426 fwrite(lp, 1, linelen, mp->mb_otf);
1427 size += linelen;
1429 ++lines;
1430 jskip:
1431 if ((expected -= linelen) <= 0)
1432 break;
1434 if (!emptyline) {
1435 /* This is very ugly; but some IMAP daemons don't end a
1436 * message with \r\n\r\n, and we need \n\n for mbox format */
1437 fputc('\n', mp->mb_otf);
1438 ++lines;
1439 ++size;
1441 fflush(mp->mb_otf);
1443 if (m != NULL) {
1444 m->m_size = size + headsize;
1445 m->m_lines = lines + headlines;
1446 m->m_block = mailx_blockof(offset);
1447 m->m_offset = mailx_offsetof(offset);
1448 switch (need) {
1449 case NEED_HEADER:
1450 m->m_have |= HAVE_HEADER;
1451 break;
1452 case NEED_BODY:
1453 m->m_have |= HAVE_HEADER | HAVE_BODY;
1454 m->m_xlines = m->m_lines;
1455 m->m_xsize = m->m_size;
1456 break;
1459 free(line);
1460 NYD_LEAVE;
1461 return excess;
1464 static void
1465 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1466 const char *head, size_t headsize, long headlines)
1468 off_t offset;
1469 size_t len;
1470 NYD_ENTER;
1472 len = strlen(str);
1473 fseek(mp->mb_otf, 0L, SEEK_END);
1474 offset = ftell(mp->mb_otf);
1475 if (head)
1476 fwrite(head, 1, headsize, mp->mb_otf);
1477 if (len > 0) {
1478 fwrite(str, 1, len, mp->mb_otf);
1479 fputc('\n', mp->mb_otf);
1480 ++len;
1482 fflush(mp->mb_otf);
1484 if (m != NULL) {
1485 m->m_size = headsize + len;
1486 m->m_lines = headlines + 1;
1487 m->m_block = mailx_blockof(offset);
1488 m->m_offset = mailx_offsetof(offset);
1489 m->m_have |= HAVE_HEADER | HAVE_BODY;
1490 m->m_xlines = m->m_lines;
1491 m->m_xsize = m->m_size;
1493 NYD_LEAVE;
1496 static enum okay
1497 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1499 char o[LINESIZE];
1500 struct message mt;
1501 sighandler_type volatile saveint, savepipe;
1502 char * volatile head;
1503 char const *cp, *loc, * volatile item, * volatile resp;
1504 size_t expected;
1505 size_t volatile headsize;
1506 int number;
1507 FILE *queuefp;
1508 long volatile headlines;
1509 long n;
1510 ul_it volatile u;
1511 enum okay ok;
1512 NYD_X;
1514 saveint = savepipe = SIG_IGN;
1515 head = NULL;
1516 cp = loc = item = resp = NULL;
1517 headsize = 0;
1518 number = (int)PTR2SIZE(m - message + 1);
1519 queuefp = NULL;
1520 headlines = 0;
1521 n = -1;
1522 u = 0;
1523 ok = STOP;
1525 if (getcache(mp, m, need) == OKAY)
1526 return OKAY;
1527 if (mp->mb_type == MB_CACHE) {
1528 fprintf(stderr, "Message %u not available.\n", number);
1529 return STOP;
1532 if (mp->mb_sock.s_fd < 0) {
1533 fprintf(stderr, "IMAP connection closed.\n");
1534 return STOP;
1537 switch (need) {
1538 case NEED_HEADER:
1539 resp = item = "RFC822.HEADER";
1540 break;
1541 case NEED_BODY:
1542 item = "BODY.PEEK[]";
1543 resp = "BODY[]";
1544 if (m->m_flag & HAVE_HEADER && m->m_size) {
1545 char *hdr = smalloc(m->m_size);
1546 fflush(mp->mb_otf);
1547 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1548 SEEK_SET) < 0 ||
1549 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1550 free(hdr);
1551 break;
1553 head = hdr;
1554 headsize = m->m_size;
1555 headlines = m->m_lines;
1556 item = "BODY.PEEK[TEXT]";
1557 resp = "BODY[TEXT]";
1559 break;
1560 case NEED_UNSPEC:
1561 return STOP;
1564 imaplock = 1;
1565 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1566 if (sigsetjmp(imapjmp, 1)) {
1567 safe_signal(SIGINT, saveint);
1568 safe_signal(SIGPIPE, savepipe);
1569 imaplock = 0;
1570 return STOP;
1572 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1573 safe_signal(SIGINT, &_imap_maincatch);
1574 if (savepipe != SIG_IGN)
1575 safe_signal(SIGPIPE, imapcatch);
1577 if (m->m_uid)
1578 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1579 tag(1), m->m_uid, item);
1580 else {
1581 if (check_expunged() == STOP)
1582 goto out;
1583 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1585 IMAP_OUT(o, MB_COMD, goto out)
1586 for (;;) {
1587 ok = imap_answer(mp, 1);
1588 if (ok == STOP)
1589 break;
1590 if (response_status != RESPONSE_OTHER ||
1591 response_other != MESSAGE_DATA_FETCH)
1592 continue;
1593 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1594 continue;
1595 if (m->m_uid) {
1596 if ((cp = asccasestr(responded_other_text, "UID "))) {
1597 u = atol(&cp[4]);
1598 n = 0;
1599 } else {
1600 n = -1;
1601 u = 0;
1603 } else
1604 n = responded_other_number;
1605 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1606 if (m->m_uid ? m->m_uid != u : n != number)
1607 continue;
1608 if ((cp = strchr(loc, '"')) != NULL) {
1609 cp = imap_unquotestr(cp);
1610 imap_putstr(mp, m, cp, head, headsize, headlines);
1611 } else {
1612 m->m_have |= HAVE_HEADER|HAVE_BODY;
1613 m->m_xlines = m->m_lines;
1614 m->m_xsize = m->m_size;
1616 goto out;
1618 expected = atol(&cp[1]);
1619 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1620 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1621 continue;
1623 mt = *m;
1624 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1625 if (n >= 0) {
1626 commitmsg(mp, m, &mt, mt.m_have);
1627 break;
1629 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1630 if (options & OPT_VERBVERB)
1631 fputs(imapbuf, stderr);
1632 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1633 u = atol(&cp[4]);
1634 if (u == m->m_uid) {
1635 commitmsg(mp, m, &mt, mt.m_have);
1636 break;
1641 out:
1642 while (mp->mb_active & MB_COMD)
1643 ok = imap_answer(mp, 1);
1645 if (saveint != SIG_IGN)
1646 safe_signal(SIGINT, saveint);
1647 if (savepipe != SIG_IGN)
1648 safe_signal(SIGPIPE, savepipe);
1649 imaplock--;
1651 if (ok == OKAY)
1652 putcache(mp, m);
1653 if (head != NULL)
1654 free(head);
1655 if (interrupts)
1656 onintr(0);
1657 return ok;
1660 FL enum okay
1661 imap_header(struct message *m)
1663 enum okay rv;
1664 NYD_ENTER;
1666 rv = imap_get(&mb, m, NEED_HEADER);
1667 NYD_LEAVE;
1668 return rv;
1672 FL enum okay
1673 imap_body(struct message *m)
1675 enum okay rv;
1676 NYD_ENTER;
1678 rv = imap_get(&mb, m, NEED_BODY);
1679 NYD_LEAVE;
1680 return rv;
1683 static void
1684 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1685 enum havespec have)
1687 NYD_ENTER;
1688 tomp->m_size = frommp->m_size;
1689 tomp->m_lines = frommp->m_lines;
1690 tomp->m_block = frommp->m_block;
1691 tomp->m_offset = frommp->m_offset;
1692 tomp->m_have = have;
1693 if (have & HAVE_BODY) {
1694 tomp->m_xlines = frommp->m_lines;
1695 tomp->m_xsize = frommp->m_size;
1697 putcache(mp, tomp);
1698 NYD_LEAVE;
1701 static enum okay
1702 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1704 /* bot > topp */
1705 char o[LINESIZE];
1706 char const *cp;
1707 struct message mt;
1708 size_t expected;
1709 int n = 0, u;
1710 FILE *queuefp = NULL;
1711 enum okay ok;
1712 NYD_X;
1714 if (m[bot].m_uid)
1715 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1716 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1717 else {
1718 if (check_expunged() == STOP)
1719 return STOP;
1720 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1721 tag(1), bot, topp);
1723 IMAP_OUT(o, MB_COMD, return STOP)
1724 for (;;) {
1725 ok = imap_answer(mp, 1);
1726 if (response_status != RESPONSE_OTHER)
1727 break;
1728 if (response_other != MESSAGE_DATA_FETCH)
1729 continue;
1730 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
1731 return STOP;
1732 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1733 continue;
1734 expected = atol(&cp[1]);
1735 if (m[bot-1].m_uid) {
1736 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1737 u = atoi(&cp[4]);
1738 for (n = bot; n <= topp; n++)
1739 if ((unsigned long)u == m[n-1].m_uid)
1740 break;
1741 if (n > topp) {
1742 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1743 continue;
1745 } else
1746 n = -1;
1747 } else {
1748 n = responded_other_number;
1749 if (n <= 0 || n > msgCount) {
1750 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1751 continue;
1754 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1755 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1756 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1757 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1758 if (options & OPT_VERBVERB)
1759 fputs(imapbuf, stderr);
1760 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1761 u = atoi(&cp[4]);
1762 for (n = bot; n <= topp; n++)
1763 if ((unsigned long)u == m[n-1].m_uid)
1764 break;
1765 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1766 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1767 n = 0;
1771 while (mp->mb_active & MB_COMD)
1772 ok = imap_answer(mp, 1);
1773 return ok;
1776 FL void
1777 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1779 sighandler_type saveint, savepipe;
1780 /* enum okay ok = STOP;*/
1781 int i, chunk = 256;
1782 NYD_X;
1784 if (mb.mb_type == MB_CACHE)
1785 return;
1786 if (bot < 1)
1787 bot = 1;
1788 if (topp > msgCount)
1789 topp = msgCount;
1790 for (i = bot; i < topp; i++) {
1791 if (message[i-1].m_have & HAVE_HEADER ||
1792 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1793 bot = i+1;
1794 else
1795 break;
1797 for (i = topp; i > bot; i--) {
1798 if (message[i-1].m_have & HAVE_HEADER ||
1799 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1800 topp = i-1;
1801 else
1802 break;
1804 if (bot >= topp)
1805 return;
1807 imaplock = 1;
1808 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1809 safe_signal(SIGINT, &_imap_maincatch);
1810 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1811 if (sigsetjmp(imapjmp, 1) == 0) {
1812 if (savepipe != SIG_IGN)
1813 safe_signal(SIGPIPE, imapcatch);
1815 for (i = bot; i <= topp; i += chunk) {
1816 int j = i + chunk - 1;
1817 j = MIN(j, topp);
1818 if (visible(message + j))
1819 /*ok = */imap_fetchheaders(&mb, message, i, j);
1820 if (interrupts)
1821 onintr(0); /* XXX imaplock? */
1824 safe_signal(SIGINT, saveint);
1825 safe_signal(SIGPIPE, savepipe);
1826 imaplock = 0;
1829 static enum okay
1830 __imap_exit(struct mailbox *mp)
1832 char o[LINESIZE];
1833 FILE *queuefp = NULL;
1834 NYD_X;
1836 mp->mb_active |= MB_BYE;
1837 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1838 IMAP_OUT(o, MB_COMD, return STOP)
1839 IMAP_ANSWER()
1840 return OKAY;
1843 static enum okay
1844 imap_exit(struct mailbox *mp)
1846 enum okay rv;
1847 NYD_ENTER;
1849 rv = __imap_exit(mp);
1850 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1851 free(mp->mb_imap_pass);
1852 free(mp->mb_imap_account);
1853 free(mp->mb_imap_mailbox);
1854 if (mp->mb_cache_directory != NULL)
1855 free(mp->mb_cache_directory);
1856 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1857 mp->mb_imap_account =
1858 mp->mb_imap_mailbox =
1859 mp->mb_cache_directory = "";
1860 #else
1861 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1862 mp->mb_imap_mailbox = NULL;
1863 mp->mb_cache_directory = NULL;
1864 #endif
1865 #endif
1866 sclose(&mp->mb_sock);
1867 NYD_LEAVE;
1868 return rv;
1871 static enum okay
1872 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1874 NYD_ENTER;
1875 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1876 if (mp->mb_type == MB_IMAP)
1877 delcache(mp, m);
1878 NYD_LEAVE;
1879 return OKAY;
1882 static enum okay
1883 imap_close(struct mailbox *mp)
1885 char o[LINESIZE];
1886 FILE *queuefp = NULL;
1887 NYD_X;
1889 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1890 IMAP_OUT(o, MB_COMD, return STOP)
1891 IMAP_ANSWER()
1892 return OKAY;
1895 static enum okay
1896 imap_update(struct mailbox *mp)
1898 struct message *m;
1899 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1900 NYD_ENTER;
1902 if (!edit && mp->mb_perm != 0) {
1903 holdbits();
1904 c = 0;
1905 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1906 if (m->m_flag & MBOX)
1907 ++c;
1908 if (c > 0)
1909 if (makembox() == STOP)
1910 goto jbypass;
1913 gotcha = held = 0;
1914 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1915 if (mp->mb_perm == 0)
1916 dodel = 0;
1917 else if (edit)
1918 dodel = ((m->m_flag & MDELETED) != 0);
1919 else
1920 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1922 /* Fetch the result after around each 800 STORE commands
1923 * sent (approx. 32k data sent). Otherwise, servers will
1924 * try to flush the return queue at some point, leading
1925 * to a deadlock if we are still writing commands but not
1926 * reading their results */
1927 needstat = stored > 0 && stored % 800 == 0;
1928 /* Even if this message has been deleted, continue
1929 * to set further flags. This is necessary to support
1930 * Gmail semantics, where "delete" actually means
1931 * "archive", and the flags are applied to the copy
1932 * in "All Mail" */
1933 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1934 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1935 stored++;
1937 if (m->m_flag & MFLAG) {
1938 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1939 stored++;
1941 if (m->m_flag & MUNFLAG) {
1942 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1943 stored++;
1945 if (m->m_flag & MANSWER) {
1946 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1947 stored++;
1949 if (m->m_flag & MUNANSWER) {
1950 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1951 stored++;
1953 if (m->m_flag & MDRAFT) {
1954 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1955 stored++;
1957 if (m->m_flag & MUNDRAFT) {
1958 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1959 stored++;
1962 if (dodel) {
1963 imap_delete(mp, m-message+1, m, needstat);
1964 stored++;
1965 gotcha++;
1966 } else if (mp->mb_type != MB_CACHE ||
1967 (!edit && !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1968 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1969 (MPRESERVE | MTOUCH) || (edit && !(m->m_flag & MDELETED)))
1970 held++;
1971 if (m->m_flag & MNEW) {
1972 m->m_flag &= ~MNEW;
1973 m->m_flag |= MSTATUS;
1976 jbypass:
1977 if (gotcha)
1978 imap_close(mp);
1980 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1981 if (!(m->m_flag & MUNLINKED) &&
1982 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1983 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1984 putcache(mp, m);
1985 modflags++;
1987 if ((gotcha || modflags) && edit) {
1988 printf(_("\"%s\" "), displayname);
1989 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1990 ? _("complete\n") : _("updated.\n"));
1991 } else if (held && !edit && mp->mb_perm != 0) {
1992 if (held == 1)
1993 printf(_("Held 1 message in %s\n"), displayname);
1994 else
1995 printf(_("Held %d messages in %s\n"), held, displayname);
1997 fflush(stdout);
1998 NYD_LEAVE;
1999 return OKAY;
2002 FL void
2003 imap_quit(void)
2005 sighandler_type volatile saveint, savepipe;
2006 NYD_ENTER;
2008 if (mb.mb_type == MB_CACHE) {
2009 imap_update(&mb);
2010 goto jleave;
2013 if (mb.mb_sock.s_fd < 0) {
2014 fprintf(stderr, "IMAP connection closed.\n");
2015 goto jleave;
2018 imaplock = 1;
2019 saveint = safe_signal(SIGINT, SIG_IGN);
2020 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2021 if (sigsetjmp(imapjmp, 1)) {
2022 safe_signal(SIGINT, saveint);
2023 safe_signal(SIGPIPE, saveint);
2024 imaplock = 0;
2025 goto jleave;
2027 if (saveint != SIG_IGN)
2028 safe_signal(SIGINT, imapcatch);
2029 if (savepipe != SIG_IGN)
2030 safe_signal(SIGPIPE, imapcatch);
2032 imap_update(&mb);
2033 if (!same_imap_account)
2034 imap_exit(&mb);
2036 safe_signal(SIGINT, saveint);
2037 safe_signal(SIGPIPE, savepipe);
2038 imaplock = 0;
2039 jleave:
2040 NYD_LEAVE;
2043 static enum okay
2044 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2045 int needstat)
2047 char o[LINESIZE];
2048 FILE *queuefp = NULL;
2049 NYD_X;
2051 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2052 return STOP;
2053 if (m->m_uid)
2054 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2055 tag(1), m->m_uid, c, sp);
2056 else {
2057 if (check_expunged() == STOP)
2058 return STOP;
2059 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2061 IMAP_OUT(o, MB_COMD, return STOP)
2062 if (needstat)
2063 IMAP_ANSWER()
2064 else
2065 mb.mb_active &= ~MB_COMD;
2066 if (queuefp != NULL)
2067 Fclose(queuefp);
2068 return OKAY;
2071 FL enum okay
2072 imap_undelete(struct message *m, int n)
2074 enum okay rv;
2075 NYD_ENTER;
2077 rv = imap_unstore(m, n, "\\Deleted");
2078 NYD_LEAVE;
2079 return rv;
2082 FL enum okay
2083 imap_unread(struct message *m, int n)
2085 enum okay rv;
2086 NYD_ENTER;
2088 rv = imap_unstore(m, n, "\\Seen");
2089 NYD_LEAVE;
2090 return rv;
2093 static enum okay
2094 imap_unstore(struct message *m, int n, const char *flag)
2096 sighandler_type saveint, savepipe;
2097 enum okay rv = STOP;
2098 NYD_ENTER;
2100 imaplock = 1;
2101 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2102 safe_signal(SIGINT, &_imap_maincatch);
2103 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2104 if (sigsetjmp(imapjmp, 1) == 0) {
2105 if (savepipe != SIG_IGN)
2106 safe_signal(SIGPIPE, imapcatch);
2108 rv = imap_store(&mb, m, n, '-', flag, 1);
2110 safe_signal(SIGINT, saveint);
2111 safe_signal(SIGPIPE, savepipe);
2112 imaplock = 0;
2114 NYD_LEAVE;
2115 if (interrupts)
2116 onintr(0);
2117 return rv;
2120 static const char *
2121 tag(int new)
2123 static char ts[20];
2124 static long n;
2125 NYD_ENTER;
2127 if (new)
2128 ++n;
2129 snprintf(ts, sizeof ts, "T%lu", n);
2130 NYD_LEAVE;
2131 return ts;
2134 FL int
2135 c_imap_imap(void *vp)
2137 char o[LINESIZE];
2138 sighandler_type saveint, savepipe;
2139 struct mailbox *mp = &mb;
2140 FILE *queuefp = NULL;
2141 enum okay volatile ok = STOP;
2142 NYD_X;
2144 if (mp->mb_type != MB_IMAP) {
2145 printf("Not operating on an IMAP mailbox.\n");
2146 return 1;
2148 imaplock = 1;
2149 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2150 safe_signal(SIGINT, &_imap_maincatch);
2151 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2152 if (sigsetjmp(imapjmp, 1) == 0) {
2153 if (savepipe != SIG_IGN)
2154 safe_signal(SIGPIPE, imapcatch);
2156 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2157 IMAP_OUT(o, MB_COMD, goto out)
2158 while (mp->mb_active & MB_COMD) {
2159 ok = imap_answer(mp, 0);
2160 fputs(responded_text, stdout);
2163 out:
2164 safe_signal(SIGINT, saveint);
2165 safe_signal(SIGPIPE, savepipe);
2166 imaplock = 0;
2168 if (interrupts)
2169 onintr(0);
2170 return ok != OKAY;
2173 FL int
2174 imap_newmail(int nmail)
2176 NYD_ENTER;
2178 if (nmail && had_exists < 0 && had_expunge < 0) {
2179 imaplock = 1;
2180 imap_noop();
2181 imaplock = 0;
2184 if (had_exists == msgCount && had_expunge < 0)
2185 /* Some servers always respond with EXISTS to NOOP. If
2186 * the mailbox has been changed but the number of messages
2187 * has not, an EXPUNGE must also had been sent; otherwise,
2188 * nothing has changed */
2189 had_exists = -1;
2190 NYD_LEAVE;
2191 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2194 static char *
2195 imap_putflags(int f)
2197 const char *cp;
2198 char *buf, *bp;
2199 NYD_ENTER;
2201 bp = buf = salloc(100);
2202 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2203 *bp++ = '(';
2204 if (f & MREAD) {
2205 if (bp[-1] != '(')
2206 *bp++ = ' ';
2207 for (cp = "\\Seen"; *cp; cp++)
2208 *bp++ = *cp;
2210 if (f & MFLAGGED) {
2211 if (bp[-1] != '(')
2212 *bp++ = ' ';
2213 for (cp = "\\Flagged"; *cp; cp++)
2214 *bp++ = *cp;
2216 if (f & MANSWERED) {
2217 if (bp[-1] != '(')
2218 *bp++ = ' ';
2219 for (cp = "\\Answered"; *cp; cp++)
2220 *bp++ = *cp;
2222 if (f & MDRAFT) {
2223 if (bp[-1] != '(')
2224 *bp++ = ' ';
2225 for (cp = "\\Draft"; *cp; cp++)
2226 *bp++ = *cp;
2228 *bp++ = ')';
2229 *bp++ = ' ';
2231 *bp = '\0';
2232 NYD_LEAVE;
2233 return buf;
2236 static void
2237 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2239 NYD_ENTER;
2240 while (*cp != ')') {
2241 if (*cp == '\\') {
2242 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2243 *f |= MREAD;
2244 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2245 *f |= MNEW;
2246 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2247 *f |= MDELETED;
2248 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2249 *f |= MFLAGGED;
2250 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2251 *f |= MANSWERED;
2252 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2253 *f |= MDRAFTED;
2255 cp++;
2258 if (xp != NULL)
2259 *xp = cp;
2260 NYD_LEAVE;
2263 static enum okay
2264 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2265 long xsize, enum mflag flag, time_t t)
2267 char o[LINESIZE], *buf;
2268 size_t bufsize, buflen, cnt;
2269 long size, lines, ysize;
2270 int twice = 0;
2271 FILE *queuefp = NULL;
2272 enum okay rv;
2273 NYD_ENTER;
2275 if (mp->mb_type == MB_CACHE) {
2276 queuefp = cache_queue(mp);
2277 if (queuefp == NULL) {
2278 rv = STOP;
2279 buf = NULL;
2280 goto jleave;
2282 rv = OKAY;
2283 } else
2284 rv = STOP;
2286 buf = smalloc(bufsize = LINESIZE);
2287 buflen = 0;
2288 jagain:
2289 size = xsize;
2290 cnt = fsize(fp);
2291 if (fseek(fp, off1, SEEK_SET) < 0) {
2292 rv = STOP;
2293 goto jleave;
2296 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2297 tag(1), imap_quotestr(name), imap_putflags(flag),
2298 imap_make_date_time(t), size);
2299 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2300 while (mp->mb_active & MB_COMD) {
2301 rv = imap_answer(mp, twice);
2302 if (response_type == RESPONSE_CONT)
2303 break;
2306 if (mp->mb_type != MB_CACHE && rv == STOP) {
2307 if (twice == 0)
2308 goto jtrycreate;
2309 else
2310 goto jleave;
2313 lines = ysize = 0;
2314 while (size > 0) {
2315 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2316 lines++;
2317 ysize += buflen;
2318 buf[buflen - 1] = '\r';
2319 buf[buflen] = '\n';
2320 if (mp->mb_type != MB_CACHE)
2321 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2322 else if (queuefp)
2323 fwrite(buf, 1, buflen+1, queuefp);
2324 size -= buflen + 1;
2326 if (mp->mb_type != MB_CACHE)
2327 swrite(&mp->mb_sock, "\r\n");
2328 else if (queuefp)
2329 fputs("\r\n", queuefp);
2330 while (mp->mb_active & MB_COMD) {
2331 rv = imap_answer(mp, 0);
2332 if (response_status == RESPONSE_NO /*&&
2333 ascncasecmp(responded_text,
2334 "[TRYCREATE] ", 12) == 0*/) {
2335 jtrycreate:
2336 if (twice++) {
2337 rv = STOP;
2338 goto jleave;
2340 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2341 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2342 while (mp->mb_active & MB_COMD)
2343 rv = imap_answer(mp, 1);
2344 if (rv == STOP)
2345 goto jleave;
2346 imap_created_mailbox++;
2347 goto jagain;
2348 } else if (rv != OKAY)
2349 fprintf(stderr, _("IMAP error: %s"), responded_text);
2350 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2351 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2353 jleave:
2354 if (queuefp != NULL)
2355 Fclose(queuefp);
2356 if (buf != NULL)
2357 free(buf);
2358 NYD_LEAVE;
2359 return rv;
2362 static enum okay
2363 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2365 char *buf, *bp, *lp;
2366 size_t bufsize, buflen, cnt;
2367 off_t off1 = -1, offs;
2368 int inhead = 1, flag = MNEW | MNEWEST;
2369 long size = 0;
2370 time_t tim;
2371 enum okay rv;
2372 NYD_ENTER;
2374 buf = smalloc(bufsize = LINESIZE);
2375 buflen = 0;
2376 cnt = fsize(fp);
2377 offs = ftell(fp);
2378 time(&tim);
2380 do {
2381 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2382 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
2383 if (off1 != (off_t)-1) {
2384 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2385 if (rv == STOP)
2386 goto jleave;
2387 fseek(fp, offs+buflen, SEEK_SET);
2389 off1 = offs + buflen;
2390 size = 0;
2391 inhead = 1;
2392 flag = MNEW;
2393 if (bp != NULL)
2394 tim = unixtime(buf);
2395 } else
2396 size += buflen+1;
2397 offs += buflen;
2399 if (bp && buf[0] == '\n')
2400 inhead = 0;
2401 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
2402 lp = &buf[6];
2403 while (whitechar(*lp))
2404 lp++;
2405 if (*lp == ':')
2406 while (*++lp != '\0')
2407 switch (*lp) {
2408 case 'R':
2409 flag |= MREAD;
2410 break;
2411 case 'O':
2412 flag &= ~MNEW;
2413 break;
2415 } else if (bp && inhead && ascncasecmp(buf, "x-status", 8) == 0) {
2416 lp = &buf[8];
2417 while (whitechar(*lp))
2418 lp++;
2419 if (*lp == ':')
2420 while (*++lp != '\0')
2421 switch (*lp) {
2422 case 'F':
2423 flag |= MFLAGGED;
2424 break;
2425 case 'A':
2426 flag |= MANSWERED;
2427 break;
2428 case 'T':
2429 flag |= MDRAFTED;
2430 break;
2433 } while (bp != NULL);
2434 rv = OKAY;
2435 jleave:
2436 free(buf);
2437 NYD_LEAVE;
2438 return rv;
2441 FL enum okay
2442 imap_append(const char *xserver, FILE *fp)
2444 sighandler_type volatile saveint, savepipe;
2445 struct url url;
2446 struct ccred ccred;
2447 char const * volatile mbx;
2448 enum okay rv = STOP;
2449 NYD_ENTER;
2451 if (!url_parse(&url, CPROTO_IMAP, xserver))
2452 goto j_leave;
2453 if (!ok_blook(v15_compat) &&
2454 (!url.url_had_user || url.url_pass.s != NULL))
2455 fprintf(stderr, "New-style URL used without *v15-compat* being set!\n");
2456 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2458 imaplock = 1;
2459 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2460 safe_signal(SIGINT, &_imap_maincatch);
2461 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2462 if (sigsetjmp(imapjmp, 1))
2463 goto jleave;
2464 if (savepipe != SIG_IGN)
2465 safe_signal(SIGPIPE, imapcatch);
2467 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2468 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
2469 rv = imap_append0(&mb, mbx, fp);
2470 } else {
2471 struct mailbox mx;
2473 memset(&mx, 0, sizeof mx);
2475 if (!_imap_getcred(&mx, &ccred, &url))
2476 goto jleave;
2478 if (disconnected(url.url_p_eu_h_p) == 0) {
2479 if (!sopen(&mx.mb_sock, &url))
2480 goto jfail;
2481 mx.mb_sock.s_desc = "IMAP";
2482 mx.mb_type = MB_IMAP;
2483 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2484 /* TODO the code now did
2485 * TODO mx.mb_imap_mailbox = mbx;
2486 * TODO though imap_mailbox is sfree()d and mbx
2487 * TODO is possibly even a constant
2488 * TODO i changed this to sstrdup() sofar, as is used
2489 * TODO somewhere else in this file for this! */
2490 mx.mb_imap_mailbox = sstrdup(mbx);
2491 if (imap_preauth(&mx, url.url_h_p.s, url.url_u_h_p.s) != OKAY ||
2492 imap_auth(&mx, &ccred) != OKAY) {
2493 sclose(&mx.mb_sock);
2494 goto jfail;
2496 rv = imap_append0(&mx, mbx, fp);
2497 imap_exit(&mx);
2498 } else {
2499 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2500 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2501 mx.mb_type = MB_CACHE;
2502 rv = imap_append0(&mx, mbx, fp);
2504 jfail:
2508 jleave:
2509 safe_signal(SIGINT, saveint);
2510 safe_signal(SIGPIPE, savepipe);
2511 imaplock = 0;
2512 j_leave:
2513 NYD_LEAVE;
2514 if (interrupts)
2515 onintr(0);
2516 return rv;
2519 static enum okay
2520 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2521 struct list_item **lend, int level)
2523 char o[LINESIZE], *cp;
2524 const char *bp;
2525 FILE *queuefp = NULL;
2526 struct list_item *lp;
2527 enum okay ok = STOP;
2528 NYD_X;
2530 *list = *lend = NULL;
2531 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2532 IMAP_OUT(o, MB_COMD, return STOP)
2533 while (mp->mb_active & MB_COMD) {
2534 ok = imap_answer(mp, 1);
2535 if (response_status == RESPONSE_OTHER &&
2536 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2537 cp = imap_unquotestr(list_name);
2538 lp = csalloc(1, sizeof *lp);
2539 lp->l_name = cp;
2540 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2541 ++cp;
2542 lp->l_base = *cp ? cp : savestr(base);
2543 lp->l_attr = list_attributes;
2544 lp->l_level = level+1;
2545 lp->l_delim = list_hierarchy_delimiter;
2546 if (*list && *lend) {
2547 (*lend)->l_next = lp;
2548 *lend = lp;
2549 } else
2550 *list = *lend = lp;
2553 return ok;
2556 static enum okay
2557 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2559 struct list_item *list, *lend, *lp, *lx, *ly;
2560 int n, depth;
2561 const char *bp;
2562 char *cp;
2563 enum okay rv;
2564 NYD_ENTER;
2566 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2567 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2568 goto jleave;
2569 rv = OKAY;
2570 if (list == NULL || lend == NULL)
2571 goto jleave;
2573 for (lp = list; lp; lp = lp->l_next)
2574 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2575 !(lp->l_attr & LIST_NOINFERIORS)) {
2576 cp = salloc((n = strlen(lp->l_name)) + 2);
2577 memcpy(cp, lp->l_name, n);
2578 cp[n] = lp->l_delim;
2579 cp[n+1] = '\0';
2580 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2581 lp->l_has_children = 1;
2582 if (strcmp(cp, lx->l_name) == 0)
2583 lx = lx->l_next;
2584 if (lx) {
2585 lend->l_next = lx;
2586 lend = ly;
2591 for (lp = list; lp; lp = lp->l_next) {
2592 if (strip) {
2593 cp = lp->l_name;
2594 for (bp = base; *bp && *bp == *cp; bp++)
2595 cp++;
2596 } else
2597 cp = lp->l_name;
2598 if (!(lp->l_attr & LIST_NOSELECT))
2599 fprintf(fp, "%s\n", *cp ? cp : base);
2600 else if (lp->l_has_children == 0)
2601 fprintf(fp, "%s%c\n", *cp ? cp : base,
2602 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2604 jleave:
2605 NYD_LEAVE;
2606 return rv;
2609 FL void
2610 imap_folders(const char * volatile name, int strip)
2612 sighandler_type saveint, savepipe;
2613 const char *fold, *cp, *sp;
2614 FILE * volatile fp;
2615 NYD_ENTER;
2617 cp = protbase(name);
2618 sp = mb.mb_imap_account;
2619 if (sp == NULL || strcmp(cp, sp)) {
2620 fprintf(stderr, _(
2621 "Cannot perform `folders' but when on the very IMAP "
2622 "account; the current one is\n `%s' -- "
2623 "try `folders @'.\n"),
2624 (sp != NULL ? sp : _("[NONE]")));
2625 goto jleave;
2628 fold = imap_fileof(name);
2629 if (options & OPT_TTYOUT) {
2630 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2631 0600)) == NULL) {
2632 perror("tmpfile");
2633 goto jleave;
2635 } else
2636 fp = stdout;
2638 imaplock = 1;
2639 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2640 safe_signal(SIGINT, &_imap_maincatch);
2641 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2642 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2643 goto junroll;
2644 if (savepipe != SIG_IGN)
2645 safe_signal(SIGPIPE, imapcatch);
2647 if (mb.mb_type == MB_CACHE)
2648 cache_list(&mb, fold, strip, fp);
2649 else
2650 imap_list(&mb, fold, strip, fp);
2652 imaplock = 0;
2653 if (interrupts) {
2654 if (options & OPT_TTYOUT)
2655 Fclose(fp);
2656 goto jleave;
2658 fflush(fp);
2660 if (options & OPT_TTYOUT) {
2661 rewind(fp);
2662 if (fsize(fp) > 0)
2663 dopr(fp);
2664 else
2665 fprintf(stderr, "Folder not found.\n");
2667 junroll:
2668 safe_signal(SIGINT, saveint);
2669 safe_signal(SIGPIPE, savepipe);
2670 if (options & OPT_TTYOUT)
2671 Fclose(fp);
2672 jleave:
2673 NYD_LEAVE;
2674 if (interrupts)
2675 onintr(0);
2678 static void
2679 dopr(FILE *fp)
2681 char o[LINESIZE];
2682 int c;
2683 long n = 0, mx = 0, columns, width;
2684 FILE *out;
2685 NYD_ENTER;
2687 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2688 == NULL) {
2689 perror("tmpfile");
2690 goto jleave;
2693 while ((c = getc(fp)) != EOF) {
2694 if (c == '\n') {
2695 if (n > mx)
2696 mx = n;
2697 n = 0;
2698 } else
2699 ++n;
2701 rewind(fp);
2703 width = scrnwidth;
2704 if (mx < width / 2) {
2705 columns = width / (mx+2);
2706 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2707 } else
2708 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2709 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2710 page_or_print(out, 0);
2711 Fclose(out);
2712 jleave:
2713 NYD_LEAVE;
2716 static enum okay
2717 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2719 char o[LINESIZE];
2720 const char *qname;
2721 int twice = 0, stored = 0;
2722 FILE *queuefp = NULL;
2723 enum okay ok = STOP;
2724 NYD_X;
2726 if (mp->mb_type == MB_CACHE) {
2727 if ((queuefp = cache_queue(mp)) == NULL)
2728 return STOP;
2729 ok = OKAY;
2731 qname = imap_quotestr(name = imap_fileof(name));
2732 /* Since it is not possible to set flags on the copy, recently
2733 * set flags must be set on the original to include it in the copy */
2734 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2735 imap_store(mp, m, n, '+', "\\Seen", 0);
2736 if (m->m_flag&MFLAG)
2737 imap_store(mp, m, n, '+', "\\Flagged", 0);
2738 if (m->m_flag&MUNFLAG)
2739 imap_store(mp, m, n, '-', "\\Flagged", 0);
2740 if (m->m_flag&MANSWER)
2741 imap_store(mp, m, n, '+', "\\Answered", 0);
2742 if (m->m_flag&MUNANSWER)
2743 imap_store(mp, m, n, '-', "\\Flagged", 0);
2744 if (m->m_flag&MDRAFT)
2745 imap_store(mp, m, n, '+', "\\Draft", 0);
2746 if (m->m_flag&MUNDRAFT)
2747 imap_store(mp, m, n, '-', "\\Draft", 0);
2748 again:
2749 if (m->m_uid)
2750 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2751 else {
2752 if (check_expunged() == STOP)
2753 goto out;
2754 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2756 IMAP_OUT(o, MB_COMD, goto out)
2757 while (mp->mb_active & MB_COMD)
2758 ok = imap_answer(mp, twice);
2760 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2761 response_status == RESPONSE_OK)
2762 imap_copyuid(mp, m, name);
2764 if (response_status == RESPONSE_NO && twice++ == 0) {
2765 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2766 IMAP_OUT(o, MB_COMD, goto out)
2767 while (mp->mb_active & MB_COMD)
2768 ok = imap_answer(mp, 1);
2769 if (ok == OKAY) {
2770 imap_created_mailbox++;
2771 goto again;
2775 if (queuefp != NULL)
2776 Fclose(queuefp);
2778 /* ... and reset the flag to its initial value so that the 'exit'
2779 * command still leaves the message unread */
2780 out:
2781 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2782 imap_store(mp, m, n, '-', "\\Seen", 0);
2783 stored++;
2785 if (m->m_flag & MFLAG) {
2786 imap_store(mp, m, n, '-', "\\Flagged", 0);
2787 stored++;
2789 if (m->m_flag & MUNFLAG) {
2790 imap_store(mp, m, n, '+', "\\Flagged", 0);
2791 stored++;
2793 if (m->m_flag & MANSWER) {
2794 imap_store(mp, m, n, '-', "\\Answered", 0);
2795 stored++;
2797 if (m->m_flag & MUNANSWER) {
2798 imap_store(mp, m, n, '+', "\\Answered", 0);
2799 stored++;
2801 if (m->m_flag & MDRAFT) {
2802 imap_store(mp, m, n, '-', "\\Draft", 0);
2803 stored++;
2805 if (m->m_flag & MUNDRAFT) {
2806 imap_store(mp, m, n, '+', "\\Draft", 0);
2807 stored++;
2809 if (stored) {
2810 mp->mb_active |= MB_COMD;
2811 (void)imap_finish(mp);
2813 return ok;
2816 FL enum okay
2817 imap_copy(struct message *m, int n, const char *name)
2819 sighandler_type saveint, savepipe;
2820 enum okay rv = STOP;
2821 NYD_ENTER;
2823 imaplock = 1;
2824 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2825 safe_signal(SIGINT, &_imap_maincatch);
2826 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2827 if (sigsetjmp(imapjmp, 1) == 0) {
2828 if (savepipe != SIG_IGN)
2829 safe_signal(SIGPIPE, imapcatch);
2831 rv = imap_copy1(&mb, m, n, name);
2833 safe_signal(SIGINT, saveint);
2834 safe_signal(SIGPIPE, savepipe);
2835 imaplock = 0;
2837 NYD_LEAVE;
2838 if (interrupts)
2839 onintr(0);
2840 return rv;
2843 static enum okay
2844 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2845 unsigned long *olduid, unsigned long *newuid)
2847 char *xp, *yp, *zp;
2848 enum okay rv;
2849 NYD_ENTER;
2851 *uidvalidity = strtoul(cp, &xp, 10);
2852 *olduid = strtoul(xp, &yp, 10);
2853 *newuid = strtoul(yp, &zp, 10);
2854 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2855 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2856 NYD_LEAVE;
2857 return rv;
2860 static enum okay
2861 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2862 unsigned long *uid)
2864 char *xp, *yp;
2865 enum okay rv;
2866 NYD_ENTER;
2868 *uidvalidity = strtoul(cp, &xp, 10);
2869 *uid = strtoul(xp, &yp, 10);
2870 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2871 *yp == ']');
2872 NYD_LEAVE;
2873 return rv;
2876 static enum okay
2877 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2879 struct mailbox xmb;
2880 struct message xm;
2881 const char *cp;
2882 unsigned long uidvalidity, olduid, newuid;
2883 enum okay rv;
2884 NYD_ENTER;
2886 rv = STOP;
2887 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2888 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2889 goto jleave;
2890 rv = OKAY;
2892 xmb = *mp;
2893 xmb.mb_cache_directory = NULL;
2894 xmb.mb_imap_mailbox = savestr(name);
2895 xmb.mb_uidvalidity = uidvalidity;
2896 initcache(&xmb);
2897 if (m == NULL) {
2898 memset(&xm, 0, sizeof xm);
2899 xm.m_uid = olduid;
2900 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2901 goto jleave;
2902 getcache(mp, &xm, NEED_HEADER);
2903 getcache(mp, &xm, NEED_BODY);
2904 } else {
2905 if ((m->m_flag & HAVE_HEADER) == 0)
2906 getcache(mp, m, NEED_HEADER);
2907 if ((m->m_flag & HAVE_BODY) == 0)
2908 getcache(mp, m, NEED_BODY);
2909 xm = *m;
2911 xm.m_uid = newuid;
2912 xm.m_flag &= ~MFULLYCACHED;
2913 putcache(&xmb, &xm);
2914 jleave:
2915 NYD_LEAVE;
2916 return rv;
2919 static enum okay
2920 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2921 long size, long lines, int flag, const char *name)
2923 struct mailbox xmb;
2924 struct message xm;
2925 const char *cp;
2926 unsigned long uidvalidity, uid;
2927 enum okay rv;
2928 NYD_ENTER;
2930 rv = STOP;
2931 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2932 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2933 goto jleave;
2934 rv = OKAY;
2936 xmb = *mp;
2937 xmb.mb_cache_directory = NULL;
2938 xmb.mb_imap_mailbox = savestr(name);
2939 xmb.mb_uidvalidity = uidvalidity;
2940 xmb.mb_otf = xmb.mb_itf = fp;
2941 initcache(&xmb);
2942 memset(&xm, 0, sizeof xm);
2943 xm.m_flag = (flag & MREAD) | MNEW;
2944 xm.m_time = t;
2945 xm.m_block = mailx_blockof(off1);
2946 xm.m_offset = mailx_offsetof(off1);
2947 xm.m_size = size;
2948 xm.m_xsize = xsize;
2949 xm.m_lines = xm.m_xlines = lines;
2950 xm.m_uid = uid;
2951 xm.m_have = HAVE_HEADER | HAVE_BODY;
2952 putcache(&xmb, &xm);
2953 jleave:
2954 NYD_LEAVE;
2955 return rv;
2958 static enum okay
2959 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
2961 FILE *tp = NULL;
2962 time_t t;
2963 long size, xsize, ysize, lines;
2964 enum mflag flag = MNEW;
2965 char *name, *buf, *bp;
2966 char const *cp;
2967 size_t bufsize, buflen, cnt;
2968 enum okay rv = STOP;
2969 NYD_ENTER;
2971 buf = smalloc(bufsize = LINESIZE);
2972 buflen = 0;
2973 cnt = fsize(fp);
2974 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
2975 goto jstop;
2977 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
2979 while (*bp == ' ')
2980 ++bp;
2982 if ((cp = strrchr(bp, '{')) == NULL)
2983 goto jstop;
2985 xsize = atol(&cp[1]) + 2;
2986 if ((name = imap_strex(&bp[7], &cp)) == NULL)
2987 goto jstop;
2988 while (*cp == ' ')
2989 cp++;
2991 if (*cp == '(') {
2992 imap_getflags(cp, &cp, &flag);
2993 while (*++cp == ' ')
2996 t = imap_read_date_time(cp);
2998 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
2999 NULL)
3000 goto jstop;
3002 size = xsize;
3003 ysize = lines = 0;
3004 while (size > 0) {
3005 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3006 goto jstop;
3007 size -= buflen;
3008 buf[--buflen] = '\0';
3009 buf[buflen-1] = '\n';
3010 fwrite(buf, 1, buflen, tp);
3011 ysize += buflen;
3012 ++lines;
3014 fflush(tp);
3015 rewind(tp);
3017 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3018 imap_unquotestr(name));
3019 rv = OKAY;
3020 jstop:
3021 free(buf);
3022 if (tp)
3023 Fclose(tp);
3024 NYD_LEAVE;
3025 return rv;
3028 #ifdef HAVE_IMAP_SEARCH
3029 static enum okay
3030 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3031 int f)
3033 char *o, *xp, *cs, c;
3034 size_t osize;
3035 FILE *queuefp = NULL;
3036 int i;
3037 unsigned long n;
3038 const char *cp;
3039 enum okay ok = STOP;
3040 NYD_X;
3042 c = 0;
3043 for (cp = spec; *cp; cp++)
3044 c |= *cp;
3045 if (c & 0200) {
3046 cp = charset_get_lc();
3047 # ifdef HAVE_ICONV
3048 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3049 iconv_t it;
3050 char *nsp, *nspec;
3051 size_t sz, nsz;
3053 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3054 sz = strlen(spec) + 1;
3055 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3056 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3057 sz == 0) {
3058 spec = nspec;
3059 cp = "utf-8";
3061 n_iconv_close(it);
3064 # endif
3065 cp = imap_quotestr(cp);
3066 cs = salloc(n = strlen(cp) + 10);
3067 snprintf(cs, n, "CHARSET %s ", cp);
3068 } else
3069 cs = UNCONST("");
3071 o = ac_alloc(osize = strlen(spec) + 60);
3072 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3073 IMAP_OUT(o, MB_COMD, goto out)
3074 while (mp->mb_active & MB_COMD) {
3075 ok = imap_answer(mp, 0);
3076 if (response_status == RESPONSE_OTHER &&
3077 response_other == MAILBOX_DATA_SEARCH) {
3078 xp = responded_other_text;
3079 while (*xp && *xp != '\r') {
3080 n = strtoul(xp, &xp, 10);
3081 for (i = 0; i < cnt; i++)
3082 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3083 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3084 mark(i+1, f);
3088 out:
3089 ac_free(o);
3090 return ok;
3093 FL enum okay
3094 imap_search1(const char * volatile spec, int f)
3096 sighandler_type saveint, savepipe;
3097 enum okay volatile rv = STOP;
3098 NYD_ENTER;
3100 if (mb.mb_type != MB_IMAP)
3101 goto jleave;
3103 imaplock = 1;
3104 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3105 safe_signal(SIGINT, &_imap_maincatch);
3106 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3107 if (sigsetjmp(imapjmp, 1) == 0) {
3108 if (savepipe != SIG_IGN)
3109 safe_signal(SIGPIPE, imapcatch);
3111 rv = imap_search2(&mb, message, msgCount, spec, f);
3113 safe_signal(SIGINT, saveint);
3114 safe_signal(SIGPIPE, savepipe);
3115 imaplock = 0;
3116 jleave:
3117 NYD_LEAVE;
3118 if (interrupts)
3119 onintr(0);
3120 return rv;
3122 #endif /* HAVE_IMAP_SEARCH */
3124 FL int
3125 imap_thisaccount(const char *cp)
3127 int rv;
3128 NYD_ENTER;
3130 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3131 rv = 0;
3132 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3133 mb.mb_imap_account == NULL)
3134 rv = 0;
3135 else
3136 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3137 NYD_LEAVE;
3138 return rv;
3141 FL enum okay
3142 imap_remove(const char * volatile name)
3144 sighandler_type volatile saveint, savepipe;
3145 enum okay rv = STOP;
3146 NYD_ENTER;
3148 if (mb.mb_type != MB_IMAP) {
3149 fprintf(stderr, "Refusing to remove \"%s\" in disconnected mode.\n",
3150 name);
3151 goto jleave;
3154 if (!imap_thisaccount(name)) {
3155 fprintf(stderr, "Can only remove mailboxes on current IMAP "
3156 "server: \"%s\" not removed.\n", name);
3157 goto jleave;
3160 imaplock = 1;
3161 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3162 safe_signal(SIGINT, &_imap_maincatch);
3163 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3164 if (sigsetjmp(imapjmp, 1) == 0) {
3165 if (savepipe != SIG_IGN)
3166 safe_signal(SIGPIPE, imapcatch);
3168 rv = imap_remove1(&mb, imap_fileof(name));
3170 safe_signal(SIGINT, saveint);
3171 safe_signal(SIGPIPE, savepipe);
3172 imaplock = 0;
3174 if (rv == OKAY)
3175 rv = cache_remove(name);
3176 jleave:
3177 NYD_LEAVE;
3178 if (interrupts)
3179 onintr(0);
3180 return rv;
3183 static enum okay
3184 imap_remove1(struct mailbox *mp, const char *name)
3186 FILE *queuefp = NULL;
3187 char *o;
3188 int os;
3189 enum okay ok = STOP;
3190 NYD_X;
3192 o = ac_alloc(os = 2*strlen(name) + 100);
3193 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3194 IMAP_OUT(o, MB_COMD, goto out)
3195 while (mp->mb_active & MB_COMD)
3196 ok = imap_answer(mp, 1);
3197 out:
3198 ac_free(o);
3199 return ok;
3202 FL enum okay
3203 imap_rename(const char *old, const char *new)
3205 sighandler_type saveint, savepipe;
3206 enum okay rv = STOP;
3207 NYD_ENTER;
3209 if (mb.mb_type != MB_IMAP) {
3210 fprintf(stderr, "Refusing to rename mailboxes in disconnected mode.\n");
3211 goto jleave;
3214 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3215 fprintf(stderr, "Can only rename mailboxes on current IMAP "
3216 "server: \"%s\" not renamed to \"%s\".\n", old, new);
3217 goto jleave;
3220 imaplock = 1;
3221 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3222 safe_signal(SIGINT, &_imap_maincatch);
3223 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3224 if (sigsetjmp(imapjmp, 1) == 0) {
3225 if (savepipe != SIG_IGN)
3226 safe_signal(SIGPIPE, imapcatch);
3228 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3230 safe_signal(SIGINT, saveint);
3231 safe_signal(SIGPIPE, savepipe);
3232 imaplock = 0;
3234 if (rv == OKAY)
3235 rv = cache_rename(old, new);
3236 jleave:
3237 NYD_LEAVE;
3238 if (interrupts)
3239 onintr(0);
3240 return rv;
3243 static enum okay
3244 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3246 FILE *queuefp = NULL;
3247 char *o;
3248 int os;
3249 enum okay ok = STOP;
3250 NYD_X;
3252 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3253 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3254 imap_quotestr(new));
3255 IMAP_OUT(o, MB_COMD, goto out)
3256 while (mp->mb_active & MB_COMD)
3257 ok = imap_answer(mp, 1);
3258 out:
3259 ac_free(o);
3260 return ok;
3263 FL enum okay
3264 imap_dequeue(struct mailbox *mp, FILE *fp)
3266 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3267 size_t bufsize, buflen, cnt;
3268 long offs, offs1, offs2, octets;
3269 int twice, gotcha = 0;
3270 FILE *queuefp = NULL;
3271 enum okay ok = OKAY, rok = OKAY;
3272 NYD_X;
3274 buf = smalloc(bufsize = LINESIZE);
3275 buflen = 0;
3276 cnt = fsize(fp);
3277 while ((offs1 = ftell(fp)) >= 0 &&
3278 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3279 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3281 while (*bp == ' ')
3282 ++bp;
3283 twice = 0;
3284 if ((offs = ftell(fp)) < 0)
3285 goto fail;
3286 again:
3287 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3288 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3289 cp = &bp[9];
3290 while (digitchar(*cp))
3291 cp++;
3292 if (*cp != ' ')
3293 goto fail;
3294 while (*cp == ' ')
3295 cp++;
3296 if ((newname = imap_strex(cp, NULL)) == NULL)
3297 goto fail;
3298 IMAP_OUT(o, MB_COMD, continue)
3299 while (mp->mb_active & MB_COMD)
3300 ok = imap_answer(mp, twice);
3301 if (response_status == RESPONSE_NO && twice++ == 0)
3302 goto trycreate;
3303 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3304 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3306 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3307 IMAP_OUT(o, MB_COMD, continue)
3308 while (mp->mb_active & MB_COMD)
3309 ok = imap_answer(mp, 1);
3310 if (ok == OKAY)
3311 gotcha++;
3312 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3313 if ((cp = strrchr(bp, '{')) == NULL)
3314 goto fail;
3315 octets = atol(&cp[1]) + 2;
3316 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3317 goto fail;
3318 IMAP_OUT(o, MB_COMD, continue)
3319 while (mp->mb_active & MB_COMD) {
3320 ok = imap_answer(mp, twice);
3321 if (response_type == RESPONSE_CONT)
3322 break;
3324 if (ok == STOP) {
3325 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3326 goto trycreate;
3327 goto fail;
3329 while (octets > 0) {
3330 size_t n = (UICMP(z, octets, >, sizeof iob)
3331 ? sizeof iob : (size_t)octets);
3332 octets -= n;
3333 if (n != fread(iob, 1, n, fp))
3334 goto fail;
3335 swrite1(&mp->mb_sock, iob, n, 1);
3337 swrite(&mp->mb_sock, "");
3338 while (mp->mb_active & MB_COMD) {
3339 ok = imap_answer(mp, 0);
3340 if (response_status == RESPONSE_NO && twice++ == 0) {
3341 if (fseek(fp, offs, SEEK_SET) < 0)
3342 goto fail;
3343 goto trycreate;
3346 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3347 if ((offs2 = ftell(fp)) < 0)
3348 goto fail;
3349 fseek(fp, offs1, SEEK_SET);
3350 if (imap_appenduid_cached(mp, fp) == STOP) {
3351 (void)fseek(fp, offs2, SEEK_SET);
3352 goto fail;
3355 } else {
3356 fail:
3357 fprintf(stderr, "Invalid command in IMAP cache queue: \"%s\"\n", bp);
3358 rok = STOP;
3360 continue;
3361 trycreate:
3362 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3363 IMAP_OUT(o, MB_COMD, continue)
3364 while (mp->mb_active & MB_COMD)
3365 ok = imap_answer(mp, 1);
3366 if (ok == OKAY)
3367 goto again;
3369 fflush(fp);
3370 rewind(fp);
3371 ftruncate(fileno(fp), 0);
3372 if (gotcha)
3373 imap_close(mp);
3374 free(buf);
3375 return rok;
3378 static char *
3379 imap_strex(char const *cp, char const **xp)
3381 char const *cq;
3382 char *n = NULL;
3383 NYD_ENTER;
3385 if (*cp != '"')
3386 goto jleave;
3388 for (cq = cp + 1; *cq != '\0'; ++cq) {
3389 if (*cq == '\\')
3390 cq++;
3391 else if (*cq == '"')
3392 break;
3394 if (*cq != '"')
3395 goto jleave;
3397 n = salloc(cq - cp + 2);
3398 memcpy(n, cp, cq - cp +1);
3399 n[cq - cp + 1] = '\0';
3400 if (xp != NULL)
3401 *xp = cq + 1;
3402 jleave:
3403 NYD_LEAVE;
3404 return n;
3407 static enum okay
3408 check_expunged(void)
3410 enum okay rv;
3411 NYD_ENTER;
3413 if (expunged_messages > 0) {
3414 fprintf(stderr, "Command not executed - messages have been expunged\n");
3415 rv = STOP;
3416 } else
3417 rv = OKAY;
3418 NYD_LEAVE;
3419 return rv;
3422 FL int
3423 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3425 struct url url;
3426 int rv, omsgCount = msgCount;
3427 NYD_ENTER;
3428 UNUSED(vp);
3430 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3431 fprintf(stderr, "Already connected.\n");
3432 rv = 1;
3433 goto jleave;
3436 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3437 rv = 1;
3438 goto jleave;
3440 var_clear_allow_undefined = TRU1;
3441 ok_bclear(disconnected);
3442 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
3443 var_clear_allow_undefined = FAL0;
3445 if (mb.mb_type == MB_CACHE) {
3446 _imap_setfile1(&url, 0, edit, 1);
3447 if (msgCount > omsgCount)
3448 newmailinfo(omsgCount);
3450 rv = 0;
3451 jleave:
3452 NYD_LEAVE;
3453 return rv;
3456 FL int
3457 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3459 struct url url;
3460 int rv = 1, *msgvec = vp;
3461 NYD_ENTER;
3463 if (mb.mb_type == MB_CACHE) {
3464 fprintf(stderr, "Not connected.\n");
3465 goto jleave;
3467 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3468 fprintf(stderr, "The current mailbox is not cached.\n");
3469 goto jleave;
3472 if (!url_parse(&url, CPROTO_IMAP, mailname))
3473 goto jleave;
3475 if (*msgvec)
3476 c_cache(vp);
3477 ok_bset(disconnected, TRU1);
3478 if (mb.mb_type == MB_IMAP) {
3479 sclose(&mb.mb_sock);
3480 _imap_setfile1(&url, 0, edit, 1);
3482 rv = 0;
3483 jleave:
3484 NYD_LEAVE;
3485 return rv;
3488 FL int
3489 c_cache(void *vp)
3491 int rv = 1, *msgvec = vp, *ip;
3492 struct message *mp;
3493 NYD_ENTER;
3495 if (mb.mb_type != MB_IMAP) {
3496 fprintf(stderr, "Not connected to an IMAP server.\n");
3497 goto jleave;
3499 if (cached_uidvalidity(&mb) == 0) {
3500 fprintf(stderr, "The current mailbox is not cached.\n");
3501 goto jleave;
3504 for (ip = msgvec; *ip; ++ip) {
3505 mp = &message[*ip - 1];
3506 if (!(mp->m_have & HAVE_BODY))
3507 get_body(mp);
3509 rv = 0;
3510 jleave:
3511 NYD_LEAVE;
3512 return rv;
3515 FL int
3516 disconnected(const char *file)
3518 struct url url;
3519 int rv = 1;
3520 NYD_ENTER;
3522 if (ok_blook(disconnected)) {
3523 rv = 1;
3524 goto jleave;
3527 if (!url_parse(&url, CPROTO_IMAP, file)) {
3528 rv = 0;
3529 goto jleave;
3531 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
3533 jleave:
3534 NYD_LEAVE;
3535 return rv;
3538 FL void
3539 transflags(struct message *omessage, long omsgCount, int transparent)
3541 struct message *omp, *nmp, *newdot, *newprevdot;
3542 int hf;
3543 NYD_ENTER;
3545 omp = omessage;
3546 nmp = message;
3547 newdot = message;
3548 newprevdot = NULL;
3549 while (PTRCMP(omp, <, omessage + omsgCount) &&
3550 PTRCMP(nmp, <, message + msgCount)) {
3551 if (dot && nmp->m_uid == dot->m_uid)
3552 newdot = nmp;
3553 if (prevdot && nmp->m_uid == prevdot->m_uid)
3554 newprevdot = nmp;
3555 if (omp->m_uid == nmp->m_uid) {
3556 hf = nmp->m_flag & MHIDDEN;
3557 if (transparent && mb.mb_type == MB_IMAP)
3558 omp->m_flag &= ~MHIDDEN;
3559 *nmp++ = *omp++;
3560 if (transparent && mb.mb_type == MB_CACHE)
3561 nmp[-1].m_flag |= hf;
3562 } else if (omp->m_uid < nmp->m_uid)
3563 ++omp;
3564 else
3565 ++nmp;
3567 dot = newdot;
3568 setdot(newdot);
3569 prevdot = newprevdot;
3570 free(omessage);
3571 NYD_LEAVE;
3574 FL time_t
3575 imap_read_date_time(const char *cp)
3577 char buf[3];
3578 time_t t;
3579 int i, year, month, day, hour, minute, second, sign = -1;
3580 NYD_ENTER;
3582 /* "25-Jul-2004 15:33:44 +0200"
3583 * | | | | | |
3584 * 0 5 10 15 20 25 */
3585 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3586 goto jinvalid;
3587 day = strtol(&cp[1], NULL, 10);
3588 for (i = 0;;) {
3589 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3590 break;
3591 if (month_names[++i][0] == '\0')
3592 goto jinvalid;
3594 month = i + 1;
3595 year = strtol(&cp[8], NULL, 10);
3596 hour = strtol(&cp[13], NULL, 10);
3597 minute = strtol(&cp[16], NULL, 10);
3598 second = strtol(&cp[19], NULL, 10);
3599 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3600 goto jinvalid;
3601 switch (cp[22]) {
3602 case '-':
3603 sign = 1;
3604 break;
3605 case '+':
3606 break;
3607 default:
3608 goto jinvalid;
3610 buf[2] = '\0';
3611 buf[0] = cp[23];
3612 buf[1] = cp[24];
3613 t += strtol(buf, NULL, 10) * sign * 3600;
3614 buf[0] = cp[25];
3615 buf[1] = cp[26];
3616 t += strtol(buf, NULL, 10) * sign * 60;
3617 jleave:
3618 NYD_LEAVE;
3619 return t;
3620 jinvalid:
3621 time(&t);
3622 goto jleave;
3625 FL const char *
3626 imap_make_date_time(time_t t)
3628 static char s[30];
3629 struct tm *tmptr;
3630 int tzdiff, tzdiff_hour, tzdiff_min;
3631 NYD_ENTER;
3633 tzdiff = t - mktime(gmtime(&t));
3634 tzdiff_hour = (int)(tzdiff / 60);
3635 tzdiff_min = tzdiff_hour % 60;
3636 tzdiff_hour /= 60;
3637 tmptr = localtime(&t);
3638 if (tmptr->tm_isdst > 0)
3639 tzdiff_hour++;
3640 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3641 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3642 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3643 NYD_LEAVE;
3644 return s;
3646 #endif /* HAVE_IMAP */
3648 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3649 FL char *
3650 imap_quotestr(char const *s)
3652 char *n, *np;
3653 NYD_ENTER;
3655 np = n = salloc(2 * strlen(s) + 3);
3656 *np++ = '"';
3657 while (*s) {
3658 if (*s == '"' || *s == '\\')
3659 *np++ = '\\';
3660 *np++ = *s++;
3662 *np++ = '"';
3663 *np = '\0';
3664 NYD_LEAVE;
3665 return n;
3668 FL char *
3669 imap_unquotestr(char const *s)
3671 char *n, *np;
3672 NYD_ENTER;
3674 if (*s != '"') {
3675 n = savestr(s);
3676 goto jleave;
3679 np = n = salloc(strlen(s) + 1);
3680 while (*++s) {
3681 if (*s == '\\')
3682 s++;
3683 else if (*s == '"')
3684 break;
3685 *np++ = *s;
3687 *np = '\0';
3688 jleave:
3689 NYD_LEAVE;
3690 return n;
3692 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3694 /* vim:set fenc=utf-8:s-it-mode */