FIX "," message specification (since [1c4b8c9], v14.8.4)..
[s-mailx.git] / imap.c
blobb59a40d0d4e1b9e67a6bc44f716a19c255470108
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 = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -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 = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1313 goto jleave;
1315 } else /* same account */
1316 mb.mb_flags |= same_flags;
1318 if (options & OPT_R_FLAG)
1319 fm |= FEDIT_RDONLY;
1320 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
1321 mb.mb_type = MB_IMAP;
1322 cache_dequeue(&mb);
1323 if (imap_select(&mb, &mailsize, &msgCount,
1324 (urlp->url_path.s != NULL ? urlp->url_path.s : "INBOX"), fm) != OKAY) {
1325 /*sclose(&mb.mb_sock);
1326 imap_timer_off();*/
1327 safe_signal(SIGINT, saveint);
1328 safe_signal(SIGPIPE, savepipe);
1329 imaplock = 0;
1330 mb.mb_type = MB_VOID;
1331 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
1332 goto jleave;
1335 jnmail:
1336 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
1337 UNVOLATILE(&prevcount));
1338 jdone:
1339 setmsize(msgCount);
1340 if (!(fm & FEDIT_NEWMAIL) && !transparent)
1341 pstate &= ~PS_SAW_COMMAND;
1342 safe_signal(SIGINT, saveint);
1343 safe_signal(SIGPIPE, savepipe);
1344 imaplock = 0;
1346 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
1347 purgecache(&mb, message, msgCount);
1348 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
1349 mb.mb_threaded = 0;
1350 c_sort((void*)-1);
1353 if ((options & OPT_EXISTONLY) && (mb.mb_type == MB_IMAP ||
1354 mb.mb_type == MB_CACHE)) {
1355 rv = (msgCount == 0);
1356 goto jleave;
1359 if (!(fm & FEDIT_NEWMAIL) && !(pstate & PS_EDIT) && msgCount == 0) {
1360 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
1361 !ok_blook(emptystart))
1362 n_err(_("No mail at %s\n"), urlp->url_p_eu_h_p_p);
1363 rv = 1;
1364 goto jleave;
1367 if (fm & FEDIT_NEWMAIL)
1368 newmailinfo(prevcount);
1369 rv = 0;
1370 jleave:
1371 NYD_LEAVE;
1372 return rv;
1375 static int
1376 imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
1377 int need, const char *head, size_t headsize, long headlines)
1379 char *line = NULL, *lp;
1380 size_t linesize = 0, linelen, size = 0;
1381 int emptyline = 0, lines = 0, excess = 0;
1382 off_t offset;
1383 NYD_ENTER;
1385 fseek(mp->mb_otf, 0L, SEEK_END);
1386 offset = ftell(mp->mb_otf);
1388 if (head)
1389 fwrite(head, 1, headsize, mp->mb_otf);
1391 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
1392 lp = line;
1393 if (linelen > expected) {
1394 excess = linelen - expected;
1395 linelen = expected;
1397 /* TODO >>
1398 * Need to mask 'From ' lines. This cannot be done properly
1399 * since some servers pass them as 'From ' and others as
1400 * '>From '. Although one could identify the first kind of
1401 * server in principle, it is not possible to identify the
1402 * second as '>From ' may also come from a server of the
1403 * first type as actual data. So do what is absolutely
1404 * necessary only - mask 'From '.
1406 * If the line is the first line of the message header, it
1407 * is likely a real 'From ' line. In this case, it is just
1408 * ignored since it violates all standards.
1409 * TODO can the latter *really* happen??
1410 * TODO <<
1412 /* Since we simply copy over data without doing any transfer
1413 * encoding reclassification/adjustment we *have* to perform
1414 * RFC 4155 compliant From_ quoting here */
1415 if (emptyline && is_head(lp, linelen, FAL0)) {
1416 fputc('>', mp->mb_otf);
1417 ++size;
1419 emptyline = 0;
1420 if (lp[linelen-1] == '\n' && (linelen == 1 || lp[linelen-2] == '\r')) {
1421 if (linelen > 2) {
1422 fwrite(lp, 1, linelen - 2, mp->mb_otf);
1423 size += linelen - 1;
1424 } else {
1425 emptyline = 1;
1426 ++size;
1428 fputc('\n', mp->mb_otf);
1429 } else {
1430 fwrite(lp, 1, linelen, mp->mb_otf);
1431 size += linelen;
1433 ++lines;
1434 if ((expected -= linelen) <= 0)
1435 break;
1437 if (!emptyline) {
1438 /* This is very ugly; but some IMAP daemons don't end a
1439 * message with \r\n\r\n, and we need \n\n for mbox format */
1440 fputc('\n', mp->mb_otf);
1441 ++lines;
1442 ++size;
1444 fflush(mp->mb_otf);
1446 if (m != NULL) {
1447 m->m_size = size + headsize;
1448 m->m_lines = lines + headlines;
1449 m->m_block = mailx_blockof(offset);
1450 m->m_offset = mailx_offsetof(offset);
1451 switch (need) {
1452 case NEED_HEADER:
1453 m->m_have |= HAVE_HEADER;
1454 break;
1455 case NEED_BODY:
1456 m->m_have |= HAVE_HEADER | HAVE_BODY;
1457 m->m_xlines = m->m_lines;
1458 m->m_xsize = m->m_size;
1459 break;
1462 free(line);
1463 NYD_LEAVE;
1464 return excess;
1467 static void
1468 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
1469 const char *head, size_t headsize, long headlines)
1471 off_t offset;
1472 size_t len;
1473 NYD_ENTER;
1475 len = strlen(str);
1476 fseek(mp->mb_otf, 0L, SEEK_END);
1477 offset = ftell(mp->mb_otf);
1478 if (head)
1479 fwrite(head, 1, headsize, mp->mb_otf);
1480 if (len > 0) {
1481 fwrite(str, 1, len, mp->mb_otf);
1482 fputc('\n', mp->mb_otf);
1483 ++len;
1485 fflush(mp->mb_otf);
1487 if (m != NULL) {
1488 m->m_size = headsize + len;
1489 m->m_lines = headlines + 1;
1490 m->m_block = mailx_blockof(offset);
1491 m->m_offset = mailx_offsetof(offset);
1492 m->m_have |= HAVE_HEADER | HAVE_BODY;
1493 m->m_xlines = m->m_lines;
1494 m->m_xsize = m->m_size;
1496 NYD_LEAVE;
1499 static enum okay
1500 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
1502 char o[LINESIZE];
1503 struct message mt;
1504 sighandler_type volatile saveint, savepipe;
1505 char * volatile head;
1506 char const *cp, *loc, * volatile item, * volatile resp;
1507 size_t expected;
1508 size_t volatile headsize;
1509 int number;
1510 FILE *queuefp;
1511 long volatile headlines;
1512 long n;
1513 ul_i volatile u;
1514 enum okay ok;
1515 NYD_X;
1517 saveint = savepipe = SIG_IGN;
1518 head = NULL;
1519 cp = loc = item = resp = NULL;
1520 headsize = 0;
1521 number = (int)PTR2SIZE(m - message + 1);
1522 queuefp = NULL;
1523 headlines = 0;
1524 n = -1;
1525 u = 0;
1526 ok = STOP;
1528 if (getcache(mp, m, need) == OKAY)
1529 return OKAY;
1530 if (mp->mb_type == MB_CACHE) {
1531 n_err(_("Message %lu not available\n"), (ul_i)number);
1532 return STOP;
1535 if (mp->mb_sock.s_fd < 0) {
1536 n_err(_("IMAP connection closed\n"));
1537 return STOP;
1540 switch (need) {
1541 case NEED_HEADER:
1542 resp = item = "RFC822.HEADER";
1543 break;
1544 case NEED_BODY:
1545 item = "BODY.PEEK[]";
1546 resp = "BODY[]";
1547 if (m->m_flag & HAVE_HEADER && m->m_size) {
1548 char *hdr = smalloc(m->m_size);
1549 fflush(mp->mb_otf);
1550 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1551 SEEK_SET) < 0 ||
1552 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
1553 free(hdr);
1554 break;
1556 head = hdr;
1557 headsize = m->m_size;
1558 headlines = m->m_lines;
1559 item = "BODY.PEEK[TEXT]";
1560 resp = "BODY[TEXT]";
1562 break;
1563 case NEED_UNSPEC:
1564 return STOP;
1567 imaplock = 1;
1568 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1569 if (sigsetjmp(imapjmp, 1)) {
1570 safe_signal(SIGINT, saveint);
1571 safe_signal(SIGPIPE, savepipe);
1572 imaplock = 0;
1573 return STOP;
1575 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1576 safe_signal(SIGINT, &_imap_maincatch);
1577 if (savepipe != SIG_IGN)
1578 safe_signal(SIGPIPE, imapcatch);
1580 if (m->m_uid)
1581 snprintf(o, sizeof o, "%s UID FETCH %lu (%s)\r\n",
1582 tag(1), m->m_uid, item);
1583 else {
1584 if (check_expunged() == STOP)
1585 goto out;
1586 snprintf(o, sizeof o, "%s FETCH %u (%s)\r\n", tag(1), number, item);
1588 IMAP_OUT(o, MB_COMD, goto out)
1589 for (;;) {
1590 ok = imap_answer(mp, 1);
1591 if (ok == STOP)
1592 break;
1593 if (response_status != RESPONSE_OTHER ||
1594 response_other != MESSAGE_DATA_FETCH)
1595 continue;
1596 if ((loc = asccasestr(responded_other_text, resp)) == NULL)
1597 continue;
1598 if (m->m_uid) {
1599 if ((cp = asccasestr(responded_other_text, "UID "))) {
1600 u = atol(&cp[4]);
1601 n = 0;
1602 } else {
1603 n = -1;
1604 u = 0;
1606 } else
1607 n = responded_other_number;
1608 if ((cp = strrchr(responded_other_text, '{')) == NULL) {
1609 if (m->m_uid ? m->m_uid != u : n != number)
1610 continue;
1611 if ((cp = strchr(loc, '"')) != NULL) {
1612 cp = imap_unquotestr(cp);
1613 imap_putstr(mp, m, cp, head, headsize, headlines);
1614 } else {
1615 m->m_have |= HAVE_HEADER|HAVE_BODY;
1616 m->m_xlines = m->m_lines;
1617 m->m_xsize = m->m_size;
1619 goto out;
1621 expected = atol(&cp[1]);
1622 if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
1623 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
1624 continue;
1626 mt = *m;
1627 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
1628 if (n >= 0) {
1629 commitmsg(mp, m, &mt, mt.m_have);
1630 break;
1632 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1633 if (options & OPT_VERBVERB)
1634 fputs(imapbuf, stderr);
1635 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1636 u = atol(&cp[4]);
1637 if (u == m->m_uid) {
1638 commitmsg(mp, m, &mt, mt.m_have);
1639 break;
1644 out:
1645 while (mp->mb_active & MB_COMD)
1646 ok = imap_answer(mp, 1);
1648 if (saveint != SIG_IGN)
1649 safe_signal(SIGINT, saveint);
1650 if (savepipe != SIG_IGN)
1651 safe_signal(SIGPIPE, savepipe);
1652 imaplock--;
1654 if (ok == OKAY)
1655 putcache(mp, m);
1656 if (head != NULL)
1657 free(head);
1658 if (interrupts)
1659 onintr(0);
1660 return ok;
1663 FL enum okay
1664 imap_header(struct message *m)
1666 enum okay rv;
1667 NYD_ENTER;
1669 rv = imap_get(&mb, m, NEED_HEADER);
1670 NYD_LEAVE;
1671 return rv;
1675 FL enum okay
1676 imap_body(struct message *m)
1678 enum okay rv;
1679 NYD_ENTER;
1681 rv = imap_get(&mb, m, NEED_BODY);
1682 NYD_LEAVE;
1683 return rv;
1686 static void
1687 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
1688 enum havespec have)
1690 NYD_ENTER;
1691 tomp->m_size = frommp->m_size;
1692 tomp->m_lines = frommp->m_lines;
1693 tomp->m_block = frommp->m_block;
1694 tomp->m_offset = frommp->m_offset;
1695 tomp->m_have = have;
1696 if (have & HAVE_BODY) {
1697 tomp->m_xlines = frommp->m_lines;
1698 tomp->m_xsize = frommp->m_size;
1700 putcache(mp, tomp);
1701 NYD_LEAVE;
1704 static enum okay
1705 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
1707 /* bot > topp */
1708 char o[LINESIZE];
1709 char const *cp;
1710 struct message mt;
1711 size_t expected;
1712 int n = 0, u;
1713 FILE *queuefp = NULL;
1714 enum okay ok;
1715 NYD_X;
1717 if (m[bot].m_uid)
1718 snprintf(o, sizeof o, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1719 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
1720 else {
1721 if (check_expunged() == STOP)
1722 return STOP;
1723 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1724 tag(1), bot, topp);
1726 IMAP_OUT(o, MB_COMD, return STOP)
1728 srelax_hold();
1729 for (;;) {
1730 ok = imap_answer(mp, 1);
1731 if (response_status != RESPONSE_OTHER)
1732 break;
1733 if (response_other != MESSAGE_DATA_FETCH)
1734 continue;
1735 if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0) {
1736 srelax_rele();
1737 return STOP;
1739 if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
1740 continue;
1741 expected = atol(&cp[1]);
1742 if (m[bot-1].m_uid) {
1743 if ((cp = asccasestr(responded_other_text, "UID ")) != NULL) {
1744 u = atoi(&cp[4]);
1745 for (n = bot; n <= topp; n++)
1746 if ((unsigned long)u == m[n-1].m_uid)
1747 break;
1748 if (n > topp) {
1749 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1750 continue;
1752 } else
1753 n = -1;
1754 } else {
1755 n = responded_other_number;
1756 if (n <= 0 || n > msgCount) {
1757 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
1758 continue;
1761 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
1762 if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
1763 commitmsg(mp, &m[n-1], &mt, HAVE_HEADER);
1764 if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
1765 if (options & OPT_VERBVERB)
1766 fputs(imapbuf, stderr);
1767 if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
1768 u = atoi(&cp[4]);
1769 for (n = bot; n <= topp; n++)
1770 if ((unsigned long)u == m[n-1].m_uid)
1771 break;
1772 if (n <= topp && !(m[n-1].m_have & HAVE_HEADER))
1773 commitmsg(mp, &m[n-1], &mt,HAVE_HEADER);
1774 n = 0;
1777 srelax();
1779 srelax_rele();
1781 while (mp->mb_active & MB_COMD)
1782 ok = imap_answer(mp, 1);
1783 return ok;
1786 FL void
1787 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
1789 sighandler_type saveint, savepipe;
1790 /*enum okay ok = STOP;*/
1791 int i, chunk = 256;
1792 NYD_X;
1794 if (mb.mb_type == MB_CACHE)
1795 return;
1796 if (bot < 1)
1797 bot = 1;
1798 if (topp > msgCount)
1799 topp = msgCount;
1800 for (i = bot; i < topp; i++) {
1801 if (message[i-1].m_have & HAVE_HEADER ||
1802 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1803 bot = i+1;
1804 else
1805 break;
1807 for (i = topp; i > bot; i--) {
1808 if (message[i-1].m_have & HAVE_HEADER ||
1809 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
1810 topp = i-1;
1811 else
1812 break;
1814 if (bot >= topp)
1815 return;
1817 imaplock = 1;
1818 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1819 safe_signal(SIGINT, &_imap_maincatch);
1820 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1821 if (sigsetjmp(imapjmp, 1) == 0) {
1822 if (savepipe != SIG_IGN)
1823 safe_signal(SIGPIPE, imapcatch);
1825 for (i = bot; i <= topp; i += chunk) {
1826 int j = i + chunk - 1;
1827 j = MIN(j, topp);
1828 if (visible(message + j))
1829 /*ok = */imap_fetchheaders(&mb, message, i, j);
1830 if (interrupts)
1831 onintr(0); /* XXX imaplock? */
1834 safe_signal(SIGINT, saveint);
1835 safe_signal(SIGPIPE, savepipe);
1836 imaplock = 0;
1839 static enum okay
1840 __imap_exit(struct mailbox *mp)
1842 char o[LINESIZE];
1843 FILE *queuefp = NULL;
1844 NYD_X;
1846 mp->mb_active |= MB_BYE;
1847 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
1848 IMAP_OUT(o, MB_COMD, return STOP)
1849 IMAP_ANSWER()
1850 return OKAY;
1853 static enum okay
1854 imap_exit(struct mailbox *mp)
1856 enum okay rv;
1857 NYD_ENTER;
1859 rv = __imap_exit(mp);
1860 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1861 free(mp->mb_imap_pass);
1862 free(mp->mb_imap_account);
1863 free(mp->mb_imap_mailbox);
1864 if (mp->mb_cache_directory != NULL)
1865 free(mp->mb_cache_directory);
1866 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1867 mp->mb_imap_account =
1868 mp->mb_imap_mailbox =
1869 mp->mb_cache_directory = "";
1870 #else
1871 mp->mb_imap_account = NULL; /* for assert legacy time.. */
1872 mp->mb_imap_mailbox = NULL;
1873 mp->mb_cache_directory = NULL;
1874 #endif
1875 #endif
1876 sclose(&mp->mb_sock);
1877 NYD_LEAVE;
1878 return rv;
1881 static enum okay
1882 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
1884 NYD_ENTER;
1885 imap_store(mp, m, n, '+', "\\Deleted", needstat);
1886 if (mp->mb_type == MB_IMAP)
1887 delcache(mp, m);
1888 NYD_LEAVE;
1889 return OKAY;
1892 static enum okay
1893 imap_close(struct mailbox *mp)
1895 char o[LINESIZE];
1896 FILE *queuefp = NULL;
1897 NYD_X;
1899 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
1900 IMAP_OUT(o, MB_COMD, return STOP)
1901 IMAP_ANSWER()
1902 return OKAY;
1905 static enum okay
1906 imap_update(struct mailbox *mp)
1908 struct message *m;
1909 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
1910 NYD_ENTER;
1912 if (!(pstate & PS_EDIT) && mp->mb_perm != 0) {
1913 holdbits();
1914 c = 0;
1915 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
1916 if (m->m_flag & MBOX)
1917 ++c;
1918 if (c > 0)
1919 if (makembox() == STOP)
1920 goto jbypass;
1923 gotcha = held = 0;
1924 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
1925 if (mp->mb_perm == 0)
1926 dodel = 0;
1927 else if (pstate & PS_EDIT)
1928 dodel = ((m->m_flag & MDELETED) != 0);
1929 else
1930 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
1932 /* Fetch the result after around each 800 STORE commands
1933 * sent (approx. 32k data sent). Otherwise, servers will
1934 * try to flush the return queue at some point, leading
1935 * to a deadlock if we are still writing commands but not
1936 * reading their results */
1937 needstat = stored > 0 && stored % 800 == 0;
1938 /* Even if this message has been deleted, continue
1939 * to set further flags. This is necessary to support
1940 * Gmail semantics, where "delete" actually means
1941 * "archive", and the flags are applied to the copy
1942 * in "All Mail" */
1943 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
1944 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
1945 stored++;
1947 if (m->m_flag & MFLAG) {
1948 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
1949 stored++;
1951 if (m->m_flag & MUNFLAG) {
1952 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
1953 stored++;
1955 if (m->m_flag & MANSWER) {
1956 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
1957 stored++;
1959 if (m->m_flag & MUNANSWER) {
1960 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
1961 stored++;
1963 if (m->m_flag & MDRAFT) {
1964 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
1965 stored++;
1967 if (m->m_flag & MUNDRAFT) {
1968 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
1969 stored++;
1972 if (dodel) {
1973 imap_delete(mp, m-message+1, m, needstat);
1974 stored++;
1975 gotcha++;
1976 } else if (mp->mb_type != MB_CACHE ||
1977 (!(pstate & PS_EDIT) &&
1978 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
1979 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
1980 (MPRESERVE | MTOUCH) ||
1981 ((pstate & PS_EDIT) && !(m->m_flag & MDELETED)))
1982 held++;
1983 if (m->m_flag & MNEW) {
1984 m->m_flag &= ~MNEW;
1985 m->m_flag |= MSTATUS;
1988 jbypass:
1989 if (gotcha)
1990 imap_close(mp);
1992 for (m = &message[0]; PTRCMP(m, <, message + msgCount); ++m)
1993 if (!(m->m_flag & MUNLINKED) &&
1994 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
1995 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
1996 putcache(mp, m);
1997 modflags++;
2000 /* XXX should be readonly (but our IMAP code is weird...) */
2001 if (!(options & (OPT_EXISTONLY | OPT_HEADERSONLY | OPT_HEADERLIST))) {
2002 if ((gotcha || modflags) && (pstate & PS_EDIT)) {
2003 printf(_("\"%s\" "), displayname);
2004 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2005 ? _("complete\n") : _("updated.\n"));
2006 } else if (held && !(pstate & PS_EDIT) && mp->mb_perm != 0) {
2007 if (held == 1)
2008 printf(_("Held 1 message in %s\n"), displayname);
2009 else
2010 printf(_("Held %d messages in %s\n"), held, displayname);
2012 fflush(stdout);
2014 NYD_LEAVE;
2015 return OKAY;
2018 FL void
2019 imap_quit(void)
2021 sighandler_type volatile saveint, savepipe;
2022 NYD_ENTER;
2024 if (mb.mb_type == MB_CACHE) {
2025 imap_update(&mb);
2026 goto jleave;
2029 if (mb.mb_sock.s_fd < 0) {
2030 n_err(_("IMAP connection closed\n"));
2031 goto jleave;
2034 imaplock = 1;
2035 saveint = safe_signal(SIGINT, SIG_IGN);
2036 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2037 if (sigsetjmp(imapjmp, 1)) {
2038 safe_signal(SIGINT, saveint);
2039 safe_signal(SIGPIPE, saveint);
2040 imaplock = 0;
2041 goto jleave;
2043 if (saveint != SIG_IGN)
2044 safe_signal(SIGINT, imapcatch);
2045 if (savepipe != SIG_IGN)
2046 safe_signal(SIGPIPE, imapcatch);
2048 imap_update(&mb);
2049 if (!same_imap_account)
2050 imap_exit(&mb);
2052 safe_signal(SIGINT, saveint);
2053 safe_signal(SIGPIPE, savepipe);
2054 imaplock = 0;
2055 jleave:
2056 NYD_LEAVE;
2059 static enum okay
2060 imap_store(struct mailbox *mp, struct message *m, int n, int c, const char *sp,
2061 int needstat)
2063 char o[LINESIZE];
2064 FILE *queuefp = NULL;
2065 NYD_X;
2067 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2068 return STOP;
2069 if (m->m_uid)
2070 snprintf(o, sizeof o, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2071 tag(1), m->m_uid, c, sp);
2072 else {
2073 if (check_expunged() == STOP)
2074 return STOP;
2075 snprintf(o, sizeof o, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n, c, sp);
2077 IMAP_OUT(o, MB_COMD, return STOP)
2078 if (needstat)
2079 IMAP_ANSWER()
2080 else
2081 mb.mb_active &= ~MB_COMD;
2082 if (queuefp != NULL)
2083 Fclose(queuefp);
2084 return OKAY;
2087 FL enum okay
2088 imap_undelete(struct message *m, int n)
2090 enum okay rv;
2091 NYD_ENTER;
2093 rv = imap_unstore(m, n, "\\Deleted");
2094 NYD_LEAVE;
2095 return rv;
2098 FL enum okay
2099 imap_unread(struct message *m, int n)
2101 enum okay rv;
2102 NYD_ENTER;
2104 rv = imap_unstore(m, n, "\\Seen");
2105 NYD_LEAVE;
2106 return rv;
2109 static enum okay
2110 imap_unstore(struct message *m, int n, const char *flag)
2112 sighandler_type saveint, savepipe;
2113 enum okay rv = STOP;
2114 NYD_ENTER;
2116 imaplock = 1;
2117 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2118 safe_signal(SIGINT, &_imap_maincatch);
2119 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2120 if (sigsetjmp(imapjmp, 1) == 0) {
2121 if (savepipe != SIG_IGN)
2122 safe_signal(SIGPIPE, imapcatch);
2124 rv = imap_store(&mb, m, n, '-', flag, 1);
2126 safe_signal(SIGINT, saveint);
2127 safe_signal(SIGPIPE, savepipe);
2128 imaplock = 0;
2130 NYD_LEAVE;
2131 if (interrupts)
2132 onintr(0);
2133 return rv;
2136 static const char *
2137 tag(int new)
2139 static char ts[20];
2140 static long n;
2141 NYD2_ENTER;
2143 if (new)
2144 ++n;
2145 snprintf(ts, sizeof ts, "T%lu", n);
2146 NYD2_LEAVE;
2147 return ts;
2150 FL int
2151 c_imap_imap(void *vp)
2153 char o[LINESIZE];
2154 sighandler_type saveint, savepipe;
2155 struct mailbox *mp = &mb;
2156 FILE *queuefp = NULL;
2157 enum okay volatile ok = STOP;
2158 NYD_X;
2160 if (mp->mb_type != MB_IMAP) {
2161 printf("Not operating on an IMAP mailbox.\n");
2162 return 1;
2164 imaplock = 1;
2165 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2166 safe_signal(SIGINT, &_imap_maincatch);
2167 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2168 if (sigsetjmp(imapjmp, 1) == 0) {
2169 if (savepipe != SIG_IGN)
2170 safe_signal(SIGPIPE, imapcatch);
2172 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
2173 IMAP_OUT(o, MB_COMD, goto out)
2174 while (mp->mb_active & MB_COMD) {
2175 ok = imap_answer(mp, 0);
2176 fputs(responded_text, stdout);
2179 out:
2180 safe_signal(SIGINT, saveint);
2181 safe_signal(SIGPIPE, savepipe);
2182 imaplock = 0;
2184 if (interrupts)
2185 onintr(0);
2186 return ok != OKAY;
2189 FL int
2190 imap_newmail(int nmail)
2192 NYD_ENTER;
2194 if (nmail && had_exists < 0 && had_expunge < 0) {
2195 imaplock = 1;
2196 imap_noop();
2197 imaplock = 0;
2200 if (had_exists == msgCount && had_expunge < 0)
2201 /* Some servers always respond with EXISTS to NOOP. If
2202 * the mailbox has been changed but the number of messages
2203 * has not, an EXPUNGE must also had been sent; otherwise,
2204 * nothing has changed */
2205 had_exists = -1;
2206 NYD_LEAVE;
2207 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
2210 static char *
2211 imap_putflags(int f)
2213 const char *cp;
2214 char *buf, *bp;
2215 NYD2_ENTER;
2217 bp = buf = salloc(100);
2218 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
2219 *bp++ = '(';
2220 if (f & MREAD) {
2221 if (bp[-1] != '(')
2222 *bp++ = ' ';
2223 for (cp = "\\Seen"; *cp; cp++)
2224 *bp++ = *cp;
2226 if (f & MFLAGGED) {
2227 if (bp[-1] != '(')
2228 *bp++ = ' ';
2229 for (cp = "\\Flagged"; *cp; cp++)
2230 *bp++ = *cp;
2232 if (f & MANSWERED) {
2233 if (bp[-1] != '(')
2234 *bp++ = ' ';
2235 for (cp = "\\Answered"; *cp; cp++)
2236 *bp++ = *cp;
2238 if (f & MDRAFT) {
2239 if (bp[-1] != '(')
2240 *bp++ = ' ';
2241 for (cp = "\\Draft"; *cp; cp++)
2242 *bp++ = *cp;
2244 *bp++ = ')';
2245 *bp++ = ' ';
2247 *bp = '\0';
2248 NYD2_LEAVE;
2249 return buf;
2252 static void
2253 imap_getflags(const char *cp, char const **xp, enum mflag *f)
2255 NYD2_ENTER;
2256 while (*cp != ')') {
2257 if (*cp == '\\') {
2258 if (ascncasecmp(cp, "\\Seen", 5) == 0)
2259 *f |= MREAD;
2260 else if (ascncasecmp(cp, "\\Recent", 7) == 0)
2261 *f |= MNEW;
2262 else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
2263 *f |= MDELETED;
2264 else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
2265 *f |= MFLAGGED;
2266 else if (ascncasecmp(cp, "\\Answered", 9) == 0)
2267 *f |= MANSWERED;
2268 else if (ascncasecmp(cp, "\\Draft", 6) == 0)
2269 *f |= MDRAFTED;
2271 cp++;
2274 if (xp != NULL)
2275 *xp = cp;
2276 NYD2_LEAVE;
2279 static enum okay
2280 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
2281 long xsize, enum mflag flag, time_t t)
2283 char o[LINESIZE], *buf;
2284 size_t bufsize, buflen, cnt;
2285 long size, lines, ysize;
2286 int twice = 0;
2287 FILE *queuefp = NULL;
2288 enum okay rv;
2289 NYD_ENTER;
2291 if (mp->mb_type == MB_CACHE) {
2292 queuefp = cache_queue(mp);
2293 if (queuefp == NULL) {
2294 rv = STOP;
2295 buf = NULL;
2296 goto jleave;
2298 rv = OKAY;
2299 } else
2300 rv = STOP;
2302 buf = smalloc(bufsize = LINESIZE);
2303 buflen = 0;
2304 jagain:
2305 size = xsize;
2306 cnt = fsize(fp);
2307 if (fseek(fp, off1, SEEK_SET) < 0) {
2308 rv = STOP;
2309 goto jleave;
2312 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
2313 tag(1), imap_quotestr(name), imap_putflags(flag),
2314 imap_make_date_time(t), size);
2315 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2316 while (mp->mb_active & MB_COMD) {
2317 rv = imap_answer(mp, twice);
2318 if (response_type == RESPONSE_CONT)
2319 break;
2322 if (mp->mb_type != MB_CACHE && rv == STOP) {
2323 if (twice == 0)
2324 goto jtrycreate;
2325 else
2326 goto jleave;
2329 lines = ysize = 0;
2330 while (size > 0) {
2331 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2332 lines++;
2333 ysize += buflen;
2334 buf[buflen - 1] = '\r';
2335 buf[buflen] = '\n';
2336 if (mp->mb_type != MB_CACHE)
2337 swrite1(&mp->mb_sock, buf, buflen+1, 1);
2338 else if (queuefp)
2339 fwrite(buf, 1, buflen+1, queuefp);
2340 size -= buflen + 1;
2342 if (mp->mb_type != MB_CACHE)
2343 swrite(&mp->mb_sock, "\r\n");
2344 else if (queuefp)
2345 fputs("\r\n", queuefp);
2346 while (mp->mb_active & MB_COMD) {
2347 rv = imap_answer(mp, 0);
2348 if (response_status == RESPONSE_NO /*&&
2349 ascncasecmp(responded_text,
2350 "[TRYCREATE] ", 12) == 0*/) {
2351 jtrycreate:
2352 if (twice++) {
2353 rv = STOP;
2354 goto jleave;
2356 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), imap_quotestr(name));
2357 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
2358 while (mp->mb_active & MB_COMD)
2359 rv = imap_answer(mp, 1);
2360 if (rv == STOP)
2361 goto jleave;
2362 imap_created_mailbox++;
2363 goto jagain;
2364 } else if (rv != OKAY)
2365 n_err(_("IMAP error: %s"), responded_text);
2366 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
2367 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
2369 jleave:
2370 if (queuefp != NULL)
2371 Fclose(queuefp);
2372 if (buf != NULL)
2373 free(buf);
2374 NYD_LEAVE;
2375 return rv;
2378 static enum okay
2379 imap_append0(struct mailbox *mp, const char *name, FILE *fp)
2381 char *buf, *bp, *lp;
2382 size_t bufsize, buflen, cnt;
2383 off_t off1 = -1, offs;
2384 int flag;
2385 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
2386 time_t tim;
2387 long size;
2388 enum okay rv;
2389 NYD_ENTER;
2391 buf = smalloc(bufsize = LINESIZE);
2392 buflen = 0;
2393 cnt = fsize(fp);
2394 offs = ftell(fp);
2395 time(&tim);
2396 size = 0;
2398 for (flag = MNEW, state = _NLSEP;;) {
2399 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
2401 if (bp == NULL ||
2402 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
2403 is_head(buf, buflen, FAL0))) {
2404 if (off1 != (off_t)-1) {
2405 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
2406 if (rv == STOP)
2407 goto jleave;
2408 fseek(fp, offs+buflen, SEEK_SET);
2410 off1 = offs + buflen;
2411 size = 0;
2412 flag = MNEW;
2413 state = _INHEAD;
2414 if (bp == NULL)
2415 break;
2416 tim = unixtime(buf);
2417 } else
2418 size += buflen+1;
2419 offs += buflen;
2421 state &= ~_NLSEP;
2422 if (buf[0] == '\n') {
2423 state &= ~_INHEAD;
2424 state |= _NLSEP;
2425 } else if (state & _INHEAD) {
2426 if (ascncasecmp(buf, "status", 6) == 0) {
2427 lp = &buf[6];
2428 while (whitechar(*lp))
2429 lp++;
2430 if (*lp == ':')
2431 while (*++lp != '\0')
2432 switch (*lp) {
2433 case 'R':
2434 flag |= MREAD;
2435 break;
2436 case 'O':
2437 flag &= ~MNEW;
2438 break;
2440 } else if (ascncasecmp(buf, "x-status", 8) == 0) {
2441 lp = &buf[8];
2442 while (whitechar(*lp))
2443 lp++;
2444 if (*lp == ':')
2445 while (*++lp != '\0')
2446 switch (*lp) {
2447 case 'F':
2448 flag |= MFLAGGED;
2449 break;
2450 case 'A':
2451 flag |= MANSWERED;
2452 break;
2453 case 'T':
2454 flag |= MDRAFTED;
2455 break;
2460 rv = OKAY;
2461 jleave:
2462 free(buf);
2463 NYD_LEAVE;
2464 return rv;
2467 FL enum okay
2468 imap_append(const char *xserver, FILE *fp)
2470 sighandler_type volatile saveint, savepipe;
2471 struct url url;
2472 struct ccred ccred;
2473 char const * volatile mbx;
2474 enum okay rv = STOP;
2475 NYD_ENTER;
2477 if (!url_parse(&url, CPROTO_IMAP, xserver))
2478 goto j_leave;
2479 if (!ok_blook(v15_compat) &&
2480 (!url.url_had_user || url.url_pass.s != NULL))
2481 n_err(_("New-style URL used without *v15-compat* being set!\n"));
2482 mbx = (url.url_path.s != NULL) ? url.url_path.s : "INBOX";
2484 imaplock = 1;
2485 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2486 safe_signal(SIGINT, &_imap_maincatch);
2487 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2488 if (sigsetjmp(imapjmp, 1))
2489 goto jleave;
2490 if (savepipe != SIG_IGN)
2491 safe_signal(SIGPIPE, imapcatch);
2493 if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) && mb.mb_imap_account &&
2494 !strcmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
2495 rv = imap_append0(&mb, mbx, fp);
2496 } else {
2497 struct mailbox mx;
2499 memset(&mx, 0, sizeof mx);
2501 if (!_imap_getcred(&mx, &ccred, &url))
2502 goto jleave;
2504 if (disconnected(url.url_p_eu_h_p) == 0) {
2505 if (!sopen(&mx.mb_sock, &url))
2506 goto jfail;
2507 mx.mb_sock.s_desc = "IMAP";
2508 mx.mb_type = MB_IMAP;
2509 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2510 /* TODO the code now did
2511 * TODO mx.mb_imap_mailbox = mbx;
2512 * TODO though imap_mailbox is sfree()d and mbx
2513 * TODO is possibly even a constant
2514 * TODO i changed this to sstrdup() sofar, as is used
2515 * TODO somewhere else in this file for this! */
2516 mx.mb_imap_mailbox = sstrdup(mbx);
2517 if (imap_preauth(&mx, &url) != OKAY ||
2518 imap_auth(&mx, &ccred) != OKAY) {
2519 sclose(&mx.mb_sock);
2520 goto jfail;
2522 rv = imap_append0(&mx, mbx, fp);
2523 imap_exit(&mx);
2524 } else {
2525 mx.mb_imap_account = UNCONST(url.url_p_eu_h_p);
2526 mx.mb_imap_mailbox = sstrdup(mbx); /* TODO as above */
2527 mx.mb_type = MB_CACHE;
2528 rv = imap_append0(&mx, mbx, fp);
2530 jfail:
2534 jleave:
2535 safe_signal(SIGINT, saveint);
2536 safe_signal(SIGPIPE, savepipe);
2537 imaplock = 0;
2538 j_leave:
2539 NYD_LEAVE;
2540 if (interrupts)
2541 onintr(0);
2542 return rv;
2545 static enum okay
2546 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
2547 struct list_item **lend, int level)
2549 char o[LINESIZE], *cp;
2550 const char *bp;
2551 FILE *queuefp = NULL;
2552 struct list_item *lp;
2553 enum okay ok = STOP;
2554 NYD_X;
2556 *list = *lend = NULL;
2557 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base));
2558 IMAP_OUT(o, MB_COMD, return STOP)
2559 while (mp->mb_active & MB_COMD) {
2560 ok = imap_answer(mp, 1);
2561 if (response_status == RESPONSE_OTHER &&
2562 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
2563 cp = imap_unquotestr(list_name);
2564 lp = csalloc(1, sizeof *lp);
2565 lp->l_name = cp;
2566 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
2567 ++cp;
2568 lp->l_base = *cp ? cp : savestr(base);
2569 lp->l_attr = list_attributes;
2570 lp->l_level = level+1;
2571 lp->l_delim = list_hierarchy_delimiter;
2572 if (*list && *lend) {
2573 (*lend)->l_next = lp;
2574 *lend = lp;
2575 } else
2576 *list = *lend = lp;
2579 return ok;
2582 static enum okay
2583 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
2585 struct list_item *list, *lend, *lp, *lx, *ly;
2586 int n, depth;
2587 const char *bp;
2588 char *cp;
2589 enum okay rv;
2590 NYD_ENTER;
2592 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
2593 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
2594 goto jleave;
2595 rv = OKAY;
2596 if (list == NULL || lend == NULL)
2597 goto jleave;
2599 for (lp = list; lp; lp = lp->l_next)
2600 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
2601 !(lp->l_attr & LIST_NOINFERIORS)) {
2602 cp = salloc((n = strlen(lp->l_name)) + 2);
2603 memcpy(cp, lp->l_name, n);
2604 cp[n] = lp->l_delim;
2605 cp[n+1] = '\0';
2606 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
2607 lp->l_has_children = 1;
2608 if (strcmp(cp, lx->l_name) == 0)
2609 lx = lx->l_next;
2610 if (lx) {
2611 lend->l_next = lx;
2612 lend = ly;
2617 for (lp = list; lp; lp = lp->l_next) {
2618 if (strip) {
2619 cp = lp->l_name;
2620 for (bp = base; *bp && *bp == *cp; bp++)
2621 cp++;
2622 } else
2623 cp = lp->l_name;
2624 if (!(lp->l_attr & LIST_NOSELECT))
2625 fprintf(fp, "%s\n", *cp ? cp : base);
2626 else if (lp->l_has_children == 0)
2627 fprintf(fp, "%s%c\n", *cp ? cp : base,
2628 (lp->l_delim != EOF ? lp->l_delim : '\n'));
2630 jleave:
2631 NYD_LEAVE;
2632 return rv;
2635 FL void
2636 imap_folders(const char * volatile name, int strip)
2638 sighandler_type saveint, savepipe;
2639 const char *fold, *cp, *sp;
2640 FILE * volatile fp;
2641 NYD_ENTER;
2643 cp = protbase(name);
2644 sp = mb.mb_imap_account;
2645 if (sp == NULL || strcmp(cp, sp)) {
2646 n_err(
2647 _("Cannot perform `folders' but when on the very IMAP "
2648 "account; the current one is\n `%s' -- "
2649 "try `folders @'\n"),
2650 (sp != NULL ? sp : _("[NONE]")));
2651 goto jleave;
2654 fold = imap_fileof(name);
2655 if (options & OPT_TTYOUT) {
2656 if ((fp = Ftmp(NULL, "imapfold", OF_RDWR | OF_UNLINK | OF_REGISTER,
2657 0600)) == NULL) {
2658 n_perr(_("tmpfile"), 0);
2659 goto jleave;
2661 } else
2662 fp = stdout;
2664 imaplock = 1;
2665 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2666 safe_signal(SIGINT, &_imap_maincatch);
2667 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2668 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
2669 goto junroll;
2670 if (savepipe != SIG_IGN)
2671 safe_signal(SIGPIPE, imapcatch);
2673 if (mb.mb_type == MB_CACHE)
2674 cache_list(&mb, fold, strip, fp);
2675 else
2676 imap_list(&mb, fold, strip, fp);
2678 imaplock = 0;
2679 if (interrupts) {
2680 if (options & OPT_TTYOUT)
2681 Fclose(fp);
2682 goto jleave;
2684 fflush(fp);
2686 if (options & OPT_TTYOUT) {
2687 rewind(fp);
2688 if (fsize(fp) > 0)
2689 dopr(fp);
2690 else
2691 n_err(_("Folder not found\n"));
2693 junroll:
2694 safe_signal(SIGINT, saveint);
2695 safe_signal(SIGPIPE, savepipe);
2696 if (options & OPT_TTYOUT)
2697 Fclose(fp);
2698 jleave:
2699 NYD_LEAVE;
2700 if (interrupts)
2701 onintr(0);
2704 static void
2705 dopr(FILE *fp)
2707 char o[LINESIZE];
2708 int c;
2709 long n = 0, mx = 0, columns, width;
2710 FILE *out;
2711 NYD_ENTER;
2713 if ((out = Ftmp(NULL, "imapdopr", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600))
2714 == NULL) {
2715 n_perr(_("tmpfile"), 0);
2716 goto jleave;
2719 while ((c = getc(fp)) != EOF) {
2720 if (c == '\n') {
2721 if (n > mx)
2722 mx = n;
2723 n = 0;
2724 } else
2725 ++n;
2727 rewind(fp);
2729 width = scrnwidth;
2730 if (mx < width / 2) {
2731 columns = width / (mx+2);
2732 snprintf(o, sizeof o, "sort | pr -%lu -w%lu -t", columns, width);
2733 } else
2734 strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
2735 run_command(XSHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
2736 page_or_print(out, 0);
2737 Fclose(out);
2738 jleave:
2739 NYD_LEAVE;
2742 static enum okay
2743 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
2745 char o[LINESIZE];
2746 const char *qname;
2747 int twice = 0, stored = 0;
2748 FILE *queuefp = NULL;
2749 enum okay ok = STOP;
2750 NYD_X;
2752 if (mp->mb_type == MB_CACHE) {
2753 if ((queuefp = cache_queue(mp)) == NULL)
2754 return STOP;
2755 ok = OKAY;
2757 qname = imap_quotestr(name = imap_fileof(name));
2758 /* Since it is not possible to set flags on the copy, recently
2759 * set flags must be set on the original to include it in the copy */
2760 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
2761 imap_store(mp, m, n, '+', "\\Seen", 0);
2762 if (m->m_flag&MFLAG)
2763 imap_store(mp, m, n, '+', "\\Flagged", 0);
2764 if (m->m_flag&MUNFLAG)
2765 imap_store(mp, m, n, '-', "\\Flagged", 0);
2766 if (m->m_flag&MANSWER)
2767 imap_store(mp, m, n, '+', "\\Answered", 0);
2768 if (m->m_flag&MUNANSWER)
2769 imap_store(mp, m, n, '-', "\\Flagged", 0);
2770 if (m->m_flag&MDRAFT)
2771 imap_store(mp, m, n, '+', "\\Draft", 0);
2772 if (m->m_flag&MUNDRAFT)
2773 imap_store(mp, m, n, '-', "\\Draft", 0);
2774 again:
2775 if (m->m_uid)
2776 snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n", tag(1), m->m_uid, qname);
2777 else {
2778 if (check_expunged() == STOP)
2779 goto out;
2780 snprintf(o, sizeof o, "%s COPY %u %s\r\n", tag(1), n, qname);
2782 IMAP_OUT(o, MB_COMD, goto out)
2783 while (mp->mb_active & MB_COMD)
2784 ok = imap_answer(mp, twice);
2786 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
2787 response_status == RESPONSE_OK)
2788 imap_copyuid(mp, m, name);
2790 if (response_status == RESPONSE_NO && twice++ == 0) {
2791 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
2792 IMAP_OUT(o, MB_COMD, goto out)
2793 while (mp->mb_active & MB_COMD)
2794 ok = imap_answer(mp, 1);
2795 if (ok == OKAY) {
2796 imap_created_mailbox++;
2797 goto again;
2801 if (queuefp != NULL)
2802 Fclose(queuefp);
2804 /* ... and reset the flag to its initial value so that the 'exit'
2805 * command still leaves the message unread */
2806 out:
2807 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2808 imap_store(mp, m, n, '-', "\\Seen", 0);
2809 stored++;
2811 if (m->m_flag & MFLAG) {
2812 imap_store(mp, m, n, '-', "\\Flagged", 0);
2813 stored++;
2815 if (m->m_flag & MUNFLAG) {
2816 imap_store(mp, m, n, '+', "\\Flagged", 0);
2817 stored++;
2819 if (m->m_flag & MANSWER) {
2820 imap_store(mp, m, n, '-', "\\Answered", 0);
2821 stored++;
2823 if (m->m_flag & MUNANSWER) {
2824 imap_store(mp, m, n, '+', "\\Answered", 0);
2825 stored++;
2827 if (m->m_flag & MDRAFT) {
2828 imap_store(mp, m, n, '-', "\\Draft", 0);
2829 stored++;
2831 if (m->m_flag & MUNDRAFT) {
2832 imap_store(mp, m, n, '+', "\\Draft", 0);
2833 stored++;
2835 if (stored) {
2836 mp->mb_active |= MB_COMD;
2837 (void)imap_finish(mp);
2839 return ok;
2842 FL enum okay
2843 imap_copy(struct message *m, int n, const char *name)
2845 sighandler_type saveint, savepipe;
2846 enum okay rv = STOP;
2847 NYD_ENTER;
2849 imaplock = 1;
2850 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2851 safe_signal(SIGINT, &_imap_maincatch);
2852 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2853 if (sigsetjmp(imapjmp, 1) == 0) {
2854 if (savepipe != SIG_IGN)
2855 safe_signal(SIGPIPE, imapcatch);
2857 rv = imap_copy1(&mb, m, n, name);
2859 safe_signal(SIGINT, saveint);
2860 safe_signal(SIGPIPE, savepipe);
2861 imaplock = 0;
2863 NYD_LEAVE;
2864 if (interrupts)
2865 onintr(0);
2866 return rv;
2869 static enum okay
2870 imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
2871 unsigned long *olduid, unsigned long *newuid)
2873 char *xp, *yp, *zp;
2874 enum okay rv;
2875 NYD_ENTER;
2877 *uidvalidity = strtoul(cp, &xp, 10);
2878 *olduid = strtoul(xp, &yp, 10);
2879 *newuid = strtoul(yp, &zp, 10);
2880 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
2881 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
2882 NYD_LEAVE;
2883 return rv;
2886 static enum okay
2887 imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
2888 unsigned long *uid)
2890 char *xp, *yp;
2891 enum okay rv;
2892 NYD_ENTER;
2894 *uidvalidity = strtoul(cp, &xp, 10);
2895 *uid = strtoul(xp, &yp, 10);
2896 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
2897 *yp == ']');
2898 NYD_LEAVE;
2899 return rv;
2902 static enum okay
2903 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
2905 struct mailbox xmb;
2906 struct message xm;
2907 const char *cp;
2908 unsigned long uidvalidity, olduid, newuid;
2909 enum okay rv;
2910 NYD_ENTER;
2912 memset(&xmb, 0, sizeof xmb);
2914 rv = STOP;
2915 if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
2916 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
2917 goto jleave;
2918 rv = OKAY;
2920 xmb = *mp;
2921 xmb.mb_cache_directory = NULL;
2922 xmb.mb_imap_account = sstrdup(mp->mb_imap_account);
2923 xmb.mb_imap_pass = sstrdup(mp->mb_imap_pass);
2924 xmb.mb_imap_mailbox = sstrdup(name);
2925 if (mp->mb_cache_directory != NULL)
2926 xmb.mb_cache_directory = sstrdup(mp->mb_cache_directory);
2927 xmb.mb_uidvalidity = uidvalidity;
2928 initcache(&xmb);
2930 if (m == NULL) {
2931 memset(&xm, 0, sizeof xm);
2932 xm.m_uid = olduid;
2933 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
2934 goto jleave;
2935 getcache(mp, &xm, NEED_HEADER);
2936 getcache(mp, &xm, NEED_BODY);
2937 } else {
2938 if ((m->m_flag & HAVE_HEADER) == 0)
2939 getcache(mp, m, NEED_HEADER);
2940 if ((m->m_flag & HAVE_BODY) == 0)
2941 getcache(mp, m, NEED_BODY);
2942 xm = *m;
2944 xm.m_uid = newuid;
2945 xm.m_flag &= ~MFULLYCACHED;
2946 putcache(&xmb, &xm);
2947 jleave:
2948 if (xmb.mb_cache_directory != NULL)
2949 free(xmb.mb_cache_directory);
2950 if (xmb.mb_imap_mailbox != NULL)
2951 free(xmb.mb_imap_mailbox);
2952 if (xmb.mb_imap_pass != NULL)
2953 free(xmb.mb_imap_pass);
2954 if (xmb.mb_imap_account != NULL)
2955 free(xmb.mb_imap_account);
2956 NYD_LEAVE;
2957 return rv;
2960 static enum okay
2961 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
2962 long size, long lines, int flag, const char *name)
2964 struct mailbox xmb;
2965 struct message xm;
2966 const char *cp;
2967 unsigned long uidvalidity, uid;
2968 enum okay rv;
2969 NYD_ENTER;
2971 xmb.mb_imap_mailbox = NULL;
2972 rv = STOP;
2973 if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
2974 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
2975 goto jleave;
2976 rv = OKAY;
2978 xmb = *mp;
2979 xmb.mb_cache_directory = NULL;
2980 xmb.mb_imap_mailbox = sstrdup(name);
2981 xmb.mb_uidvalidity = uidvalidity;
2982 xmb.mb_otf = xmb.mb_itf = fp;
2983 initcache(&xmb);
2984 memset(&xm, 0, sizeof xm);
2985 xm.m_flag = (flag & MREAD) | MNEW;
2986 xm.m_time = t;
2987 xm.m_block = mailx_blockof(off1);
2988 xm.m_offset = mailx_offsetof(off1);
2989 xm.m_size = size;
2990 xm.m_xsize = xsize;
2991 xm.m_lines = xm.m_xlines = lines;
2992 xm.m_uid = uid;
2993 xm.m_have = HAVE_HEADER | HAVE_BODY;
2994 putcache(&xmb, &xm);
2995 jleave:
2996 if (xmb.mb_imap_mailbox != NULL)
2997 free(xmb.mb_imap_mailbox);
2998 NYD_LEAVE;
2999 return rv;
3002 static enum okay
3003 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3005 FILE *tp = NULL;
3006 time_t t;
3007 long size, xsize, ysize, lines;
3008 enum mflag flag = MNEW;
3009 char *name, *buf, *bp;
3010 char const *cp;
3011 size_t bufsize, buflen, cnt;
3012 enum okay rv = STOP;
3013 NYD_ENTER;
3015 buf = smalloc(bufsize = LINESIZE);
3016 buflen = 0;
3017 cnt = fsize(fp);
3018 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3019 goto jstop;
3021 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3023 while (*bp == ' ')
3024 ++bp;
3026 if ((cp = strrchr(bp, '{')) == NULL)
3027 goto jstop;
3029 xsize = atol(&cp[1]) + 2;
3030 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3031 goto jstop;
3032 while (*cp == ' ')
3033 cp++;
3035 if (*cp == '(') {
3036 imap_getflags(cp, &cp, &flag);
3037 while (*++cp == ' ')
3040 t = imap_read_date_time(cp);
3042 if ((tp = Ftmp(NULL, "imapapui", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
3043 NULL)
3044 goto jstop;
3046 size = xsize;
3047 ysize = lines = 0;
3048 while (size > 0) {
3049 if (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) == NULL)
3050 goto jstop;
3051 size -= buflen;
3052 buf[--buflen] = '\0';
3053 buf[buflen-1] = '\n';
3054 fwrite(buf, 1, buflen, tp);
3055 ysize += buflen;
3056 ++lines;
3058 fflush(tp);
3059 rewind(tp);
3061 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3062 imap_unquotestr(name));
3063 rv = OKAY;
3064 jstop:
3065 free(buf);
3066 if (tp)
3067 Fclose(tp);
3068 NYD_LEAVE;
3069 return rv;
3072 #ifdef HAVE_IMAP_SEARCH
3073 static enum okay
3074 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3075 int f)
3077 char *o, *xp, *cs, c;
3078 size_t osize;
3079 FILE *queuefp = NULL;
3080 int i;
3081 unsigned long n;
3082 const char *cp;
3083 enum okay ok = STOP;
3084 NYD_X;
3086 c = 0;
3087 for (cp = spec; *cp; cp++)
3088 c |= *cp;
3089 if (c & 0200) {
3090 cp = charset_get_lc();
3091 # ifdef HAVE_ICONV
3092 if (asccasecmp(cp, "utf-8") && asccasecmp(cp, "utf8")) {
3093 iconv_t it;
3094 char *nsp, *nspec;
3095 size_t sz, nsz;
3097 if ((it = n_iconv_open("utf-8", cp)) != (iconv_t)-1) {
3098 sz = strlen(spec) + 1;
3099 nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
3100 if (n_iconv_buf(it, &spec, &sz, &nsp, &nsz, FAL0) == 0 &&
3101 sz == 0) {
3102 spec = nspec;
3103 cp = "utf-8";
3105 n_iconv_close(it);
3108 # endif
3109 cp = imap_quotestr(cp);
3110 cs = salloc(n = strlen(cp) + 10);
3111 snprintf(cs, n, "CHARSET %s ", cp);
3112 } else
3113 cs = UNCONST("");
3115 o = ac_alloc(osize = strlen(spec) + 60);
3116 snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
3117 IMAP_OUT(o, MB_COMD, goto out)
3118 while (mp->mb_active & MB_COMD) {
3119 ok = imap_answer(mp, 0);
3120 if (response_status == RESPONSE_OTHER &&
3121 response_other == MAILBOX_DATA_SEARCH) {
3122 xp = responded_other_text;
3123 while (*xp && *xp != '\r') {
3124 n = strtoul(xp, &xp, 10);
3125 for (i = 0; i < cnt; i++)
3126 if (m[i].m_uid == n && !(m[i].m_flag & MHIDDEN) &&
3127 (f == MDELETED || !(m[i].m_flag & MDELETED)))
3128 mark(i+1, f);
3132 out:
3133 ac_free(o);
3134 return ok;
3137 FL enum okay
3138 imap_search1(const char * volatile spec, int f)
3140 sighandler_type saveint, savepipe;
3141 enum okay volatile rv = STOP;
3142 NYD_ENTER;
3144 if (mb.mb_type != MB_IMAP)
3145 goto jleave;
3147 imaplock = 1;
3148 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3149 safe_signal(SIGINT, &_imap_maincatch);
3150 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3151 if (sigsetjmp(imapjmp, 1) == 0) {
3152 if (savepipe != SIG_IGN)
3153 safe_signal(SIGPIPE, imapcatch);
3155 rv = imap_search2(&mb, message, msgCount, spec, f);
3157 safe_signal(SIGINT, saveint);
3158 safe_signal(SIGPIPE, savepipe);
3159 imaplock = 0;
3160 jleave:
3161 NYD_LEAVE;
3162 if (interrupts)
3163 onintr(0);
3164 return rv;
3166 #endif /* HAVE_IMAP_SEARCH */
3168 FL int
3169 imap_thisaccount(const char *cp)
3171 int rv;
3172 NYD_ENTER;
3174 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
3175 rv = 0;
3176 else if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
3177 mb.mb_imap_account == NULL)
3178 rv = 0;
3179 else
3180 rv = !strcmp(protbase(cp), mb.mb_imap_account);
3181 NYD_LEAVE;
3182 return rv;
3185 FL enum okay
3186 imap_remove(const char * volatile name)
3188 sighandler_type volatile saveint, savepipe;
3189 enum okay volatile rv = STOP;
3190 NYD_ENTER;
3192 if (mb.mb_type != MB_IMAP) {
3193 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
3194 goto jleave;
3197 if (!imap_thisaccount(name)) {
3198 n_err(_("Can only remove mailboxes on current IMAP server: "
3199 "\"%s\" not removed\n"), name);
3200 goto jleave;
3203 imaplock = 1;
3204 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3205 safe_signal(SIGINT, &_imap_maincatch);
3206 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3207 if (sigsetjmp(imapjmp, 1) == 0) {
3208 if (savepipe != SIG_IGN)
3209 safe_signal(SIGPIPE, imapcatch);
3211 rv = imap_remove1(&mb, imap_fileof(name));
3213 safe_signal(SIGINT, saveint);
3214 safe_signal(SIGPIPE, savepipe);
3215 imaplock = 0;
3217 if (rv == OKAY)
3218 rv = cache_remove(name);
3219 jleave:
3220 NYD_LEAVE;
3221 if (interrupts)
3222 onintr(0);
3223 return rv;
3226 static enum okay
3227 imap_remove1(struct mailbox *mp, const char *name)
3229 FILE *queuefp = NULL;
3230 char *o;
3231 int os;
3232 enum okay ok = STOP;
3233 NYD_X;
3235 o = ac_alloc(os = 2*strlen(name) + 100);
3236 snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
3237 IMAP_OUT(o, MB_COMD, goto out)
3238 while (mp->mb_active & MB_COMD)
3239 ok = imap_answer(mp, 1);
3240 out:
3241 ac_free(o);
3242 return ok;
3245 FL enum okay
3246 imap_rename(const char *old, const char *new)
3248 sighandler_type saveint, savepipe;
3249 enum okay rv = STOP;
3250 NYD_ENTER;
3252 if (mb.mb_type != MB_IMAP) {
3253 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3254 goto jleave;
3257 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
3258 n_err(_("Can only rename mailboxes on current IMAP "
3259 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
3260 goto jleave;
3263 imaplock = 1;
3264 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3265 safe_signal(SIGINT, &_imap_maincatch);
3266 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3267 if (sigsetjmp(imapjmp, 1) == 0) {
3268 if (savepipe != SIG_IGN)
3269 safe_signal(SIGPIPE, imapcatch);
3271 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
3273 safe_signal(SIGINT, saveint);
3274 safe_signal(SIGPIPE, savepipe);
3275 imaplock = 0;
3277 if (rv == OKAY)
3278 rv = cache_rename(old, new);
3279 jleave:
3280 NYD_LEAVE;
3281 if (interrupts)
3282 onintr(0);
3283 return rv;
3286 static enum okay
3287 imap_rename1(struct mailbox *mp, const char *old, const char *new)
3289 FILE *queuefp = NULL;
3290 char *o;
3291 int os;
3292 enum okay ok = STOP;
3293 NYD_X;
3295 o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
3296 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old),
3297 imap_quotestr(new));
3298 IMAP_OUT(o, MB_COMD, goto out)
3299 while (mp->mb_active & MB_COMD)
3300 ok = imap_answer(mp, 1);
3301 out:
3302 ac_free(o);
3303 return ok;
3306 FL enum okay
3307 imap_dequeue(struct mailbox *mp, FILE *fp)
3309 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
3310 size_t bufsize, buflen, cnt;
3311 long offs, offs1, offs2, octets;
3312 int twice, gotcha = 0;
3313 FILE *queuefp = NULL;
3314 enum okay ok = OKAY, rok = OKAY;
3315 NYD_X;
3317 buf = smalloc(bufsize = LINESIZE);
3318 buflen = 0;
3319 cnt = fsize(fp);
3320 while ((offs1 = ftell(fp)) >= 0 &&
3321 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
3322 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3324 while (*bp == ' ')
3325 ++bp;
3326 twice = 0;
3327 if ((offs = ftell(fp)) < 0)
3328 goto fail;
3329 again:
3330 snprintf(o, sizeof o, "%s %s", tag(1), bp);
3331 if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
3332 cp = &bp[9];
3333 while (digitchar(*cp))
3334 cp++;
3335 if (*cp != ' ')
3336 goto fail;
3337 while (*cp == ' ')
3338 cp++;
3339 if ((newname = imap_strex(cp, NULL)) == NULL)
3340 goto fail;
3341 IMAP_OUT(o, MB_COMD, continue)
3342 while (mp->mb_active & MB_COMD)
3343 ok = imap_answer(mp, twice);
3344 if (response_status == RESPONSE_NO && twice++ == 0)
3345 goto trycreate;
3346 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3347 imap_copyuid(mp, NULL, imap_unquotestr(newname));
3349 } else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
3350 IMAP_OUT(o, MB_COMD, continue)
3351 while (mp->mb_active & MB_COMD)
3352 ok = imap_answer(mp, 1);
3353 if (ok == OKAY)
3354 gotcha++;
3355 } else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
3356 if ((cp = strrchr(bp, '{')) == NULL)
3357 goto fail;
3358 octets = atol(&cp[1]) + 2;
3359 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
3360 goto fail;
3361 IMAP_OUT(o, MB_COMD, continue)
3362 while (mp->mb_active & MB_COMD) {
3363 ok = imap_answer(mp, twice);
3364 if (response_type == RESPONSE_CONT)
3365 break;
3367 if (ok == STOP) {
3368 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
3369 goto trycreate;
3370 goto fail;
3372 while (octets > 0) {
3373 size_t n = (UICMP(z, octets, >, sizeof iob)
3374 ? sizeof iob : (size_t)octets);
3375 octets -= n;
3376 if (n != fread(iob, 1, n, fp))
3377 goto fail;
3378 swrite1(&mp->mb_sock, iob, n, 1);
3380 swrite(&mp->mb_sock, "");
3381 while (mp->mb_active & MB_COMD) {
3382 ok = imap_answer(mp, 0);
3383 if (response_status == RESPONSE_NO && twice++ == 0) {
3384 if (fseek(fp, offs, SEEK_SET) < 0)
3385 goto fail;
3386 goto trycreate;
3389 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
3390 if ((offs2 = ftell(fp)) < 0)
3391 goto fail;
3392 fseek(fp, offs1, SEEK_SET);
3393 if (imap_appenduid_cached(mp, fp) == STOP) {
3394 (void)fseek(fp, offs2, SEEK_SET);
3395 goto fail;
3398 } else {
3399 fail:
3400 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
3401 rok = STOP;
3403 continue;
3404 trycreate:
3405 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
3406 IMAP_OUT(o, MB_COMD, continue)
3407 while (mp->mb_active & MB_COMD)
3408 ok = imap_answer(mp, 1);
3409 if (ok == OKAY)
3410 goto again;
3412 fflush(fp);
3413 rewind(fp);
3414 ftruncate(fileno(fp), 0);
3415 if (gotcha)
3416 imap_close(mp);
3417 free(buf);
3418 return rok;
3421 static char *
3422 imap_strex(char const *cp, char const **xp)
3424 char const *cq;
3425 char *n = NULL;
3426 NYD_ENTER;
3428 if (*cp != '"')
3429 goto jleave;
3431 for (cq = cp + 1; *cq != '\0'; ++cq) {
3432 if (*cq == '\\')
3433 cq++;
3434 else if (*cq == '"')
3435 break;
3437 if (*cq != '"')
3438 goto jleave;
3440 n = salloc(cq - cp + 2);
3441 memcpy(n, cp, cq - cp +1);
3442 n[cq - cp + 1] = '\0';
3443 if (xp != NULL)
3444 *xp = cq + 1;
3445 jleave:
3446 NYD_LEAVE;
3447 return n;
3450 static enum okay
3451 check_expunged(void)
3453 enum okay rv;
3454 NYD_ENTER;
3456 if (expunged_messages > 0) {
3457 n_err(_("Command not executed - messages have been expunged\n"));
3458 rv = STOP;
3459 } else
3460 rv = OKAY;
3461 NYD_LEAVE;
3462 return rv;
3465 FL int
3466 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3468 struct url url;
3469 int rv, omsgCount = msgCount;
3470 NYD_ENTER;
3471 UNUSED(vp);
3473 if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
3474 n_err(_("Already connected\n"));
3475 rv = 1;
3476 goto jleave;
3479 if (!url_parse(&url, CPROTO_IMAP, mailname)) {
3480 rv = 1;
3481 goto jleave;
3483 ok_bclear(disconnected);
3484 vok_bclear(savecat("disconnected-", url.url_u_h_p.s));
3486 if (mb.mb_type == MB_CACHE) {
3487 enum fedit_mode fm = FEDIT_NONE;
3488 if (_imap_rdonly)
3489 fm |= FEDIT_RDONLY;
3490 if (!(pstate & PS_EDIT))
3491 fm |= FEDIT_SYSBOX;
3492 _imap_setfile1(&url, fm, 1);
3493 if (msgCount > omsgCount)
3494 newmailinfo(omsgCount);
3496 rv = 0;
3497 jleave:
3498 NYD_LEAVE;
3499 return rv;
3502 FL int
3503 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
3505 struct url url;
3506 int rv = 1, *msgvec = vp;
3507 NYD_ENTER;
3509 if (mb.mb_type == MB_CACHE) {
3510 n_err(_("Not connected\n"));
3511 goto jleave;
3513 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
3514 n_err(_("The current mailbox is not cached\n"));
3515 goto jleave;
3518 if (!url_parse(&url, CPROTO_IMAP, mailname))
3519 goto jleave;
3521 if (*msgvec)
3522 c_cache(vp);
3523 ok_bset(disconnected, TRU1);
3524 if (mb.mb_type == MB_IMAP) {
3525 enum fedit_mode fm = FEDIT_NONE;
3526 if (_imap_rdonly)
3527 fm |= FEDIT_RDONLY;
3528 if (!(pstate & PS_EDIT))
3529 fm |= FEDIT_SYSBOX;
3530 sclose(&mb.mb_sock);
3531 _imap_setfile1(&url, fm, 1);
3533 rv = 0;
3534 jleave:
3535 NYD_LEAVE;
3536 return rv;
3539 FL int
3540 c_cache(void *vp)
3542 int rv = 1, *msgvec = vp, *ip;
3543 struct message *mp;
3544 NYD_ENTER;
3546 if (mb.mb_type != MB_IMAP) {
3547 n_err(_("Not connected to an IMAP server\n"));
3548 goto jleave;
3550 if (cached_uidvalidity(&mb) == 0) {
3551 n_err(_("The current mailbox is not cached\n"));
3552 goto jleave;
3555 srelax_hold();
3556 for (ip = msgvec; *ip; ++ip) {
3557 mp = &message[*ip - 1];
3558 if (!(mp->m_have & HAVE_BODY)) {
3559 get_body(mp);
3560 srelax();
3563 srelax_rele();
3564 rv = 0;
3565 jleave:
3566 NYD_LEAVE;
3567 return rv;
3570 FL int
3571 disconnected(const char *file)
3573 struct url url;
3574 int rv = 1;
3575 NYD_ENTER;
3577 if (ok_blook(disconnected)) {
3578 rv = 1;
3579 goto jleave;
3582 if (!url_parse(&url, CPROTO_IMAP, file)) {
3583 rv = 0;
3584 goto jleave;
3586 rv = vok_blook(savecat("disconnected-", url.url_u_h_p.s));
3588 jleave:
3589 NYD_LEAVE;
3590 return rv;
3593 FL void
3594 transflags(struct message *omessage, long omsgCount, int transparent)
3596 struct message *omp, *nmp, *newdot, *newprevdot;
3597 int hf;
3598 NYD_ENTER;
3600 omp = omessage;
3601 nmp = message;
3602 newdot = message;
3603 newprevdot = NULL;
3604 while (PTRCMP(omp, <, omessage + omsgCount) &&
3605 PTRCMP(nmp, <, message + msgCount)) {
3606 if (dot && nmp->m_uid == dot->m_uid)
3607 newdot = nmp;
3608 if (prevdot && nmp->m_uid == prevdot->m_uid)
3609 newprevdot = nmp;
3610 if (omp->m_uid == nmp->m_uid) {
3611 hf = nmp->m_flag & MHIDDEN;
3612 if (transparent && mb.mb_type == MB_IMAP)
3613 omp->m_flag &= ~MHIDDEN;
3614 *nmp++ = *omp++;
3615 if (transparent && mb.mb_type == MB_CACHE)
3616 nmp[-1].m_flag |= hf;
3617 } else if (omp->m_uid < nmp->m_uid)
3618 ++omp;
3619 else
3620 ++nmp;
3622 dot = newdot;
3623 setdot(newdot);
3624 prevdot = newprevdot;
3625 free(omessage);
3626 NYD_LEAVE;
3629 FL time_t
3630 imap_read_date_time(const char *cp)
3632 char buf[3];
3633 time_t t;
3634 int i, year, month, day, hour, minute, second, sign = -1;
3635 NYD2_ENTER;
3637 /* "25-Jul-2004 15:33:44 +0200"
3638 * | | | | | |
3639 * 0 5 10 15 20 25 */
3640 if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
3641 goto jinvalid;
3642 day = strtol(&cp[1], NULL, 10);
3643 for (i = 0;;) {
3644 if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
3645 break;
3646 if (month_names[++i][0] == '\0')
3647 goto jinvalid;
3649 month = i + 1;
3650 year = strtol(&cp[8], NULL, 10);
3651 hour = strtol(&cp[13], NULL, 10);
3652 minute = strtol(&cp[16], NULL, 10);
3653 second = strtol(&cp[19], NULL, 10);
3654 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
3655 goto jinvalid;
3656 switch (cp[22]) {
3657 case '-':
3658 sign = 1;
3659 break;
3660 case '+':
3661 break;
3662 default:
3663 goto jinvalid;
3665 buf[2] = '\0';
3666 buf[0] = cp[23];
3667 buf[1] = cp[24];
3668 t += strtol(buf, NULL, 10) * sign * 3600;
3669 buf[0] = cp[25];
3670 buf[1] = cp[26];
3671 t += strtol(buf, NULL, 10) * sign * 60;
3672 jleave:
3673 NYD2_LEAVE;
3674 return t;
3675 jinvalid:
3676 time(&t);
3677 goto jleave;
3680 FL const char *
3681 imap_make_date_time(time_t t)
3683 static char s[30];
3684 struct tm *tmptr;
3685 int tzdiff, tzdiff_hour, tzdiff_min;
3686 NYD2_ENTER;
3688 tzdiff = t - mktime(gmtime(&t));
3689 tzdiff_hour = (int)(tzdiff / 60);
3690 tzdiff_min = tzdiff_hour % 60;
3691 tzdiff_hour /= 60;
3692 tmptr = localtime(&t);
3693 if (tmptr->tm_isdst > 0)
3694 tzdiff_hour++;
3695 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3696 tmptr->tm_mday, month_names[tmptr->tm_mon], tmptr->tm_year + 1900,
3697 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min);
3698 NYD2_LEAVE;
3699 return s;
3701 #endif /* HAVE_IMAP */
3703 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3704 FL char *
3705 imap_quotestr(char const *s)
3707 char *n, *np;
3708 NYD2_ENTER;
3710 np = n = salloc(2 * strlen(s) + 3);
3711 *np++ = '"';
3712 while (*s) {
3713 if (*s == '"' || *s == '\\')
3714 *np++ = '\\';
3715 *np++ = *s++;
3717 *np++ = '"';
3718 *np = '\0';
3719 NYD2_LEAVE;
3720 return n;
3723 FL char *
3724 imap_unquotestr(char const *s)
3726 char *n, *np;
3727 NYD2_ENTER;
3729 if (*s != '"') {
3730 n = savestr(s);
3731 goto jleave;
3734 np = n = salloc(strlen(s) + 1);
3735 while (*++s) {
3736 if (*s == '\\')
3737 s++;
3738 else if (*s == '"')
3739 break;
3740 *np++ = *s;
3742 *np = '\0';
3743 jleave:
3744 NYD2_LEAVE;
3745 return n;
3747 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */
3749 /* s-it-mode */