*features* and `features' are "the same"
[s-mailx.git] / imap.c
blobbe866b698bc66fca7e0f7b686323f9197b0f8f63
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 - 2015 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 n_err(">>> %s", X);\
84 mp->mb_active |= Y;\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
86 ACTIONERR;\
88 } else {\
89 if (queuefp != NULL)\
90 fputs(X, queuefp);\
92 } while (0);
94 static struct record {
95 struct record *rec_next;
96 unsigned long rec_count;
97 enum rec_type {
98 REC_EXISTS,
99 REC_EXPUNGE
100 } rec_type;
101 } *record, *recend;
103 static enum {
104 RESPONSE_TAGGED,
105 RESPONSE_DATA,
106 RESPONSE_FATAL,
107 RESPONSE_CONT,
108 RESPONSE_ILLEGAL
109 } response_type;
111 static enum {
112 RESPONSE_OK,
113 RESPONSE_NO,
114 RESPONSE_BAD,
115 RESPONSE_PREAUTH,
116 RESPONSE_BYE,
117 RESPONSE_OTHER,
118 RESPONSE_UNKNOWN
119 } response_status;
121 static char *responded_tag;
122 static char *responded_text;
123 static char *responded_other_text;
124 static long responded_other_number;
126 static enum {
127 MAILBOX_DATA_FLAGS,
128 MAILBOX_DATA_LIST,
129 MAILBOX_DATA_LSUB,
130 MAILBOX_DATA_MAILBOX,
131 MAILBOX_DATA_SEARCH,
132 MAILBOX_DATA_STATUS,
133 MAILBOX_DATA_EXISTS,
134 MAILBOX_DATA_RECENT,
135 MESSAGE_DATA_EXPUNGE,
136 MESSAGE_DATA_FETCH,
137 CAPABILITY_DATA,
138 RESPONSE_OTHER_UNKNOWN
139 } response_other;
141 static enum list_attributes {
142 LIST_NONE = 000,
143 LIST_NOINFERIORS = 001,
144 LIST_NOSELECT = 002,
145 LIST_MARKED = 004,
146 LIST_UNMARKED = 010
147 } list_attributes;
149 static int list_hierarchy_delimiter;
150 static char *list_name;
152 struct list_item {
153 struct list_item *l_next;
154 char *l_name;
155 char *l_base;
156 enum list_attributes l_attr;
157 int l_delim;
158 int l_level;
159 int l_has_children;
162 static char *imapbuf; /* TODO not static, use pool */
163 static size_t imapbufsize;
164 static sigjmp_buf imapjmp;
165 static sighandler_type savealrm;
166 static int imapkeepalive;
167 static long had_exists = -1;
168 static long had_expunge = -1;
169 static long expunged_messages;
170 static int volatile imaplock;
171 static int same_imap_account;
172 static bool_t _imap_rdonly;
174 static void imap_other_get(char *pp);
175 static void imap_response_get(const char **cp);
176 static void imap_response_parse(void);
177 static enum okay imap_answer(struct mailbox *mp, int errprnt);
178 static enum okay imap_parse_list(void);
179 static enum okay imap_finish(struct mailbox *mp);
180 static void imap_timer_off(void);
181 static void imapcatch(int s);
182 static void _imap_maincatch(int s);
183 static enum okay imap_noop1(struct mailbox *mp);
184 static void rec_queue(enum rec_type type, unsigned long cnt);
185 static enum okay rec_dequeue(void);
186 static void rec_rmqueue(void);
187 static void imapalarm(int s);
188 static enum okay imap_preauth(struct mailbox *mp, struct url const *urlp);
189 static enum okay imap_capability(struct mailbox *mp);
190 static enum okay imap_auth(struct mailbox *mp, struct ccred *ccred);
191 #ifdef HAVE_MD5
192 static enum okay imap_cram_md5(struct mailbox *mp, struct ccred *ccred);
193 #endif
194 static enum okay imap_login(struct mailbox *mp, struct ccred *ccred);
195 #ifdef HAVE_GSSAPI
196 static enum okay _imap_gssapi(struct mailbox *mp, struct ccred *ccred);
197 #endif
198 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
199 static void imap_init(struct mailbox *mp, int n);
200 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
201 int *prevcount);
202 static bool_t _imap_getcred(struct mailbox *mbp, struct ccred *ccredp,
203 struct url *urlp);
204 static int _imap_setfile1(struct url *urlp, enum fedit_mode fm,
205 int transparent);
206 static int imap_fetchdata(struct mailbox *mp, struct message *m,
207 size_t expected, int need, const char *head,
208 size_t headsize, long headlines);
209 static void imap_putstr(struct mailbox *mp, struct message *m,
210 const char *str, const char *head, size_t headsize,
211 long headlines);
212 static enum okay imap_get(struct mailbox *mp, struct message *m,
213 enum needspec need);
214 static void commitmsg(struct mailbox *mp, struct message *to,
215 struct message *from, enum havespec have);
216 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
217 int bot, int top);
218 static enum okay imap_exit(struct mailbox *mp);
219 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
220 int needstat);
221 static enum okay imap_close(struct mailbox *mp);
222 static enum okay imap_update(struct mailbox *mp);
223 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
224 int c, const char *sp, int needstat);
225 static enum okay imap_unstore(struct message *m, int n, const char *flag);
226 static const char *tag(int new);
227 static char * imap_putflags(int f);
228 static void imap_getflags(const char *cp, char const **xp, enum mflag *f);
229 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
230 off_t off1, long xsize, enum mflag flag, time_t t);
231 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
232 static enum okay imap_list1(struct mailbox *mp, const char *base,
233 struct list_item **list, struct list_item **lend,
234 int level);
235 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
236 FILE *fp);
237 static void dopr(FILE *fp);
238 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
239 const char *name);
240 static enum okay imap_copyuid_parse(const char *cp,
241 unsigned long *uidvalidity, unsigned long *olduid,
242 unsigned long *newuid);
243 static enum okay imap_appenduid_parse(const char *cp,
244 unsigned long *uidvalidity, unsigned long *uid);
245 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
246 const char *name);
247 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
248 long off1, long xsize, long size, long lines, int flag,
249 const char *name);
250 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
251 #ifdef HAVE_IMAP_SEARCH
252 static enum okay imap_search2(struct mailbox *mp, struct message *m, int cnt,
253 const char *spec, int f);
254 #endif
255 static enum okay imap_remove1(struct mailbox *mp, const char *name);
256 static enum okay imap_rename1(struct mailbox *mp, const char *old,
257 const char *new);
258 static char * imap_strex(char const *cp, char const **xp);
259 static enum okay check_expunged(void);
261 static void
262 imap_other_get(char *pp)
264 char *xp;
265 NYD2_ENTER;
267 if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
268 pp += 6;
269 response_other = MAILBOX_DATA_FLAGS;
270 } else if (ascncasecmp(pp, "LIST ", 5) == 0) {
271 pp += 5;
272 response_other = MAILBOX_DATA_LIST;
273 } else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
274 pp += 5;
275 response_other = MAILBOX_DATA_LSUB;
276 } else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
277 pp += 8;
278 response_other = MAILBOX_DATA_MAILBOX;
279 } else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
280 pp += 7;
281 response_other = MAILBOX_DATA_SEARCH;
282 } else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
283 pp += 7;
284 response_other = MAILBOX_DATA_STATUS;
285 } else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
286 pp += 11;
287 response_other = CAPABILITY_DATA;
288 } else {
289 responded_other_number = strtol(pp, &xp, 10);
290 while (*xp == ' ')
291 ++xp;
292 if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
293 response_other = MAILBOX_DATA_EXISTS;
294 } else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
295 response_other = MAILBOX_DATA_RECENT;
296 } else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
297 response_other = MESSAGE_DATA_EXPUNGE;
298 } else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
299 pp = &xp[6];
300 response_other = MESSAGE_DATA_FETCH;
301 } else
302 response_other = RESPONSE_OTHER_UNKNOWN;
304 responded_other_text = pp;
305 NYD2_LEAVE;
308 static void
309 imap_response_get(const char **cp)
311 NYD2_ENTER;
312 if (ascncasecmp(*cp, "OK ", 3) == 0) {
313 *cp += 3;
314 response_status = RESPONSE_OK;
315 } else if (ascncasecmp(*cp, "NO ", 3) == 0) {
316 *cp += 3;
317 response_status = RESPONSE_NO;
318 } else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
319 *cp += 4;
320 response_status = RESPONSE_BAD;
321 } else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
322 *cp += 8;
323 response_status = RESPONSE_PREAUTH;
324 } else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
325 *cp += 4;
326 response_status = RESPONSE_BYE;
327 } else
328 response_status = RESPONSE_OTHER;
329 NYD2_LEAVE;
332 static void
333 imap_response_parse(void)
335 static char *parsebuf; /* TODO Use pool */
336 static size_t parsebufsize;
338 const char *ip = imapbuf;
339 char *pp;
340 NYD2_ENTER;
342 if (parsebufsize < imapbufsize)
343 parsebuf = srealloc(parsebuf, parsebufsize = imapbufsize);
344 memcpy(parsebuf, imapbuf, strlen(imapbuf) + 1);
345 pp = parsebuf;
346 switch (*ip) {
347 case '+':
348 response_type = RESPONSE_CONT;
349 ip++;
350 pp++;
351 while (*ip == ' ') {
352 ip++;
353 pp++;
355 break;
356 case '*':
357 ip++;
358 pp++;
359 while (*ip == ' ') {
360 ip++;
361 pp++;
363 imap_response_get(&ip);
364 pp = &parsebuf[ip - imapbuf];
365 switch (response_status) {
366 case RESPONSE_BYE:
367 response_type = RESPONSE_FATAL;
368 break;
369 default:
370 response_type = RESPONSE_DATA;
372 break;
373 default:
374 responded_tag = parsebuf;
375 while (*pp && *pp != ' ')
376 pp++;
377 if (*pp == '\0') {
378 response_type = RESPONSE_ILLEGAL;
379 break;
381 *pp++ = '\0';
382 while (*pp && *pp == ' ')
383 pp++;
384 if (*pp == '\0') {
385 response_type = RESPONSE_ILLEGAL;
386 break;
388 ip = &imapbuf[pp - parsebuf];
389 response_type = RESPONSE_TAGGED;
390 imap_response_get(&ip);
391 pp = &parsebuf[ip - imapbuf];
393 responded_text = pp;
394 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
395 response_status == RESPONSE_OTHER)
396 imap_other_get(pp);
397 NYD2_LEAVE;
400 static enum okay
401 imap_answer(struct mailbox *mp, int errprnt)
403 int i, complete;
404 enum okay rv;
405 NYD2_ENTER;
407 rv = OKAY;
408 if (mp->mb_type == MB_CACHE)
409 goto jleave;
410 rv = STOP;
411 jagain:
412 if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
413 if (options & OPT_VERBVERB)
414 fputs(imapbuf, stderr);
415 imap_response_parse();
416 if (response_type == RESPONSE_ILLEGAL)
417 goto jagain;
418 if (response_type == RESPONSE_CONT) {
419 rv = OKAY;
420 goto jleave;
422 if (response_status == RESPONSE_OTHER) {
423 if (response_other == MAILBOX_DATA_EXISTS) {
424 had_exists = responded_other_number;
425 rec_queue(REC_EXISTS, responded_other_number);
426 if (had_expunge > 0)
427 had_expunge = 0;
428 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
429 rec_queue(REC_EXPUNGE, responded_other_number);
430 if (had_expunge < 0)
431 had_expunge = 0;
432 had_expunge++;
433 expunged_messages++;
436 complete = 0;
437 if (response_type == RESPONSE_TAGGED) {
438 if (asccasecmp(responded_tag, tag(0)) == 0)
439 complete |= 1;
440 else
441 goto jagain;
443 switch (response_status) {
444 case RESPONSE_PREAUTH:
445 mp->mb_active &= ~MB_PREAUTH;
446 /*FALLTHRU*/
447 case RESPONSE_OK:
448 jokay:
449 rv = OKAY;
450 complete |= 2;
451 break;
452 case RESPONSE_NO:
453 case RESPONSE_BAD:
454 jstop:
455 rv = STOP;
456 complete |= 2;
457 if (errprnt)
458 n_err(_("IMAP error: %s"), responded_text);
459 break;
460 case RESPONSE_UNKNOWN: /* does not happen */
461 case RESPONSE_BYE:
462 i = mp->mb_active;
463 mp->mb_active = MB_NONE;
464 if (i & MB_BYE)
465 goto jokay;
466 goto jstop;
467 case RESPONSE_OTHER:
468 rv = OKAY;
469 break;
471 if (response_status != RESPONSE_OTHER &&
472 ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
473 n_err(_("IMAP alert: %s"), &responded_text[8]);
474 if (complete == 3)
475 mp->mb_active &= ~MB_COMD;
476 } else {
477 rv = STOP;
478 mp->mb_active = MB_NONE;
480 jleave:
481 NYD2_LEAVE;
482 return rv;
485 static enum okay
486 imap_parse_list(void)
488 char *cp;
489 enum okay rv;
490 NYD2_ENTER;
492 rv = STOP;
494 cp = responded_other_text;
495 list_attributes = LIST_NONE;
496 if (*cp == '(') {
497 while (*cp && *cp != ')') {
498 if (*cp == '\\') {
499 if (ascncasecmp(&cp[1], "Noinferiors ", 12) == 0) {
500 list_attributes |= LIST_NOINFERIORS;
501 cp += 12;
502 } else if (ascncasecmp(&cp[1], "Noselect ", 9) == 0) {
503 list_attributes |= LIST_NOSELECT;
504 cp += 9;
505 } else if (ascncasecmp(&cp[1], "Marked ", 7) == 0) {
506 list_attributes |= LIST_MARKED;
507 cp += 7;
508 } else if (ascncasecmp(&cp[1], "Unmarked ", 9) == 0) {
509 list_attributes |= LIST_UNMARKED;
510 cp += 9;
513 cp++;
515 if (*++cp != ' ')
516 goto jleave;
517 while (*cp == ' ')
518 cp++;
521 list_hierarchy_delimiter = EOF;
522 if (*cp == '"') {
523 if (*++cp == '\\')
524 cp++;
525 list_hierarchy_delimiter = *cp++ & 0377;
526 if (cp[0] != '"' || cp[1] != ' ')
527 goto jleave;
528 cp++;
529 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
530 list_hierarchy_delimiter = EOF;
531 cp += 3;
534 while (*cp == ' ')
535 cp++;
536 list_name = cp;
537 while (*cp && *cp != '\r')
538 cp++;
539 *cp = '\0';
540 rv = OKAY;
541 jleave:
542 NYD2_LEAVE;
543 return rv;
546 static enum okay
547 imap_finish(struct mailbox *mp)
549 NYD_ENTER;
550 while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
551 imap_answer(mp, 1);
552 NYD_LEAVE;
553 return OKAY;
556 static void
557 imap_timer_off(void)
559 NYD_ENTER;
560 if (imapkeepalive > 0) {
561 alarm(0);
562 safe_signal(SIGALRM, savealrm);
564 NYD_LEAVE;
567 static void
568 imapcatch(int s)
570 NYD_X; /* Signal handler */
571 switch (s) {
572 case SIGINT:
573 n_err_sighdl(_("Interrupt\n"));
574 siglongjmp(imapjmp, 1);
575 /*NOTREACHED*/
576 case SIGPIPE:
577 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
578 break;
582 static void
583 _imap_maincatch(int s)
585 NYD_X; /* Signal handler */
586 UNUSED(s);
587 if (interrupts++ == 0) {
588 n_err_sighdl(_("Interrupt\n"));
589 return;
591 onintr(0);
594 static enum okay
595 imap_noop1(struct mailbox *mp)
597 char o[LINESIZE];
598 FILE *queuefp = NULL;
599 NYD_X;
601 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
602 IMAP_OUT(o, MB_COMD, return STOP)
603 IMAP_ANSWER()
604 return OKAY;
607 FL char const *
608 imap_fileof(char const *xcp)
610 char const *cp = xcp;
611 int state = 0;
612 NYD_ENTER;
614 while (*cp) {
615 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
616 cp += 3;
617 state = 1;
619 if (cp[0] == '/' && state == 1) {
620 ++cp;
621 goto jleave;
623 if (cp[0] == '/') {
624 cp = xcp;
625 goto jleave;
627 ++cp;
629 jleave:
630 NYD_LEAVE;
631 return cp;
634 FL enum okay
635 imap_noop(void)
637 sighandler_type volatile oldint, oldpipe;
638 enum okay rv = STOP;
639 NYD_ENTER;
641 if (mb.mb_type != MB_IMAP)
642 goto jleave;
644 imaplock = 1;
645 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
646 safe_signal(SIGINT, &_imap_maincatch);
647 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
648 if (sigsetjmp(imapjmp, 1) == 0) {
649 if (oldpipe != SIG_IGN)
650 safe_signal(SIGPIPE, imapcatch);
652 rv = imap_noop1(&mb);
654 safe_signal(SIGINT, oldint);
655 safe_signal(SIGPIPE, oldpipe);
656 imaplock = 0;
657 jleave:
658 NYD_LEAVE;
659 if (interrupts)
660 onintr(0);
661 return rv;
664 static void
665 rec_queue(enum rec_type rt, unsigned long cnt)
667 struct record *rp;
668 NYD_ENTER;
670 rp = scalloc(1, sizeof *rp);
671 rp->rec_type = rt;
672 rp->rec_count = cnt;
673 if (record && recend) {
674 recend->rec_next = rp;
675 recend = rp;
676 } else
677 record = recend = rp;
678 NYD_LEAVE;
681 static enum okay
682 rec_dequeue(void)
684 struct message *omessage;
685 struct record *rp, *rq;
686 uiz_t exists = 0, i;
687 enum okay rv = STOP;
688 NYD_ENTER;
690 if (record == NULL)
691 goto jleave;
693 omessage = message;
694 message = smalloc((msgCount+1) * sizeof *message);
695 if (msgCount)
696 memcpy(message, omessage, msgCount * sizeof *message);
697 memset(&message[msgCount], 0, sizeof *message);
699 rp = record, rq = NULL;
700 rv = OKAY;
701 while (rp != NULL) {
702 switch (rp->rec_type) {
703 case REC_EXISTS:
704 exists = rp->rec_count;
705 break;
706 case REC_EXPUNGE:
707 if (rp->rec_count == 0) {
708 rv = STOP;
709 break;
711 if (rp->rec_count > (unsigned long)msgCount) {
712 if (exists == 0 || rp->rec_count > exists--)
713 rv = STOP;
714 break;
716 if (exists > 0)
717 exists--;
718 delcache(&mb, &message[rp->rec_count-1]);
719 memmove(&message[rp->rec_count-1], &message[rp->rec_count],
720 ((msgCount - rp->rec_count + 1) * sizeof *message));
721 --msgCount;
722 /* If the message was part of a collapsed thread,
723 * the m_collapsed field of one of its ancestors
724 * should be incremented. It seems hardly possible
725 * to do this with the current message structure,
726 * though. The result is that a '+' may be shown
727 * in the header summary even if no collapsed
728 * children exists */
729 break;
731 if (rq != NULL)
732 free(rq);
733 rq = rp;
734 rp = rp->rec_next;
736 if (rq != NULL)
737 free(rq);
739 record = recend = NULL;
740 if (rv == OKAY && UICMP(z, exists, >, msgCount)) {
741 message = srealloc(message, (exists + 1) * sizeof *message);
742 memset(&message[msgCount], 0, (exists - msgCount + 1) * sizeof *message);
743 for (i = msgCount; i < exists; ++i)
744 imap_init(&mb, i);
745 imap_flags(&mb, msgCount+1, exists);
746 msgCount = exists;
749 if (rv == STOP) {
750 free(message);
751 message = omessage;
753 jleave:
754 NYD_LEAVE;
755 return rv;
758 static void
759 rec_rmqueue(void)
761 struct record *rp;
762 NYD_ENTER;
764 for (rp = record; rp != NULL;) {
765 struct record *tmp = rp;
766 rp = rp->rec_next;
767 free(tmp);
769 record = recend = NULL;
770 NYD_LEAVE;
773 /*ARGSUSED*/
774 static void
775 imapalarm(int s)
777 sighandler_type volatile saveint, savepipe;
778 NYD_X; /* Signal handler */
779 UNUSED(s);
781 if (imaplock++ == 0) {
782 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
783 safe_signal(SIGINT, &_imap_maincatch);
784 savepipe = safe_signal(SIGPIPE, SIG_IGN);
785 if (sigsetjmp(imapjmp, 1)) {
786 safe_signal(SIGINT, saveint);
787 safe_signal(SIGPIPE, savepipe);
788 goto jbrk;
790 if (savepipe != SIG_IGN)
791 safe_signal(SIGPIPE, imapcatch);
792 if (imap_noop1(&mb) != OKAY) {
793 safe_signal(SIGINT, saveint);
794 safe_signal(SIGPIPE, savepipe);
795 goto jleave;
797 safe_signal(SIGINT, saveint);
798 safe_signal(SIGPIPE, savepipe);
800 jbrk:
801 alarm(imapkeepalive);
802 jleave:
803 --imaplock;
806 static enum okay
807 imap_preauth(struct mailbox *mp, struct url const *urlp)
809 NYD_X;
811 mp->mb_active |= MB_PREAUTH;
812 imap_answer(mp, 1);
814 #ifdef HAVE_SSL
815 if (!mp->mb_sock.s_use_ssl && xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
816 FILE *queuefp = NULL;
817 char o[LINESIZE];
819 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
820 IMAP_OUT(o, MB_COMD, return STOP)
821 IMAP_ANSWER()
822 if (ssl_open(urlp, &mp->mb_sock) != OKAY)
823 return STOP;
825 #else
826 if (xok_blook(imap_use_starttls, urlp, OXM_ALL)) {
827 n_err(_("No SSL support compiled in\n"));
828 return STOP;
830 #endif
832 imap_capability(mp);
833 return OKAY;
836 static enum okay
837 imap_capability(struct mailbox *mp)
839 char o[LINESIZE];
840 FILE *queuefp = NULL;
841 enum okay ok = STOP;
842 const char *cp;
843 NYD_X;
845 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
846 IMAP_OUT(o, MB_COMD, return STOP)
847 while (mp->mb_active & MB_COMD) {
848 ok = imap_answer(mp, 0);
849 if (response_status == RESPONSE_OTHER &&
850 response_other == CAPABILITY_DATA) {
851 cp = responded_other_text;
852 while (*cp) {
853 while (spacechar(*cp))
854 ++cp;
855 if (strncmp(cp, "UIDPLUS", 7) == 0 && spacechar(cp[7]))
856 /* RFC 2359 */
857 mp->mb_flags |= MB_UIDPLUS;
858 while (*cp && !spacechar(*cp))
859 ++cp;
863 return ok;
866 static enum okay
867 imap_auth(struct mailbox *mp, struct ccred *ccred)
869 enum okay rv;
870 NYD_ENTER;
872 if (!(mp->mb_active & MB_PREAUTH)) {
873 rv = OKAY;
874 goto jleave;
877 switch (ccred->cc_authtype) {
878 case AUTHTYPE_LOGIN:
879 rv = imap_login(mp, ccred);
880 break;
881 #ifdef HAVE_MD5
882 case AUTHTYPE_CRAM_MD5:
883 rv = imap_cram_md5(mp, ccred);
884 break;
885 #endif
886 #ifdef HAVE_GSSAPI
887 case AUTHTYPE_GSSAPI:
888 rv = _imap_gssapi(mp, ccred);
889 break;
890 #endif
891 default:
892 rv = STOP;
893 break;
895 jleave:
896 NYD_LEAVE;
897 return rv;
900 #ifdef HAVE_MD5
901 static enum okay
902 imap_cram_md5(struct mailbox *mp, struct ccred *ccred)
904 char o[LINESIZE], *cp;
905 FILE *queuefp = NULL;
906 enum okay rv = STOP;
907 NYD_ENTER;
909 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
910 IMAP_XOUT(o, 0, goto jleave, goto jleave);
911 imap_answer(mp, 1);
912 if (response_type != RESPONSE_CONT)
913 goto jleave;
915 cp = cram_md5_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
916 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
917 while (mp->mb_active & MB_COMD)
918 rv = imap_answer(mp, 1);
919 jleave:
920 NYD_LEAVE;
921 return rv;
923 #endif /* HAVE_MD5 */
925 static enum okay
926 imap_login(struct mailbox *mp, struct ccred *ccred)
928 char o[LINESIZE];
929 FILE *queuefp = NULL;
930 enum okay rv = STOP;
931 NYD_ENTER;
933 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
934 tag(1), imap_quotestr(ccred->cc_user.s), imap_quotestr(ccred->cc_pass.s));
935 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
936 while (mp->mb_active & MB_COMD)
937 rv = imap_answer(mp, 1);
938 jleave:
939 NYD_LEAVE;
940 return rv;
943 #ifdef HAVE_GSSAPI
944 # include "imap_gssapi.h"
945 #endif
947 FL enum okay
948 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
949 enum fedit_mode fm)
951 enum okay ok = OKAY;
952 char const *cp;
953 char o[LINESIZE];
954 FILE *queuefp = NULL;
955 NYD_X;
956 UNUSED(size);
958 mp->mb_uidvalidity = 0;
959 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
960 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), imap_quotestr(mbx));
961 IMAP_OUT(o, MB_COMD, return STOP)
962 while (mp->mb_active & MB_COMD) {
963 ok = imap_answer(mp, 1);
964 if (response_status != RESPONSE_OTHER &&
965 (cp = asccasestr(responded_text, "[UIDVALIDITY ")) != NULL)
966 mp->mb_uidvalidity = atol(&cp[13]);
968 *cnt = (had_exists > 0) ? had_exists : 0;
969 if (response_status != RESPONSE_OTHER &&
970 ascncasecmp(responded_text, "[READ-ONLY] ", 12) == 0)
971 mp->mb_perm = 0;
972 return ok;
975 static enum okay
976 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
978 char o[LINESIZE];
979 FILE *queuefp = NULL;
980 char const *cp;
981 struct message *m;
982 unsigned x = X, y = Y, n;
983 NYD_X;
985 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
986 IMAP_OUT(o, MB_COMD, return STOP)
987 while (mp->mb_active & MB_COMD) {
988 imap_answer(mp, 1);
989 if (response_status == RESPONSE_OTHER &&
990 response_other == MESSAGE_DATA_FETCH) {
991 n = responded_other_number;
992 if (n < x || n > y)
993 continue;
994 m = &message[n-1];
995 m->m_xsize = 0;
996 } else
997 continue;
999 if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
1000 cp += 5;
1001 while (*cp == ' ')
1002 cp++;
1003 if (*cp == '(')
1004 imap_getflags(cp, &cp, &m->m_flag);
1007 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
1008 m->m_uid = strtoul(&cp[4], NULL, 10);
1009 getcache1(mp, m, NEED_UNSPEC, 1);
1010 m->m_flag &= ~MHIDDEN;
1013 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1014 x++;
1015 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1016 y--;
1017 if (x <= y) {
1018 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1019 tag(1), x, y);
1020 IMAP_OUT(o, MB_COMD, return STOP)
1021 while (mp->mb_active & MB_COMD) {
1022 imap_answer(mp, 1);
1023 if (response_status == RESPONSE_OTHER &&
1024 response_other == MESSAGE_DATA_FETCH) {
1025 n = responded_other_number;
1026 if (n < x || n > y)
1027 continue;
1028 m = &message[n-1];
1029 } else
1030 continue;
1031 if ((cp = asccasestr(responded_other_text, "RFC822.SIZE ")) != NULL)
1032 m->m_xsize = strtol(&cp[12], NULL, 10);
1033 if ((cp = asccasestr(responded_other_text, "INTERNALDATE ")) != NULL)
1034 m->m_time = imap_read_date_time(&cp[13]);
1038 srelax_hold();
1039 for (n = X; n <= Y; ++n) {
1040 putcache(mp, &message[n-1]);
1041 srelax();
1043 srelax_rele();
1044 return OKAY;
1047 static void
1048 imap_init(struct mailbox *mp, int n)
1050 struct message *m;
1051 NYD_ENTER;
1052 UNUSED(mp);
1054 m = message + n;
1055 m->m_flag = MUSED | MNOFROM;
1056 m->m_block = 0;
1057 m->m_offset = 0;
1058 NYD_LEAVE;
1061 static void
1062 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1064 struct message *omessage = 0;
1065 int i, omsgCount = 0;
1066 enum okay dequeued = STOP;
1067 NYD_ENTER;
1069 if (nmail || transparent) {
1070 omessage = message;
1071 omsgCount = msgCount;
1073 if (nmail)
1074 dequeued = rec_dequeue();
1076 if (had_exists >= 0) {
1077 if (dequeued != OKAY)
1078 msgCount = had_exists;
1079 had_exists = -1;
1081 if (had_expunge >= 0) {
1082 if (dequeued != OKAY)
1083 msgCount -= had_expunge;
1084 had_expunge = -1;
1087 if (nmail && expunged_messages)
1088 printf("Expunged %ld message%s.\n", expunged_messages,
1089 (expunged_messages != 1 ? "s" : ""));
1090 *prevcount = omsgCount - expunged_messages;
1091 expunged_messages = 0;
1092 if (msgCount < 0) {
1093 fputs("IMAP error: Negative message count\n", stderr);
1094 msgCount = 0;
1097 if (dequeued != OKAY) {
1098 message = scalloc(msgCount + 1, sizeof *message);
1099 for (i = 0; i < msgCount; i++)
1100 imap_init(mp, i);
1101 if (!nmail && mp->mb_type == MB_IMAP)
1102 initcache(mp);
1103 if (msgCount > 0)
1104 imap_flags(mp, 1, msgCount);
1105 message[msgCount].m_size = 0;
1106 message[msgCount].m_lines = 0;
1107 rec_rmqueue();
1109 if (nmail || transparent)
1110 transflags(omessage, omsgCount, transparent);
1111 else
1112 setdot(message);
1113 NYD_LEAVE;
1116 FL int
1117 imap_setfile(const char *xserver, enum fedit_mode fm)
1119 struct url url;
1120 int rv;
1121 NYD_ENTER;
1123 if (!url_parse(&url, CPROTO_IMAP, xserver)) {
1124 rv = 1;
1125 goto jleave;
1127 if (!ok_blook(v15_compat) &&
1128 (!url.url_had_user || url.url_pass.s != NULL))
1129 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1131 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1132 rv = _imap_setfile1(&url, fm, 0);
1133 jleave:
1134 NYD_LEAVE;
1135 return rv;
1138 static bool_t
1139 _imap_getcred(struct mailbox *mbp, struct ccred *ccredp, struct url *urlp)
1141 bool_t rv = FAL0;
1142 NYD_ENTER;
1144 if (ok_blook(v15_compat))
1145 rv = ccred_lookup(ccredp, urlp);
1146 else {
1147 char *var, *old,
1148 *xuhp = (urlp->url_had_user ? urlp->url_eu_h_p.s : urlp->url_u_h_p.s);
1150 if ((var = mbp->mb_imap_pass) != NULL) {
1151 var = savecat("password-", xuhp);
1152 if ((old = vok_vlook(var)) != NULL)
1153 old = sstrdup(old);
1154 vok_vset(var, mbp->mb_imap_pass);
1156 rv = ccred_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1157 if (var != NULL) {
1158 if (old != NULL) {
1159 vok_vset(var, old);
1160 free(old);
1161 } else
1162 vok_vclear(var);
1166 NYD_LEAVE;
1167 return rv;
1170 static int
1171 _imap_setfile1(struct url *urlp, enum fedit_mode fm, int volatile transparent)
1173 struct sock so;
1174 struct ccred ccred;
1175 sighandler_type volatile saveint, savepipe;
1176 char const *cp;
1177 int rv;
1178 int volatile prevcount = 0;
1179 enum mbflags same_flags;
1180 NYD_ENTER;
1182 if (fm & FEDIT_NEWMAIL) {
1183 saveint = safe_signal(SIGINT, SIG_IGN);
1184 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1185 if (saveint != SIG_IGN)
1186 safe_signal(SIGINT, imapcatch);
1187 if (savepipe != SIG_IGN)
1188 safe_signal(SIGPIPE, imapcatch);
1189 imaplock = 1;
1190 goto jnmail;
1193 same_flags = mb.mb_flags;
1194 same_imap_account = 0;
1195 if (mb.mb_imap_account != NULL &&
1196 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1197 if (mb.mb_sock.s_fd > 0 && mb.mb_sock.s_rsz >= 0 &&
1198 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1199 disconnected(mb.mb_imap_account) == 0) {
1200 same_imap_account = 1;
1201 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1203 goto jduppass;
1204 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1205 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1206 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1207 jduppass:
1209 urlp->url_pass.l = strlen(urlp->url_pass.s = savestr(mb.mb_imap_pass));
1213 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1214 free(mb.mb_imap_pass);
1215 mb.mb_imap_pass = NULL;
1217 if (!_imap_getcred(&mb, &ccred, urlp)) {
1218 rv = -1;
1219 goto jleave;
1222 so.s_fd = -1;
1223 if (!same_imap_account) {
1224 if (!disconnected(urlp->url_p_eu_h_p) && !sopen(&so, urlp)) {
1225 rv = -1;
1226 goto jleave;
1228 } else
1229 so = mb.mb_sock;
1230 if (!transparent)
1231 quit();
1233 if (fm & FEDIT_SYSBOX)
1234 pstate &= ~PS_EDIT;
1235 else
1236 pstate |= PS_EDIT;
1237 if (mb.mb_imap_account != NULL)
1238 free(mb.mb_imap_account);
1239 if (mb.mb_imap_pass != NULL)
1240 free(mb.mb_imap_pass);
1241 mb.mb_imap_account = sstrdup(urlp->url_p_eu_h_p);
1242 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1243 * TODO object, and mailbox will naturally have an URL and credentials */
1244 mb.mb_imap_pass = sbufdup(ccred.cc_pass.s, ccred.cc_pass.l);
1246 if (!same_imap_account) {
1247 if (mb.mb_sock.s_fd >= 0)
1248 sclose(&mb.mb_sock);
1250 same_imap_account = 0;
1252 if (!transparent) {
1253 if (mb.mb_itf) {
1254 fclose(mb.mb_itf);
1255 mb.mb_itf = NULL;
1257 if (mb.mb_otf) {
1258 fclose(mb.mb_otf);
1259 mb.mb_otf = NULL;
1261 if (mb.mb_imap_mailbox != NULL)
1262 free(mb.mb_imap_mailbox);
1263 mb.mb_imap_mailbox = sstrdup((urlp->url_path.s != NULL)
1264 ? urlp->url_path.s : "INBOX");
1265 initbox(urlp->url_p_eu_h_p_p);
1267 mb.mb_type = MB_VOID;
1268 mb.mb_active = MB_NONE;
1270 imaplock = 1;
1271 saveint = safe_signal(SIGINT, SIG_IGN);
1272 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1273 if (sigsetjmp(imapjmp, 1)) {
1274 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1275 sclose(&mb.mb_sock);
1276 safe_signal(SIGINT, saveint);
1277 safe_signal(SIGPIPE, savepipe);
1278 imaplock = 0;
1280 mb.mb_type = MB_VOID;
1281 mb.mb_active = MB_NONE;
1282 rv = -1;
1283 goto jleave;
1285 if (saveint != SIG_IGN)
1286 safe_signal(SIGINT, imapcatch);
1287 if (savepipe != SIG_IGN)
1288 safe_signal(SIGPIPE, imapcatch);
1290 if (mb.mb_sock.s_fd < 0) {
1291 if (disconnected(mb.mb_imap_account)) {
1292 if (cache_setptr(fm, transparent) == STOP)
1293 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
1294 goto jdone;
1296 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
1297 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
1298 savealrm = safe_signal(SIGALRM, imapalarm);
1299 alarm(imapkeepalive);
1303 mb.mb_sock = so;
1304 mb.mb_sock.s_desc = "IMAP";
1305 mb.mb_sock.s_onclose = imap_timer_off;
1306 if (imap_preauth(&mb, urlp) != OKAY || imap_auth(&mb, &ccred) != OKAY) {
1307 sclose(&mb.mb_sock);
1308 imap_timer_off();
1309 safe_signal(SIGINT, saveint);
1310 safe_signal(SIGPIPE, savepipe);
1311 imaplock = 0;
1312 rv = -1;
1313 goto jleave;
1315 } else /* same account */
1316 mb.mb_flags |= same_flags;
1318 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
1319 mb.mb_type = MB_IMAP;
1320 cache_dequeue(&mb);
1321 if (imap_select(&mb, &mailsize, &msgCount,
1322 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX"), fm) != OKAY) {
1323 /*sclose(&mb.mb_sock);
1324 imap_timer_off();*/
1325 safe_signal(SIGINT, saveint);
1326 safe_signal(SIGPIPE, savepipe);
1327 imaplock = 0;
1328 mb.mb_type = MB_VOID;
1329 rv = -1;
1330 goto jleave;
1333 jnmail:
1334 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1335 UNVOLATILE(&prevcount));
1336 jdone:
1337 setmsize(msgCount);
1338 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1339 pstate &= ~PS_SAW_COMMAND;
1340 safe_signal(SIGINT, saveint);
1341 safe_signal(SIGPIPE, savepipe);
1342 imaplock = 0;
1344 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1345 purgecache(&mb, message, msgCount);
1346 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1347 mb.mb_threaded = 0;
1348 c_sort((void*)-1);
1351 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1352 mb.mb_type == MB_CACHE)) {
1353 rv = (msgCount == 0);
1354 goto jleave;
1357 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1358 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1359 !ok_blook(emptystart))
1360 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1361 rv = 1;
1362 goto jleave;
1365 if (fm & FEDIT_NEWMAIL)
1366 newmailinfo(prevcount);
1367 rv = 0;
1368 jleave:
1369 NYD_LEAVE;
1370 return rv;
1373 static int
1374 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1375 int need, const char *head, size_t headsize, long headlines)
1377 char *line = NULL, *lp;
1378 size_t linesize = 0, linelen, size = 0;
1379 int emptyline = 0, lines = 0, excess = 0;
1380 off_t offset;
1381 NYD_ENTER;
1383 fseek(mp->mb_otf, 0L, SEEK_END);
1384 offset = ftell(mp->mb_otf);
1386 if (head)
1387 fwrite(head, 1, headsize, mp->mb_otf);
1389 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1390 lp = line;
1391 if (linelen > expected) {
1392 excess = linelen - expected;
1393 linelen = expected;
1395 /* TODO >>
1396 * Need to mask 'From ' lines. This cannot be done properly
1397 * since some servers pass them as 'From ' and others as
1398 * '>From '. Although one could identify the first kind of
1399 * server in principle, it is not possible to identify the
1400 * second as '>From ' may also come from a server of the
1401 * first type as actual data. So do what is absolutely
1402 * necessary only - mask 'From '.
1404 * If the line is the first line of the message header, it
1405 * is likely a real 'From ' line. In this case, it is just
1406 * ignored since it violates all standards.
1407 * TODO can the latter *really* happen??
1408 * TODO <<
1410 /* Since we simply copy over data without doing any transfer
1411 * encoding reclassification/adjustment we *have* to perform
1412 * RFC 4155 compliant From_ quoting here */
1413 if (emptyline && is_head(lp, linelen, TRU1)) {
1414 fputc('>', mp->mb_otf);
1415 ++size;
1417 emptyline = 0;
1418 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1419 if (linelen > 2) {
1420 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1421 size += linelen - 1;
1422 } else {
1423 emptyline = 1;
1424 ++size;
1426 fputc('\n', mp->mb_otf);
1427 } else {
1428 fwrite(lp, 1, linelen, mp->mb_otf);
1429 size += linelen;
1431 ++lines;
1432 if ((expected -= linelen) <= 0)
1433 break;
1435 if (!emptyline) {
1436 /* This is very ugly; but some IMAP daemons don't end a
1437 * message with \r\n\r\n, and we need \n\n for mbox format */
1438 fputc('\n', mp->mb_otf);
1439 ++lines;
1440 ++size;
1442 fflush(mp->mb_otf);
1444 if (m != NULL) {
1445 m->m_size = size + headsize;
1446 m->m_lines = lines + headlines;
1447 m->m_block = mailx_blockof(offset);
1448 m->m_offset = mailx_offsetof(offset);
1449 switch (need) {
1450 case NEED_HEADER:
1451 m->m_have |= HAVE_HEADER;
1452 break;
1453 case NEED_BODY:
1454 m->m_have |= HAVE_HEADER | HAVE_BODY;
1455 m->m_xlines = m->m_lines;
1456 m->m_xsize = m->m_size;
1457 break;
1460 free(line);
1461 NYD_LEAVE;
1462 return excess;
1465 static void
1466 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1467 const char *head, size_t headsize, long headlines)
1469 off_t offset;
1470 size_t len;
1471 NYD_ENTER;
1473 len = strlen(str);
1474 fseek(mp->mb_otf, 0L, SEEK_END);
1475 offset = ftell(mp->mb_otf);
1476 if (head)
1477 fwrite(head, 1, headsize, mp->mb_otf);
1478 if (len > 0) {
1479 fwrite(str, 1, len, mp->mb_otf);
1480 fputc('\n', mp->mb_otf);
1481 ++len;
1483 fflush(mp->mb_otf);
1485 if (m != NULL) {
1486 m->m_size = headsize + len;
1487 m->m_lines = headlines + 1;
1488 m->m_block = mailx_blockof(offset);
1489 m->m_offset = mailx_offsetof(offset);
1490 m->m_have |= HAVE_HEADER | HAVE_BODY;
1491 m->m_xlines = m->m_lines;
1492 m->m_xsize = m->m_size;
1494 NYD_LEAVE;
1497 static enum okay
1498 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1500 char o[LINESIZE];
1501 struct message mt;
1502 sighandler_type volatile saveint, savepipe;
1503 char * volatile head;
1504 char const *cp, *loc, * volatile item, * volatile resp;
1505 size_t expected;
1506 size_t volatile headsize;
1507 int number;
1508 FILE *queuefp;
1509 long volatile headlines;
1510 long n;
1511 ul_i volatile u;
1512 enum okay ok;
1513 NYD_X;
1515 saveint = savepipe = SIG_IGN;
1516 head = NULL;
1517 cp = loc = item = resp = NULL;
1518 headsize = 0;
1519 number = (int)PTR2SIZE(m - message + 1);
1520 queuefp = NULL;
1521 headlines = 0;
1522 n = -1;
1523 u = 0;
1524 ok = STOP;
1526 if (getcache(mp, m, need) == OKAY)
1527 return OKAY;
1528 if (mp->mb_type == MB_CACHE) {
1529 n_err(_("Message %lu not available\n"), (ul_i)number);
1530 return STOP;
1533 if (mp->mb_sock.s_fd < 0) {
1534 n_err(_("IMAP connection closed\n"));
1535 return STOP;
1538 switch (need) {
1539 case NEED_HEADER:
1540 resp = item = "RFC822.HEADER";
1541 break;
1542 case NEED_BODY:
1543 item = "BODY.PEEK[]";
1544 resp = "BODY[]";
1545 if (m->m_flag & HAVE_HEADER && m->m_size) {
1546 char *hdr = smalloc(m->m_size);
1547 fflush(mp->mb_otf);
1548 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1549 SEEK_SET) < 0 ||
1550 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1551 free(hdr);
1552 break;
1554 head = hdr;
1555 headsize = m->m_size;
1556 headlines = m->m_lines;
1557 item = "BODY.PEEK[TEXT]";
1558 resp = "BODY[TEXT]";
1560 break;
1561 case NEED_UNSPEC:
1562 return STOP;
1565 imaplock = 1;
1566 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1567 if (sigsetjmp(imapjmp, 1)) {
1568 safe_signal(SIGINT, saveint);
1569 safe_signal(SIGPIPE, savepipe);
1570 imaplock = 0;
1571 return STOP;
1573 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1574 safe_signal(SIGINT, &_imap_maincatch);
1575 if (savepipe != SIG_IGN)
1576 safe_signal(SIGPIPE, imapcatch);
1578 if (m->m_uid)
1579 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1580 tag(1), m->m_uid, item);
1581 else {
1582 if (check_expunged() == STOP)
1583 goto out;
1584 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1586 IMAP_OUT(o, MB_COMD, goto out)
1587 for (;;) {
1588 ok = imap_answer(mp, 1);
1589 if (ok == STOP)
1590 break;
1591 if (response_status != RESPONSE_OTHER ||
1592 response_other != MESSAGE_DATA_FETCH)
1593 continue;
1594 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1595 continue;
1596 if (m->m_uid) {
1597 if ((cp = asccasestr(responded_other_text, "UID "))) {
1598 u = atol(&cp[4]);
1599 n = 0;
1600 } else {
1601 n = -1;
1602 u = 0;
1604 } else
1605 n = responded_other_number;
1606 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1607 if (m->m_uid ? m->m_uid != u : n != number)
1608 continue;
1609 if ((cp = strchr(loc, '"')) != NULL) {
1610 cp = imap_unquotestr(cp);
1611 imap_putstr(mp, m, cp, head, headsize, headlines);
1612 } else {
1613 m->m_have |= HAVE_HEADER|HAVE_BODY;
1614 m->m_xlines = m->m_lines;
1615 m->m_xsize = m->m_size;
1617 goto out;
1619 expected = atol(&cp[1]);
1620 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1621 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1622 continue;
1624 mt = *m;
1625 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1626 if (n >= 0) {
1627 commitmsg(mp, m, &mt, mt.m_have);
1628 break;
1630 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1631 if (options & OPT_VERBVERB)
1632 fputs(imapbuf, stderr);
1633 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1634 u = atol(&cp[4]);
1635 if (u == m->m_uid) {
1636 commitmsg(mp, m, &mt, mt.m_have);
1637 break;
1642 out:
1643 while (mp->mb_active & MB_COMD)
1644 ok = imap_answer(mp, 1);
1646 if (saveint != SIG_IGN)
1647 safe_signal(SIGINT, saveint);
1648 if (savepipe != SIG_IGN)
1649 safe_signal(SIGPIPE, savepipe);
1650 imaplock--;
1652 if (ok == OKAY)
1653 putcache(mp, m);
1654 if (head != NULL)
1655 free(head);
1656 if (interrupts)
1657 onintr(0);
1658 return ok;
1661 FL enum okay
1662 imap_header(struct message *m)
1664 enum okay rv;
1665 NYD_ENTER;
1667 rv = imap_get(&mb, m, NEED_HEADER);
1668 NYD_LEAVE;
1669 return rv;
1673 FL enum okay
1674 imap_body(struct message *m)
1676 enum okay rv;
1677 NYD_ENTER;
1679 rv = imap_get(&mb, m, NEED_BODY);
1680 NYD_LEAVE;
1681 return rv;
1684 static void
1685 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1686 enum havespec have)
1688 NYD_ENTER;
1689 tomp->m_size = frommp->m_size;
1690 tomp->m_lines = frommp->m_lines;
1691 tomp->m_block = frommp->m_block;
1692 tomp->m_offset = frommp->m_offset;
1693 tomp->m_have = have;
1694 if (have & HAVE_BODY) {
1695 tomp->m_xlines = frommp->m_lines;
1696 tomp->m_xsize = frommp->m_size;
1698 putcache(mp, tomp);
1699 NYD_LEAVE;
1702 static enum okay
1703 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1705 /* bot > topp */
1706 char o[LINESIZE];
1707 char const *cp;
1708 struct message mt;
1709 size_t expected;
1710 int n = 0, u;
1711 FILE *queuefp = NULL;
1712 enum okay ok;
1713 NYD_X;
1715 if (m[bot].m_uid)
1716 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1717 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1718 else {
1719 if (check_expunged() == STOP)
1720 return STOP;
1721 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1722 tag(1), bot, topp);
1724 IMAP_OUT(o, MB_COMD, return STOP)
1726 srelax_hold();
1727 for (;;) {
1728 ok = imap_answer(mp, 1);
1729 if (response_status != RESPONSE_OTHER)
1730 break;
1731 if (response_other != MESSAGE_DATA_FETCH)
1732 continue;
1733 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
1734 srelax_rele();
1735 return STOP;
1737 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1738 continue;
1739 expected = atol(&cp[1]);
1740 if (m[bot-1].m_uid) {
1741 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1742 u = atoi(&cp[4]);
1743 for (n = bot; n <= topp; n++)
1744 if ((unsigned long)u == m[n-1].m_uid)
1745 break;
1746 if (n > topp) {
1747 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1748 continue;
1750 } else
1751 n = -1;
1752 } else {
1753 n = responded_other_number;
1754 if (n <= 0 || n > msgCount) {
1755 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1756 continue;
1759 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1760 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1761 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1762 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1763 if (options & OPT_VERBVERB)
1764 fputs(imapbuf, stderr);
1765 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1766 u = atoi(&cp[4]);
1767 for (n = bot; n <= topp; n++)
1768 if ((unsigned long)u == m[n-1].m_uid)
1769 break;
1770 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1771 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1772 n = 0;
1775 srelax();
1777 srelax_rele();
1779 while (mp->mb_active & MB_COMD)
1780 ok = imap_answer(mp, 1);
1781 return ok;
1784 FL void
1785 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1787 sighandler_type saveint, savepipe;
1788 /*enum okay ok = STOP;*/
1789 int i, chunk = 256;
1790 NYD_X;
1792 if (mb.mb_type == MB_CACHE)
1793 return;
1794 if (bot < 1)
1795 bot = 1;
1796 if (topp > msgCount)
1797 topp = msgCount;
1798 for (i = bot; i < topp; i++) {
1799 if (message[i-1].m_have & HAVE_HEADER ||
1800 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1801 bot = i+1;
1802 else
1803 break;
1805 for (i = topp; i > bot; i--) {
1806 if (message[i-1].m_have & HAVE_HEADER ||
1807 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1808 topp = i-1;
1809 else
1810 break;
1812 if (bot >= topp)
1813 return;
1815 imaplock = 1;
1816 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1817 safe_signal(SIGINT, &_imap_maincatch);
1818 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1819 if (sigsetjmp(imapjmp, 1) == 0) {
1820 if (savepipe != SIG_IGN)
1821 safe_signal(SIGPIPE, imapcatch);
1823 for (i = bot; i <= topp; i += chunk) {
1824 int j = i + chunk - 1;
1825 j = MIN(j, topp);
1826 if (visible(message + j))
1827 /*ok = */imap_fetchheaders(&mb, message, i, j);
1828 if (interrupts)
1829 onintr(0); /* XXX imaplock? */
1832 safe_signal(SIGINT, saveint);
1833 safe_signal(SIGPIPE, savepipe);
1834 imaplock = 0;
1837 static enum okay
1838 __imap_exit(struct mailbox *mp)
1840 char o[LINESIZE];
1841 FILE *queuefp = NULL;
1842 NYD_X;
1844 mp->mb_active |= MB_BYE;
1845 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1846 IMAP_OUT(o, MB_COMD, return STOP)
1847 IMAP_ANSWER()
1848 return OKAY;
1851 static enum okay
1852 imap_exit(struct mailbox *mp)
1854 enum okay rv;
1855 NYD_ENTER;
1857 rv = __imap_exit(mp);
1858 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1859 free(mp->mb_imap_pass);
1860 free(mp->mb_imap_account);
1861 free(mp->mb_imap_mailbox);
1862 if (mp->mb_cache_directory != NULL)
1863 free(mp->mb_cache_directory);
1864 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1865 mp->mb_imap_account =
1866 mp->mb_imap_mailbox =
1867 mp->mb_cache_directory = "";
1868 #else
1869 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1870 mp->mb_imap_mailbox = NULL;
1871 mp->mb_cache_directory = NULL;
1872 #endif
1873 #endif
1874 sclose(&mp->mb_sock);
1875 NYD_LEAVE;
1876 return rv;
1879 static enum okay
1880 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1882 NYD_ENTER;
1883 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1884 if (mp->mb_type == MB_IMAP)
1885 delcache(mp, m);
1886 NYD_LEAVE;
1887 return OKAY;
1890 static enum okay
1891 imap_close(struct mailbox *mp)
1893 char o[LINESIZE];
1894 FILE *queuefp = NULL;
1895 NYD_X;
1897 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1898 IMAP_OUT(o, MB_COMD, return STOP)
1899 IMAP_ANSWER()
1900 return OKAY;
1903 static enum okay
1904 imap_update(struct mailbox *mp)
1906 struct message *m;
1907 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1908 NYD_ENTER;
1910 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
1911 holdbits();
1912 c = 0;
1913 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1914 if (m->m_flag & MBOX)
1915 ++c;
1916 if (c > 0)
1917 if (makembox() == STOP)
1918 goto jbypass;
1921 gotcha = held = 0;
1922 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1923 if (mp->mb_perm == 0)
1924 dodel = 0;
1925 else if (pstate & PS_EDIT)
1926 dodel = ((m->m_flag & MDELETED) != 0);
1927 else
1928 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1930 /* Fetch the result after around each 800 STORE commands
1931 * sent (approx. 32k data sent). Otherwise, servers will
1932 * try to flush the return queue at some point, leading
1933 * to a deadlock if we are still writing commands but not
1934 * reading their results */
1935 needstat = stored > 0 && stored % 800 == 0;
1936 /* Even if this message has been deleted, continue
1937 * to set further flags. This is necessary to support
1938 * Gmail semantics, where "delete" actually means
1939 * "archive", and the flags are applied to the copy
1940 * in "All Mail" */
1941 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1942 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1943 stored++;
1945 if (m->m_flag & MFLAG) {
1946 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1947 stored++;
1949 if (m->m_flag & MUNFLAG) {
1950 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1951 stored++;
1953 if (m->m_flag & MANSWER) {
1954 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1955 stored++;
1957 if (m->m_flag & MUNANSWER) {
1958 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1959 stored++;
1961 if (m->m_flag & MDRAFT) {
1962 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1963 stored++;
1965 if (m->m_flag & MUNDRAFT) {
1966 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1967 stored++;
1970 if (dodel) {
1971 imap_delete(mp, m-message+1, m, needstat);
1972 stored++;
1973 gotcha++;
1974 } else if (mp->mb_type != MB_CACHE ||
1975 (!(pstate & PS_EDIT) &&
1976 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1977 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1978 (MPRESERVE | MTOUCH) ||
1979 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
1980 held++;
1981 if (m->m_flag & MNEW) {
1982 m->m_flag &= ~MNEW;
1983 m->m_flag |= MSTATUS;
1986 jbypass:
1987 if (gotcha)
1988 imap_close(mp);
1990 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1991 if (!(m->m_flag & MUNLINKED) &&
1992 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1993 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1994 putcache(mp, m);
1995 modflags++;
1998 /* XXX should be readonly (but our IMAP code is weird...) */
1999 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST))) {
2000 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2001 printf(_("\"%s\" "), displayname);
2002 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2003 ? _("complete\n") : _("updated.\n"));
2004 } else if (held && !(pstate & PS_EDIT) && mp->mb_perm != 0) {
2005 if (held == 1)
2006 printf(_("Held 1 message in %s\n"), displayname);
2007 else
2008 printf(_("Held %d messages in %s\n"), held, displayname);
2010 fflush(stdout);
2012 NYD_LEAVE;
2013 return OKAY;
2016 FL void
2017 imap_quit(void)
2019 sighandler_type volatile saveint, savepipe;
2020 NYD_ENTER;
2022 if (mb.mb_type == MB_CACHE) {
2023 imap_update(&mb);
2024 goto jleave;
2027 if (mb.mb_sock.s_fd < 0) {
2028 n_err(_("IMAP connection closed\n"));
2029 goto jleave;
2032 imaplock = 1;
2033 saveint = safe_signal(SIGINT, SIG_IGN);
2034 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2035 if (sigsetjmp(imapjmp, 1)) {
2036 safe_signal(SIGINT, saveint);
2037 safe_signal(SIGPIPE, saveint);
2038 imaplock = 0;
2039 goto jleave;
2041 if (saveint != SIG_IGN)
2042 safe_signal(SIGINT, imapcatch);
2043 if (savepipe != SIG_IGN)
2044 safe_signal(SIGPIPE, imapcatch);
2046 imap_update(&mb);
2047 if (!same_imap_account)
2048 imap_exit(&mb);
2050 safe_signal(SIGINT, saveint);
2051 safe_signal(SIGPIPE, savepipe);
2052 imaplock = 0;
2053 jleave:
2054 NYD_LEAVE;
2057 static enum okay
2058 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2059 int needstat)
2061 char o[LINESIZE];
2062 FILE *queuefp = NULL;
2063 NYD_X;
2065 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2066 return STOP;
2067 if (m->m_uid)
2068 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2069 tag(1), m->m_uid, c, sp);
2070 else {
2071 if (check_expunged() == STOP)
2072 return STOP;
2073 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2075 IMAP_OUT(o, MB_COMD, return STOP)
2076 if (needstat)
2077 IMAP_ANSWER()
2078 else
2079 mb.mb_active &= ~MB_COMD;
2080 if (queuefp != NULL)
2081 Fclose(queuefp);
2082 return OKAY;
2085 FL enum okay
2086 imap_undelete(struct message *m, int n)
2088 enum okay rv;
2089 NYD_ENTER;
2091 rv = imap_unstore(m, n, "\\Deleted");
2092 NYD_LEAVE;
2093 return rv;
2096 FL enum okay
2097 imap_unread(struct message *m, int n)
2099 enum okay rv;
2100 NYD_ENTER;
2102 rv = imap_unstore(m, n, "\\Seen");
2103 NYD_LEAVE;
2104 return rv;
2107 static enum okay
2108 imap_unstore(struct message *m, int n, const char *flag)
2110 sighandler_type saveint, savepipe;
2111 enum okay rv = STOP;
2112 NYD_ENTER;
2114 imaplock = 1;
2115 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2116 safe_signal(SIGINT, &_imap_maincatch);
2117 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2118 if (sigsetjmp(imapjmp, 1) == 0) {
2119 if (savepipe != SIG_IGN)
2120 safe_signal(SIGPIPE, imapcatch);
2122 rv = imap_store(&mb, m, n, '-', flag, 1);
2124 safe_signal(SIGINT, saveint);
2125 safe_signal(SIGPIPE, savepipe);
2126 imaplock = 0;
2128 NYD_LEAVE;
2129 if (interrupts)
2130 onintr(0);
2131 return rv;
2134 static const char *
2135 tag(int new)
2137 static char ts[20];
2138 static long n;
2139 NYD2_ENTER;
2141 if (new)
2142 ++n;
2143 snprintf(ts, sizeof ts, "T%lu", n);
2144 NYD2_LEAVE;
2145 return ts;
2148 FL int
2149 c_imap_imap(void *vp)
2151 char o[LINESIZE];
2152 sighandler_type saveint, savepipe;
2153 struct mailbox *mp = &mb;
2154 FILE *queuefp = NULL;
2155 enum okay volatile ok = STOP;
2156 NYD_X;
2158 if (mp->mb_type != MB_IMAP) {
2159 printf("Not operating on an IMAP mailbox.\n");
2160 return 1;
2162 imaplock = 1;
2163 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2164 safe_signal(SIGINT, &_imap_maincatch);
2165 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2166 if (sigsetjmp(imapjmp, 1) == 0) {
2167 if (savepipe != SIG_IGN)
2168 safe_signal(SIGPIPE, imapcatch);
2170 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2171 IMAP_OUT(o, MB_COMD, goto out)
2172 while (mp->mb_active & MB_COMD) {
2173 ok = imap_answer(mp, 0);
2174 fputs(responded_text, stdout);
2177 out:
2178 safe_signal(SIGINT, saveint);
2179 safe_signal(SIGPIPE, savepipe);
2180 imaplock = 0;
2182 if (interrupts)
2183 onintr(0);
2184 return ok != OKAY;
2187 FL int
2188 imap_newmail(int nmail)
2190 NYD_ENTER;
2192 if (nmail && had_exists < 0 && had_expunge < 0) {
2193 imaplock = 1;
2194 imap_noop();
2195 imaplock = 0;
2198 if (had_exists == msgCount && had_expunge < 0)
2199 /* Some servers always respond with EXISTS to NOOP. If
2200 * the mailbox has been changed but the number of messages
2201 * has not, an EXPUNGE must also had been sent; otherwise,
2202 * nothing has changed */
2203 had_exists = -1;
2204 NYD_LEAVE;
2205 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2208 static char *
2209 imap_putflags(int f)
2211 const char *cp;
2212 char *buf, *bp;
2213 NYD2_ENTER;
2215 bp = buf = salloc(100);
2216 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2217 *bp++ = '(';
2218 if (f & MREAD) {
2219 if (bp[-1] != '(')
2220 *bp++ = ' ';
2221 for (cp = "\\Seen"; *cp; cp++)
2222 *bp++ = *cp;
2224 if (f & MFLAGGED) {
2225 if (bp[-1] != '(')
2226 *bp++ = ' ';
2227 for (cp = "\\Flagged"; *cp; cp++)
2228 *bp++ = *cp;
2230 if (f & MANSWERED) {
2231 if (bp[-1] != '(')
2232 *bp++ = ' ';
2233 for (cp = "\\Answered"; *cp; cp++)
2234 *bp++ = *cp;
2236 if (f & MDRAFT) {
2237 if (bp[-1] != '(')
2238 *bp++ = ' ';
2239 for (cp = "\\Draft"; *cp; cp++)
2240 *bp++ = *cp;
2242 *bp++ = ')';
2243 *bp++ = ' ';
2245 *bp = '\0';
2246 NYD2_LEAVE;
2247 return buf;
2250 static void
2251 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2253 NYD2_ENTER;
2254 while (*cp != ')') {
2255 if (*cp == '\\') {
2256 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2257 *f |= MREAD;
2258 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2259 *f |= MNEW;
2260 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2261 *f |= MDELETED;
2262 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2263 *f |= MFLAGGED;
2264 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2265 *f |= MANSWERED;
2266 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2267 *f |= MDRAFTED;
2269 cp++;
2272 if (xp != NULL)
2273 *xp = cp;
2274 NYD2_LEAVE;
2277 static enum okay
2278 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2279 long xsize, enum mflag flag, time_t t)
2281 char o[LINESIZE], *buf;
2282 size_t bufsize, buflen, cnt;
2283 long size, lines, ysize;
2284 int twice = 0;
2285 FILE *queuefp = NULL;
2286 enum okay rv;
2287 NYD_ENTER;
2289 if (mp->mb_type == MB_CACHE) {
2290 queuefp = cache_queue(mp);
2291 if (queuefp == NULL) {
2292 rv = STOP;
2293 buf = NULL;
2294 goto jleave;
2296 rv = OKAY;
2297 } else
2298 rv = STOP;
2300 buf = smalloc(bufsize = LINESIZE);
2301 buflen = 0;
2302 jagain:
2303 size = xsize;
2304 cnt = fsize(fp);
2305 if (fseek(fp, off1, SEEK_SET) < 0) {
2306 rv = STOP;
2307 goto jleave;
2310 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2311 tag(1), imap_quotestr(name), imap_putflags(flag),
2312 imap_make_date_time(t), size);
2313 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2314 while (mp->mb_active & MB_COMD) {
2315 rv = imap_answer(mp, twice);
2316 if (response_type == RESPONSE_CONT)
2317 break;
2320 if (mp->mb_type != MB_CACHE && rv == STOP) {
2321 if (twice == 0)
2322 goto jtrycreate;
2323 else
2324 goto jleave;
2327 lines = ysize = 0;
2328 while (size > 0) {
2329 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2330 lines++;
2331 ysize += buflen;
2332 buf[buflen - 1] = '\r';
2333 buf[buflen] = '\n';
2334 if (mp->mb_type != MB_CACHE)
2335 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2336 else if (queuefp)
2337 fwrite(buf, 1, buflen+1, queuefp);
2338 size -= buflen + 1;
2340 if (mp->mb_type != MB_CACHE)
2341 swrite(&mp->mb_sock, "\r\n");
2342 else if (queuefp)
2343 fputs("\r\n", queuefp);
2344 while (mp->mb_active & MB_COMD) {
2345 rv = imap_answer(mp, 0);
2346 if (response_status == RESPONSE_NO /*&&
2347 ascncasecmp(responded_text,
2348 "[TRYCREATE] ", 12) == 0*/) {
2349 jtrycreate:
2350 if (twice++) {
2351 rv = STOP;
2352 goto jleave;
2354 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2355 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2356 while (mp->mb_active & MB_COMD)
2357 rv = imap_answer(mp, 1);
2358 if (rv == STOP)
2359 goto jleave;
2360 imap_created_mailbox++;
2361 goto jagain;
2362 } else if (rv != OKAY)
2363 n_err(_("IMAP error: %s"), responded_text);
2364 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2365 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2367 jleave:
2368 if (queuefp != NULL)
2369 Fclose(queuefp);
2370 if (buf != NULL)
2371 free(buf);
2372 NYD_LEAVE;
2373 return rv;
2376 static enum okay
2377 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2379 char *buf, *bp, *lp;
2380 size_t bufsize, buflen, cnt;
2381 off_t off1 = -1, offs;
2382 int flag;
2383 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2384 time_t tim;
2385 long size;
2386 enum okay rv;
2387 NYD_ENTER;
2389 buf = smalloc(bufsize = LINESIZE);
2390 buflen = 0;
2391 cnt = fsize(fp);
2392 offs = ftell(fp);
2393 time(&tim);
2394 size = 0;
2396 for (flag = MNEW, state = _NLSEP;;) {
2397 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2399 if (bp == NULL ||
2400 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2401 is_head(buf, buflen, FAL0))) {
2402 if (off1 != (off_t)-1) {
2403 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2404 if (rv == STOP)
2405 goto jleave;
2406 fseek(fp, offs+buflen, SEEK_SET);
2408 off1 = offs + buflen;
2409 size = 0;
2410 flag = MNEW;
2411 state = _INHEAD;
2412 if (bp == NULL)
2413 break;
2414 tim = unixtime(buf);
2415 } else
2416 size += buflen+1;
2417 offs += buflen;
2419 state &= ~_NLSEP;
2420 if (buf[0] == '\n') {
2421 state &= ~_INHEAD;
2422 state |= _NLSEP;
2423 } else if (state & _INHEAD) {
2424 if (ascncasecmp(buf, "status", 6) == 0) {
2425 lp = &buf[6];
2426 while (whitechar(*lp))
2427 lp++;
2428 if (*lp == ':')
2429 while (*++lp != '\0')
2430 switch (*lp) {
2431 case 'R':
2432 flag |= MREAD;
2433 break;
2434 case 'O':
2435 flag &= ~MNEW;
2436 break;
2438 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2439 lp = &buf[8];
2440 while (whitechar(*lp))
2441 lp++;
2442 if (*lp == ':')
2443 while (*++lp != '\0')
2444 switch (*lp) {
2445 case 'F':
2446 flag |= MFLAGGED;
2447 break;
2448 case 'A':
2449 flag |= MANSWERED;
2450 break;
2451 case 'T':
2452 flag |= MDRAFTED;
2453 break;
2458 rv = OKAY;
2459 jleave:
2460 free(buf);
2461 NYD_LEAVE;
2462 return rv;
2465 FL enum okay
2466 imap_append(const char *xserver, FILE *fp)
2468 sighandler_type volatile saveint, savepipe;
2469 struct url url;
2470 struct ccred ccred;
2471 char const * volatile mbx;
2472 enum okay rv = STOP;
2473 NYD_ENTER;
2475 if (!url_parse(&url, CPROTO_IMAP, xserver))
2476 goto j_leave;
2477 if (!ok_blook(v15_compat) &&
2478 (!url.url_had_user || url.url_pass.s != NULL))
2479 n_err(_("New-style URL used without *v15-compat* being set!\n"));
2480 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2482 imaplock = 1;
2483 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2484 safe_signal(SIGINT, &_imap_maincatch);
2485 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2486 if (sigsetjmp(imapjmp, 1))
2487 goto jleave;
2488 if (savepipe != SIG_IGN)
2489 safe_signal(SIGPIPE, imapcatch);
2491 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2492 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
2493 rv = imap_append0(&mb, mbx, fp);
2494 } else {
2495 struct mailbox mx;
2497 memset(&mx, 0, sizeof mx);
2499 if (!_imap_getcred(&mx, &ccred, &url))
2500 goto jleave;
2502 if (disconnected(url.url_p_eu_h_p) == 0) {
2503 if (!sopen(&mx.mb_sock, &url))
2504 goto jfail;
2505 mx.mb_sock.s_desc = "IMAP";
2506 mx.mb_type = MB_IMAP;
2507 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2508 /* TODO the code now did
2509 * TODO mx.mb_imap_mailbox = mbx;
2510 * TODO though imap_mailbox is sfree()d and mbx
2511 * TODO is possibly even a constant
2512 * TODO i changed this to sstrdup() sofar, as is used
2513 * TODO somewhere else in this file for this! */
2514 mx.mb_imap_mailbox = sstrdup(mbx);
2515 if (imap_preauth(&mx, &url) != OKAY ||
2516 imap_auth(&mx, &ccred) != OKAY) {
2517 sclose(&mx.mb_sock);
2518 goto jfail;
2520 rv = imap_append0(&mx, mbx, fp);
2521 imap_exit(&mx);
2522 } else {
2523 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2524 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2525 mx.mb_type = MB_CACHE;
2526 rv = imap_append0(&mx, mbx, fp);
2528 jfail:
2532 jleave:
2533 safe_signal(SIGINT, saveint);
2534 safe_signal(SIGPIPE, savepipe);
2535 imaplock = 0;
2536 j_leave:
2537 NYD_LEAVE;
2538 if (interrupts)
2539 onintr(0);
2540 return rv;
2543 static enum okay
2544 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2545 struct list_item **lend, int level)
2547 char o[LINESIZE], *cp;
2548 const char *bp;
2549 FILE *queuefp = NULL;
2550 struct list_item *lp;
2551 enum okay ok = STOP;
2552 NYD_X;
2554 *list = *lend = NULL;
2555 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2556 IMAP_OUT(o, MB_COMD, return STOP)
2557 while (mp->mb_active & MB_COMD) {
2558 ok = imap_answer(mp, 1);
2559 if (response_status == RESPONSE_OTHER &&
2560 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2561 cp = imap_unquotestr(list_name);
2562 lp = csalloc(1, sizeof *lp);
2563 lp->l_name = cp;
2564 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2565 ++cp;
2566 lp->l_base = *cp ? cp : savestr(base);
2567 lp->l_attr = list_attributes;
2568 lp->l_level = level+1;
2569 lp->l_delim = list_hierarchy_delimiter;
2570 if (*list && *lend) {
2571 (*lend)->l_next = lp;
2572 *lend = lp;
2573 } else
2574 *list = *lend = lp;
2577 return ok;
2580 static enum okay
2581 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2583 struct list_item *list, *lend, *lp, *lx, *ly;
2584 int n, depth;
2585 const char *bp;
2586 char *cp;
2587 enum okay rv;
2588 NYD_ENTER;
2590 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2591 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2592 goto jleave;
2593 rv = OKAY;
2594 if (list == NULL || lend == NULL)
2595 goto jleave;
2597 for (lp = list; lp; lp = lp->l_next)
2598 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2599 !(lp->l_attr & LIST_NOINFERIORS)) {
2600 cp = salloc((n = strlen(lp->l_name)) + 2);
2601 memcpy(cp, lp->l_name, n);
2602 cp[n] = lp->l_delim;
2603 cp[n+1] = '\0';
2604 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2605 lp->l_has_children = 1;
2606 if (strcmp(cp, lx->l_name) == 0)
2607 lx = lx->l_next;
2608 if (lx) {
2609 lend->l_next = lx;
2610 lend = ly;
2615 for (lp = list; lp; lp = lp->l_next) {
2616 if (strip) {
2617 cp = lp->l_name;
2618 for (bp = base; *bp && *bp == *cp; bp++)
2619 cp++;
2620 } else
2621 cp = lp->l_name;
2622 if (!(lp->l_attr & LIST_NOSELECT))
2623 fprintf(fp, "%s\n", *cp ? cp : base);
2624 else if (lp->l_has_children == 0)
2625 fprintf(fp, "%s%c\n", *cp ? cp : base,
2626 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2628 jleave:
2629 NYD_LEAVE;
2630 return rv;
2633 FL void
2634 imap_folders(const char * volatile name, int strip)
2636 sighandler_type saveint, savepipe;
2637 const char *fold, *cp, *sp;
2638 FILE * volatile fp;
2639 NYD_ENTER;
2641 cp = protbase(name);
2642 sp = mb.mb_imap_account;
2643 if (sp == NULL || strcmp(cp, sp)) {
2644 n_err(
2645 _("Cannot perform `folders' but when on the very IMAP "
2646 "account; the current one is\n `%s' -- "
2647 "try `folders @'\n"),
2648 (sp != NULL ? sp : _("[NONE]")));
2649 goto jleave;
2652 fold = imap_fileof(name);
2653 if (options & OPT_TTYOUT) {
2654 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2655 0600)) == NULL) {
2656 n_perr(_("tmpfile"), 0);
2657 goto jleave;
2659 } else
2660 fp = stdout;
2662 imaplock = 1;
2663 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2664 safe_signal(SIGINT, &_imap_maincatch);
2665 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2666 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2667 goto junroll;
2668 if (savepipe != SIG_IGN)
2669 safe_signal(SIGPIPE, imapcatch);
2671 if (mb.mb_type == MB_CACHE)
2672 cache_list(&mb, fold, strip, fp);
2673 else
2674 imap_list(&mb, fold, strip, fp);
2676 imaplock = 0;
2677 if (interrupts) {
2678 if (options & OPT_TTYOUT)
2679 Fclose(fp);
2680 goto jleave;
2682 fflush(fp);
2684 if (options & OPT_TTYOUT) {
2685 rewind(fp);
2686 if (fsize(fp) > 0)
2687 dopr(fp);
2688 else
2689 n_err(_("Folder not found\n"));
2691 junroll:
2692 safe_signal(SIGINT, saveint);
2693 safe_signal(SIGPIPE, savepipe);
2694 if (options & OPT_TTYOUT)
2695 Fclose(fp);
2696 jleave:
2697 NYD_LEAVE;
2698 if (interrupts)
2699 onintr(0);
2702 static void
2703 dopr(FILE *fp)
2705 char o[LINESIZE];
2706 int c;
2707 long n = 0, mx = 0, columns, width;
2708 FILE *out;
2709 NYD_ENTER;
2711 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2712 == NULL) {
2713 n_perr(_("tmpfile"), 0);
2714 goto jleave;
2717 while ((c = getc(fp)) != EOF) {
2718 if (c == '\n') {
2719 if (n > mx)
2720 mx = n;
2721 n = 0;
2722 } else
2723 ++n;
2725 rewind(fp);
2727 width = scrnwidth;
2728 if (mx < width / 2) {
2729 columns = width / (mx+2);
2730 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2731 } else
2732 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2733 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL, NULL);
2734 page_or_print(out, 0);
2735 Fclose(out);
2736 jleave:
2737 NYD_LEAVE;
2740 static enum okay
2741 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2743 char o[LINESIZE];
2744 const char *qname;
2745 int twice = 0, stored = 0;
2746 FILE *queuefp = NULL;
2747 enum okay ok = STOP;
2748 NYD_X;
2750 if (mp->mb_type == MB_CACHE) {
2751 if ((queuefp = cache_queue(mp)) == NULL)
2752 return STOP;
2753 ok = OKAY;
2755 qname = imap_quotestr(name = imap_fileof(name));
2756 /* Since it is not possible to set flags on the copy, recently
2757 * set flags must be set on the original to include it in the copy */
2758 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2759 imap_store(mp, m, n, '+', "\\Seen", 0);
2760 if (m->m_flag&MFLAG)
2761 imap_store(mp, m, n, '+', "\\Flagged", 0);
2762 if (m->m_flag&MUNFLAG)
2763 imap_store(mp, m, n, '-', "\\Flagged", 0);
2764 if (m->m_flag&MANSWER)
2765 imap_store(mp, m, n, '+', "\\Answered", 0);
2766 if (m->m_flag&MUNANSWER)
2767 imap_store(mp, m, n, '-', "\\Flagged", 0);
2768 if (m->m_flag&MDRAFT)
2769 imap_store(mp, m, n, '+', "\\Draft", 0);
2770 if (m->m_flag&MUNDRAFT)
2771 imap_store(mp, m, n, '-', "\\Draft", 0);
2772 again:
2773 if (m->m_uid)
2774 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2775 else {
2776 if (check_expunged() == STOP)
2777 goto out;
2778 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2780 IMAP_OUT(o, MB_COMD, goto out)
2781 while (mp->mb_active & MB_COMD)
2782 ok = imap_answer(mp, twice);
2784 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2785 response_status == RESPONSE_OK)
2786 imap_copyuid(mp, m, name);
2788 if (response_status == RESPONSE_NO && twice++ == 0) {
2789 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2790 IMAP_OUT(o, MB_COMD, goto out)
2791 while (mp->mb_active & MB_COMD)
2792 ok = imap_answer(mp, 1);
2793 if (ok == OKAY) {
2794 imap_created_mailbox++;
2795 goto again;
2799 if (queuefp != NULL)
2800 Fclose(queuefp);
2802 /* ... and reset the flag to its initial value so that the 'exit'
2803 * command still leaves the message unread */
2804 out:
2805 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2806 imap_store(mp, m, n, '-', "\\Seen", 0);
2807 stored++;
2809 if (m->m_flag & MFLAG) {
2810 imap_store(mp, m, n, '-', "\\Flagged", 0);
2811 stored++;
2813 if (m->m_flag & MUNFLAG) {
2814 imap_store(mp, m, n, '+', "\\Flagged", 0);
2815 stored++;
2817 if (m->m_flag & MANSWER) {
2818 imap_store(mp, m, n, '-', "\\Answered", 0);
2819 stored++;
2821 if (m->m_flag & MUNANSWER) {
2822 imap_store(mp, m, n, '+', "\\Answered", 0);
2823 stored++;
2825 if (m->m_flag & MDRAFT) {
2826 imap_store(mp, m, n, '-', "\\Draft", 0);
2827 stored++;
2829 if (m->m_flag & MUNDRAFT) {
2830 imap_store(mp, m, n, '+', "\\Draft", 0);
2831 stored++;
2833 if (stored) {
2834 mp->mb_active |= MB_COMD;
2835 (void)imap_finish(mp);
2837 return ok;
2840 FL enum okay
2841 imap_copy(struct message *m, int n, const char *name)
2843 sighandler_type saveint, savepipe;
2844 enum okay rv = STOP;
2845 NYD_ENTER;
2847 imaplock = 1;
2848 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2849 safe_signal(SIGINT, &_imap_maincatch);
2850 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2851 if (sigsetjmp(imapjmp, 1) == 0) {
2852 if (savepipe != SIG_IGN)
2853 safe_signal(SIGPIPE, imapcatch);
2855 rv = imap_copy1(&mb, m, n, name);
2857 safe_signal(SIGINT, saveint);
2858 safe_signal(SIGPIPE, savepipe);
2859 imaplock = 0;
2861 NYD_LEAVE;
2862 if (interrupts)
2863 onintr(0);
2864 return rv;
2867 static enum okay
2868 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2869 unsigned long *olduid, unsigned long *newuid)
2871 char *xp, *yp, *zp;
2872 enum okay rv;
2873 NYD_ENTER;
2875 *uidvalidity = strtoul(cp, &xp, 10);
2876 *olduid = strtoul(xp, &yp, 10);
2877 *newuid = strtoul(yp, &zp, 10);
2878 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2879 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2880 NYD_LEAVE;
2881 return rv;
2884 static enum okay
2885 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2886 unsigned long *uid)
2888 char *xp, *yp;
2889 enum okay rv;
2890 NYD_ENTER;
2892 *uidvalidity = strtoul(cp, &xp, 10);
2893 *uid = strtoul(xp, &yp, 10);
2894 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2895 *yp == ']');
2896 NYD_LEAVE;
2897 return rv;
2900 static enum okay
2901 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2903 struct mailbox xmb;
2904 struct message xm;
2905 const char *cp;
2906 unsigned long uidvalidity, olduid, newuid;
2907 enum okay rv;
2908 NYD_ENTER;
2910 memset(&xmb, 0, sizeof xmb);
2912 rv = STOP;
2913 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2914 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2915 goto jleave;
2916 rv = OKAY;
2918 xmb = *mp;
2919 xmb.mb_cache_directory = NULL;
2920 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
2921 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
2922 xmb.mb_imap_mailbox = sstrdup(name);
2923 if (mp->mb_cache_directory != NULL)
2924 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
2925 xmb.mb_uidvalidity = uidvalidity;
2926 initcache(&xmb);
2928 if (m == NULL) {
2929 memset(&xm, 0, sizeof xm);
2930 xm.m_uid = olduid;
2931 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2932 goto jleave;
2933 getcache(mp, &xm, NEED_HEADER);
2934 getcache(mp, &xm, NEED_BODY);
2935 } else {
2936 if ((m->m_flag & HAVE_HEADER) == 0)
2937 getcache(mp, m, NEED_HEADER);
2938 if ((m->m_flag & HAVE_BODY) == 0)
2939 getcache(mp, m, NEED_BODY);
2940 xm = *m;
2942 xm.m_uid = newuid;
2943 xm.m_flag &= ~MFULLYCACHED;
2944 putcache(&xmb, &xm);
2945 jleave:
2946 if (xmb.mb_cache_directory != NULL)
2947 free(xmb.mb_cache_directory);
2948 if (xmb.mb_imap_mailbox != NULL)
2949 free(xmb.mb_imap_mailbox);
2950 if (xmb.mb_imap_pass != NULL)
2951 free(xmb.mb_imap_pass);
2952 if (xmb.mb_imap_account != NULL)
2953 free(xmb.mb_imap_account);
2954 NYD_LEAVE;
2955 return rv;
2958 static enum okay
2959 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2960 long size, long lines, int flag, const char *name)
2962 struct mailbox xmb;
2963 struct message xm;
2964 const char *cp;
2965 unsigned long uidvalidity, uid;
2966 enum okay rv;
2967 NYD_ENTER;
2969 xmb.mb_imap_mailbox = NULL;
2970 rv = STOP;
2971 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2972 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2973 goto jleave;
2974 rv = OKAY;
2976 xmb = *mp;
2977 xmb.mb_cache_directory = NULL;
2978 xmb.mb_imap_mailbox = sstrdup(name);
2979 xmb.mb_uidvalidity = uidvalidity;
2980 xmb.mb_otf = xmb.mb_itf = fp;
2981 initcache(&xmb);
2982 memset(&xm, 0, sizeof xm);
2983 xm.m_flag = (flag & MREAD) | MNEW;
2984 xm.m_time = t;
2985 xm.m_block = mailx_blockof(off1);
2986 xm.m_offset = mailx_offsetof(off1);
2987 xm.m_size = size;
2988 xm.m_xsize = xsize;
2989 xm.m_lines = xm.m_xlines = lines;
2990 xm.m_uid = uid;
2991 xm.m_have = HAVE_HEADER | HAVE_BODY;
2992 putcache(&xmb, &xm);
2993 jleave:
2994 if (xmb.mb_imap_mailbox != NULL)
2995 free(xmb.mb_imap_mailbox);
2996 NYD_LEAVE;
2997 return rv;
3000 static enum okay
3001 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3003 FILE *tp = NULL;
3004 time_t t;
3005 long size, xsize, ysize, lines;
3006 enum mflag flag = MNEW;
3007 char *name, *buf, *bp;
3008 char const *cp;
3009 size_t bufsize, buflen, cnt;
3010 enum okay rv = STOP;
3011 NYD_ENTER;
3013 buf = smalloc(bufsize = LINESIZE);
3014 buflen = 0;
3015 cnt = fsize(fp);
3016 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3017 goto jstop;
3019 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3021 while (*bp == ' ')
3022 ++bp;
3024 if ((cp = strrchr(bp, '{')) == NULL)
3025 goto jstop;
3027 xsize = atol(&cp[1]) + 2;
3028 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3029 goto jstop;
3030 while (*cp == ' ')
3031 cp++;
3033 if (*cp == '(') {
3034 imap_getflags(cp, &cp, &flag);
3035 while (*++cp == ' ')
3038 t = imap_read_date_time(cp);
3040 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3041 NULL)
3042 goto jstop;
3044 size = xsize;
3045 ysize = lines = 0;
3046 while (size > 0) {
3047 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3048 goto jstop;
3049 size -= buflen;
3050 buf[--buflen] = '\0';
3051 buf[buflen-1] = '\n';
3052 fwrite(buf, 1, buflen, tp);
3053 ysize += buflen;
3054 ++lines;
3056 fflush(tp);
3057 rewind(tp);
3059 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3060 imap_unquotestr(name));
3061 rv = OKAY;
3062 jstop:
3063 free(buf);
3064 if (tp)
3065 Fclose(tp);
3066 NYD_LEAVE;
3067 return rv;
3070 #ifdef HAVE_IMAP_SEARCH
3071 static enum okay
3072 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3073 int f)
3075 char *o, *xp, *cs, c;
3076 size_t osize;
3077 FILE *queuefp = NULL;
3078 int i;
3079 unsigned long n;
3080 const char *cp;
3081 enum okay ok = STOP;
3082 NYD_X;
3084 c = 0;
3085 for (cp = spec; *cp; cp++)
3086 c |= *cp;
3087 if (c & 0200) {
3088 cp = charset_get_lc();
3089 # ifdef HAVE_ICONV
3090 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3091 iconv_t it;
3092 char *nsp, *nspec;
3093 size_t sz, nsz;
3095 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3096 sz = strlen(spec) + 1;
3097 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3098 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3099 sz == 0) {
3100 spec = nspec;
3101 cp = "utf-8";
3103 n_iconv_close(it);
3106 # endif
3107 cp = imap_quotestr(cp);
3108 cs = salloc(n = strlen(cp) + 10);
3109 snprintf(cs, n, "CHARSET %s ", cp);
3110 } else
3111 cs = UNCONST("");
3113 o = ac_alloc(osize = strlen(spec) + 60);
3114 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3115 IMAP_OUT(o, MB_COMD, goto out)
3116 while (mp->mb_active & MB_COMD) {
3117 ok = imap_answer(mp, 0);
3118 if (response_status == RESPONSE_OTHER &&
3119 response_other == MAILBOX_DATA_SEARCH) {
3120 xp = responded_other_text;
3121 while (*xp && *xp != '\r') {
3122 n = strtoul(xp, &xp, 10);
3123 for (i = 0; i < cnt; i++)
3124 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3125 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3126 mark(i+1, f);
3130 out:
3131 ac_free(o);
3132 return ok;
3135 FL enum okay
3136 imap_search1(const char * volatile spec, int f)
3138 sighandler_type saveint, savepipe;
3139 enum okay volatile rv = STOP;
3140 NYD_ENTER;
3142 if (mb.mb_type != MB_IMAP)
3143 goto jleave;
3145 imaplock = 1;
3146 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3147 safe_signal(SIGINT, &_imap_maincatch);
3148 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3149 if (sigsetjmp(imapjmp, 1) == 0) {
3150 if (savepipe != SIG_IGN)
3151 safe_signal(SIGPIPE, imapcatch);
3153 rv = imap_search2(&mb, message, msgCount, spec, f);
3155 safe_signal(SIGINT, saveint);
3156 safe_signal(SIGPIPE, savepipe);
3157 imaplock = 0;
3158 jleave:
3159 NYD_LEAVE;
3160 if (interrupts)
3161 onintr(0);
3162 return rv;
3164 #endif /* HAVE_IMAP_SEARCH */
3166 FL int
3167 imap_thisaccount(const char *cp)
3169 int rv;
3170 NYD_ENTER;
3172 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3173 rv = 0;
3174 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3175 mb.mb_imap_account == NULL)
3176 rv = 0;
3177 else
3178 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3179 NYD_LEAVE;
3180 return rv;
3183 FL enum okay
3184 imap_remove(const char * volatile name)
3186 sighandler_type volatile saveint, savepipe;
3187 enum okay volatile rv = STOP;
3188 NYD_ENTER;
3190 if (mb.mb_type != MB_IMAP) {
3191 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3192 goto jleave;
3195 if (!imap_thisaccount(name)) {
3196 n_err(_("Can only remove mailboxes on current IMAP server: "
3197 "\"%s\" not removed\n"), name);
3198 goto jleave;
3201 imaplock = 1;
3202 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3203 safe_signal(SIGINT, &_imap_maincatch);
3204 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3205 if (sigsetjmp(imapjmp, 1) == 0) {
3206 if (savepipe != SIG_IGN)
3207 safe_signal(SIGPIPE, imapcatch);
3209 rv = imap_remove1(&mb, imap_fileof(name));
3211 safe_signal(SIGINT, saveint);
3212 safe_signal(SIGPIPE, savepipe);
3213 imaplock = 0;
3215 if (rv == OKAY)
3216 rv = cache_remove(name);
3217 jleave:
3218 NYD_LEAVE;
3219 if (interrupts)
3220 onintr(0);
3221 return rv;
3224 static enum okay
3225 imap_remove1(struct mailbox *mp, const char *name)
3227 FILE *queuefp = NULL;
3228 char *o;
3229 int os;
3230 enum okay ok = STOP;
3231 NYD_X;
3233 o = ac_alloc(os = 2*strlen(name) + 100);
3234 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3235 IMAP_OUT(o, MB_COMD, goto out)
3236 while (mp->mb_active & MB_COMD)
3237 ok = imap_answer(mp, 1);
3238 out:
3239 ac_free(o);
3240 return ok;
3243 FL enum okay
3244 imap_rename(const char *old, const char *new)
3246 sighandler_type saveint, savepipe;
3247 enum okay rv = STOP;
3248 NYD_ENTER;
3250 if (mb.mb_type != MB_IMAP) {
3251 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3252 goto jleave;
3255 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3256 n_err(_("Can only rename mailboxes on current IMAP "
3257 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3258 goto jleave;
3261 imaplock = 1;
3262 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3263 safe_signal(SIGINT, &_imap_maincatch);
3264 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3265 if (sigsetjmp(imapjmp, 1) == 0) {
3266 if (savepipe != SIG_IGN)
3267 safe_signal(SIGPIPE, imapcatch);
3269 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3271 safe_signal(SIGINT, saveint);
3272 safe_signal(SIGPIPE, savepipe);
3273 imaplock = 0;
3275 if (rv == OKAY)
3276 rv = cache_rename(old, new);
3277 jleave:
3278 NYD_LEAVE;
3279 if (interrupts)
3280 onintr(0);
3281 return rv;
3284 static enum okay
3285 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3287 FILE *queuefp = NULL;
3288 char *o;
3289 int os;
3290 enum okay ok = STOP;
3291 NYD_X;
3293 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3294 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3295 imap_quotestr(new));
3296 IMAP_OUT(o, MB_COMD, goto out)
3297 while (mp->mb_active & MB_COMD)
3298 ok = imap_answer(mp, 1);
3299 out:
3300 ac_free(o);
3301 return ok;
3304 FL enum okay
3305 imap_dequeue(struct mailbox *mp, FILE *fp)
3307 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3308 size_t bufsize, buflen, cnt;
3309 long offs, offs1, offs2, octets;
3310 int twice, gotcha = 0;
3311 FILE *queuefp = NULL;
3312 enum okay ok = OKAY, rok = OKAY;
3313 NYD_X;
3315 buf = smalloc(bufsize = LINESIZE);
3316 buflen = 0;
3317 cnt = fsize(fp);
3318 while ((offs1 = ftell(fp)) >= 0 &&
3319 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3320 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3322 while (*bp == ' ')
3323 ++bp;
3324 twice = 0;
3325 if ((offs = ftell(fp)) < 0)
3326 goto fail;
3327 again:
3328 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3329 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3330 cp = &bp[9];
3331 while (digitchar(*cp))
3332 cp++;
3333 if (*cp != ' ')
3334 goto fail;
3335 while (*cp == ' ')
3336 cp++;
3337 if ((newname = imap_strex(cp, NULL)) == NULL)
3338 goto fail;
3339 IMAP_OUT(o, MB_COMD, continue)
3340 while (mp->mb_active & MB_COMD)
3341 ok = imap_answer(mp, twice);
3342 if (response_status == RESPONSE_NO && twice++ == 0)
3343 goto trycreate;
3344 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3345 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3347 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3348 IMAP_OUT(o, MB_COMD, continue)
3349 while (mp->mb_active & MB_COMD)
3350 ok = imap_answer(mp, 1);
3351 if (ok == OKAY)
3352 gotcha++;
3353 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3354 if ((cp = strrchr(bp, '{')) == NULL)
3355 goto fail;
3356 octets = atol(&cp[1]) + 2;
3357 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3358 goto fail;
3359 IMAP_OUT(o, MB_COMD, continue)
3360 while (mp->mb_active & MB_COMD) {
3361 ok = imap_answer(mp, twice);
3362 if (response_type == RESPONSE_CONT)
3363 break;
3365 if (ok == STOP) {
3366 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3367 goto trycreate;
3368 goto fail;
3370 while (octets > 0) {
3371 size_t n = (UICMP(z, octets, >, sizeof iob)
3372 ? sizeof iob : (size_t)octets);
3373 octets -= n;
3374 if (n != fread(iob, 1, n, fp))
3375 goto fail;
3376 swrite1(&mp->mb_sock, iob, n, 1);
3378 swrite(&mp->mb_sock, "");
3379 while (mp->mb_active & MB_COMD) {
3380 ok = imap_answer(mp, 0);
3381 if (response_status == RESPONSE_NO && twice++ == 0) {
3382 if (fseek(fp, offs, SEEK_SET) < 0)
3383 goto fail;
3384 goto trycreate;
3387 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3388 if ((offs2 = ftell(fp)) < 0)
3389 goto fail;
3390 fseek(fp, offs1, SEEK_SET);
3391 if (imap_appenduid_cached(mp, fp) == STOP) {
3392 (void)fseek(fp, offs2, SEEK_SET);
3393 goto fail;
3396 } else {
3397 fail:
3398 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3399 rok = STOP;
3401 continue;
3402 trycreate:
3403 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3404 IMAP_OUT(o, MB_COMD, continue)
3405 while (mp->mb_active & MB_COMD)
3406 ok = imap_answer(mp, 1);
3407 if (ok == OKAY)
3408 goto again;
3410 fflush(fp);
3411 rewind(fp);
3412 ftruncate(fileno(fp), 0);
3413 if (gotcha)
3414 imap_close(mp);
3415 free(buf);
3416 return rok;
3419 static char *
3420 imap_strex(char const *cp, char const **xp)
3422 char const *cq;
3423 char *n = NULL;
3424 NYD_ENTER;
3426 if (*cp != '"')
3427 goto jleave;
3429 for (cq = cp + 1; *cq != '\0'; ++cq) {
3430 if (*cq == '\\')
3431 cq++;
3432 else if (*cq == '"')
3433 break;
3435 if (*cq != '"')
3436 goto jleave;
3438 n = salloc(cq - cp + 2);
3439 memcpy(n, cp, cq - cp +1);
3440 n[cq - cp + 1] = '\0';
3441 if (xp != NULL)
3442 *xp = cq + 1;
3443 jleave:
3444 NYD_LEAVE;
3445 return n;
3448 static enum okay
3449 check_expunged(void)
3451 enum okay rv;
3452 NYD_ENTER;
3454 if (expunged_messages > 0) {
3455 n_err(_("Command not executed - messages have been expunged\n"));
3456 rv = STOP;
3457 } else
3458 rv = OKAY;
3459 NYD_LEAVE;
3460 return rv;
3463 FL int
3464 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3466 struct url url;
3467 int rv, omsgCount = msgCount;
3468 NYD_ENTER;
3469 UNUSED(vp);
3471 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3472 n_err(_("Already connected\n"));
3473 rv = 1;
3474 goto jleave;
3477 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3478 rv = 1;
3479 goto jleave;
3481 ok_bclear(disconnected);
3482 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
3484 if (mb.mb_type == MB_CACHE) {
3485 enum fedit_mode fm = FEDIT_NONE;
3486 if (_imap_rdonly)
3487 fm |= FEDIT_RDONLY;
3488 if (!(pstate & PS_EDIT))
3489 fm |= FEDIT_SYSBOX;
3490 _imap_setfile1(&url, fm, 1);
3491 if (msgCount > omsgCount)
3492 newmailinfo(omsgCount);
3494 rv = 0;
3495 jleave:
3496 NYD_LEAVE;
3497 return rv;
3500 FL int
3501 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3503 struct url url;
3504 int rv = 1, *msgvec = vp;
3505 NYD_ENTER;
3507 if (mb.mb_type == MB_CACHE) {
3508 n_err(_("Not connected\n"));
3509 goto jleave;
3511 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3512 n_err(_("The current mailbox is not cached\n"));
3513 goto jleave;
3516 if (!url_parse(&url, CPROTO_IMAP, mailname))
3517 goto jleave;
3519 if (*msgvec)
3520 c_cache(vp);
3521 ok_bset(disconnected, TRU1);
3522 if (mb.mb_type == MB_IMAP) {
3523 enum fedit_mode fm = FEDIT_NONE;
3524 if (_imap_rdonly)
3525 fm |= FEDIT_RDONLY;
3526 if (!(pstate & PS_EDIT))
3527 fm |= FEDIT_SYSBOX;
3528 sclose(&mb.mb_sock);
3529 _imap_setfile1(&url, fm, 1);
3531 rv = 0;
3532 jleave:
3533 NYD_LEAVE;
3534 return rv;
3537 FL int
3538 c_cache(void *vp)
3540 int rv = 1, *msgvec = vp, *ip;
3541 struct message *mp;
3542 NYD_ENTER;
3544 if (mb.mb_type != MB_IMAP) {
3545 n_err(_("Not connected to an IMAP server\n"));
3546 goto jleave;
3548 if (cached_uidvalidity(&mb) == 0) {
3549 n_err(_("The current mailbox is not cached\n"));
3550 goto jleave;
3553 srelax_hold();
3554 for (ip = msgvec; *ip; ++ip) {
3555 mp = &message[*ip - 1];
3556 if (!(mp->m_have & HAVE_BODY)) {
3557 get_body(mp);
3558 srelax();
3561 srelax_rele();
3562 rv = 0;
3563 jleave:
3564 NYD_LEAVE;
3565 return rv;
3568 FL int
3569 disconnected(const char *file)
3571 struct url url;
3572 int rv = 1;
3573 NYD_ENTER;
3575 if (ok_blook(disconnected)) {
3576 rv = 1;
3577 goto jleave;
3580 if (!url_parse(&url, CPROTO_IMAP, file)) {
3581 rv = 0;
3582 goto jleave;
3584 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
3586 jleave:
3587 NYD_LEAVE;
3588 return rv;
3591 FL void
3592 transflags(struct message *omessage, long omsgCount, int transparent)
3594 struct message *omp, *nmp, *newdot, *newprevdot;
3595 int hf;
3596 NYD_ENTER;
3598 omp = omessage;
3599 nmp = message;
3600 newdot = message;
3601 newprevdot = NULL;
3602 while (PTRCMP(omp, <, omessage + omsgCount) &&
3603 PTRCMP(nmp, <, message + msgCount)) {
3604 if (dot && nmp->m_uid == dot->m_uid)
3605 newdot = nmp;
3606 if (prevdot && nmp->m_uid == prevdot->m_uid)
3607 newprevdot = nmp;
3608 if (omp->m_uid == nmp->m_uid) {
3609 hf = nmp->m_flag & MHIDDEN;
3610 if (transparent && mb.mb_type == MB_IMAP)
3611 omp->m_flag &= ~MHIDDEN;
3612 *nmp++ = *omp++;
3613 if (transparent && mb.mb_type == MB_CACHE)
3614 nmp[-1].m_flag |= hf;
3615 } else if (omp->m_uid < nmp->m_uid)
3616 ++omp;
3617 else
3618 ++nmp;
3620 dot = newdot;
3621 setdot(newdot);
3622 prevdot = newprevdot;
3623 free(omessage);
3624 NYD_LEAVE;
3627 FL time_t
3628 imap_read_date_time(const char *cp)
3630 char buf[3];
3631 time_t t;
3632 int i, year, month, day, hour, minute, second, sign = -1;
3633 NYD2_ENTER;
3635 /* "25-Jul-2004 15:33:44 +0200"
3636 * | | | | | |
3637 * 0 5 10 15 20 25 */
3638 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3639 goto jinvalid;
3640 day = strtol(&cp[1], NULL, 10);
3641 for (i = 0;;) {
3642 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3643 break;
3644 if (month_names[++i][0] == '\0')
3645 goto jinvalid;
3647 month = i + 1;
3648 year = strtol(&cp[8], NULL, 10);
3649 hour = strtol(&cp[13], NULL, 10);
3650 minute = strtol(&cp[16], NULL, 10);
3651 second = strtol(&cp[19], NULL, 10);
3652 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3653 goto jinvalid;
3654 switch (cp[22]) {
3655 case '-':
3656 sign = 1;
3657 break;
3658 case '+':
3659 break;
3660 default:
3661 goto jinvalid;
3663 buf[2] = '\0';
3664 buf[0] = cp[23];
3665 buf[1] = cp[24];
3666 t += strtol(buf, NULL, 10) * sign * 3600;
3667 buf[0] = cp[25];
3668 buf[1] = cp[26];
3669 t += strtol(buf, NULL, 10) * sign * 60;
3670 jleave:
3671 NYD2_LEAVE;
3672 return t;
3673 jinvalid:
3674 time(&t);
3675 goto jleave;
3678 FL const char *
3679 imap_make_date_time(time_t t)
3681 static char s[30];
3682 struct tm *tmptr;
3683 int tzdiff, tzdiff_hour, tzdiff_min;
3684 NYD2_ENTER;
3686 tzdiff = t - mktime(gmtime(&t));
3687 tzdiff_hour = (int)(tzdiff / 60);
3688 tzdiff_min = tzdiff_hour % 60;
3689 tzdiff_hour /= 60;
3690 tmptr = localtime(&t);
3691 if (tmptr->tm_isdst > 0)
3692 tzdiff_hour++;
3693 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3694 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3695 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3696 NYD2_LEAVE;
3697 return s;
3699 #endif /* HAVE_IMAP */
3701 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3702 FL char *
3703 imap_quotestr(char const *s)
3705 char *n, *np;
3706 NYD2_ENTER;
3708 np = n = salloc(2 * strlen(s) + 3);
3709 *np++ = '"';
3710 while (*s) {
3711 if (*s == '"' || *s == '\\')
3712 *np++ = '\\';
3713 *np++ = *s++;
3715 *np++ = '"';
3716 *np = '\0';
3717 NYD2_LEAVE;
3718 return n;
3721 FL char *
3722 imap_unquotestr(char const *s)
3724 char *n, *np;
3725 NYD2_ENTER;
3727 if (*s != '"') {
3728 n = savestr(s);
3729 goto jleave;
3732 np = n = salloc(strlen(s) + 1);
3733 while (*++s) {
3734 if (*s == '\\')
3735 s++;
3736 else if (*s == '"')
3737 break;
3738 *np++ = *s;
3740 *np = '\0';
3741 jleave:
3742 NYD2_LEAVE;
3743 return n;
3745 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3747 /* s-it-mode */