1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ TODO Anything. SASL-IR for more.
4 *
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 * SPDX-License-Identifier: BSD-4-Clause
8 */
9 /*
10 * Copyright (c) 2004
11 * Gunnar Ritter. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41 #undef su_FILE
42 #define su_FILE obs_imap
43 #define mx_SOURCE
44 #define mx_SOURCE_NET_IMAP
45
46 #ifndef mx_HAVE_AMALGAMATION
47 # include "mx/nail.h"
48 #endif
49
50 su_EMPTY_FILE()
51 #ifdef mx_HAVE_IMAP
52 #include <sys/socket.h>
53
54 #include <netdb.h>
55 #ifdef mx_HAVE_ARPA_INET_H
56 # include <arpa/inet.h>
57 #endif
58 #include <netinet/in.h>
59
60 #include <su/cs.h>
61 #include <su/icodec.h>
62 #include <su/mem.h>
63 #include <su/utf.h>
64
65 #include "mx/cmd.h"
66 #include "mx/cred-auth.h"
67 #include "mx/cred-md5.h"
68 #include "mx/iconv.h"
69 #include "mx/file-streams.h"
70 #include "mx/sigs.h"
71 #include "mx/net-socket.h"
72 #include "mx/ui-str.h"
73
74 #ifdef mx_HAVE_GSSAPI
75 # include "mx/net-gssapi.h" /* $(MX_SRCDIR) */
76 #endif
77
78 /* TODO fake */
79 #include "su/code-in.h"
80
81 #define IMAP_ANSWER() \
82 {\
83 if (mp->mb_type != MB_CACHE) {\
84 enum okay ok = OKAY;\
85 while (mp->mb_active & MB_COMD)\
86 ok = imap_answer(mp, 1);\
87 if (ok == STOP)\
88 return STOP;\
89 }\
90 }
91
92 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
93 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
94 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
95 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
96 * TODO I.e., that should be a function, not a macro ... or so.
97 * TODO This entire module needs MASSIVE work! */
98 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
99 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
100 {\
101 if (mp->mb_type != MB_CACHE) {\
102 if (imap_finish(mp) == STOP) {\
103 ACTIONBAIL;\
104 }\
105 if (n_poption & n_PO_D_VV)\
106 n_err(">>> %s", X);\
107 mp->mb_active |= Y;\
108 if (mx_socket_write(mp->mb_sock, X) == STOP) {\
109 ACTIONERR;\
110 }\
111 } else {\
112 if (queuefp != NULL)\
113 fputs(X, queuefp);\
114 }\
115 }
116
117 static struct record {
118 struct record *rec_next;
119 unsigned long rec_count;
120 enum rec_type {
121 REC_EXISTS,
122 REC_EXPUNGE
123 } rec_type;
124 } *record, *recend;
125
126 static enum {
127 RESPONSE_TAGGED,
128 RESPONSE_DATA,
129 RESPONSE_FATAL,
130 RESPONSE_CONT,
131 RESPONSE_ILLEGAL
132 } response_type;
133
134 static enum {
135 RESPONSE_OK,
136 RESPONSE_NO,
137 RESPONSE_BAD,
138 RESPONSE_PREAUTH,
139 RESPONSE_BYE,
140 RESPONSE_OTHER,
141 RESPONSE_UNKNOWN
142 } response_status;
143
144 static char *responded_tag;
145 static char *responded_text;
146 static char *responded_other_text;
147 static long responded_other_number;
148
149 static enum {
150 MAILBOX_DATA_FLAGS,
151 MAILBOX_DATA_LIST,
152 MAILBOX_DATA_LSUB,
153 MAILBOX_DATA_MAILBOX,
154 MAILBOX_DATA_SEARCH,
155 MAILBOX_DATA_STATUS,
156 MAILBOX_DATA_EXISTS,
157 MAILBOX_DATA_RECENT,
158 MESSAGE_DATA_EXPUNGE,
159 MESSAGE_DATA_FETCH,
160 CAPABILITY_DATA,
161 RESPONSE_OTHER_UNKNOWN
162 } response_other;
163
164 static enum list_attributes {
165 LIST_NONE = 000,
166 LIST_NOINFERIORS = 001,
167 LIST_NOSELECT = 002,
168 LIST_MARKED = 004,
169 LIST_UNMARKED = 010
170 } list_attributes;
171
172 static int list_hierarchy_delimiter;
173 static char *list_name;
174
175 struct list_item {
176 struct list_item *l_next;
177 char *l_name;
178 char *l_base;
179 enum list_attributes l_attr;
180 int l_delim;
181 int l_level;
182 int l_has_children;
183 };
184
185 static char *imapbuf; /* TODO not static, use pool */
186 static uz imapbufsize;
187 static sigjmp_buf imapjmp;
188 static n_sighdl_t savealrm;
189 static int imapkeepalive;
190 static long had_exists = -1;
191 static long had_expunge = -1;
192 static long expunged_messages;
193 static int volatile imaplock;
194 static int same_imap_account;
195 static boole _imap_rdonly;
196
197 static char *imap_quotestr(char const *s);
198 static char *imap_unquotestr(char const *s);
199 static void imap_delim_init(struct mailbox *mp, struct mx_url const *urlp);
200 static char const *a_imap_path_normalize(struct mailbox *mp, char const *cp,
201 boole look_delim); /* for `imapcodec' only! */
202 /* Returns NULL on error */
203 static char *imap_path_quote(struct mailbox *mp, char const *cp);
204 static void imap_other_get(char *pp);
205 static void imap_response_get(const char **cp);
206 static void imap_response_parse(void);
207 static enum okay imap_answer(struct mailbox *mp, int errprnt);
208 static enum okay imap_parse_list(void);
209 static enum okay imap_finish(struct mailbox *mp);
210 static void imap_timer_off(void);
211 static void imapcatch(int s);
212 static void _imap_maincatch(int s);
213 static enum okay imap_noop1(struct mailbox *mp);
214 static void rec_queue(enum rec_type type, unsigned long cnt);
215 static enum okay rec_dequeue(void);
216 static void rec_rmqueue(void);
217 static void imapalarm(int s);
218 static enum okay imap_preauth(struct mailbox *mp, struct mx_url *urlp,
219 struct mx_cred_ctx *ccred);
220 static enum okay imap_capability(struct mailbox *mp);
221 static enum okay a_imap_auth(struct mailbox *mp, struct mx_url *urlp,
222 struct mx_cred_ctx *ccredp);
223 #ifdef mx_HAVE_MD5
224 static enum okay imap_cram_md5(struct mailbox *mp,
225 struct mx_cred_ctx *ccred);
226 #endif
227 static enum okay imap_login(struct mailbox *mp, struct mx_cred_ctx *ccred);
228 static enum okay a_imap_oauthbearer(struct mailbox *mp,
229 struct mx_cred_ctx *ccp);
230 static enum okay a_imap_external(struct mailbox *mp, struct mx_cred_ctx *ccp);
231 static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
232 static void imap_init(struct mailbox *mp, int n);
233 static void imap_setptr(struct mailbox *mp, int nmail, int transparent,
234 int *prevcount);
235 static boole _imap_getcred(struct mailbox *mbp, struct mx_cred_ctx *ccredp,
236 struct mx_url *urlp);
237 static int _imap_setfile1(char const *who, struct mx_url *urlp,
238 enum fedit_mode fm, int transparent);
239 static void imap_fetchdata(struct mailbox *mp, struct message *m,
240 uz expected, int need, const char *head,
241 uz headsize, long headlines);
242 static void imap_putstr(struct mailbox *mp, struct message *m,
243 const char *str, const char *head, uz headsize,
244 long headlines);
245 static enum okay imap_get(struct mailbox *mp, struct message *m,
246 enum needspec need);
247 static void commitmsg(struct mailbox *mp, struct message *to,
248 struct message *from, enum content_info content_info);
249 static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
250 int bot, int top);
251 static enum okay imap_exit(struct mailbox *mp);
252 static enum okay imap_delete(struct mailbox *mp, int n, struct message *m,
253 int needstat);
254 static enum okay imap_close(struct mailbox *mp);
255 static enum okay imap_update(struct mailbox *mp);
256 static enum okay imap_store(struct mailbox *mp, struct message *m, int n,
257 int c, const char *xsp, int needstat);
258 static enum okay imap_unstore(struct message *m, int n, const char *flag);
259 static const char *tag(int new);
260 static char * imap_putflags(int f);
261 static void imap_getflags(const char *cp, char const **xp,enum mflag *f);
262 static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
263 off_t off1, long xsize, enum mflag flag, time_t t);
264 static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp,
265 long offset);
266 static enum okay imap_list1(struct mailbox *mp, const char *base,
267 struct list_item **list, struct list_item **lend,
268 int level);
269 static enum okay imap_list(struct mailbox *mp, const char *base, int strip,
270 FILE *fp);
271 static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
272 const char *name);
273 static enum okay imap_copyuid_parse(const char *cp,
274 u64 *uidvalidity, u64 *olduid, u64 *newuid);
275 static enum okay imap_appenduid_parse(const char *cp,
276 u64 *uidvalidity, u64 *uid);
277 static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
278 const char *name);
279 static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
280 long off1, long xsize, long size, long lines, int flag,
281 const char *name);
282 static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
283 #ifdef mx_HAVE_IMAP_SEARCH
284 static sz imap_search2(struct mailbox *mp, struct message *m, int cnt,
285 const char *spec, int f);
286 #endif
287 static enum okay imap_remove1(struct mailbox *mp, const char *name);
288 static enum okay imap_rename1(struct mailbox *mp, const char *old,
289 const char *new);
290 static char * imap_strex(char const *cp, char const **xp);
291 static enum okay check_expunged(void);
292
293 #ifdef mx_HAVE_GSSAPI
294 # include <mx/net-gssapi.h>
295 #endif
296
297 static char *
imap_quotestr(char const * s)298 imap_quotestr(char const *s)
299 {
300 char *n, *np;
301 NYD2_IN;
302
303 np = n = n_autorec_alloc(2 * su_cs_len(s) + 3);
304 *np++ = '"';
305 while (*s) {
306 if (*s == '"' || *s == '\\')
307 *np++ = '\\';
308 *np++ = *s++;
309 }
310 *np++ = '"';
311 *np = '\0';
312 NYD2_OU;
313 return n;
314 }
315
316 static char *
imap_unquotestr(char const * s)317 imap_unquotestr(char const *s)
318 {
319 char *n, *np;
320 NYD2_IN;
321
322 if (*s != '"') {
323 n = savestr(s);
324 goto jleave;
325 }
326
327 np = n = n_autorec_alloc(su_cs_len(s) + 1);
328 while (*++s) {
329 if (*s == '\\')
330 s++;
331 else if (*s == '"')
332 break;
333 *np++ = *s;
334 }
335 *np = '\0';
336 jleave:
337 NYD2_OU;
338 return n;
339 }
340
341 static void
imap_delim_init(struct mailbox * mp,struct mx_url const * urlp)342 imap_delim_init(struct mailbox *mp, struct mx_url const *urlp){
343 uz i;
344 char const *cp;
345 NYD2_IN;
346
347 mp->mb_imap_delim[0] = '\0';
348
349 if((cp = xok_vlook(imap_delim, urlp, OXM_ALL)) != NULL){
350 i = su_cs_len(cp);
351
352 if(i == 0){
353 cp = n_IMAP_DELIM;
354 i = sizeof(n_IMAP_DELIM) -1;
355 goto jcopy;
356 }
357
358 if(i < NELEM(mp->mb_imap_delim))
359 jcopy:
360 su_mem_copy(&mb.mb_imap_delim[0], cp, i +1);
361 else
362 n_err(_("*imap-delim* for %s is too long: %s\n"),
363 urlp->url_input, cp);
364 }
365 NYD2_OU;
366 }
367
368 static char const *
a_imap_path_normalize(struct mailbox * mp,char const * cp,boole look_delim)369 a_imap_path_normalize(struct mailbox *mp, char const *cp, boole look_delim){
370 char const *dcp;
371 boole dosrch;
372 char dc, *rv_base, *rv, c, lc;
373 NYD2_IN;
374 ASSERT(mp == NIL || !look_delim);
375 ASSERT(!look_delim || mp == NIL);
376
377 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean
378 * "use exactly what i have specified" */
379 if(mp == NIL){
380 if(look_delim && (dcp = ok_vlook(imap_delim)) != NIL)
381 dc = *dcp;
382 else{
383 dcp = n_IMAP_DELIM;
384 dc = '\0';
385 }
386 }else if((dc = (dcp = mp->mb_imap_delim)[0]) == '\0')
387 dcp = n_IMAP_DELIM;
388
389 dosrch = (dcp[1] != '\0');
390
391 /* Plain names don't need path quoting */
392 /* C99 */{
393 uz i, j;
394 char const *cpx;
395
396 for(cpx = cp;; ++cpx)
397 if((c = *cpx) == '\0')
398 goto jleave;
399 /* Without *imap-delim*, use the first separator discovered in path */
400 else if(dc == '\0'){
401 ASSERT(!su_cs_cmp(dcp, n_IMAP_DELIM));
402 if(su_cs_find_c(dcp, c)){
403 dc = c;
404 break;
405 }
406 }else if(c == dc)
407 break;
408 else if(dosrch && su_cs_find_c(dcp, c) != NIL)
409 break;
410
411 /* And we don't need to reevaluate what we have seen yet */
412 i = P2UZ(cpx - cp);
413 rv = rv_base = n_autorec_alloc(i + (j = su_cs_len(cpx) +1));
414 if(i > 0)
415 su_mem_copy(rv, cp, i);
416 su_mem_copy(&rv[i], cpx, j);
417 rv += i;
418 cp = cpx;
419 }
420
421 /* Squeeze adjacent delimiters, convert remain to dc */
422 for(lc = '\0'; (c = *cp++) != '\0'; lc = c){
423 if(c != dc && dosrch && su_cs_find_c(dcp, c) != NIL)
424 c = dc;
425 if(c != dc || lc != dc)
426 *rv++ = c;
427 }
428 *rv = '\0';
429
430 cp = rv_base;
431 jleave:
432 NYD2_OU;
433 return cp;
434 }
435
436 #ifdef mx_HAVE_GSSAPI
437 # include <mx/net-gssapi.h>
438 #endif
439
440 FL char const *
imap_path_encode(char const * cp,boole * err_or_null)441 imap_path_encode(char const *cp, boole *err_or_null){
442 /* To a large extend inspired by dovecot(1) */
443 struct str out;
444 boole err_def;
445 u8 *be16p_base, *be16p;
446 char const *emsg;
447 char c;
448 uz l, l_plain;
449 NYD2_IN;
450
451 if(err_or_null == NULL)
452 err_or_null = &err_def;
453 *err_or_null = FAL0;
454
455 /* Is this a string that works out as "plain US-ASCII"? */
456 for(l = 0;; ++l)
457 if((c = cp[l]) == '\0')
458 goto jleave;
459 else if(c <= 0x1F || c >= 0x7F || c == '&')
460 break;
461
462 *err_or_null = TRU1;
463
464 /* We need to encode in mUTF-7! For that, we first have to convert the
465 * local charset to UTF-8, then convert all characters which need to be
466 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
467 * We can skip the UTF-8 conversion occasionally, however */
468 #if (defined mx_HAVE_DEVEL || !defined mx_HAVE_ALWAYS_UNICODE_LOCALE) &&\
469 defined mx_HAVE_ICONV
470 if(!(n_psonce & n_PSO_UNICODE)){
471 char const *x;
472
473 emsg = N_("iconv(3) from locale charset to UTF-8 failed");
474 if((x = n_iconv_onetime_cp(n_ICONV_NONE, "utf-8", ok_vlook(ttycharset),
475 cp)) == NULL)
476 goto jerr;
477 cp = x;
478
479 /* So: Why not start all over again?
480 * Is this a string that works out as "plain US-ASCII"? */
481 for(l = 0;; ++l)
482 if((c = cp[l]) == '\0')
483 goto jleave;
484 else if(c <= 0x1F || c >= 0x7F || c == '&')
485 break;
486 }
487 #endif
488
489 /* We need to encode, save what we have, encode the rest */
490 l_plain = l;
491
492 for(cp += l, l = 0; cp[l] != '\0'; ++l)
493 ;
494 be16p_base = n_autorec_alloc((l << 1) +1); /* XXX use n_string, resize */
495
496 out.s = n_autorec_alloc(l_plain + (l << 2) +1); /* XXX use n_string.. */
497 if(l_plain > 0)
498 su_mem_copy(out.s, &cp[-l_plain], out.l = l_plain);
499 else
500 out.l = 0;
501 su_DBG( l_plain += (l << 2); )
502
503 while(l > 0){
504 c = *cp++;
505 --l;
506
507 if(c == '&'){
508 out.s[out.l + 0] = '&';
509 out.s[out.l + 1] = '-';
510 out.l += 2;
511 }else if(c > 0x1F && c < 0x7F)
512 out.s[out.l++] = c;
513 else{
514 static char const mb64ct[] =
515 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
516 u32 utf32;
517
518 /* Convert consecutive non-representables */
519 emsg = N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
520
521 for(be16p = be16p_base, --cp, ++l;;){
522 if((utf32 = su_utf8_to_32(&cp, &l)) == U32_MAX)
523 goto jerr;
524
525 /* TODO S-CText: magic utf16 conversions */
526 if(utf32 < 0x10000){
527 be16p[1] = utf32 & 0xFF;
528 be16p[0] = (utf32 >>= 8, utf32 &= 0xFF);
529 be16p += 2;
530 }else{
531 u16 s7e;
532
533 utf32 -= 0x10000;
534 s7e = 0xD800u | (utf32 >> 10);
535 be16p[1] = s7e & 0xFF;
536 be16p[0] = (s7e >>= 8, s7e &= 0xFF);
537 s7e = 0xDC00u | (utf32 &= 0x03FF);
538 be16p[3] = s7e & 0xFF;
539 be16p[2] = (s7e >>= 8, s7e &= 0xFF);
540 be16p += 4;
541 }
542
543 if(l == 0)
544 break;
545 if((c = *cp) > 0x1F && c < 0x7F)
546 break;
547 }
548
549 /* And then warp that UTF-16BE to mUTF-7 */
550 out.s[out.l++] = '&';
551 utf32 = (u32)P2UZ(be16p - be16p_base);
552 be16p = be16p_base;
553
554 for(; utf32 >= 3; be16p += 3, utf32 -= 3){
555 uz i = out.l;
556 out.l += 4;
557 out.s[i + 0] = mb64ct[ be16p[0] >> 2 ];
558 out.s[i + 1] = mb64ct[((be16p[0] & 0x03) << 4) | (be16p[1] >> 4)];
559 out.s[i + 2] = mb64ct[((be16p[1] & 0x0F) << 2) | (be16p[2] >> 6)];
560 out.s[i + 3] = mb64ct[ be16p[2] & 0x3F];
561 }
562 if(utf32 > 0){
563 out.s[out.l + 0] = mb64ct[be16p[0] >> 2];
564 if(--utf32 == 0){
565 out.s[out.l + 1] = mb64ct[ (be16p[0] & 0x03) << 4];
566 out.l += 2;
567 }else{
568 out.s[out.l + 1] = mb64ct[((be16p[0] & 0x03) << 4) |
569 (be16p[1] >> 4)];
570 out.s[out.l + 2] = mb64ct[ (be16p[1] & 0x0F) << 2];
571 out.l += 3;
572 }
573 }
574 out.s[out.l++] = '-';
575 }
576 }
577 out.s[out.l] = '\0';
578 ASSERT(out.l <= l_plain);
579 *err_or_null = FAL0;
580 cp = out.s;
581 jleave:
582 NYD2_OU;
583 return cp;
584 jerr:
585 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp, V_(emsg));
586 UNUSED(emsg);
587 goto jleave;
588 }
589
590 FL char *
imap_path_decode(char const * path,boole * err_or_null)591 imap_path_decode(char const *path, boole *err_or_null){
592 /* To a large extend inspired by dovecot(1) TODO use string */
593 boole err_def;
594 u8 *mb64p_base, *mb64p, *mb64xp;
595 char const *emsg, *cp;
596 char *rv_base, *rv, c;
597 uz l_orig, l, i;
598 NYD2_IN;
599
600 if(err_or_null == NULL)
601 err_or_null = &err_def;
602 *err_or_null = FAL0;
603
604 l = l_orig = su_cs_len(path);
605 rv = rv_base = n_autorec_alloc(l << 1);
606 su_mem_copy(rv, path, l +1);
607
608 /* xxx Don't check for invalid characters from malicious servers */
609 if(l == 0 || (cp = su_mem_find(path, '&', l)) == NULL)
610 goto jleave;
611
612 *err_or_null = TRU1;
613
614 emsg = N_("Invalid mUTF-7 encoding");
615 i = P2UZ(cp - path);
616 rv += i;
617 l -= i;
618 mb64p_base = NULL;
619
620 while(l > 0){
621 if((c = *cp) != '&'){
622 if(c <= 0x1F || c >= 0x7F){
623 emsg = N_("Invalid mUTF-7: unencoded control or 8-bit byte");
624 goto jerr;
625 }
626 *rv++ = c;
627 ++cp;
628 --l;
629 }else if(--l == 0)
630 goto jeincpl;
631 else if(*++cp == '-'){
632 *rv++ = '&';
633 ++cp;
634 --l;
635 }else if(l < 3){
636 jeincpl:
637 emsg = N_("Invalid mUTF-7: incomplete input");
638 goto jerr;
639 }else{
640 /* mUTF-7 -> UTF-16BE -> UTF-8 */
641 static u8 const mb64dt[256] = {
642 #undef XX
643 #define XX 0xFFu
644 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
645 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
646 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
647 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
648 XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
649 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
650 XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
651 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
652 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
653 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
654 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
655 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
656 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
657 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
658 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
659 XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
660 };
661
662 if(mb64p_base == NULL)
663 mb64p_base = n_autorec_alloc(l);
664
665 /* Decode the mUTF-7 to what is indeed UTF-16BE */
666 for(mb64p = mb64p_base;;){
667 ASSERT(l >= 3);
668 if((mb64p[0] = mb64dt[(u8)cp[0]]) == XX ||
669 (mb64p[1] = mb64dt[(u8)cp[1]]) == XX)
670 goto jerr;
671 mb64p += 2;
672
673 c = cp[2];
674 cp += 3;
675 l -= 3;
676 if(c == '-')
677 break;
678 if((*mb64p++ = mb64dt[(u8)c]) == XX)
679 goto jerr;
680
681 if(l == 0)
682 goto jerr;
683 --l;
684 if((c = *cp++) == '-')
685 break;
686 if((*mb64p++ = mb64dt[(u8)c]) == XX)
687 goto jerr;
688
689 if(l < 3){
690 if(l > 0 && *cp == '-'){
691 --l;
692 ++cp;
693 break;
694 }
695 goto jerr;
696 }
697 }
698 #undef XX
699
700 if(l >= 2 && cp[0] == '&' && cp[1] != '-'){
701 emsg = N_("Invalid mUTF-7, consecutive encoded sequences");
702 goto jerr;
703 }
704
705 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
706 i = P2UZ(mb64p - mb64p_base);
707 mb64p = mb64xp = mb64p_base;
708
709 while(i > 0){
710 u8 unil, u0, u1, u2, u3;
711
712 unil = (i >= 4) ? 4 : i & 0x3;
713 i -= unil;
714 u0 = mb64xp[0];
715 u1 = mb64xp[1];
716 u2 = (unil < 3) ? 0 : mb64xp[2];
717 u3 = (unil < 4) ? 0 : mb64xp[3];
718 mb64xp += unil;
719 *mb64p++ = (u0 <<= 2) | (u1 >> 4);
720 if(unil < 3)
721 break;
722 *mb64p++ = (u1 <<= 4) | (u2 >> 2);
723 if(unil < 4)
724 break;
725 *mb64p++ = (u2 <<= 6, u2 &= 0xC0) | u3;
726 }
727
728 /* UTF-16BE we convert to UTF-8 */
729 i = P2UZ(mb64p - mb64p_base);
730 if(i & 1){
731 emsg = N_("Odd bytecount for UTF-16BE input");
732 goto jerr;
733 }
734
735 /* TODO S-CText: magic utf16 conversions */
736 emsg = N_("Invalid UTF-16BE encoding");
737
738 for(mb64p = mb64p_base; i > 0;){
739 u32 utf32;
740 u16 uhi, ulo;
741
742 uhi = mb64p[0];
743 uhi <<= 8;
744 uhi |= mb64p[1];
745
746 /* Not a surrogate? */
747 if(uhi < 0xD800 || uhi > 0xDFFF){
748 utf32 = uhi;
749 mb64p += 2;
750 i -= 2;
751 }else if(uhi > 0xDBFF)
752 goto jerr;
753 else if(i < 4){
754 emsg = N_("Incomplete UTF-16BE surrogate pair");
755 goto jerr;
756 }else{
757 ulo = mb64p[2];
758 ulo <<= 8;
759 ulo |= mb64p[3];
760 if(ulo < 0xDC00 || ulo > 0xDFFF)
761 goto jerr;
762
763 utf32 = (uhi &= 0x03FF);
764 utf32 <<= 10;
765 utf32 += 0x10000;
766 utf32 |= (ulo &= 0x03FF);
767 mb64p += 4;
768 i -= 4;
769 }
770
771 utf32 = su_utf32_to_8(utf32, rv);
772 rv += utf32;
773 }
774 }
775 }
776 *rv = '\0';
777
778 /* We can skip the UTF-8 conversion occasionally */
779 #if (defined mx_HAVE_DEVEL || !defined mx_HAVE_ALWAYS_UNICODE_LOCALE) &&\
780 defined mx_HAVE_ICONV
781 if(!(n_psonce & n_PSO_UNICODE)){
782 emsg = N_("iconv(3) from UTF-8 to locale charset failed");
783 if((rv = n_iconv_onetime_cp(n_ICONV_NONE, NULL, NULL, rv_base)) == NULL)
784 goto jerr;
785 }
786 #endif
787
788 *err_or_null = FAL0;
789 rv = rv_base;
790 jleave:
791 NYD2_OU;
792 return rv;
793 jerr:
794 n_err(_("Cannot decode IMAP path %s\n %s\n"), path, V_(emsg));
795 UNUSED(emsg);
796 su_mem_copy(rv = rv_base, path, ++l_orig);
797 goto jleave;
798 }
799
800 static char *
imap_path_quote(struct mailbox * mp,char const * cp)801 imap_path_quote(struct mailbox *mp, char const *cp){
802 boole err;
803 char *rv;
804 NYD2_IN;
805
806 cp = a_imap_path_normalize(mp, cp, FAL0);
807 cp = imap_path_encode(cp, &err);
808 rv = err ? NULL : imap_quotestr(cp);
809 NYD2_OU;
810 return rv;
811 }
812
813 static void
imap_other_get(char * pp)814 imap_other_get(char *pp)
815 {
816 char *xp;
817 NYD2_IN;
818
819 if (su_cs_cmp_case_n(pp, "FLAGS ", 6) == 0) {
820 pp += 6;
821 response_other = MAILBOX_DATA_FLAGS;
822 } else if (su_cs_cmp_case_n(pp, "LIST ", 5) == 0) {
823 pp += 5;
824 response_other = MAILBOX_DATA_LIST;
825 } else if (su_cs_cmp_case_n(pp, "LSUB ", 5) == 0) {
826 pp += 5;
827 response_other = MAILBOX_DATA_LSUB;
828 } else if (su_cs_cmp_case_n(pp, "MAILBOX ", 8) == 0) {
829 pp += 8;
830 response_other = MAILBOX_DATA_MAILBOX;
831 } else if (su_cs_cmp_case_n(pp, "SEARCH ", 7) == 0) {
832 pp += 7;
833 response_other = MAILBOX_DATA_SEARCH;
834 } else if (su_cs_cmp_case_n(pp, "STATUS ", 7) == 0) {
835 pp += 7;
836 response_other = MAILBOX_DATA_STATUS;
837 } else if (su_cs_cmp_case_n(pp, "CAPABILITY ", 11) == 0) {
838 pp += 11;
839 response_other = CAPABILITY_DATA;
840 } else {
841 responded_other_number = strtol(pp, &xp, 10);
842 while (*xp == ' ')
843 ++xp;
844 if (su_cs_cmp_case_n(xp, "EXISTS\r\n", 8) == 0) {
845 response_other = MAILBOX_DATA_EXISTS;
846 } else if (su_cs_cmp_case_n(xp, "RECENT\r\n", 8) == 0) {
847 response_other = MAILBOX_DATA_RECENT;
848 } else if (su_cs_cmp_case_n(xp, "EXPUNGE\r\n", 9) == 0) {
849 response_other = MESSAGE_DATA_EXPUNGE;
850 } else if (su_cs_cmp_case_n(xp, "FETCH ", 6) == 0) {
851 pp = &xp[6];
852 response_other = MESSAGE_DATA_FETCH;
853 } else
854 response_other = RESPONSE_OTHER_UNKNOWN;
855 }
856 responded_other_text = pp;
857 NYD2_OU;
858 }
859
860 static void
imap_response_get(const char ** cp)861 imap_response_get(const char **cp)
862 {
863 NYD2_IN;
864 if (su_cs_cmp_case_n(*cp, "OK ", 3) == 0) {
865 *cp += 3;
866 response_status = RESPONSE_OK;
867 } else if (su_cs_cmp_case_n(*cp, "NO ", 3) == 0) {
868 *cp += 3;
869 response_status = RESPONSE_NO;
870 } else if (su_cs_cmp_case_n(*cp, "BAD ", 4) == 0) {
871 *cp += 4;
872 response_status = RESPONSE_BAD;
873 } else if (su_cs_cmp_case_n(*cp, "PREAUTH ", 8) == 0) {
874 *cp += 8;
875 response_status = RESPONSE_PREAUTH;
876 } else if (su_cs_cmp_case_n(*cp, "BYE ", 4) == 0) {
877 *cp += 4;
878 response_status = RESPONSE_BYE;
879 } else
880 response_status = RESPONSE_OTHER;
881 NYD2_OU;
882 }
883
884 static void
imap_response_parse(void)885 imap_response_parse(void)
886 {
887 static char *parsebuf; /* TODO Use pool */
888 static uz parsebufsize;
889
890 const char *ip = imapbuf;
891 char *pp;
892 NYD2_IN;
893
894 if (parsebufsize < imapbufsize + 1)
895 parsebuf = n_realloc(parsebuf, parsebufsize = imapbufsize);
896 su_mem_copy(parsebuf, imapbuf, su_cs_len(imapbuf) + 1);
897 pp = parsebuf;
898 switch (*ip) {
899 case '+':
900 response_type = RESPONSE_CONT;
901 ip++;
902 pp++;
903 while (*ip == ' ') {
904 ip++;
905 pp++;
906 }
907 break;
908 case '*':
909 ip++;
910 pp++;
911 while (*ip == ' ') {
912 ip++;
913 pp++;
914 }
915 imap_response_get(&ip);
916 pp = &parsebuf[ip - imapbuf];
917 switch (response_status) {
918 case RESPONSE_BYE:
919 response_type = RESPONSE_FATAL;
920 break;
921 default:
922 response_type = RESPONSE_DATA;
923 }
924 break;
925 default:
926 responded_tag = parsebuf;
927 while (*pp && *pp != ' ')
928 pp++;
929 if (*pp == '\0') {
930 response_type = RESPONSE_ILLEGAL;
931 break;
932 }
933 *pp++ = '\0';
934 while (*pp && *pp == ' ')
935 pp++;
936 if (*pp == '\0') {
937 response_type = RESPONSE_ILLEGAL;
938 break;
939 }
940 ip = &imapbuf[pp - parsebuf];
941 response_type = RESPONSE_TAGGED;
942 imap_response_get(&ip);
943 pp = &parsebuf[ip - imapbuf];
944 }
945 responded_text = pp;
946 if (response_type != RESPONSE_CONT && response_type != RESPONSE_ILLEGAL &&
947 response_status == RESPONSE_OTHER)
948 imap_other_get(pp);
949 NYD2_OU;
950 }
951
952 static enum okay
imap_answer(struct mailbox * mp,int errprnt)953 imap_answer(struct mailbox *mp, int errprnt)
954 {
955 int i, complete;
956 enum okay rv;
957 NYD2_IN;
958
959 rv = OKAY;
960 if (mp->mb_type == MB_CACHE)
961 goto jleave;
962 rv = STOP;
963 jagain:
964 if (mx_socket_getline(&imapbuf, &imapbufsize, NULL, mp->mb_sock) > 0) {
965 if (n_poption & n_PO_D_VV)
966 n_err(">>> SERVER: %s", imapbuf);
967 imap_response_parse();
968 if (response_type == RESPONSE_ILLEGAL)
969 goto jagain;
970 if (response_type == RESPONSE_CONT) {
971 rv = OKAY;
972 goto jleave;
973 }
974 if (response_status == RESPONSE_OTHER) {
975 if (response_other == MAILBOX_DATA_EXISTS) {
976 had_exists = responded_other_number;
977 rec_queue(REC_EXISTS, responded_other_number);
978 if (had_expunge > 0)
979 had_expunge = 0;
980 } else if (response_other == MESSAGE_DATA_EXPUNGE) {
981 rec_queue(REC_EXPUNGE, responded_other_number);
982 if (had_expunge < 0)
983 had_expunge = 0;
984 had_expunge++;
985 expunged_messages++;
986 }
987 }
988 complete = 0;
989 if (response_type == RESPONSE_TAGGED) {
990 if (su_cs_cmp_case(responded_tag, tag(0)) == 0)
991 complete |= 1;
992 else
993 goto jagain;
994 }
995 switch (response_status) {
996 case RESPONSE_PREAUTH:
997 mp->mb_active &= ~MB_PREAUTH;
998 /*FALLTHRU*/
999 case RESPONSE_OK:
1000 jokay:
1001 rv = OKAY;
1002 complete |= 2;
1003 break;
1004 case RESPONSE_NO:
1005 case RESPONSE_BAD:
1006 jstop:
1007 complete |= 2;
1008 if (errprnt)
1009 n_err(_("IMAP error: %s"), responded_text);
1010 break;
1011 case RESPONSE_UNKNOWN: /* does not happen */
1012 case RESPONSE_BYE:
1013 i = mp->mb_active;
1014 mp->mb_active = MB_NONE;
1015 if (i & MB_BYE)
1016 goto jokay;
1017 goto jstop;
1018 case RESPONSE_OTHER:
1019 rv = OKAY;
1020 break;
1021 }
1022 if (response_status != RESPONSE_OTHER &&
1023 su_cs_cmp_case_n(responded_text, "[ALERT] ", 8) == 0)
1024 n_err(_("IMAP alert: %s"), &responded_text[8]);
1025 if (complete == 3)
1026 mp->mb_active &= ~MB_COMD;
1027 } else
1028 mp->mb_active = MB_NONE;
1029 jleave:
1030 NYD2_OU;
1031 return rv;
1032 }
1033
1034 static enum okay
imap_parse_list(void)1035 imap_parse_list(void)
1036 {
1037 char *cp;
1038 enum okay rv;
1039 NYD2_IN;
1040
1041 rv = STOP;
1042
1043 cp = responded_other_text;
1044 list_attributes = LIST_NONE;
1045 if (*cp == '(') {
1046 while (*cp && *cp != ')') {
1047 if (*cp == '\\') {
1048 if (su_cs_cmp_case_n(&cp[1], "Noinferiors ", 12) == 0) {
1049 list_attributes |= LIST_NOINFERIORS;
1050 cp += 12;
1051 } else if (su_cs_cmp_case_n(&cp[1], "Noselect ", 9) == 0) {
1052 list_attributes |= LIST_NOSELECT;
1053 cp += 9;
1054 } else if (su_cs_cmp_case_n(&cp[1], "Marked ", 7) == 0) {
1055 list_attributes |= LIST_MARKED;
1056 cp += 7;
1057 } else if (su_cs_cmp_case_n(&cp[1], "Unmarked ", 9) == 0) {
1058 list_attributes |= LIST_UNMARKED;
1059 cp += 9;
1060 }
1061 }
1062 cp++;
1063 }
1064 if (*++cp != ' ')
1065 goto jleave;
1066 while (*cp == ' ')
1067 cp++;
1068 }
1069
1070 list_hierarchy_delimiter = EOF;
1071 if (*cp == '"') {
1072 if (*++cp == '\\')
1073 cp++;
1074 list_hierarchy_delimiter = *cp++ & 0377;
1075 if (cp[0] != '"' || cp[1] != ' ')
1076 goto jleave;
1077 cp++;
1078 } else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' && cp[3] == ' ') {
1079 list_hierarchy_delimiter = EOF;
1080 cp += 3;
1081 }
1082
1083 while (*cp == ' ')
1084 cp++;
1085 list_name = cp;
1086 while (*cp && *cp != '\r')
1087 cp++;
1088 *cp = '\0';
1089 rv = OKAY;
1090 jleave:
1091 NYD2_OU;
1092 return rv;
1093 }
1094
1095 static enum okay
imap_finish(struct mailbox * mp)1096 imap_finish(struct mailbox *mp)
1097 {
1098 NYD_IN;
1099 while(mp->mb_sock != NIL && mp->mb_sock->s_fd > 0 &&
1100 (mp->mb_active & MB_COMD))
1101 imap_answer(mp, 1);
1102 NYD_OU;
1103 return OKAY;
1104 }
1105
1106 static void
imap_timer_off(void)1107 imap_timer_off(void)
1108 {
1109 NYD_IN;
1110 if (imapkeepalive > 0) {
1111 n_pstate &= ~n_PS_SIGALARM;
1112 alarm(0);
1113 safe_signal(SIGALRM, savealrm);
1114 }
1115 NYD_OU;
1116 }
1117
1118 static void
imapcatch(int s)1119 imapcatch(int s)
1120 {
1121 NYD; /* Signal handler */
1122 switch (s) {
1123 case SIGINT:
1124 n_err_sighdl(_("Interrupt\n"));
1125 siglongjmp(imapjmp, 1);
1126 /*NOTREACHED*/
1127 case SIGPIPE:
1128 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1129 break;
1130 }
1131 }
1132
1133 static void
_imap_maincatch(int s)1134 _imap_maincatch(int s)
1135 {
1136 NYD; /* Signal handler */
1137 UNUSED(s);
1138 if (interrupts++ == 0) {
1139 n_err_sighdl(_("Interrupt\n"));
1140 return;
1141 }
1142 n_go_onintr_for_imap();
1143 }
1144
1145 static enum okay
imap_noop1(struct mailbox * mp)1146 imap_noop1(struct mailbox *mp)
1147 {
1148 char o[LINESIZE];
1149 FILE *queuefp = NULL;
1150 NYD;
1151
1152 snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
1153 IMAP_OUT(o, MB_COMD, return STOP)
1154 IMAP_ANSWER()
1155 return OKAY;
1156 }
1157
1158 FL char const *
imap_fileof(char const * xcp)1159 imap_fileof(char const *xcp)
1160 {
1161 char const *cp = xcp;
1162 int state = 0;
1163 NYD_IN;
1164
1165 while (*cp) {
1166 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
1167 cp += 3;
1168 state = 1;
1169 }
1170 if (cp[0] == '/' && state == 1) {
1171 ++cp;
1172 goto jleave;
1173 }
1174 if (cp[0] == '/') {
1175 cp = xcp;
1176 goto jleave;
1177 }
1178 ++cp;
1179 }
1180 jleave:
1181 NYD_OU;
1182 return cp;
1183 }
1184
1185 FL enum okay
imap_noop(void)1186 imap_noop(void)
1187 {
1188 n_sighdl_t volatile oldint, oldpipe;
1189 enum okay volatile rv = STOP;
1190 NYD_IN;
1191
1192 if (mb.mb_type != MB_IMAP)
1193 goto jleave;
1194
1195 imaplock = 1;
1196 if ((oldint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1197 safe_signal(SIGINT, &_imap_maincatch);
1198 oldpipe = safe_signal(SIGPIPE, SIG_IGN);
1199 if (sigsetjmp(imapjmp, 1) == 0) {
1200 if (oldpipe != SIG_IGN)
1201 safe_signal(SIGPIPE, imapcatch);
1202
1203 rv = imap_noop1(&mb);
1204 }
1205 safe_signal(SIGINT, oldint);
1206 safe_signal(SIGPIPE, oldpipe);
1207 imaplock = 0;
1208 jleave:
1209 NYD_OU;
1210 if (interrupts)
1211 n_go_onintr_for_imap();
1212 return rv;
1213 }
1214
1215 static void
rec_queue(enum rec_type rt,unsigned long cnt)1216 rec_queue(enum rec_type rt, unsigned long cnt)
1217 {
1218 struct record *rp;
1219 NYD_IN;
1220
1221 rp = n_calloc(1, sizeof *rp);
1222 rp->rec_type = rt;
1223 rp->rec_count = cnt;
1224 if (record && recend) {
1225 recend->rec_next = rp;
1226 recend = rp;
1227 } else
1228 record = recend = rp;
1229 NYD_OU;
1230 }
1231
1232 static enum okay
rec_dequeue(void)1233 rec_dequeue(void)
1234 {
1235 struct message *omessage;
1236 struct record *rp, *rq;
1237 uz exists = 0, i;
1238 enum okay rv = STOP;
1239 NYD_IN;
1240
1241 if (record == NULL)
1242 goto jleave;
1243
1244 omessage = message;
1245 message = n_alloc((msgCount+1) * sizeof *message);
1246 if (msgCount)
1247 su_mem_copy(message, omessage, msgCount * sizeof *message);
1248 su_mem_set(&message[msgCount], 0, sizeof *message);
1249
1250 rp = record, rq = NULL;
1251 rv = OKAY;
1252 while (rp != NULL) {
1253 switch (rp->rec_type) {
1254 case REC_EXISTS:
1255 exists = rp->rec_count;
1256 break;
1257 case REC_EXPUNGE:
1258 if (rp->rec_count == 0) {
1259 rv = STOP;
1260 break;
1261 }
1262 if (rp->rec_count > (unsigned long)msgCount) {
1263 if (exists == 0 || rp->rec_count > exists--)
1264 rv = STOP;
1265 break;
1266 }
1267 if (exists > 0)
1268 exists--;
1269 delcache(&mb, &message[rp->rec_count-1]);
1270 su_mem_move(&message[rp->rec_count-1], &message[rp->rec_count],
1271 ((msgCount - rp->rec_count + 1) * sizeof *message));
1272 --msgCount;
1273 /* If the message was part of a collapsed thread,
1274 * the m_collapsed field of one of its ancestors
1275 * should be incremented. It seems hardly possible
1276 * to do this with the current message structure,
1277 * though. The result is that a '+' may be shown
1278 * in the header summary even if no collapsed
1279 * children exists */
1280 break;
1281 }
1282 if (rq != NULL)
1283 n_free(rq);
1284 rq = rp;
1285 rp = rp->rec_next;
1286 }
1287 if (rq != NULL)
1288 n_free(rq);
1289
1290 record = recend = NULL;
1291 if (rv == OKAY && UCMP(z, exists, >, msgCount)) {
1292 message = n_realloc(message, (exists + 1) * sizeof *message);
1293 su_mem_set(&message[msgCount], 0,
1294 (exists - msgCount + 1) * sizeof *message);
1295 for (i = msgCount; i < exists; ++i)
1296 imap_init(&mb, i);
1297 imap_flags(&mb, msgCount+1, exists);
1298 msgCount = exists;
1299 }
1300
1301 if (rv == STOP) {
1302 n_free(message);
1303 message = omessage;
1304 }
1305 jleave:
1306 NYD_OU;
1307 return rv;
1308 }
1309
1310 static void
rec_rmqueue(void)1311 rec_rmqueue(void)
1312 {
1313 struct record *rp;
1314 NYD_IN;
1315
1316 for (rp = record; rp != NULL;) {
1317 struct record *tmp = rp;
1318 rp = rp->rec_next;
1319 n_free(tmp);
1320 }
1321 record = recend = NULL;
1322 NYD_OU;
1323 }
1324
1325 /*ARGSUSED*/
1326 static void
imapalarm(int s)1327 imapalarm(int s)
1328 {
1329 n_sighdl_t volatile saveint, savepipe;
1330 NYD; /* Signal handler */
1331 UNUSED(s);
1332
1333 if (imaplock++ == 0) {
1334 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
1335 safe_signal(SIGINT, &_imap_maincatch);
1336 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1337 if (sigsetjmp(imapjmp, 1)) {
1338 safe_signal(SIGINT, saveint);
1339 safe_signal(SIGPIPE, savepipe);
1340 goto jbrk;
1341 }
1342 if (savepipe != SIG_IGN)
1343 safe_signal(SIGPIPE, imapcatch);
1344 if (imap_noop1(&mb) != OKAY) {
1345 safe_signal(SIGINT, saveint);
1346 safe_signal(SIGPIPE, savepipe);
1347 goto jleave;
1348 }
1349 safe_signal(SIGINT, saveint);
1350 safe_signal(SIGPIPE, savepipe);
1351 }
1352 jbrk:
1353 n_pstate |= n_PS_SIGALARM;
1354 alarm(imapkeepalive);
1355 jleave:
1356 --imaplock;
1357 }
1358
1359 static enum okay
imap_preauth(struct mailbox * mp,struct mx_url * urlp,struct mx_cred_ctx * ccred)1360 imap_preauth(struct mailbox *mp, struct mx_url *urlp,
1361 struct mx_cred_ctx *ccred)
1362 {
1363 NYD;
1364
1365 mp->mb_active |= MB_PREAUTH;
1366 imap_answer(mp, 1);
1367
1368 #ifdef mx_HAVE_TLS
1369 if(!mp->mb_sock->s_use_tls){
1370 if(xok_blook(imap_use_starttls, urlp, OXM_ALL)){
1371 FILE *queuefp = NULL;
1372 char o[LINESIZE];
1373
1374 snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
1375 IMAP_OUT(o, MB_COMD, return STOP)
1376 IMAP_ANSWER()
1377 if(!n_tls_open(urlp, mp->mb_sock))
1378 return STOP;
1379 }else if(ccred->cc_needs_tls){
1380 n_err(_("IMAP authentication %s needs TLS "
1381 "(*imap-use-starttls* set?)\n"),
1382 ccred->cc_auth);
1383 return STOP;
1384 }
1385 }
1386 #else
1387 if(ccred->cc_needs_tls || xok_blook(imap_use_starttls, urlp, OXM_ALL)){
1388 n_err(_("No TLS support compiled in\n"));
1389 return STOP;
1390 }
1391 #endif
1392
1393 imap_capability(mp);
1394 return OKAY;
1395 }
1396
1397 static enum okay
imap_capability(struct mailbox * mp)1398 imap_capability(struct mailbox *mp)
1399 {
1400 char o[LINESIZE];
1401 FILE *queuefp = NULL;
1402 enum okay ok = STOP;
1403 const char *cp;
1404 NYD;
1405
1406 snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
1407 IMAP_OUT(o, MB_COMD, return STOP)
1408 while (mp->mb_active & MB_COMD) {
1409 ok = imap_answer(mp, 0);
1410 if (response_status == RESPONSE_OTHER &&
1411 response_other == CAPABILITY_DATA) {
1412 cp = responded_other_text;
1413 while (*cp) {
1414 while (su_cs_is_space(*cp))
1415 ++cp;
1416 if (strncmp(cp, "UIDPLUS", 7) == 0 && su_cs_is_space(cp[7]))
1417 /* RFC 2359 */
1418 mp->mb_flags |= MB_UIDPLUS;
1419 else if(strncmp(cp, "SASL-IR ", 7) == 0 && su_cs_is_space(cp[7]))
1420 /* RFC 4959 */
1421 mp->mb_flags |= MB_SASL_IR;
1422 while (*cp && !su_cs_is_space(*cp))
1423 ++cp;
1424 }
1425 }
1426 }
1427 return ok;
1428 }
1429
1430 static enum okay
a_imap_auth(struct mailbox * mp,struct mx_url * urlp,struct mx_cred_ctx * ccredp)1431 a_imap_auth(struct mailbox *mp, struct mx_url *urlp,
1432 struct mx_cred_ctx *ccredp){
1433 enum okay rv;
1434 NYD_IN;
1435 UNUSED(urlp);
1436
1437 if(!(mp->mb_active & MB_PREAUTH))
1438 rv = OKAY;
1439 else switch(ccredp->cc_authtype){
1440 case mx_CRED_AUTHTYPE_LOGIN:
1441 rv = imap_login(mp, ccredp);
1442 break;
1443 case mx_CRED_AUTHTYPE_OAUTHBEARER:
1444 rv = a_imap_oauthbearer(mp, ccredp);
1445 break;
1446 case mx_CRED_AUTHTYPE_EXTERNAL:
1447 case mx_CRED_AUTHTYPE_EXTERNANON:
1448 rv = a_imap_external(mp, ccredp);
1449 break;
1450 #ifdef mx_HAVE_MD5
1451 case mx_CRED_AUTHTYPE_CRAM_MD5:
1452 rv = imap_cram_md5(mp, ccredp);
1453 break;
1454 #endif
1455 #ifdef mx_HAVE_GSSAPI
1456 case mx_CRED_AUTHTYPE_GSSAPI:
1457 rv = OKAY;
1458 if(n_poption & n_PO_D)
1459 n_err(_(">>> We would perform GSS-API authentication now\n"));
1460 else if(!su_CONCAT(su_FILE,_gss)(mp->mb_sock, urlp, ccredp, mp))
1461 rv = STOP;
1462 break;
1463 #endif
1464 default:
1465 rv = STOP;
1466 break;
1467 }
1468
1469 NYD_OU;
1470 return rv;
1471 }
1472
1473 #ifdef mx_HAVE_MD5
1474 static enum okay
imap_cram_md5(struct mailbox * mp,struct mx_cred_ctx * ccred)1475 imap_cram_md5(struct mailbox *mp, struct mx_cred_ctx *ccred)
1476 {
1477 char o[LINESIZE], *cp;
1478 FILE *queuefp = NULL;
1479 enum okay rv = STOP;
1480 NYD_IN;
1481
1482 snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1483 IMAP_XOUT(o, 0, goto jleave, goto jleave);
1484 imap_answer(mp, 1);
1485 if (response_type != RESPONSE_CONT)
1486 goto jleave;
1487
1488 cp = mx_md5_cram_string(&ccred->cc_user, &ccred->cc_pass, responded_text);
1489 if(cp == NULL)
1490 goto jleave;
1491 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1492 while (mp->mb_active & MB_COMD)
1493 rv = imap_answer(mp, 1);
1494 jleave:
1495 NYD_OU;
1496 return rv;
1497 }
1498 #endif /* mx_HAVE_MD5 */
1499
1500 static enum okay
imap_login(struct mailbox * mp,struct mx_cred_ctx * ccred)1501 imap_login(struct mailbox *mp, struct mx_cred_ctx *ccred)
1502 {
1503 char o[LINESIZE];
1504 FILE *queuefp = NULL;
1505 enum okay rv = STOP;
1506 NYD_IN;
1507
1508 snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
1509 tag(1), imap_quotestr(ccred->cc_user.s),
1510 imap_quotestr(ccred->cc_pass.s));
1511 IMAP_XOUT(o, MB_COMD, goto jleave, goto jleave);
1512 while (mp->mb_active & MB_COMD)
1513 rv = imap_answer(mp, 1);
1514 jleave:
1515 NYD_OU;
1516 return rv;
1517 }
1518
1519 static enum okay
a_imap_oauthbearer(struct mailbox * mp,struct mx_cred_ctx * ccp)1520 a_imap_oauthbearer(struct mailbox *mp, struct mx_cred_ctx *ccp){
1521 struct str b64;
1522 int i;
1523 uz cnt;
1524 boole nsaslir;
1525 char *cp;
1526 FILE *queuefp;
1527 enum okay rv;
1528 NYD_IN;
1529
1530 rv = STOP;
1531 queuefp = NIL;
1532 cp = NIL;
1533 nsaslir = !(mp->mb_flags & MB_SASL_IR);
1534
1535 /* Calculate required storage */
1536 cnt = ccp->cc_user.l;
1537 #define a_MAX \
1538 (sizeof("T1 ") -1 +\
1539 sizeof("AUTHENTICATE XOAUTH2 ") -1 +\
1540 2 + sizeof("user=\001auth=Bearer \001\001") -1 +\
1541 sizeof(NETNL) -1 +1)
1542
1543 if(ccp->cc_pass.l >= UZ_MAX - a_MAX ||
1544 cnt >= UZ_MAX - a_MAX - ccp->cc_pass.l){
1545 jerr_cred:
1546 n_err(_("Credentials overflow buffer sizes\n"));
1547 goto jleave;
1548 }
1549 cnt += ccp->cc_pass.l;
1550
1551 cnt += a_MAX;
1552 #undef a_MAX
1553 if((cnt = b64_encode_calc_size(cnt)) == UZ_MAX)
1554 goto jerr_cred;
1555
1556 cp = n_lofi_alloc(cnt +1);
1557
1558 /* Then create login query */
1559 i = snprintf(cp, cnt +1, "user=%s\001auth=Bearer %s\001\001",
1560 ccp->cc_user.s, ccp->cc_pass.s);
1561 if(b64_encode_buf(&b64, cp, i, B64_SALLOC) == NIL)
1562 goto jleave;
1563 else{
1564 char const *tp;
1565
1566 tp = tag(TRU1);
1567 cnt = su_cs_len(tp);
1568 su_mem_copy(cp, tp, cnt);
1569 }
1570 su_mem_copy(&cp[cnt], " AUTHENTICATE XOAUTH2 ",
1571 sizeof(" AUTHENTICATE XOAUTH2 ") /*-1*/);
1572 cnt += sizeof(" AUTHENTICATE XOAUTH2 ") -1 - 1;
1573 if(!nsaslir){
1574 su_mem_copy(&cp[++cnt], b64.s, b64.l);
1575 cnt += b64.l;
1576 }
1577 su_mem_copy(&cp[cnt], NETNL, sizeof(NETNL));
1578
1579 IMAP_XOUT(cp, (nsaslir ? 0 : MB_COMD), goto jleave, goto jleave);
1580 rv = imap_answer(mp, 1);
1581 if(rv == STOP)
1582 goto jleave;
1583
1584 if(nsaslir){
1585 if(response_type != RESPONSE_CONT)
1586 goto jleave;
1587
1588 su_mem_copy(cp, b64.s, b64.l);
1589 su_mem_copy(&cp[b64.l], NETNL, sizeof(NETNL));
1590 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1591 }
1592
1593 while(mp->mb_active & MB_COMD)
1594 rv = imap_answer(mp, 1);
1595 jleave:
1596 if(cp != NIL)
1597 n_lofi_free(cp);
1598 NYD_OU;
1599 return rv;
1600 }
1601
1602 static enum okay
a_imap_external(struct mailbox * mp,struct mx_cred_ctx * ccp)1603 a_imap_external(struct mailbox *mp, struct mx_cred_ctx *ccp){
1604 struct str s;
1605 uz cnt;
1606 boole nsaslir;
1607 char *cp;
1608 FILE *queuefp;
1609 enum okay rv;
1610 NYD_IN;
1611
1612 rv = STOP;
1613 queuefp = NIL;
1614 cp = NIL;
1615
1616 nsaslir = !(mp->mb_flags & MB_SASL_IR);
1617
1618 if(ccp->cc_authtype == mx_CRED_AUTHTYPE_EXTERNANON){
1619 ccp->cc_user.l = !nsaslir;
1620 ccp->cc_user.s = UNCONST(char*,"=");
1621 }
1622
1623 /* Calculate required storage */
1624 #define a_MAX \
1625 (sizeof("T1 ") -1 +\
1626 sizeof("AUTHENTICATE EXTERNAL ") -1 +\
1627 sizeof(NETNL) -1 +1)
1628
1629 if(ccp->cc_authtype == mx_CRED_AUTHTYPE_EXTERNANON){
1630 ccp->cc_user.s = UNCONST(char*,"=");
1631 cnt = ccp->cc_user.l = !nsaslir;
1632 }else{
1633 cnt = ccp->cc_user.l;
1634 cnt = b64_encode_calc_size(cnt);
1635 }
1636 if(cnt >= UZ_MAX - a_MAX){
1637 n_err(_("Credentials overflow buffer sizes\n"));
1638 goto jleave;
1639 }
1640 cnt += a_MAX;
1641 #undef a_MAX
1642
1643 cp = n_lofi_alloc(cnt +1);
1644
1645 /* C99 */{
1646 char const *tp;
1647
1648 tp = tag(TRU1);
1649 cnt = su_cs_len(tp);
1650 su_mem_copy(cp, tp, cnt);
1651 }
1652 su_mem_copy(&cp[cnt], " AUTHENTICATE EXTERNAL ",
1653 sizeof(" AUTHENTICATE EXTERNAL ") /*-1*/);
1654 cnt += sizeof(" AUTHENTICATE EXTERNAL ") -1 - 1;
1655
1656 if(!nsaslir){
1657 if(ccp->cc_authtype != mx_CRED_AUTHTYPE_EXTERNANON){
1658 s.s = &cp[++cnt];
1659 b64_encode_buf(&s, ccp->cc_user.s, ccp->cc_user.l, B64_BUF);
1660 cnt += s.l;
1661 }else{
1662 su_mem_copy(&cp[++cnt], ccp->cc_user.s, ccp->cc_user.l);
1663 cnt += ccp->cc_user.l;
1664 }
1665 }
1666 su_mem_copy(&cp[cnt], NETNL, sizeof(NETNL));
1667
1668 IMAP_XOUT(cp, (nsaslir ? 0 : MB_COMD), goto jleave, goto jleave);
1669 rv = imap_answer(mp, 1);
1670 if(rv == STOP)
1671 goto jleave;
1672
1673 if(nsaslir){
1674 if(response_type != RESPONSE_CONT)
1675 goto jleave;
1676
1677 if(ccp->cc_authtype != mx_CRED_AUTHTYPE_EXTERNANON){
1678 s.s = &cp[cnt = 0];
1679 b64_encode_buf(&s, ccp->cc_user.s, ccp->cc_user.l, B64_BUF);
1680 cnt = s.l;
1681 }else{
1682 su_mem_copy(&cp[0], ccp->cc_user.s, ccp->cc_user.l);
1683 cnt = ccp->cc_user.l;
1684 }
1685 su_mem_copy(&cp[cnt], NETNL, sizeof(NETNL));
1686
1687 IMAP_XOUT(cp, MB_COMD, goto jleave, goto jleave);
1688 }
1689
1690 while(mp->mb_active & MB_COMD)
1691 rv = imap_answer(mp, 1);
1692 jleave:
1693 if(cp != NIL)
1694 n_lofi_free(cp);
1695 NYD_OU;
1696 return rv;
1697 }
1698
1699 FL enum okay
imap_select(struct mailbox * mp,off_t * size,int * cnt,const char * mbx,enum fedit_mode fm)1700 imap_select(struct mailbox *mp, off_t *size, int *cnt, const char *mbx,
1701 enum fedit_mode fm)
1702 {
1703 char o[LINESIZE];
1704 char const *qname, *cp;
1705 FILE *queuefp;
1706 enum okay ok;
1707 NYD;
1708 UNUSED(size);
1709
1710 ok = STOP;
1711 queuefp = NULL;
1712
1713 if((qname = imap_path_quote(mp, mbx)) == NULL)
1714 goto jleave;
1715
1716 ok = OKAY;
1717
1718 mp->mb_uidvalidity = 0;
1719 snprintf(o, sizeof o, "%s %s %s\r\n", tag(1),
1720 (fm & FEDIT_RDONLY ? "EXAMINE" : "SELECT"), qname);
1721 IMAP_OUT(o, MB_COMD, ok = STOP;goto jleave)
1722 while (mp->mb_active & MB_COMD) {
1723 ok = imap_answer(mp, 1);
1724 if (response_status != RESPONSE_OTHER &&
1725 (cp = su_cs_find_case(responded_text, "[UIDVALIDITY ")) != NULL)
1726 su_idec_u64_cp(&mp->mb_uidvalidity, &cp[13], 10, NULL);/* TODO err? */
1727 }
1728 *cnt = (had_exists > 0) ? had_exists : 0;
1729 if (response_status != RESPONSE_OTHER &&
1730 su_cs_cmp_case_n(responded_text, "[READ-ONLY] ", 12) == 0)
1731 mp->mb_perm = 0;
1732 jleave:
1733 return ok;
1734 }
1735
1736 static enum okay
imap_flags(struct mailbox * mp,unsigned X,unsigned Y)1737 imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
1738 {
1739 char o[LINESIZE];
1740 FILE *queuefp = NULL;
1741 char const *cp;
1742 struct message *m;
1743 unsigned x = X, y = Y, n;
1744 NYD;
1745
1746 snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
1747 IMAP_OUT(o, MB_COMD, return STOP)
1748 while (mp->mb_active & MB_COMD) {
1749 imap_answer(mp, 1);
1750 if (response_status == RESPONSE_OTHER &&
1751 response_other == MESSAGE_DATA_FETCH) {
1752 n = responded_other_number;
1753 if (n < x || n > y)
1754 continue;
1755 m = &message[n-1];
1756 m->m_xsize = 0;
1757 } else
1758 continue;
1759
1760 if ((cp = su_cs_find_case(responded_other_text, "FLAGS ")) != NULL) {
1761 cp += 5;
1762 while (*cp == ' ')
1763 cp++;
1764 if (*cp == '(')
1765 imap_getflags(cp, &cp, &m->m_flag);
1766 }
1767
1768 if ((cp = su_cs_find_case(responded_other_text, "UID ")) != NULL)
1769 su_idec_u64_cp(&m->m_uid, &cp[4], 10, NULL);/* TODO errors? */
1770 getcache1(mp, m, NEED_UNSPEC, 1);
1771 m->m_flag &= ~MHIDDEN;
1772 }
1773
1774 while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
1775 x++;
1776 while (y > x && message[y-1].m_xsize && message[y-1].m_time)
1777 y--;
1778 if (x <= y) {
1779 snprintf(o, sizeof o, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1780 tag(1), x, y);
1781 IMAP_OUT(o, MB_COMD, return STOP)
1782 while (mp->mb_active & MB_COMD) {
1783 imap_answer(mp, 1);
1784 if (response_status == RESPONSE_OTHER &&
1785 response_other == MESSAGE_DATA_FETCH) {
1786 n = responded_other_number;
1787 if (n < x || n > y)
1788 continue;
1789 m = &message[n-1];
1790 } else
1791 continue;
1792 if ((cp = su_cs_find_case(responded_other_text, "RFC822.SIZE ")
1793 ) != NULL)
1794 m->m_xsize = strtol(&cp[12], NULL, 10);
1795 if ((cp = su_cs_find_case(responded_other_text, "INTERNALDATE ")
1796 ) != NULL)
1797 m->m_time = imap_read_date_time(&cp[13]);
1798 }
1799 }
1800
1801 srelax_hold();
1802 for (n = X; n <= Y; ++n) {
1803 putcache(mp, &message[n-1]);
1804 srelax();
1805 }
1806 srelax_rele();
1807 return OKAY;
1808 }
1809
1810 static void
imap_init(struct mailbox * mp,int n)1811 imap_init(struct mailbox *mp, int n)
1812 {
1813 struct message *m;
1814 NYD_IN;
1815 UNUSED(mp);
1816
1817 m = message + n;
1818 m->m_flag = MUSED | MNOFROM;
1819 m->m_block = 0;
1820 m->m_offset = 0;
1821 NYD_OU;
1822 }
1823
1824 static void
imap_setptr(struct mailbox * mp,int nmail,int transparent,int * prevcount)1825 imap_setptr(struct mailbox *mp, int nmail, int transparent, int *prevcount)
1826 {
1827 struct message *omessage = 0;
1828 int i, omsgCount = 0;
1829 enum okay dequeued = STOP;
1830 NYD_IN;
1831
1832 if (nmail || transparent) {
1833 omessage = message;
1834 omsgCount = msgCount;
1835 }
1836 if (nmail)
1837 dequeued = rec_dequeue();
1838
1839 if (had_exists >= 0) {
1840 if (dequeued != OKAY)
1841 msgCount = had_exists;
1842 had_exists = -1;
1843 }
1844 if (had_expunge >= 0) {
1845 if (dequeued != OKAY)
1846 msgCount -= had_expunge;
1847 had_expunge = -1;
1848 }
1849
1850 if (nmail && expunged_messages)
1851 printf("Expunged %ld message%s.\n", expunged_messages,
1852 (expunged_messages != 1 ? "s" : ""));
1853 *prevcount = omsgCount - expunged_messages;
1854 expunged_messages = 0;
1855 if (msgCount < 0) {
1856 fputs("IMAP error: Negative message count\n", stderr);
1857 msgCount = 0;
1858 }
1859
1860 if (dequeued != OKAY) {
1861 message = n_calloc(msgCount + 1, sizeof *message);
1862 for (i = 0; i < msgCount; i++)
1863 imap_init(mp, i);
1864 if (!nmail && mp->mb_type == MB_IMAP)
1865 initcache(mp);
1866 if (msgCount > 0)
1867 imap_flags(mp, 1, msgCount);
1868 message[msgCount].m_size = 0;
1869 message[msgCount].m_lines = 0;
1870 rec_rmqueue();
1871 }
1872 if (nmail || transparent)
1873 transflags(omessage, omsgCount, transparent);
1874 else
1875 setdot(message);
1876 NYD_OU;
1877 }
1878
1879 FL int
imap_setfile(char const * volatile who,const char * xserver,enum fedit_mode fm)1880 imap_setfile(char const * volatile who, const char *xserver,
1881 enum fedit_mode fm)
1882 {
1883 struct mx_url url;
1884 int rv;
1885 NYD_IN;
1886
1887 if(!mx_url_parse(&url, CPROTO_IMAP, xserver)){
1888 rv = 1;
1889 goto jleave;
1890 }
1891 if (ok_vlook(v15_compat) == NIL && url.url_pass.s != NIL)
1892 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1893
1894 _imap_rdonly = ((fm & FEDIT_RDONLY) != 0);
1895 rv = _imap_setfile1(who, &url, fm, 0);
1896 jleave:
1897 NYD_OU;
1898 return rv;
1899 }
1900
1901 static boole
_imap_getcred(struct mailbox * mbp,struct mx_cred_ctx * ccredp,struct mx_url * urlp)1902 _imap_getcred(struct mailbox *mbp, struct mx_cred_ctx *ccredp,
1903 struct mx_url *urlp)
1904 {
1905 boole rv = FAL0;
1906 NYD_IN;
1907
1908 if (ok_vlook(v15_compat) != su_NIL)
1909 rv = mx_cred_auth_lookup(ccredp, urlp);
1910 else {
1911 char *xuhp, *var, *old;
1912
1913 xuhp = ((urlp->url_flags & mx_URL_HAD_USER) ? urlp->url_eu_h_p.s
1914 : urlp->url_u_h_p.s);
1915 UNINIT(old, NIL);
1916
1917 if ((var = mbp->mb_imap_pass) != NULL) {
1918 var = savecat("password-", xuhp);
1919 if ((old = n_UNCONST(n_var_vlook(var, FAL0))) != NULL)
1920 old = su_cs_dup(old, 0);
1921 n_var_vset(var, (up)mbp->mb_imap_pass);
1922 }
1923 rv = mx_cred_auth_lookup_old(ccredp, CPROTO_IMAP, xuhp);
1924 if (var != NULL) {
1925 if (old != NULL) {
1926 n_var_vset(var, (up)old);
1927 n_free(old);
1928 } else
1929 n_var_vclear(var);
1930 }
1931 }
1932
1933 NYD_OU;
1934 return rv;
1935 }
1936
1937 static int
_imap_setfile1(char const * volatile who,struct mx_url * urlp,enum fedit_mode volatile fm,int volatile transparent)1938 _imap_setfile1(char const * volatile who, struct mx_url *urlp,
1939 enum fedit_mode volatile fm, int volatile transparent)
1940 {
1941 struct mx_socket so;
1942 struct mx_cred_ctx ccred;
1943 n_sighdl_t volatile saveint, savepipe;
1944 char const *cp;
1945 int rv;
1946 int volatile prevcount = 0;
1947 enum mbflags same_flags;
1948 NYD_IN;
1949
1950 if (fm & FEDIT_NEWMAIL) {
1951 saveint = safe_signal(SIGINT, SIG_IGN);
1952 savepipe = safe_signal(SIGPIPE, SIG_IGN);
1953 if (saveint != SIG_IGN)
1954 safe_signal(SIGINT, imapcatch);
1955 if (savepipe != SIG_IGN)
1956 safe_signal(SIGPIPE, imapcatch);
1957 imaplock = 1;
1958 goto jnmail;
1959 }
1960
1961 same_flags = mb.mb_flags;
1962 same_imap_account = 0;
1963 if (mb.mb_imap_account != NULL &&
1964 (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)) {
1965 if(mb.mb_sock != NIL && mb.mb_sock->s_fd > 0 && mb.mb_sock->s_rsz >= 0 &&
1966 !su_cs_cmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1967 disconnected(mb.mb_imap_account) == 0) {
1968 same_imap_account = 1;
1969 if (urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1970 /*
1971 goto jduppass;
1972 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1973 !su_cs_cmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1974 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1975 jduppass:
1976 */
1977 urlp->url_pass.l = su_cs_len(urlp->url_pass.s =
1978 savestr(mb.mb_imap_pass));
1979 }
1980 }
1981
1982 if (!same_imap_account && mb.mb_imap_pass != NULL) {
1983 n_free(mb.mb_imap_pass);
1984 mb.mb_imap_pass = NULL;
1985 }
1986 if (!_imap_getcred(&mb, &ccred, urlp)) {
1987 rv = -1;
1988 goto jleave;
1989 }
1990
1991 su_mem_set(&so, 0, sizeof so);
1992 so.s_fd = -1;
1993 if (!same_imap_account) {
1994 if (!disconnected(urlp->url_p_eu_h_p) && !mx_socket_open(&so, urlp)) {
1995 rv = -1;
1996 goto jleave;
1997 }
1998 }else if(mb.mb_sock != NIL)
1999 so = *mb.mb_sock;
2000
2001 if(!transparent){
2002 if(!quit(FAL0)){
2003 rv = -1;
2004 goto jleave;
2005 }
2006 }
2007
2008 if (fm & FEDIT_SYSBOX)
2009 n_pstate &= ~n_PS_EDIT;
2010 else
2011 n_pstate |= n_PS_EDIT;
2012 if (mb.mb_imap_account != NULL)
2013 n_free(mb.mb_imap_account);
2014 if (mb.mb_imap_pass != NULL)
2015 n_free(mb.mb_imap_pass);
2016 mb.mb_imap_account = su_cs_dup(urlp->url_p_eu_h_p, 0);
2017 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
2018 * TODO object, and mailbox will naturally have an URL and credentials */
2019 mb.mb_imap_pass = su_cs_dup_cbuf(ccred.cc_pass.s, ccred.cc_pass.l, 0);
2020
2021 if(!same_imap_account && mb.mb_sock != NIL){
2022 if(mb.mb_sock->s_fd >= 0)
2023 mx_socket_close(mb.mb_sock);
2024 su_FREE(mb.mb_sock);
2025 mb.mb_sock = NIL;
2026 }
2027 same_imap_account = 0;
2028
2029 if (!transparent) {
2030 if (mb.mb_itf) {
2031 fclose(mb.mb_itf);
2032 mb.mb_itf = NULL;
2033 }
2034 if (mb.mb_otf) {
2035 fclose(mb.mb_otf);
2036 mb.mb_otf = NULL;
2037 }
2038 if (mb.mb_imap_mailbox != NULL)
2039 n_free(mb.mb_imap_mailbox);
2040 ASSERT(urlp->url_path.s != NULL);
2041 imap_delim_init(&mb, urlp);
2042 mb.mb_imap_mailbox = su_cs_dup(a_imap_path_normalize(&mb,
2043 urlp->url_path.s, FAL0), 0);
2044 initbox(savecatsep(urlp->url_p_eu_h_p,
2045 (mb.mb_imap_delim[0] != '\0' ? mb.mb_imap_delim[0] : n_IMAP_DELIM[0]),
2046 mb.mb_imap_mailbox));
2047 }
2048 mb.mb_type = MB_VOID;
2049 mb.mb_active = MB_NONE;
2050
2051 imaplock = 1;
2052 saveint = safe_signal(SIGINT, SIG_IGN);
2053 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2054 if (sigsetjmp(imapjmp, 1)) {
2055 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
2056 if(mb.mb_sock != NIL){
2057 if(mb.mb_sock->s_fd >= 0)
2058 mx_socket_close(mb.mb_sock);
2059 su_FREE(mb.mb_sock);
2060 mb.mb_sock = NIL;
2061 }
2062 safe_signal(SIGINT, saveint);
2063 safe_signal(SIGPIPE, savepipe);
2064 imaplock = 0;
2065
2066 mb.mb_type = MB_VOID;
2067 mb.mb_active = MB_NONE;
2068 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
2069 goto jleave;
2070 }
2071 if (saveint != SIG_IGN)
2072 safe_signal(SIGINT, imapcatch);
2073 if (savepipe != SIG_IGN)
2074 safe_signal(SIGPIPE, imapcatch);
2075
2076 if(mb.mb_sock == NIL || mb.mb_sock->s_fd < 0){
2077 if (disconnected(mb.mb_imap_account)) {
2078 if (cache_setptr(fm, transparent) == STOP)
2079 n_err(_("Mailbox \"%s\" is not cached\n"), urlp->url_p_eu_h_p_p);
2080 goto jdone;
2081 }
2082 if ((cp = xok_vlook(imap_keepalive, urlp, OXM_ALL)) != NULL) {
2083 if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
2084 n_pstate |= n_PS_SIGALARM;
2085 savealrm = safe_signal(SIGALRM, imapalarm);
2086 alarm(imapkeepalive);
2087 }
2088 }
2089
2090 if(mb.mb_sock == NIL)
2091 mb.mb_sock = su_TALLOC(struct mx_socket, 1);
2092 *mb.mb_sock = so;
2093 mb.mb_sock->s_desc = "IMAP";
2094 mb.mb_sock->s_onclose = imap_timer_off;
2095 if (imap_preauth(&mb, urlp, &ccred) != OKAY ||
2096 a_imap_auth(&mb, urlp, &ccred) != OKAY) {
2097 if(mb.mb_sock->s_fd >= 0)
2098 mx_socket_close(mb.mb_sock);
2099 su_FREE(mb.mb_sock);
2100 mb.mb_sock = NIL;
2101 imap_timer_off();
2102 safe_signal(SIGINT, saveint);
2103 safe_signal(SIGPIPE, savepipe);
2104 imaplock = 0;
2105 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
2106 goto jleave;
2107 }
2108 } else /* same account */
2109 mb.mb_flags |= same_flags;
2110
2111 if (n_poption & n_PO_R_FLAG)
2112 fm |= FEDIT_RDONLY;
2113 mb.mb_perm = (fm & FEDIT_RDONLY) ? 0 : MB_DELE;
2114 mb.mb_type = MB_IMAP;
2115 cache_dequeue(&mb);
2116 ASSERT(urlp->url_path.s != NULL);
2117 if (imap_select(&mb, &mailsize, &msgCount, urlp->url_path.s, fm) != OKAY) {
2118 mx_socket_close(mb.mb_sock);
2119 su_FREE(mb.mb_sock);
2120 mb.mb_sock = NIL;
2121 /*imap_timer_off();*/
2122 safe_signal(SIGINT, saveint);
2123 safe_signal(SIGPIPE, savepipe);
2124 imaplock = 0;
2125 mb.mb_type = MB_VOID;
2126 rv = (fm & (FEDIT_SYSBOX | FEDIT_NEWMAIL)) ? 1 : -1;
2127 goto jleave;
2128 }
2129
2130 jnmail:
2131 imap_setptr(&mb, ((fm & FEDIT_NEWMAIL) != 0), transparent,
2132 UNVOLATILE(int*,&prevcount));
2133 jdone:
2134 setmsize(msgCount);
2135 safe_signal(SIGINT, saveint);
2136 safe_signal(SIGPIPE, savepipe);
2137 imaplock = 0;
2138
2139 if (!(fm & FEDIT_NEWMAIL) && mb.mb_type == MB_IMAP)
2140 purgecache(&mb, message, msgCount);
2141 if (((fm & FEDIT_NEWMAIL) || transparent) && mb.mb_sorted) {
2142 mb.mb_threaded = 0;
2143 c_sort((void*)-1);
2144 }
2145
2146 if (!(fm & FEDIT_NEWMAIL) && !transparent) {
2147 n_pstate &= ~n_PS_SAW_COMMAND;
2148 n_pstate |= n_PS_SETFILE_OPENED;
2149 }
2150
2151 if ((n_poption & n_PO_EXISTONLY) && (mb.mb_type == MB_IMAP ||
2152 mb.mb_type == MB_CACHE)) {
2153 rv = (msgCount == 0);
2154 goto jleave;
2155 }
2156
2157 if (!(fm & FEDIT_NEWMAIL) && !(n_pstate & n_PS_EDIT) && msgCount == 0) {
2158 if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
2159 !ok_blook(emptystart)){
2160 char const *intro;
2161
2162 if(who == NULL)
2163 intro = who = n_empty;
2164 else
2165 intro = _(" for ");
2166 n_err(_("No mail%s%s at %s\n"), intro, who, urlp->url_p_eu_h_p_p);
2167 }
2168 rv = 1;
2169 goto jleave;
2170 }
2171
2172 if (fm & FEDIT_NEWMAIL)
2173 newmailinfo(prevcount);
2174 rv = 0;
2175 jleave:
2176 NYD_OU;
2177 return rv;
2178 }
2179
2180 static void
imap_fetchdata(struct mailbox * mp,struct message * m,uz expected,int need,const char * head,uz headsize,long headlines)2181 imap_fetchdata(struct mailbox *mp, struct message *m, uz expected,
2182 int need, const char *head, uz headsize, long headlines)
2183 {
2184 char *line, *lp;
2185 uz linesize, linelen, size = 0;
2186 int emptyline = 0, lines = 0;
2187 off_t offset;
2188 NYD_IN;
2189
2190 mx_fs_linepool_aquire(&line, &linesize);
2191
2192 fseek(mp->mb_otf, 0L, SEEK_END);
2193 offset = ftell(mp->mb_otf);
2194
2195 if(head)
2196 fwrite(head, 1, headsize, mp->mb_otf);
2197
2198 while(expected > 0 &&
2199 mx_socket_getline(&line, &linesize, &linelen, mp->mb_sock) > 0){
2200 lp = line;
2201 if(linelen > expected)
2202 lp[linelen = expected] = '\0';
2203 expected -= linelen;
2204
2205 while(linelen > 0 && (lp[linelen - 1] == NETNL[0] ||
2206 lp[linelen - 1] == NETNL[1]))
2207 lp[--linelen] = '\0';
2208
2209 if(n_poption & n_PO_D_VVV)
2210 n_err(">>> SERVER: %s\n", lp);
2211
2212 /* TODO >>
2213 * Need to mask 'From ' lines. This cannot be done properly
2214 * since some servers pass them as 'From ' and others as
2215 * '>From '. Although one could identify the first kind of
2216 * server in principle, it is not possible to identify the
2217 * second as '>From ' may also come from a server of the
2218 * first type as actual data. So do what is absolutely
2219 * necessary only - mask 'From '.
2220 *
2221 * If the line is the first line of the message header, it
2222 * is likely a real 'From ' line. In this case, it is just
2223 * ignored since it violates all standards.
2224 * TODO i have *never* seen the latter?!?!?
2225 * TODO <<
2226 */
2227 /* TODO Since we simply copy over data without doing any transfer
2228 * TODO encoding reclassification/adjustment we *have* to perform
2229 * TODO RFC 4155 compliant From_ quoting here TODO REALLY NOT! */
2230 if(emptyline && is_head(lp, linelen, FAL0)){
2231 fputc('>', mp->mb_otf);
2232 ++size;
2233 }
2234 if(!(emptyline = (linelen == 0)))
2235 fwrite(lp, 1, linelen, mp->mb_otf);
2236 putc('\n', mp->mb_otf);
2237 size += ++linelen;
2238 ++lines;
2239 }
2240
2241 if(!emptyline){
2242 /* TODO This is very ugly; but some IMAP daemons don't end a
2243 * TODO message with \r\n\r\n, and we need \n\n for mbox format.
2244 * TODO That is to say we do it wrong here in order to get it right
2245 * TODO when send.c stuff or with MBOX handling, even though THIS
2246 * TODO line is solely a property of the MBOX database format! */
2247 fputc('\n', mp->mb_otf);
2248 ++size;
2249 ++lines;
2250 }
2251
2252 fflush(mp->mb_otf);
2253
2254 if(m != NIL){
2255 m->m_size = size + headsize;
2256 m->m_lines = lines + headlines;
2257 m->m_block = mailx_blockof(offset);
2258 m->m_offset = mailx_offsetof(offset);
2259 switch (need) {
2260 case NEED_HEADER:
2261 m->m_content_info = CI_HAVE_HEADER;
2262 break;
2263 case NEED_BODY:
2264 m->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
2265 m->m_xlines = m->m_lines;
2266 m->m_xsize = m->m_size;
2267 break;
2268 }
2269 }
2270
2271 mx_fs_linepool_release(line, linesize);
2272 NYD_OU;
2273 }
2274
2275 static void
imap_putstr(struct mailbox * mp,struct message * m,const char * str,const char * head,uz headsize,long headlines)2276 imap_putstr(struct mailbox *mp, struct message *m, const char *str,
2277 const char *head, uz headsize, long headlines)
2278 {
2279 off_t offset;
2280 uz len;
2281 NYD_IN;
2282
2283 len = su_cs_len(str);
2284 fseek(mp->mb_otf, 0L, SEEK_END);
2285 offset = ftell(mp->mb_otf);
2286 if (head)
2287 fwrite(head, 1, headsize, mp->mb_otf);
2288 if (len > 0) {
2289 fwrite(str, 1, len, mp->mb_otf);
2290 fputc('\n', mp->mb_otf);
2291 ++len;
2292 }
2293 fflush(mp->mb_otf);
2294
2295 if (m != NULL) {
2296 m->m_size = headsize + len;
2297 m->m_lines = headlines + 1;
2298 m->m_block = mailx_blockof(offset);
2299 m->m_offset = mailx_offsetof(offset);
2300 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2301 m->m_xlines = m->m_lines;
2302 m->m_xsize = m->m_size;
2303 }
2304 NYD_OU;
2305 }
2306
2307 static enum okay
imap_get(struct mailbox * mp,struct message * m,enum needspec need)2308 imap_get(struct mailbox *mp, struct message *m, enum needspec need)
2309 {
2310 char o[LINESIZE];
2311 struct message mt;
2312 n_sighdl_t volatile saveint, savepipe;
2313 char * volatile head;
2314 char const *cp, *loc, * volatile item, * volatile resp;
2315 uz expected;
2316 uz volatile headsize;
2317 int number;
2318 FILE *queuefp;
2319 long volatile headlines;
2320 long n;
2321 enum okay volatile ok;
2322 NYD;
2323
2324 saveint = savepipe = SIG_IGN;
2325 head = NULL;
2326 cp = loc = item = resp = NULL;
2327 headsize = 0;
2328 number = (int)P2UZ(m - message + 1);
2329 queuefp = NULL;
2330 headlines = 0;
2331 ok = STOP;
2332
2333 if (getcache(mp, m, need) == OKAY)
2334 return OKAY;
2335 if (mp->mb_type == MB_CACHE) {
2336 n_err(_("Message %lu not available\n"), (ul)number);
2337 return STOP;
2338 }
2339
2340 if(mp->mb_sock == NIL || mp->mb_sock->s_fd < 0){
2341 n_err(_("IMAP connection closed\n"));
2342 return STOP;
2343 }
2344
2345 switch (need) {
2346 case NEED_HEADER:
2347 resp = item = "RFC822.HEADER";
2348 break;
2349 case NEED_BODY:
2350 item = "BODY.PEEK[]";
2351 resp = "BODY[]";
2352 if ((m->m_content_info & CI_HAVE_HEADER) && m->m_size) {
2353 char *hdr = n_alloc(m->m_size);
2354 fflush(mp->mb_otf);
2355 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
2356 SEEK_SET) < 0 ||
2357 fread(hdr, 1, m->m_size, mp->mb_itf) != m->m_size) {
2358 n_free(hdr);
2359 break;
2360 }
2361 head = hdr;
2362 headsize = m->m_size;
2363 headlines = m->m_lines;
2364 item = "BODY.PEEK[TEXT]";
2365 resp = "BODY[TEXT]";
2366 }
2367 break;
2368 case NEED_UNSPEC:
2369 return STOP;
2370 }
2371
2372 imaplock = 1;
2373 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2374 if (sigsetjmp(imapjmp, 1)) {
2375 safe_signal(SIGINT, saveint);
2376 safe_signal(SIGPIPE, savepipe);
2377 imaplock = 0;
2378 return STOP;
2379 }
2380 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2381 safe_signal(SIGINT, &_imap_maincatch);
2382 if (savepipe != SIG_IGN)
2383 safe_signal(SIGPIPE, imapcatch);
2384
2385 if (m->m_uid)
2386 snprintf(o, sizeof o, "%s UID FETCH %" PRIu64 " (%s)\r\n",
2387 tag(1), m->m_uid, item);
2388 else {
2389 if (check_expunged() == STOP)
2390 goto out;
2391 snprintf(o, sizeof o, "%s FETCH %d (%s)\r\n", tag(1), number, item);
2392 }
2393 IMAP_OUT(o, MB_COMD, goto out)
2394 for (;;) {
2395 u64 uid;
2396
2397 ok = imap_answer(mp, 1);
2398 if (ok == STOP)
2399 break;
2400 if (response_status != RESPONSE_OTHER ||
2401 response_other != MESSAGE_DATA_FETCH)
2402 continue;
2403 if ((loc = su_cs_find_case(responded_other_text, resp)) == NULL)
2404 continue;
2405 uid = 0;
2406 if (m->m_uid) {
2407 if ((cp = su_cs_find_case(responded_other_text, "UID "))) {
2408 su_idec_u64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2409 n = 0;
2410 } else
2411 n = -1;
2412 } else
2413 n = responded_other_number;
2414 if ((cp = su_cs_rfind_c(responded_other_text, '{')) == NULL) {
2415 if (m->m_uid ? m->m_uid != uid : n != number)
2416 continue;
2417 if ((cp = su_cs_find_c(loc, '"')) != NULL) {
2418 cp = imap_unquotestr(cp);
2419 imap_putstr(mp, m, cp, head, headsize, headlines);
2420 } else {
2421 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
2422 m->m_xlines = m->m_lines;
2423 m->m_xsize = m->m_size;
2424 }
2425 goto out;
2426 }
2427 expected = atol(&cp[1]);
2428 if (m->m_uid ? n == 0 && m->m_uid != uid : n != number) {
2429 imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
2430 continue;
2431 }
2432 mt = *m;
2433 imap_fetchdata(mp, &mt, expected, need, head, headsize, headlines);
2434 if (n >= 0) {
2435 commitmsg(mp, m, &mt, mt.m_content_info);
2436 break;
2437 }
2438 if (n == -1 &&
2439 mx_socket_getline(&imapbuf, &imapbufsize, NULL, mp->mb_sock
2440 ) > 0) {
2441 if (n_poption & n_PO_VV)
2442 fputs(imapbuf, stderr);
2443 if ((cp = su_cs_find_case(imapbuf, "UID ")) != NULL) {
2444 su_idec_u64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2445 if (uid == m->m_uid) {
2446 commitmsg(mp, m, &mt, mt.m_content_info);
2447 break;
2448 }
2449 }
2450 }
2451 }
2452 out:
2453 while (mp->mb_active & MB_COMD)
2454 ok = imap_answer(mp, 1);
2455
2456 if (saveint != SIG_IGN)
2457 safe_signal(SIGINT, saveint);
2458 if (savepipe != SIG_IGN)
2459 safe_signal(SIGPIPE, savepipe);
2460 imaplock--;
2461
2462 if (ok == OKAY)
2463 putcache(mp, m);
2464 if (head != NULL)
2465 n_free(head);
2466 if (interrupts)
2467 n_go_onintr_for_imap();
2468 return ok;
2469 }
2470
2471 FL enum okay
imap_header(struct message * m)2472 imap_header(struct message *m)
2473 {
2474 enum okay rv;
2475 NYD_IN;
2476
2477 rv = imap_get(&mb, m, NEED_HEADER);
2478 NYD_OU;
2479 return rv;
2480 }
2481
2482
2483 FL enum okay
imap_body(struct message * m)2484 imap_body(struct message *m)
2485 {
2486 enum okay rv;
2487 NYD_IN;
2488
2489 rv = imap_get(&mb, m, NEED_BODY);
2490 NYD_OU;
2491 return rv;
2492 }
2493
2494 static void
commitmsg(struct mailbox * mp,struct message * tomp,struct message * frommp,enum content_info content_info)2495 commitmsg(struct mailbox *mp, struct message *tomp, struct message *frommp,
2496 enum content_info content_info)
2497 {
2498 NYD_IN;
2499 tomp->m_size = frommp->m_size;
2500 tomp->m_lines = frommp->m_lines;
2501 tomp->m_block = frommp->m_block;
2502 tomp->m_offset = frommp->m_offset;
2503 tomp->m_content_info = content_info & CI_HAVE_MASK;
2504 if (content_info & CI_HAVE_BODY) {
2505 tomp->m_xlines = frommp->m_lines;
2506 tomp->m_xsize = frommp->m_size;
2507 }
2508 putcache(mp, tomp);
2509 NYD_OU;
2510 }
2511
2512 static enum okay
imap_fetchheaders(struct mailbox * mp,struct message * m,int bot,int topp)2513 imap_fetchheaders(struct mailbox *mp, struct message *m, int bot, int topp)
2514 {
2515 /* bot > topp */
2516 char o[LINESIZE];
2517 char const *cp;
2518 struct message mt;
2519 uz expected;
2520 int n = 0;
2521 FILE *queuefp = NULL;
2522 enum okay ok;
2523 NYD;
2524
2525 if (m[bot].m_uid)
2526 snprintf(o, sizeof o,
2527 "%s UID FETCH %" PRIu64 ":%" PRIu64 " (RFC822.HEADER)\r\n",
2528 tag(1), m[bot-1].m_uid, m[topp-1].m_uid);
2529 else {
2530 if (check_expunged() == STOP)
2531 return STOP;
2532 snprintf(o, sizeof o, "%s FETCH %d:%d (RFC822.HEADER)\r\n",
2533 tag(1), bot, topp);
2534 }
2535 IMAP_OUT(o, MB_COMD, return STOP)
2536
2537 srelax_hold();
2538 for (;;) {
2539 ok = imap_answer(mp, 1);
2540 if (response_status != RESPONSE_OTHER)
2541 break;
2542 if (response_other != MESSAGE_DATA_FETCH)
2543 continue;
2544 if (ok == STOP || (cp=su_cs_rfind_c(responded_other_text, '{')) == 0) {
2545 srelax_rele();
2546 return STOP;
2547 }
2548 if (su_cs_find_case(responded_other_text, "RFC822.HEADER") == NULL)
2549 continue;
2550 expected = atol(&cp[1]);
2551 if (m[bot-1].m_uid) {
2552 if ((cp = su_cs_find_case(responded_other_text, "UID ")) != NULL) {
2553 u64 uid;
2554
2555 su_idec_u64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2556 for (n = bot; n <= topp; n++)
2557 if (uid == m[n-1].m_uid)
2558 break;
2559 if (n > topp) {
2560 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2561 continue;
2562 }
2563 } else
2564 n = -1;
2565 } else {
2566 n = responded_other_number;
2567 if (n <= 0 || n > msgCount) {
2568 imap_fetchdata(mp, NULL, expected, NEED_HEADER, NULL, 0, 0);
2569 continue;
2570 }
2571 }
2572 imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
2573 if (n >= 0 && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2574 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2575 if(n == -1 &&
2576 mx_socket_getline(&imapbuf, &imapbufsize, NULL, mp->mb_sock) > 0){
2577 if (n_poption & n_PO_VV)
2578 fputs(imapbuf, stderr);
2579 if ((cp = su_cs_find_case(imapbuf, "UID ")) != NULL) {
2580 u64 uid;
2581
2582 su_idec_u64_cp(&uid, &cp[4], 10, NULL);/* TODO errors? */
2583 for (n = bot; n <= topp; n++)
2584 if (uid == m[n-1].m_uid)
2585 break;
2586 if (n <= topp && !(m[n-1].m_content_info & CI_HAVE_HEADER))
2587 commitmsg(mp, &m[n-1], &mt, CI_HAVE_HEADER);
2588 }
2589 }
2590 srelax();
2591 }
2592 srelax_rele();
2593
2594 while (mp->mb_active & MB_COMD)
2595 ok = imap_answer(mp, 1);
2596 return ok;
2597 }
2598
2599 FL void
imap_getheaders(int volatile bot,int volatile topp)2600 imap_getheaders(int volatile bot, int volatile topp) /* TODO iterator!! */
2601 {
2602 n_sighdl_t saveint, savepipe;
2603 /*enum okay ok = STOP;*/
2604 int i, chunk = 256;
2605 NYD;
2606
2607 if (mb.mb_type == MB_CACHE)
2608 return;
2609 if (bot < 1)
2610 bot = 1;
2611 if (topp > msgCount)
2612 topp = msgCount;
2613 for (i = bot; i < topp; i++) {
2614 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2615 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2616 bot = i+1;
2617 else
2618 break;
2619 }
2620 for (i = topp; i > bot; i--) {
2621 if ((message[i-1].m_content_info & CI_HAVE_HEADER) ||
2622 getcache(&mb, &message[i-1], NEED_HEADER) == OKAY)
2623 topp = i-1;
2624 else
2625 break;
2626 }
2627 if (bot >= topp)
2628 return;
2629
2630 imaplock = 1;
2631 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2632 safe_signal(SIGINT, &_imap_maincatch);
2633 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2634 if (sigsetjmp(imapjmp, 1) == 0) {
2635 if (savepipe != SIG_IGN)
2636 safe_signal(SIGPIPE, imapcatch);
2637
2638 for (i = bot; i <= topp; i += chunk) {
2639 int j = i + chunk - 1;
2640 j = MIN(j, topp);
2641 if (visible(message + j))
2642 /*ok = */imap_fetchheaders(&mb, message, i, j);
2643 if (interrupts)
2644 n_go_onintr_for_imap(); /* XXX imaplock? */
2645 }
2646 }
2647 safe_signal(SIGINT, saveint);
2648 safe_signal(SIGPIPE, savepipe);
2649 imaplock = 0;
2650 }
2651
2652 static enum okay
__imap_exit(struct mailbox * mp)2653 __imap_exit(struct mailbox *mp)
2654 {
2655 char o[LINESIZE];
2656 FILE *queuefp = NULL;
2657 NYD;
2658
2659 mp->mb_active |= MB_BYE;
2660 snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
2661 IMAP_OUT(o, MB_COMD, return STOP)
2662 IMAP_ANSWER()
2663 return OKAY;
2664 }
2665
2666 static enum okay
imap_exit(struct mailbox * mp)2667 imap_exit(struct mailbox *mp)
2668 {
2669 enum okay rv;
2670 NYD_IN;
2671
2672 rv = __imap_exit(mp);
2673 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2674 n_free(mp->mb_imap_pass);
2675 n_free(mp->mb_imap_account);
2676 n_free(mp->mb_imap_mailbox);
2677 if (mp->mb_cache_directory != NULL)
2678 n_free(mp->mb_cache_directory);
2679 #ifndef mx_HAVE_DEBUG /* TODO ASSERT LEGACY */
2680 mp->mb_imap_account =
2681 mp->mb_imap_mailbox =
2682 mp->mb_cache_directory = "";
2683 #else
2684 mp->mb_imap_account = NULL; /* for ASSERT legacy time.. */
2685 mp->mb_imap_mailbox = NULL;
2686 mp->mb_cache_directory = NULL;
2687 #endif
2688 #endif
2689 if(mp->mb_sock != NIL){
2690 if(mp->mb_sock->s_fd >= 0)
2691 mx_socket_close(mp->mb_sock);
2692 su_FREE(mp->mb_sock);
2693 mp->mb_sock = NIL;
2694 }
2695 NYD_OU;
2696 return rv;
2697 }
2698
2699 static enum okay
imap_delete(struct mailbox * mp,int n,struct message * m,int needstat)2700 imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
2701 {
2702 NYD_IN;
2703 imap_store(mp, m, n, '+', "\\Deleted", needstat);
2704 if (mp->mb_type == MB_IMAP)
2705 delcache(mp, m);
2706 NYD_OU;
2707 return OKAY;
2708 }
2709
2710 static enum okay
imap_close(struct mailbox * mp)2711 imap_close(struct mailbox *mp)
2712 {
2713 char o[LINESIZE];
2714 FILE *queuefp = NULL;
2715 NYD;
2716
2717 snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
2718 IMAP_OUT(o, MB_COMD, return STOP)
2719 IMAP_ANSWER()
2720 return OKAY;
2721 }
2722
2723 static enum okay
imap_update(struct mailbox * mp)2724 imap_update(struct mailbox *mp)
2725 {
2726 struct message *m;
2727 int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
2728 NYD_IN;
2729
2730 if (!(n_pstate & n_PS_EDIT) && mp->mb_perm != 0) {
2731 holdbits();
2732 c = 0;
2733 for (m = message; PCMP(m, <, message + msgCount); ++m)
2734 if (m->m_flag & MBOX)
2735 ++c;
2736 if (c > 0)
2737 if (makembox() == STOP)
2738 goto jbypass;
2739 }
2740
2741 gotcha = held = 0;
2742 for (m = message; PCMP(m, <, message + msgCount); ++m) {
2743 if (mp->mb_perm == 0)
2744 dodel = 0;
2745 else if (n_pstate & n_PS_EDIT)
2746 dodel = ((m->m_flag & MDELETED) != 0);
2747 else
2748 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
2749
2750 /* Fetch the result after around each 800 STORE commands
2751 * sent (approx. 32k data sent). Otherwise, servers will
2752 * try to flush the return queue at some point, leading
2753 * to a deadlock if we are still writing commands but not
2754 * reading their results */
2755 needstat = stored > 0 && stored % 800 == 0;
2756 /* Even if this message has been deleted, continue
2757 * to set further flags. This is necessary to support
2758 * Gmail semantics, where "delete" actually means
2759 * "archive", and the flags are applied to the copy
2760 * in "All Mail" */
2761 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
2762 imap_store(mp, m, m-message+1, '+', "\\Seen", needstat);
2763 stored++;
2764 }
2765 if (m->m_flag & MFLAG) {
2766 imap_store(mp, m, m-message+1, '+', "\\Flagged", needstat);
2767 stored++;
2768 }
2769 if (m->m_flag & MUNFLAG) {
2770 imap_store(mp, m, m-message+1, '-', "\\Flagged", needstat);
2771 stored++;
2772 }
2773 if (m->m_flag & MANSWER) {
2774 imap_store(mp, m, m-message+1, '+', "\\Answered", needstat);
2775 stored++;
2776 }
2777 if (m->m_flag & MUNANSWER) {
2778 imap_store(mp, m, m-message+1, '-', "\\Answered", needstat);
2779 stored++;
2780 }
2781 if (m->m_flag & MDRAFT) {
2782 imap_store(mp, m, m-message+1, '+', "\\Draft", needstat);
2783 stored++;
2784 }
2785 if (m->m_flag & MUNDRAFT) {
2786 imap_store(mp, m, m-message+1, '-', "\\Draft", needstat);
2787 stored++;
2788 }
2789
2790 if (dodel) {
2791 imap_delete(mp, m-message+1, m, needstat);
2792 stored++;
2793 gotcha++;
2794 } else if (mp->mb_type != MB_CACHE ||
2795 (!(n_pstate & n_PS_EDIT) &&
2796 !(m->m_flag & (MBOXED | MSAVED | MDELETED))) ||
2797 (m->m_flag & (MBOXED | MPRESERVE | MTOUCH)) ==
2798 (MPRESERVE | MTOUCH) ||
2799 ((n_pstate & n_PS_EDIT) && !(m->m_flag & MDELETED)))
2800 held++;
2801 if (m->m_flag & MNEW) {
2802 m->m_flag &= ~MNEW;
2803 m->m_flag |= MSTATUS;
2804 }
2805 }
2806 jbypass:
2807 if (gotcha)
2808 imap_close(mp);
2809
2810 for (m = &message[0]; PCMP(m, <, message + msgCount); ++m)
2811 if (!(m->m_flag & MUNLINKED) &&
2812 m->m_flag & (MBOXED | MDELETED | MSAVED | MSTATUS | MFLAG |
2813 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT)) {
2814 putcache(mp, m);
2815 modflags++;
2816 }
2817
2818 /* XXX should be readonly (but our IMAP code is weird...) */
2819 if (!(n_poption & (n_PO_EXISTONLY | n_PO_HEADERSONLY | n_PO_HEADERLIST)) &&
2820 mb.mb_perm != 0) {
2821 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
2822 printf(_("\"%s\" "), displayname);
2823 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
2824 ? _("complete\n") : _("updated.\n"));
2825 } else if (held && !(n_pstate & n_PS_EDIT)) {
2826 if (held == 1)
2827 printf(_("Held 1 message in %s\n"), displayname);
2828 else
2829 printf(_("Held %d messages in %s\n"), held, displayname);
2830 }
2831 fflush(stdout);
2832 }
2833 NYD_OU;
2834 return OKAY;
2835 }
2836
2837 FL boole
imap_quit(boole hold_sigs_on)2838 imap_quit(boole hold_sigs_on)
2839 {
2840 n_sighdl_t volatile saveint, savepipe;
2841 boole rv;
2842 NYD_IN;
2843
2844 if(hold_sigs_on)
2845 rele_sigs();
2846
2847 if (mb.mb_type == MB_CACHE) {
2848 rv = (imap_update(&mb) == OKAY);
2849 goto jleave;
2850 }
2851
2852 rv = FAL0;
2853
2854 if(mb.mb_sock == NIL || mb.mb_sock->s_fd < 0){
2855 n_err(_("IMAP connection closed\n"));
2856 goto jleave;
2857 }
2858
2859 imaplock = 1;
2860 saveint = safe_signal(SIGINT, SIG_IGN);
2861 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2862 if (sigsetjmp(imapjmp, 1)) {
2863 safe_signal(SIGINT, saveint);
2864 safe_signal(SIGPIPE, saveint);
2865 imaplock = 0;
2866 goto jleave;
2867 }
2868 if (saveint != SIG_IGN)
2869 safe_signal(SIGINT, imapcatch);
2870 if (savepipe != SIG_IGN)
2871 safe_signal(SIGPIPE, imapcatch);
2872
2873 rv = (imap_update(&mb) == OKAY);
2874 if(!same_imap_account && imap_exit(&mb) != OKAY)
2875 rv = FAL0;
2876
2877 safe_signal(SIGINT, saveint);
2878 safe_signal(SIGPIPE, savepipe);
2879 imaplock = 0;
2880 jleave:
2881 if(hold_sigs_on)
2882 hold_sigs();
2883 NYD_OU;
2884 return rv;
2885 }
2886
2887 static enum okay
imap_store(struct mailbox * mp,struct message * m,int n,int c,const char * xsp,int needstat)2888 imap_store(struct mailbox *mp, struct message *m, int n, int c,
2889 const char *xsp, int needstat)
2890 {
2891 char o[LINESIZE];
2892 FILE *queuefp = NULL;
2893 NYD;
2894
2895 if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
2896 return STOP;
2897 if (m->m_uid)
2898 snprintf(o, sizeof o, "%s UID STORE %" PRIu64 " %cFLAGS (%s)\r\n",
2899 tag(1), m->m_uid, S(char,c), xsp);
2900 else {
2901 if (check_expunged() == STOP)
2902 return STOP;
2903 snprintf(o, sizeof o, "%s STORE %d %cFLAGS (%s)\r\n",
2904 tag(1), n, S(char,c), xsp);
2905 }
2906 IMAP_OUT(o, MB_COMD, return STOP)
2907 if (needstat)
2908 IMAP_ANSWER()
2909 else
2910 mb.mb_active &= ~MB_COMD;
2911
2912 if(queuefp != NIL)
2913 mx_fs_close(queuefp);
2914 return OKAY;
2915 }
2916
2917 FL enum okay
imap_undelete(struct message * m,int n)2918 imap_undelete(struct message *m, int n)
2919 {
2920 enum okay rv;
2921 NYD_IN;
2922
2923 rv = imap_unstore(m, n, "\\Deleted");
2924 NYD_OU;
2925 return rv;
2926 }
2927
2928 FL enum okay
imap_unread(struct message * m,int n)2929 imap_unread(struct message *m, int n)
2930 {
2931 enum okay rv;
2932 NYD_IN;
2933
2934 rv = imap_unstore(m, n, "\\Seen");
2935 NYD_OU;
2936 return rv;
2937 }
2938
2939 static enum okay
imap_unstore(struct message * m,int n,const char * flag)2940 imap_unstore(struct message *m, int n, const char *flag)
2941 {
2942 n_sighdl_t saveint, savepipe;
2943 enum okay volatile rv = STOP;
2944 NYD_IN;
2945
2946 imaplock = 1;
2947 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
2948 safe_signal(SIGINT, &_imap_maincatch);
2949 savepipe = safe_signal(SIGPIPE, SIG_IGN);
2950 if (sigsetjmp(imapjmp, 1) == 0) {
2951 if (savepipe != SIG_IGN)
2952 safe_signal(SIGPIPE, imapcatch);
2953
2954 rv = imap_store(&mb, m, n, '-', flag, 1);
2955 }
2956 safe_signal(SIGINT, saveint);
2957 safe_signal(SIGPIPE, savepipe);
2958 imaplock = 0;
2959
2960 NYD_OU;
2961 if (interrupts)
2962 n_go_onintr_for_imap();
2963 return rv;
2964 }
2965
2966 static const char *
tag(int new)2967 tag(int new)
2968 {
2969 static char ts[24];
2970 static long n;
2971 NYD2_IN;
2972
2973 if(new)
2974 ++n;
2975 snprintf(ts, sizeof ts, "T%ld", n);
2976
2977 NYD2_OU;
2978 return ts;
2979 }
2980
2981 FL int
c_imapcodec(void * vp)2982 c_imapcodec(void *vp){
2983 boole err;
2984 uz alen;
2985 char const **argv, *varname, *varres, *act, *cp;
2986 NYD_IN;
2987
2988 argv = vp;
2989 varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NULL;
2990
2991 act = *argv;
2992 for(cp = act; *cp != '\0' && !su_cs_is_space(*cp); ++cp)
2993 ;
2994 if(act == cp)
2995 goto jesynopsis;
2996 alen = P2UZ(cp - act);
2997 if(*cp != '\0')
2998 ++cp;
2999
3000 n_pstate_err_no = su_ERR_NONE;
3001 varres = a_imap_path_normalize(NULL, cp, TRU1);
3002
3003 if(su_cs_starts_with_case_n("encode", act, alen))
3004 varres = imap_path_encode(varres, &err);
3005 else if(su_cs_starts_with_case_n("decode", act, alen))
3006 varres = imap_path_decode(varres, &err);
3007 else
3008 goto jesynopsis;
3009
3010 if(err){
3011 n_pstate_err_no = su_ERR_CANCELED;
3012 varres = cp;
3013 vp = NULL;
3014 }
3015
3016 if(varname != NULL){
3017 if(!n_var_vset(varname, (up)varres)){
3018 n_pstate_err_no = su_ERR_NOTSUP;
3019 vp = NULL;
3020 }
3021 }else{
3022 struct str in, out;
3023
3024 in.l = su_cs_len(in.s = n_UNCONST(varres));
3025 makeprint(&in, &out);
3026 if(fprintf(n_stdout, "%s\n", out.s) < 0){
3027 n_pstate_err_no = su_err_no();
3028 vp = NULL;
3029 }
3030 n_free(out.s);
3031 }
3032
3033 jleave:
3034 NYD_OU;
3035 return (vp != NULL ? 0 : 1);
3036 jesynopsis:
3037 mx_cmd_print_synopsis(mx_cmd_firstfit("imapcodec"), NIL);
3038 n_pstate_err_no = su_ERR_INVAL;
3039 vp = NULL;
3040 goto jleave;
3041 }
3042
3043 FL int
c_imap_imap(void * vp)3044 c_imap_imap(void *vp)
3045 {
3046 char o[LINESIZE];
3047 n_sighdl_t saveint, savepipe;
3048 struct mailbox *mp = &mb;
3049 FILE *queuefp = NULL;
3050 enum okay volatile ok = STOP;
3051 NYD;
3052
3053 if (mp->mb_type != MB_IMAP) {
3054 printf("Not operating on an IMAP mailbox.\n");
3055 return 1;
3056 }
3057 imaplock = 1;
3058 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3059 safe_signal(SIGINT, &_imap_maincatch);
3060 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3061 if (sigsetjmp(imapjmp, 1) == 0) {
3062 if (savepipe != SIG_IGN)
3063 safe_signal(SIGPIPE, imapcatch);
3064
3065 snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
3066 IMAP_OUT(o, MB_COMD, goto out)
3067 while (mp->mb_active & MB_COMD) {
3068 ok = imap_answer(mp, 0);
3069 fputs(responded_text, stdout);
3070 }
3071 }
3072 out:
3073 safe_signal(SIGINT, saveint);
3074 safe_signal(SIGPIPE, savepipe);
3075 imaplock = 0;
3076
3077 if (interrupts)
3078 n_go_onintr_for_imap();
3079 return ok != OKAY;
3080 }
3081
3082 FL int
imap_newmail(int nmail)3083 imap_newmail(int nmail)
3084 {
3085 NYD_IN;
3086
3087 if (nmail && had_exists < 0 && had_expunge < 0) {
3088 imaplock = 1;
3089 imap_noop();
3090 imaplock = 0;
3091 }
3092
3093 if (had_exists == msgCount && had_expunge < 0)
3094 /* Some servers always respond with EXISTS to NOOP. If
3095 * the mailbox has been changed but the number of messages
3096 * has not, an EXPUNGE must also had been sent; otherwise,
3097 * nothing has changed */
3098 had_exists = -1;
3099 NYD_OU;
3100 return (had_expunge >= 0 ? 2 : (had_exists >= 0 ? 1 : 0));
3101 }
3102
3103 static char *
imap_putflags(int f)3104 imap_putflags(int f)
3105 {
3106 const char *cp;
3107 char *buf, *bp;
3108 NYD2_IN;
3109
3110 bp = buf = n_autorec_alloc(100);
3111 if (f & (MREAD | MFLAGGED | MANSWERED | MDRAFTED)) {
3112 *bp++ = '(';
3113 if (f & MREAD) {
3114 if (bp[-1] != '(')
3115 *bp++ = ' ';
3116 for (cp = "\\Seen"; *cp; cp++)
3117 *bp++ = *cp;
3118 }
3119 if (f & MFLAGGED) {
3120 if (bp[-1] != '(')
3121 *bp++ = ' ';
3122 for (cp = "\\Flagged"; *cp; cp++)
3123 *bp++ = *cp;
3124 }
3125 if (f & MANSWERED) {
3126 if (bp[-1] != '(')
3127 *bp++ = ' ';
3128 for (cp = "\\Answered"; *cp; cp++)
3129 *bp++ = *cp;
3130 }
3131 if (f & MDRAFT) {
3132 if (bp[-1] != '(')
3133 *bp++ = ' ';
3134 for (cp = "\\Draft"; *cp; cp++)
3135 *bp++ = *cp;
3136 }
3137 *bp++ = ')';
3138 *bp++ = ' ';
3139 }
3140 *bp = '\0';
3141 NYD2_OU;
3142 return buf;
3143 }
3144
3145 static void
imap_getflags(const char * cp,char const ** xp,enum mflag * f)3146 imap_getflags(const char *cp, char const **xp, enum mflag *f)
3147 {
3148 NYD2_IN;
3149 while (*cp != ')') {
3150 if (*cp == '\\') {
3151 if (su_cs_cmp_case_n(cp, "\\Seen", 5) == 0)
3152 *f |= MREAD;
3153 else if (su_cs_cmp_case_n(cp, "\\Recent", 7) == 0)
3154 *f |= MNEW;
3155 else if (su_cs_cmp_case_n(cp, "\\Deleted", 8) == 0)
3156 *f |= MDELETED;
3157 else if (su_cs_cmp_case_n(cp, "\\Flagged", 8) == 0)
3158 *f |= MFLAGGED;
3159 else if (su_cs_cmp_case_n(cp, "\\Answered", 9) == 0)
3160 *f |= MANSWERED;
3161 else if (su_cs_cmp_case_n(cp, "\\Draft", 6) == 0)
3162 *f |= MDRAFTED;
3163 }
3164 cp++;
3165 }
3166
3167 if (xp != NULL)
3168 *xp = cp;
3169 NYD2_OU;
3170 }
3171
3172 static enum okay
imap_append1(struct mailbox * mp,const char * name,FILE * fp,off_t off1,long xsize,enum mflag flag,time_t t)3173 imap_append1(struct mailbox *mp, const char *name, FILE *fp, off_t off1,
3174 long xsize, enum mflag flag, time_t t)
3175 {
3176 char o[LINESIZE], *buf;
3177 uz bufsize, buflen, cnt;
3178 long size, lines, ysize;
3179 char const *qname;
3180 boole twice;
3181 FILE *queuefp;
3182 enum okay rv;
3183 NYD_IN;
3184
3185 rv = STOP;
3186 queuefp = NULL;
3187 twice = FAL0;
3188 mx_fs_linepool_aquire(&buf, &bufsize);
3189
3190 if((qname = imap_path_quote(mp, name)) == NULL)
3191 goto jleave;
3192
3193 if (mp->mb_type == MB_CACHE) {
3194 queuefp = cache_queue(mp);
3195 if (queuefp == NULL)
3196 goto jleave;
3197 rv = OKAY;
3198 }
3199
3200 jagain:
3201 size = xsize;
3202 cnt = fsize(fp);
3203 if (fseek(fp, off1, SEEK_SET) < 0) {
3204 rv = STOP;
3205 goto jleave;
3206 }
3207
3208 snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
3209 tag(1), qname, imap_putflags(flag), imap_make_date_time(t), size);
3210 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
3211 while (mp->mb_active & MB_COMD) {
3212 rv = imap_answer(mp, twice);
3213 if (response_type == RESPONSE_CONT)
3214 break;
3215 }
3216
3217 if (mp->mb_type != MB_CACHE && rv == STOP) {
3218 if (!twice)
3219 goto jtrycreate;
3220 else
3221 goto jleave;
3222 }
3223
3224 lines = ysize = 0;
3225 while (size > 0) {
3226 if(fgetline(&buf, &bufsize, &cnt, &buflen, fp, TRU1) == NIL){
3227 if(ferror(fp)){
3228 rv = STOP;
3229 goto jleave;
3230 }
3231 break;
3232 }
3233 lines++;
3234 ysize += buflen;
3235 buf[buflen - 1] = '\r';
3236 buf[buflen] = '\n';
3237 if (mp->mb_type != MB_CACHE)
3238 mx_socket_write1(mp->mb_sock, buf, buflen+1, 1);
3239 else if (queuefp)
3240 fwrite(buf, 1, buflen+1, queuefp);
3241 size -= buflen + 1;
3242 }
3243 if (mp->mb_type != MB_CACHE)
3244 mx_socket_write(mp->mb_sock, "\r\n");
3245 else if (queuefp)
3246 fputs("\r\n", queuefp);
3247 while (mp->mb_active & MB_COMD) {
3248 rv = imap_answer(mp, 0);
3249 if (response_status == RESPONSE_NO /*&&
3250 su_cs_cmp_case_n(responded_text,
3251 "[TRYCREATE] ", 12) == 0*/) {
3252 jtrycreate:
3253 if (twice) {
3254 rv = STOP;
3255 goto jleave;
3256 }
3257 twice = TRU1;
3258 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3259 IMAP_XOUT(o, MB_COMD, goto jleave, rv=STOP;goto jleave)
3260 while (mp->mb_active & MB_COMD)
3261 rv = imap_answer(mp, 1);
3262 if (rv == STOP)
3263 goto jleave;
3264 imap_created_mailbox++;
3265 goto jagain;
3266 } else if (rv != OKAY)
3267 n_err(_("IMAP error: %s"), responded_text);
3268 else if (response_status == RESPONSE_OK && (mp->mb_flags & MB_UIDPLUS))
3269 imap_appenduid(mp, fp, t, off1, xsize, ysize, lines, flag, name);
3270 }
3271
3272 jleave:
3273 if(queuefp != NIL)
3274 mx_fs_close(queuefp);
3275
3276 mx_fs_linepool_release(buf, bufsize);
3277 NYD_OU;
3278 return rv;
3279 }
3280
3281 static enum okay
imap_append0(struct mailbox * mp,const char * name,FILE * fp,long offset)3282 imap_append0(struct mailbox *mp, const char *name, FILE *fp, long offset)
3283 {
3284 char *buf, *bp, *lp;
3285 uz bufsize, buflen, cnt;
3286 off_t off1 = -1, offs;
3287 int flag;
3288 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
3289 time_t tim;
3290 long size;
3291 enum okay rv;
3292 NYD_IN;
3293
3294 mx_fs_linepool_aquire(&buf, &bufsize);
3295 cnt = fsize(fp);
3296 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
3297 time(&tim);
3298 size = 0;
3299
3300 for (flag = MNEW, state = _NLSEP;;) {
3301 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, TRU1);
3302
3303 if (bp == NULL ||
3304 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
3305 is_head(buf, buflen, FAL0))) {
3306 if (off1 != (off_t)-1) {
3307 rv = imap_append1(mp, name, fp, off1, size, flag, tim);
3308 if (rv == STOP)
3309 goto jleave;
3310 fseek(fp, offs+buflen, SEEK_SET);
3311 }
3312 off1 = offs + buflen;
3313 size = 0;
3314 flag = MNEW;
3315 state = _INHEAD;
3316 if(bp == NIL){
3317 if(ferror(fp)){
3318 rv = STOP;
3319 goto jleave;
3320 }
3321 break;
3322 }
3323 tim = unixtime(buf);
3324 } else
3325 size += buflen+1;
3326 offs += buflen;
3327
3328 state &= ~_NLSEP;
3329 if (buf[0] == '\n') {
3330 state &= ~_INHEAD;
3331 state |= _NLSEP;
3332 } else if (state & _INHEAD) {
3333 if (su_cs_cmp_case_n(buf, "status", 6) == 0) {
3334 lp = &buf[6];
3335 while (su_cs_is_white(*lp))
3336 lp++;
3337 if (*lp == ':')
3338 while (*++lp != '\0')
3339 switch (*lp) {
3340 case 'R':
3341 flag |= MREAD;
3342 break;
3343 case 'O':
3344 flag &= ~MNEW;
3345 break;
3346 }
3347 } else if (su_cs_cmp_case_n(buf, "x-status", 8) == 0) {
3348 lp = &buf[8];
3349 while (su_cs_is_white(*lp))
3350 lp++;
3351 if (*lp == ':')
3352 while (*++lp != '\0')
3353 switch (*lp) {
3354 case 'F':
3355 flag |= MFLAGGED;
3356 break;
3357 case 'A':
3358 flag |= MANSWERED;
3359 break;
3360 case 'T':
3361 flag |= MDRAFTED;
3362 break;
3363 }
3364 }
3365 }
3366 }
3367
3368 rv = OKAY;
3369 jleave:
3370 mx_fs_linepool_release(buf, bufsize);
3371 NYD_OU;
3372 return rv;
3373 }
3374
3375 FL enum okay
imap_append(const char * xserver,FILE * fp,long offset)3376 imap_append(const char *xserver, FILE *fp, long offset)
3377 {
3378 n_sighdl_t volatile saveint, savepipe;
3379 struct mx_url url;
3380 struct mx_cred_ctx ccred;
3381 enum okay volatile rv = STOP;
3382 NYD_IN;
3383
3384 if(!mx_url_parse(&url, CPROTO_IMAP, xserver))
3385 goto j_leave;
3386 if(ok_vlook(v15_compat) == NIL &&
3387 (!(url.url_flags & mx_URL_HAD_USER) || url.url_pass.s != NIL))
3388 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3389 ASSERT(url.url_path.s != NULL);
3390
3391 imaplock = 1;
3392 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3393 safe_signal(SIGINT, &_imap_maincatch);
3394 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3395 if (sigsetjmp(imapjmp, 1))
3396 goto jleave;
3397 if (savepipe != SIG_IGN)
3398 safe_signal(SIGPIPE, imapcatch);
3399
3400 if((mb.mb_type == MB_CACHE ||
3401 (mb.mb_sock != NIL && mb.mb_sock->s_fd > 0)) &&
3402 mb.mb_imap_account &&
3403 !su_cs_cmp(url.url_p_eu_h_p, mb.mb_imap_account)) {
3404 rv = imap_append0(&mb, url.url_path.s, fp, offset);
3405 } else {
3406 struct mailbox mx;
3407
3408 su_mem_set(&mx, 0, sizeof mx);
3409
3410 if (!_imap_getcred(&mx, &ccred, &url))
3411 goto jleave;
3412
3413 imap_delim_init(&mx, &url);
3414 mx.mb_imap_mailbox = su_cs_dup(
3415 a_imap_path_normalize(&mx, url.url_path.s, FAL0), 0);
3416
3417 if(disconnected(url.url_p_eu_h_p) == 0){
3418 mx.mb_sock = su_TALLOC(struct mx_socket, 1);
3419 if(!mx_socket_open(mx.mb_sock, &url)){
3420 su_FREE(mx.mb_sock);
3421 goto jfail;
3422 }
3423 mx.mb_sock->s_desc = "IMAP";
3424 mx.mb_type = MB_IMAP;
3425 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3426 /* TODO the code now did
3427 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3428 * TODO though imap_mailbox is sfree()d and mbx
3429 * TODO is possibly even a constant
3430 * TODO i changed this to su_cs_dup() sofar, as is used
3431 * TODO somewhere else in this file for this! */
3432 if(imap_preauth(&mx, &url, &ccred) != OKAY ||
3433 a_imap_auth(&mx, &url, &ccred) != OKAY){
3434 mx_socket_close(mx.mb_sock);
3435 su_FREE(mx.mb_sock);
3436 goto jfail;
3437 }
3438 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3439 imap_exit(&mx);
3440 } else {
3441 mx.mb_imap_account = n_UNCONST(url.url_p_eu_h_p);
3442 mx.mb_type = MB_CACHE;
3443 rv = imap_append0(&mx, url.url_path.s, fp, offset);
3444 }
3445 jfail:
3446 ;
3447 }
3448
3449 jleave:
3450 safe_signal(SIGINT, saveint);
3451 safe_signal(SIGPIPE, savepipe);
3452 imaplock = 0;
3453 j_leave:
3454 NYD_OU;
3455 if (interrupts)
3456 n_go_onintr_for_imap();
3457 return rv;
3458 }
3459
3460 static enum okay
imap_list1(struct mailbox * mp,const char * base,struct list_item ** list,struct list_item ** lend,int level)3461 imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
3462 struct list_item **lend, int level)
3463 {
3464 char o[LINESIZE], *cp;
3465 struct list_item *lp;
3466 const char *qname, *bp;
3467 FILE *queuefp;
3468 enum okay ok;
3469 NYD;
3470
3471 ok = STOP;
3472 queuefp = NULL;
3473
3474 if((qname = imap_path_quote(mp, base)) == NULL)
3475 goto jleave;
3476
3477 *list = *lend = NULL;
3478 snprintf(o, sizeof o, "%s LIST %s %%\r\n", tag(1), qname);
3479 IMAP_OUT(o, MB_COMD, goto jleave)
3480 while (mp->mb_active & MB_COMD) {
3481 ok = imap_answer(mp, 1);
3482 if (response_status == RESPONSE_OTHER &&
3483 response_other == MAILBOX_DATA_LIST && imap_parse_list() == OKAY) {
3484 cp = imap_path_decode(imap_unquotestr(list_name), NULL);
3485 lp = n_autorec_calloc(1, sizeof *lp);
3486 lp->l_name = cp;
3487 for (bp = base; *bp != '\0' && *bp == *cp; ++bp)
3488 ++cp;
3489 lp->l_base = *cp ? cp : savestr(base);
3490 lp->l_attr = list_attributes;
3491 lp->l_level = level+1;
3492 lp->l_delim = list_hierarchy_delimiter;
3493 if (*list && *lend) {
3494 (*lend)->l_next = lp;
3495 *lend = lp;
3496 } else
3497 *list = *lend = lp;
3498 }
3499 }
3500 jleave:
3501 return ok;
3502 }
3503
3504 static enum okay
imap_list(struct mailbox * mp,const char * base,int strip,FILE * fp)3505 imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
3506 {
3507 struct list_item *list, *lend, *lp, *lx, *ly;
3508 int n, depth;
3509 const char *bp;
3510 char *cp;
3511 enum okay rv;
3512 NYD_IN;
3513
3514 depth = (cp = ok_vlook(imap_list_depth)) != NULL ? atoi(cp) : 2;
3515 if ((rv = imap_list1(mp, base, &list, &lend, 0)) == STOP)
3516 goto jleave;
3517 rv = OKAY;
3518 if (list == NULL || lend == NULL)
3519 goto jleave;
3520
3521 for (lp = list; lp; lp = lp->l_next)
3522 if (lp->l_delim != '/' && lp->l_delim != EOF && lp->l_level < depth &&
3523 !(lp->l_attr & LIST_NOINFERIORS)) {
3524 cp = n_autorec_alloc((n = su_cs_len(lp->l_name)) + 2);
3525 su_mem_copy(cp, lp->l_name, n);
3526 cp[n] = lp->l_delim;
3527 cp[n+1] = '\0';
3528 if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY && lx && ly) {
3529 lp->l_has_children = 1;
3530 if (su_cs_cmp(cp, lx->l_name) == 0)
3531 lx = lx->l_next;
3532 if (lx) {
3533 lend->l_next = lx;
3534 lend = ly;
3535 }
3536 }
3537 }
3538
3539 for (lp = list; lp; lp = lp->l_next) {
3540 if (strip) {
3541 cp = lp->l_name;
3542 for (bp = base; *bp && *bp == *cp; bp++)
3543 cp++;
3544 } else
3545 cp = lp->l_name;
3546 if (!(lp->l_attr & LIST_NOSELECT))
3547 fprintf(fp, "%s\n", *cp ? cp : base);
3548 else if (lp->l_has_children == 0)
3549 fprintf(fp, "%s%c\n", *cp ? cp : base,
3550 (lp->l_delim != EOF ? lp->l_delim : '\n'));
3551 }
3552 jleave:
3553 NYD_OU;
3554 return rv;
3555 }
3556
3557 FL int
imap_folders(const char * volatile name,int strip)3558 imap_folders(const char * volatile name, int strip)
3559 {
3560 n_sighdl_t saveint, savepipe;
3561 const char * volatile fold, *cp, *xsp;
3562 int volatile rv = 1;
3563 FILE * volatile fp;
3564 NYD_IN;
3565
3566 fp = n_stdout;
3567
3568 if(mb.mb_sock == NIL){
3569 n_err(_("No active folder, need reconnect\n"));
3570 goto jleave;
3571 }
3572
3573 cp = protbase(name);
3574 xsp = mb.mb_imap_account;
3575 if (xsp == NULL || su_cs_cmp(cp, xsp)) {
3576 n_err(
3577 _("Cannot perform `folders' but when on the very IMAP "
3578 "account; the current one is\n `%s' -- "
3579 "try `folders @'\n"),
3580 (xsp != NULL ? xsp : _("[NONE]")));
3581 goto jleave;
3582 }
3583
3584 fold = imap_fileof(name);
3585 if((fp = mx_fs_tmp_open("imapfold", (mx_FS_O_RDWR | mx_FS_O_UNLINK |
3586 mx_FS_O_REGISTER), NIL)) == NIL)
3587 fp = n_stdout;
3588
3589 imaplock = 1;
3590 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3591 safe_signal(SIGINT, &_imap_maincatch);
3592 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3593 if (sigsetjmp(imapjmp, 1)) /* TODO imaplock? */
3594 goto junroll;
3595 if (savepipe != SIG_IGN)
3596 safe_signal(SIGPIPE, imapcatch);
3597
3598 if (mb.mb_type == MB_CACHE)
3599 cache_list(&mb, fold, strip, fp);
3600 else
3601 imap_list(&mb, fold, strip, fp);
3602
3603 imaplock = 0;
3604 if (interrupts) {
3605 rv = 0;
3606 goto jleave;
3607 }
3608
3609 if(fp != n_stdout)
3610 page_or_print(fp, 0);
3611
3612 rv = 0;
3613 junroll:
3614 safe_signal(SIGINT, saveint);
3615 safe_signal(SIGPIPE, savepipe);
3616 jleave:
3617 if(fp != n_stdout)
3618 mx_fs_close(fp);
3619
3620 NYD_OU;
3621 if (interrupts)
3622 n_go_onintr_for_imap();
3623 return rv;
3624 }
3625
3626 static enum okay
imap_copy1(struct mailbox * mp,struct message * m,int n,const char * name)3627 imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
3628 {
3629 char o[LINESIZE];
3630 const char *qname;
3631 boole twice, stored;
3632 FILE *queuefp;
3633 enum okay ok;
3634 NYD;
3635
3636 ok = STOP;
3637 queuefp = NULL;
3638 twice = stored = FAL0;
3639
3640 /* C99 */{
3641 uz i;
3642
3643 i = su_cs_len(name = imap_fileof(name));
3644 if(i == 0 || (i > 0 && name[i - 1] == '/'))
3645 name = savecat(name, "INBOX");
3646 if((qname = imap_path_quote(mp, name)) == NULL)
3647 goto jleave;
3648 }
3649
3650 if (mp->mb_type == MB_CACHE) {
3651 if ((queuefp = cache_queue(mp)) == NULL)
3652 goto jleave;
3653 ok = OKAY;
3654 }
3655
3656 /* Since it is not possible to set flags on the copy, recently
3657 * set flags must be set on the original to include it in the copy */
3658 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS))
3659 imap_store(mp, m, n, '+', "\\Seen", 0);
3660 if (m->m_flag&MFLAG)
3661 imap_store(mp, m, n, '+', "\\Flagged", 0);
3662 if (m->m_flag&MUNFLAG)
3663 imap_store(mp, m, n, '-', "\\Flagged", 0);
3664 if (m->m_flag&MANSWER)
3665 imap_store(mp, m, n, '+', "\\Answered", 0);
3666 if (m->m_flag&MUNANSWER)
3667 imap_store(mp, m, n, '-', "\\Answered", 0);
3668 if (m->m_flag&MDRAFT)
3669 imap_store(mp, m, n, '+', "\\Draft", 0);
3670 if (m->m_flag&MUNDRAFT)
3671 imap_store(mp, m, n, '-', "\\Draft", 0);
3672 again:
3673 if (m->m_uid)
3674 snprintf(o, sizeof o, "%s UID COPY %" PRIu64 " %s\r\n",
3675 tag(1), m->m_uid, qname);
3676 else {
3677 if (check_expunged() == STOP)
3678 goto out;
3679 snprintf(o, sizeof o, "%s COPY %d %s\r\n", tag(1), n, qname);
3680 }
3681 IMAP_OUT(o, MB_COMD, goto out)
3682 while (mp->mb_active & MB_COMD)
3683 ok = imap_answer(mp, twice);
3684
3685 if (mp->mb_type == MB_IMAP && mp->mb_flags & MB_UIDPLUS &&
3686 response_status == RESPONSE_OK)
3687 imap_copyuid(mp, m, name);
3688
3689 if (response_status == RESPONSE_NO && !twice) {
3690 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
3691 IMAP_OUT(o, MB_COMD, goto out)
3692 while (mp->mb_active & MB_COMD)
3693 ok = imap_answer(mp, 1);
3694 if (ok == OKAY) {
3695 imap_created_mailbox++;
3696 goto again;
3697 }
3698 }
3699
3700 if(queuefp != NIL)
3701 mx_fs_close(queuefp);
3702
3703 /* ... and reset the flag to its initial value so that the 'exit'
3704 * command still leaves the message unread */
3705 out:
3706 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS)) {
3707 imap_store(mp, m, n, '-', "\\Seen", 0);
3708 stored = TRU1;
3709 }
3710 if (m->m_flag & MFLAG) {
3711 imap_store(mp, m, n, '-', "\\Flagged", 0);
3712 stored = TRU1;
3713 }
3714 if (m->m_flag & MUNFLAG) {
3715 imap_store(mp, m, n, '+', "\\Flagged", 0);
3716 stored = TRU1;
3717 }
3718 if (m->m_flag & MANSWER) {
3719 imap_store(mp, m, n, '-', "\\Answered", 0);
3720 stored = TRU1;
3721 }
3722 if (m->m_flag & MUNANSWER) {
3723 imap_store(mp, m, n, '+', "\\Answered", 0);
3724 stored = TRU1;
3725 }
3726 if (m->m_flag & MDRAFT) {
3727 imap_store(mp, m, n, '-', "\\Draft", 0);
3728 stored = TRU1;
3729 }
3730 if (m->m_flag & MUNDRAFT) {
3731 imap_store(mp, m, n, '+', "\\Draft", 0);
3732 stored = TRU1;
3733 }
3734 if (stored) {
3735 mp->mb_active |= MB_COMD;
3736 (void)imap_finish(mp);
3737 }
3738 jleave:
3739 return ok;
3740 }
3741
3742 FL enum okay
imap_copy(struct message * m,int n,const char * name)3743 imap_copy(struct message *m, int n, const char *name)
3744 {
3745 n_sighdl_t saveint, savepipe;
3746 enum okay volatile rv = STOP;
3747 NYD_IN;
3748
3749 imaplock = 1;
3750 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
3751 safe_signal(SIGINT, &_imap_maincatch);
3752 savepipe = safe_signal(SIGPIPE, SIG_IGN);
3753 if (sigsetjmp(imapjmp, 1) == 0) {
3754 if (savepipe != SIG_IGN)
3755 safe_signal(SIGPIPE, imapcatch);
3756
3757 rv = imap_copy1(&mb, m, n, name);
3758 }
3759 safe_signal(SIGINT, saveint);
3760 safe_signal(SIGPIPE, savepipe);
3761 imaplock = 0;
3762
3763 NYD_OU;
3764 if (interrupts)
3765 n_go_onintr_for_imap();
3766 return rv;
3767 }
3768
3769 static enum okay
imap_copyuid_parse(const char * cp,u64 * uidvalidity,u64 * olduid,u64 * newuid)3770 imap_copyuid_parse(const char *cp, u64 *uidvalidity, u64 *olduid,
3771 u64 *newuid)
3772 {
3773 char const *xp, *yp, *zp;
3774 enum okay rv;
3775 NYD_IN;
3776
3777 su_idec_u64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3778 su_idec_u64_cp(olduid, xp, 10, &yp); /* TODO errors */
3779 su_idec_u64_cp(newuid, yp, 10, &zp); /* TODO errors */
3780 rv = (*uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
3781 yp > xp && *yp == ' ' && zp > yp && *zp == ']');
3782 NYD_OU;
3783 return rv;
3784 }
3785
3786 static enum okay
imap_appenduid_parse(const char * cp,u64 * uidvalidity,u64 * uid)3787 imap_appenduid_parse(const char *cp, u64 *uidvalidity, u64 *uid)
3788 {
3789 char const *xp, *yp;
3790 enum okay rv;
3791 NYD_IN;
3792
3793 su_idec_u64_cp(uidvalidity, cp, 10, &xp); /* TODO errors */
3794 su_idec_u64_cp(uid, xp, 10, &yp); /* TODO errors */
3795 rv = (*uidvalidity && *uid && xp > cp && *xp == ' ' && yp > xp &&
3796 *yp == ']');
3797 NYD_OU;
3798 return rv;
3799 }
3800
3801 static enum okay
imap_copyuid(struct mailbox * mp,struct message * m,const char * name)3802 imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
3803 {
3804 struct mailbox xmb;
3805 struct message xm;
3806 const char *cp;
3807 u64 uidvalidity, olduid, newuid;
3808 enum okay rv;
3809 NYD_IN;
3810
3811 rv = STOP;
3812
3813 su_mem_set(&xmb, 0, sizeof xmb);
3814
3815 if ((cp = su_cs_find_case(responded_text, "[COPYUID ")) == NULL ||
3816 imap_copyuid_parse(&cp[9], &uidvalidity, &olduid, &newuid) == STOP)
3817 goto jleave;
3818
3819 rv = OKAY;
3820
3821 xmb = *mp;
3822 xmb.mb_cache_directory = NULL;
3823 xmb.mb_imap_account = su_cs_dup(mp->mb_imap_account, 0);
3824 xmb.mb_imap_pass = su_cs_dup(mp->mb_imap_pass, 0);
3825 su_mem_copy(&xmb.mb_imap_delim[0], &mp->mb_imap_delim[0],
3826 sizeof(xmb.mb_imap_delim));
3827 xmb.mb_imap_mailbox = su_cs_dup(a_imap_path_normalize(&xmb, name, FAL0), 0);
3828 if (mp->mb_cache_directory != NULL)
3829 xmb.mb_cache_directory = su_cs_dup(mp->mb_cache_directory, 0);
3830 xmb.mb_uidvalidity = uidvalidity;
3831 initcache(&xmb);
3832
3833 if (m == NULL) {
3834 su_mem_set(&xm, 0, sizeof xm);
3835 xm.m_uid = olduid;
3836 if ((rv = getcache1(mp, &xm, NEED_UNSPEC, 3)) != OKAY)
3837 goto jleave;
3838 getcache(mp, &xm, NEED_HEADER);
3839 getcache(mp, &xm, NEED_BODY);
3840 } else {
3841 if ((m->m_content_info & CI_HAVE_HEADER) == 0)
3842 getcache(mp, m, NEED_HEADER);
3843 if ((m->m_content_info & CI_HAVE_BODY) == 0)
3844 getcache(mp, m, NEED_BODY);
3845 xm = *m;
3846 }
3847 xm.m_uid = newuid;
3848 xm.m_flag &= ~MFULLYCACHED;
3849 putcache(&xmb, &xm);
3850 jleave:
3851 if (xmb.mb_cache_directory != NULL)
3852 n_free(xmb.mb_cache_directory);
3853 if (xmb.mb_imap_mailbox != NULL)
3854 n_free(xmb.mb_imap_mailbox);
3855 if (xmb.mb_imap_pass != NULL)
3856 n_free(xmb.mb_imap_pass);
3857 if (xmb.mb_imap_account != NULL)
3858 n_free(xmb.mb_imap_account);
3859 NYD_OU;
3860 return rv;
3861 }
3862
3863 static enum okay
imap_appenduid(struct mailbox * mp,FILE * fp,time_t t,long off1,long xsize,long size,long lines,int flag,const char * name)3864 imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1, long xsize,
3865 long size, long lines, int flag, const char *name)
3866 {
3867 struct mailbox xmb;
3868 struct message xm;
3869 const char *cp;
3870 u64 uidvalidity, uid;
3871 enum okay rv;
3872 NYD_IN;
3873
3874 rv = STOP;
3875
3876 if ((cp = su_cs_find_case(responded_text, "[APPENDUID ")) == NULL ||
3877 imap_appenduid_parse(&cp[11], &uidvalidity, &uid) == STOP)
3878 goto jleave;
3879
3880 rv = OKAY;
3881
3882 xmb = *mp;
3883 xmb.mb_cache_directory = NULL;
3884 /* XXX mb_imap_delim reused */
3885 xmb.mb_imap_mailbox = su_cs_dup(a_imap_path_normalize(&xmb, name, FAL0), 0);
3886 xmb.mb_uidvalidity = uidvalidity;
3887 xmb.mb_otf = xmb.mb_itf = fp;
3888 initcache(&xmb);
3889 su_mem_set(&xm, 0, sizeof xm);
3890 xm.m_flag = (flag & MREAD) | MNEW;
3891 xm.m_time = t;
3892 xm.m_block = mailx_blockof(off1);
3893 xm.m_offset = mailx_offsetof(off1);
3894 xm.m_size = size;
3895 xm.m_xsize = xsize;
3896 xm.m_lines = xm.m_xlines = lines;
3897 xm.m_uid = uid;
3898 xm.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
3899 putcache(&xmb, &xm);
3900
3901 n_free(xmb.mb_imap_mailbox);
3902 jleave:
3903 NYD_OU;
3904 return rv;
3905 }
3906
3907 static enum okay
imap_appenduid_cached(struct mailbox * mp,FILE * fp)3908 imap_appenduid_cached(struct mailbox *mp, FILE *fp)
3909 {
3910 FILE *tp = NULL;
3911 time_t t;
3912 long size, xsize, ysize, lines;
3913 enum mflag flag = MNEW;
3914 char *name, *buf, *bp;
3915 char const *cp;
3916 uz bufsize, buflen, cnt;
3917 enum okay rv = STOP;
3918 NYD_IN;
3919
3920 mx_fs_linepool_aquire(&buf, &bufsize);
3921 cnt = fsize(fp);
3922 if(fgetline(&buf, &bufsize, &cnt, &buflen, fp, FAL0) == NIL)
3923 goto jstop;
3924
3925 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
3926 ;
3927 while (*bp == ' ')
3928 ++bp;
3929
3930 if ((cp = su_cs_rfind_c(bp, '{')) == NULL)
3931 goto jstop;
3932
3933 xsize = atol(&cp[1]) + 2;
3934 if ((name = imap_strex(&bp[7], &cp)) == NULL)
3935 goto jstop;
3936 while (*cp == ' ')
3937 cp++;
3938
3939 if (*cp == '(') {
3940 imap_getflags(cp, &cp, &flag);
3941 while (*++cp == ' ')
3942 ;
3943 }
3944 t = imap_read_date_time(cp);
3945
3946 if((tp = mx_fs_tmp_open("imapapui", (mx_FS_O_RDWR | mx_FS_O_UNLINK |
3947 mx_FS_O_REGISTER), NIL)) == NIL)
3948 goto jstop;
3949
3950 size = xsize;
3951 ysize = lines = 0;
3952 while(size > 0){
3953 if(fgetline(&buf, &bufsize, &cnt, &buflen, fp, FAL0) == NIL)
3954 goto jstop;
3955 size -= buflen;
3956 buf[--buflen] = '\0';
3957 buf[buflen-1] = '\n';
3958 fwrite(buf, 1, buflen, tp);
3959 ysize += buflen;
3960 ++lines;
3961 }
3962 fflush(tp);
3963 rewind(tp);
3964
3965 imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
3966 imap_unquotestr(name));
3967 rv = OKAY;
3968
3969 jstop:
3970 if(tp != NIL)
3971 mx_fs_close(tp);
3972 mx_fs_linepool_release(buf, bufsize);
3973 NYD_OU;
3974 return rv;
3975 }
3976
3977 #ifdef mx_HAVE_IMAP_SEARCH
3978 static sz
imap_search2(struct mailbox * mp,struct message * m,int cnt,const char * spec,int f)3979 imap_search2(struct mailbox *mp, struct message *m, int cnt, const char *spec,
3980 int f)
3981 {
3982 char *o, *cs, c;
3983 uz n;
3984 FILE *queuefp = NULL;
3985 int i;
3986 const char *cp, *xp;
3987 sz rv = -1;
3988 NYD;
3989
3990 c = 0;
3991 for (cp = spec; *cp; cp++)
3992 c |= *cp;
3993 if (c & 0200) {
3994 cp = ok_vlook(ttycharset);
3995 # ifdef mx_HAVE_ICONV
3996 if(su_cs_cmp_case(cp, "utf-8") && su_cs_cmp_case(cp, "utf8")){ /* XXX */
3997 char const *nspec;
3998
3999 if((nspec = n_iconv_onetime_cp(n_ICONV_DEFAULT, "utf-8", cp, spec)
4000 ) != NULL){
4001 spec = nspec;
4002 cp = "utf-8";
4003 }
4004 }
4005 # endif
4006 cp = imap_quotestr(cp);
4007 cs = n_lofi_alloc(n = su_cs_len(cp) + 10);
4008 snprintf(cs, n, "CHARSET %s ", cp);
4009 } else
4010 cs = n_UNCONST(n_empty);
4011
4012 o = n_lofi_alloc(n = su_cs_len(spec) + 60);
4013 snprintf(o, n, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
4014 IMAP_OUT(o, MB_COMD, goto out)
4015 /* C99 */{
4016 enum okay ok;
4017
4018 for (rv = 0, ok = OKAY; (mp->mb_active & MB_COMD);) {
4019 if (imap_answer(mp, 0) != OKAY) {
4020 rv = -1;
4021 ok = STOP;
4022 }
4023 if (response_status == RESPONSE_OTHER &&
4024 response_other == MAILBOX_DATA_SEARCH) {
4025 xp = responded_other_text;
4026 while (*xp && *xp != '\r') {
4027 u64 uid;
4028
4029 su_idec_u64_cp(&uid, xp, 10, &xp);/* TODO errors? */
4030 for (i = 0; i < cnt; i++)
4031 if (m[i].m_uid == uid && !(m[i].m_flag & MHIDDEN) &&
4032 (f == MDELETED || !(m[i].m_flag & MDELETED))){
4033 if(ok == OKAY){
4034 mark(i+1, f);
4035 ++rv;
4036 }
4037 }
4038 }
4039 }
4040 }
4041 }
4042 out:
4043 n_lofi_free(o);
4044 if(cs != n_empty)
4045 n_lofi_free(cs);
4046 return rv;
4047 }
4048
4049 FL sz
imap_search1(const char * volatile spec,int f)4050 imap_search1(const char * volatile spec, int f)
4051 {
4052 n_sighdl_t saveint, savepipe;
4053 sz volatile rv = -1;
4054 NYD_IN;
4055
4056 if (mb.mb_type != MB_IMAP)
4057 goto jleave;
4058
4059 imaplock = 1;
4060 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
4061 safe_signal(SIGINT, &_imap_maincatch);
4062 savepipe = safe_signal(SIGPIPE, SIG_IGN);
4063 if (sigsetjmp(imapjmp, 1) == 0) {
4064 if (savepipe != SIG_IGN)
4065 safe_signal(SIGPIPE, imapcatch);
4066
4067 rv = imap_search2(&mb, message, msgCount, spec, f);
4068 }
4069 safe_signal(SIGINT, saveint);
4070 safe_signal(SIGPIPE, savepipe);
4071 imaplock = 0;
4072 jleave:
4073 NYD_OU;
4074 if (interrupts)
4075 n_go_onintr_for_imap();
4076 return rv;
4077 }
4078 #endif /* mx_HAVE_IMAP_SEARCH */
4079
4080 FL int
imap_thisaccount(const char * cp)4081 imap_thisaccount(const char *cp)
4082 {
4083 int rv;
4084 NYD_IN;
4085
4086 if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
4087 rv = 0;
4088 else if ((mb.mb_type != MB_CACHE &&
4089 (mb.mb_sock == NIL || mb.mb_sock->s_fd < 0)) ||
4090 mb.mb_imap_account == NULL)
4091 rv = 0;
4092 else
4093 rv = !su_cs_cmp(protbase(cp), mb.mb_imap_account);
4094 NYD_OU;
4095 return rv;
4096 }
4097
4098 FL enum okay
imap_remove(const char * volatile name)4099 imap_remove(const char * volatile name)
4100 {
4101 n_sighdl_t volatile saveint, savepipe;
4102 enum okay volatile rv = STOP;
4103 NYD_IN;
4104
4105 if (mb.mb_type != MB_IMAP) {
4106 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name);
4107 goto jleave;
4108 }
4109
4110 if (!imap_thisaccount(name)) {
4111 n_err(_("Can only remove mailboxes on current IMAP server: "
4112 "\"%s\" not removed\n"), name);
4113 goto jleave;
4114 }
4115
4116 imaplock = 1;
4117 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
4118 safe_signal(SIGINT, &_imap_maincatch);
4119 savepipe = safe_signal(SIGPIPE, SIG_IGN);
4120 if (sigsetjmp(imapjmp, 1) == 0) {
4121 if (savepipe != SIG_IGN)
4122 safe_signal(SIGPIPE, imapcatch);
4123
4124 rv = imap_remove1(&mb, imap_fileof(name));
4125 }
4126 safe_signal(SIGINT, saveint);
4127 safe_signal(SIGPIPE, savepipe);
4128 imaplock = 0;
4129
4130 if (rv == OKAY)
4131 rv = cache_remove(name);
4132 jleave:
4133 NYD_OU;
4134 if (interrupts)
4135 n_go_onintr_for_imap();
4136 return rv;
4137 }
4138
4139 static enum okay
imap_remove1(struct mailbox * mp,const char * name)4140 imap_remove1(struct mailbox *mp, const char *name)
4141 {
4142 char *o;
4143 int os;
4144 char const *qname;
4145 FILE *queuefp;
4146 enum okay ok;
4147 NYD;
4148
4149 ok = STOP;
4150 queuefp = NULL;
4151
4152 if((qname = imap_path_quote(mp, name)) != NULL){
4153 o = n_lofi_alloc(os = su_cs_len(qname) + 100);
4154 snprintf(o, os, "%s DELETE %s\r\n", tag(1), qname);
4155 IMAP_OUT(o, MB_COMD, goto out)
4156 while (mp->mb_active & MB_COMD)
4157 ok = imap_answer(mp, 1);
4158 out:
4159 n_lofi_free(o);
4160 }
4161 return ok;
4162 }
4163
4164 FL enum okay
imap_rename(const char * old,const char * new)4165 imap_rename(const char *old, const char *new)
4166 {
4167 n_sighdl_t saveint, savepipe;
4168 enum okay volatile rv = STOP;
4169 NYD_IN;
4170
4171 if (mb.mb_type != MB_IMAP) {
4172 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
4173 goto jleave;
4174 }
4175
4176 if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
4177 n_err(_("Can only rename mailboxes on current IMAP "
4178 "server: \"%s\" not renamed to \"%s\"\n"), old, new);
4179 goto jleave;
4180 }
4181
4182 imaplock = 1;
4183 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
4184 safe_signal(SIGINT, &_imap_maincatch);
4185 savepipe = safe_signal(SIGPIPE, SIG_IGN);
4186 if (sigsetjmp(imapjmp, 1) == 0) {
4187 if (savepipe != SIG_IGN)
4188 safe_signal(SIGPIPE, imapcatch);
4189
4190 rv = imap_rename1(&mb, imap_fileof(old), imap_fileof(new));
4191 }
4192 safe_signal(SIGINT, saveint);
4193 safe_signal(SIGPIPE, savepipe);
4194 imaplock = 0;
4195
4196 if (rv == OKAY)
4197 rv = cache_rename(old, new);
4198 jleave:
4199 NYD_OU;
4200 if (interrupts)
4201 n_go_onintr_for_imap();
4202 return rv;
4203 }
4204
4205 static enum okay
imap_rename1(struct mailbox * mp,const char * old,const char * new)4206 imap_rename1(struct mailbox *mp, const char *old, const char *new)
4207 {
4208 char *o;
4209 int os;
4210 char const *qoname, *qnname;
4211 FILE *queuefp;
4212 enum okay ok;
4213 NYD;
4214
4215 ok = STOP;
4216 queuefp = NULL;
4217
4218 if((qoname = imap_path_quote(mp, old)) != NULL &&
4219 (qnname = imap_path_quote(mp, new)) != NULL){
4220 o = n_lofi_alloc(os = su_cs_len(qoname) + su_cs_len(qnname) + 100);
4221 snprintf(o, os, "%s RENAME %s %s\r\n", tag(1), qoname, qnname);
4222 IMAP_OUT(o, MB_COMD, goto out)
4223 while (mp->mb_active & MB_COMD)
4224 ok = imap_answer(mp, 1);
4225 out:
4226 n_lofi_free(o);
4227 }
4228 return ok;
4229 }
4230
4231 FL enum okay
imap_dequeue(struct mailbox * mp,FILE * fp)4232 imap_dequeue(struct mailbox *mp, FILE *fp)
4233 {
4234 char o[LINESIZE], *newname, *buf, *bp, *cp, iob[4096];
4235 uz bufsize, buflen, cnt;
4236 long offs, offs1, offs2, octets;
4237 int twice, gotcha = 0;
4238 FILE *queuefp = NULL;
4239 enum okay ok = OKAY, rok = OKAY;
4240 NYD;
4241
4242 mx_fs_linepool_aquire(&buf, &bufsize);
4243 cnt = fsize(fp);
4244 while((offs1 = ftell(fp)) >= 0 &&
4245 fgetline(&buf, &bufsize, &cnt, &buflen, fp, FAL0) != NIL){
4246 for (bp = buf; *bp != ' '; ++bp) /* strip old tag */
4247 ;
4248 while (*bp == ' ')
4249 ++bp;
4250 twice = 0;
4251 if ((offs = ftell(fp)) < 0)
4252 goto fail;
4253 again:
4254 snprintf(o, sizeof o, "%s %s", tag(1), bp);
4255 if (su_cs_cmp_case_n(bp, "UID COPY ", 9) == 0) {
4256 cp = &bp[9];
4257 while (su_cs_is_digit(*cp))
4258 cp++;
4259 if (*cp != ' ')
4260 goto fail;
4261 while (*cp == ' ')
4262 cp++;
4263 if ((newname = imap_strex(cp, NULL)) == NULL)
4264 goto fail;
4265 IMAP_OUT(o, MB_COMD, continue)
4266 while (mp->mb_active & MB_COMD)
4267 ok = imap_answer(mp, twice);
4268 if (response_status == RESPONSE_NO && twice++ == 0)
4269 goto trycreate;
4270 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4271 imap_copyuid(mp, NULL, imap_unquotestr(newname));
4272 }
4273 } else if (su_cs_cmp_case_n(bp, "UID STORE ", 10) == 0) {
4274 IMAP_OUT(o, MB_COMD, continue)
4275 while (mp->mb_active & MB_COMD)
4276 ok = imap_answer(mp, 1);
4277 if (ok == OKAY)
4278 gotcha++;
4279 } else if (su_cs_cmp_case_n(bp, "APPEND ", 7) == 0) {
4280 if ((cp = su_cs_rfind_c(bp, '{')) == NULL)
4281 goto fail;
4282 octets = atol(&cp[1]) + 2;
4283 if ((newname = imap_strex(&bp[7], NULL)) == NULL)
4284 goto fail;
4285 IMAP_OUT(o, MB_COMD, continue)
4286 while (mp->mb_active & MB_COMD) {
4287 ok = imap_answer(mp, twice);
4288 if (response_type == RESPONSE_CONT)
4289 break;
4290 }
4291 if (ok == STOP) {
4292 if (twice++ == 0 && fseek(fp, offs, SEEK_SET) >= 0)
4293 goto trycreate;
4294 goto fail;
4295 }
4296 while (octets > 0) {
4297 uz n = (UCMP(z, octets, >, sizeof iob)
4298 ? sizeof iob : (uz)octets);
4299 octets -= n;
4300 if (n != fread(iob, 1, n, fp))
4301 goto fail;
4302 mx_socket_write1(mp->mb_sock, iob, n, 1);
4303 }
4304 mx_socket_write(mp->mb_sock, "");
4305 while (mp->mb_active & MB_COMD) {
4306 ok = imap_answer(mp, 0);
4307 if (response_status == RESPONSE_NO && twice++ == 0) {
4308 if (fseek(fp, offs, SEEK_SET) < 0)
4309 goto fail;
4310 goto trycreate;
4311 }
4312 }
4313 if (response_status == RESPONSE_OK && mp->mb_flags & MB_UIDPLUS) {
4314 if ((offs2 = ftell(fp)) < 0)
4315 goto fail;
4316 fseek(fp, offs1, SEEK_SET);
4317 if (imap_appenduid_cached(mp, fp) == STOP) {
4318 (void)fseek(fp, offs2, SEEK_SET);
4319 goto fail;
4320 }
4321 }
4322 } else {
4323 fail:
4324 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp);
4325 rok = STOP;
4326 }
4327 continue;
4328 trycreate:
4329 snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), newname);
4330 IMAP_OUT(o, MB_COMD, continue)
4331 while (mp->mb_active & MB_COMD)
4332 ok = imap_answer(mp, 1);
4333 if (ok == OKAY)
4334 goto again;
4335 }
4336 if(ferror(fp))
4337 rok = STOP;
4338
4339 fflush_rewind(fp);
4340 ftruncate(fileno(fp), 0);
4341 if (gotcha)
4342 imap_close(mp);
4343 mx_fs_linepool_release(buf, bufsize);
4344 return rok;
4345 }
4346
4347 static char *
imap_strex(char const * cp,char const ** xp)4348 imap_strex(char const *cp, char const **xp)
4349 {
4350 char const *cq;
4351 char *n = NULL;
4352 NYD_IN;
4353
4354 if (*cp != '"')
4355 goto jleave;
4356
4357 for (cq = cp + 1; *cq != '\0'; ++cq) {
4358 if (*cq == '\\')
4359 cq++;
4360 else if (*cq == '"')
4361 break;
4362 }
4363 if (*cq != '"')
4364 goto jleave;
4365
4366 n = n_autorec_alloc(cq - cp + 2);
4367 su_mem_copy(n, cp, cq - cp +1);
4368 n[cq - cp + 1] = '\0';
4369 if (xp != NULL)
4370 *xp = cq + 1;
4371 jleave:
4372 NYD_OU;
4373 return n;
4374 }
4375
4376 static enum okay
check_expunged(void)4377 check_expunged(void)
4378 {
4379 enum okay rv;
4380 NYD_IN;
4381
4382 if (expunged_messages > 0) {
4383 n_err(_("Command not executed - messages have been expunged\n"));
4384 rv = STOP;
4385 } else
4386 rv = OKAY;
4387 NYD_OU;
4388 return rv;
4389 }
4390
4391 FL int
c_connect(void * vp)4392 c_connect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4393 {
4394 struct mx_url url;
4395 int rv, omsgCount = msgCount;
4396 NYD_IN;
4397 UNUSED(vp);
4398
4399 if(mb.mb_type == MB_IMAP && mb.mb_sock != NIL && mb.mb_sock->s_fd > 0){
4400 n_err(_("Already connected\n"));
4401 rv = 1;
4402 goto jleave;
4403 }
4404
4405 if(!mx_url_parse(&url, CPROTO_IMAP, mailname)){
4406 rv = 1;
4407 goto jleave;
4408 }
4409 ok_bclear(disconnected);
4410 n_var_vclear(savecat("disconnected-", url.url_u_h_p.s));
4411
4412 if (mb.mb_type == MB_CACHE) {
4413 enum fedit_mode fm = FEDIT_NONE;
4414 if (_imap_rdonly)
4415 fm |= FEDIT_RDONLY;
4416 if (!(n_pstate & n_PS_EDIT))
4417 fm |= FEDIT_SYSBOX;
4418 _imap_setfile1(NULL, &url, fm, 1);
4419 if (msgCount > omsgCount)
4420 newmailinfo(omsgCount);
4421 }
4422 rv = 0;
4423 jleave:
4424 NYD_OU;
4425 return rv;
4426 }
4427
4428 FL int
c_disconnect(void * vp)4429 c_disconnect(void *vp) /* TODO v15-compat mailname<->URL (with password) */
4430 {
4431 struct mx_url url;
4432 int rv = 1, *msgvec = vp;
4433 NYD_IN;
4434
4435 if (mb.mb_type == MB_CACHE) {
4436 n_err(_("Not connected\n"));
4437 goto jleave;
4438 }
4439 if (mb.mb_type != MB_IMAP || cached_uidvalidity(&mb) == 0) {
4440 n_err(_("The current mailbox is not cached\n"));
4441 goto jleave;
4442 }
4443
4444 if(!mx_url_parse(&url, CPROTO_IMAP, mailname))
4445 goto jleave;
4446
4447 if (*msgvec)
4448 c_cache(vp);
4449 ok_bset(disconnected);
4450 if (mb.mb_type == MB_IMAP) {
4451 enum fedit_mode fm = FEDIT_NONE;
4452 if (_imap_rdonly)
4453 fm |= FEDIT_RDONLY;
4454 if (!(n_pstate & n_PS_EDIT))
4455 fm |= FEDIT_SYSBOX;
4456 if(mb.mb_sock != NIL){
4457 if(mb.mb_sock->s_fd >= 0)
4458 mx_socket_close(mb.mb_sock);
4459 su_FREE(mb.mb_sock);
4460 mb.mb_sock = NIL;
4461 }
4462 _imap_setfile1(NULL, &url, fm, 1);
4463 }
4464 rv = 0;
4465 jleave:
4466 NYD_OU;
4467 return rv;
4468 }
4469
4470 FL int
c_cache(void * vp)4471 c_cache(void *vp)
4472 {
4473 int rv = 1, *msgvec = vp, *ip;
4474 struct message *mp;
4475 NYD_IN;
4476
4477 if (mb.mb_type != MB_IMAP) {
4478 n_err(_("Not connected to an IMAP server\n"));
4479 goto jleave;
4480 }
4481 if (cached_uidvalidity(&mb) == 0) {
4482 n_err(_("The current mailbox is not cached\n"));
4483 goto jleave;
4484 }
4485
4486 srelax_hold();
4487 for (ip = msgvec; *ip; ++ip) {
4488 mp = &message[*ip - 1];
4489 if (!(mp->m_content_info & CI_HAVE_BODY)) {
4490 get_body(mp);
4491 srelax();
4492 }
4493 }
4494 srelax_rele();
4495 rv = 0;
4496 jleave:
4497 NYD_OU;
4498 return rv;
4499 }
4500
4501 FL int
disconnected(const char * file)4502 disconnected(const char *file)
4503 {
4504 struct mx_url url;
4505 int rv = 1;
4506 NYD_IN;
4507
4508 if (ok_blook(disconnected)) {
4509 rv = 1;
4510 goto jleave;
4511 }
4512
4513 if(!mx_url_parse(&url, CPROTO_IMAP, file)){
4514 rv = 0;
4515 goto jleave;
4516 }
4517 rv = (n_var_vlook(savecat("disconnected-", url.url_u_h_p.s), FAL0) != NULL);
4518
4519 jleave:
4520 NYD_OU;
4521 return rv;
4522 }
4523
4524 FL void
transflags(struct message * omessage,long omsgCount,int transparent)4525 transflags(struct message *omessage, long omsgCount, int transparent)
4526 {
4527 struct message *omp, *nmp, *newdot, *newprevdot;
4528 int hf;
4529 NYD_IN;
4530
4531 omp = omessage;
4532 nmp = message;
4533 newdot = message;
4534 newprevdot = NULL;
4535 while (PCMP(omp, <, omessage + omsgCount) &&
4536 PCMP(nmp, <, message + msgCount)) {
4537 if (dot && nmp->m_uid == dot->m_uid)
4538 newdot = nmp;
4539 if (prevdot && nmp->m_uid == prevdot->m_uid)
4540 newprevdot = nmp;
4541 if (omp->m_uid == nmp->m_uid) {
4542 hf = nmp->m_flag & MHIDDEN;
4543 if (transparent && mb.mb_type == MB_IMAP)
4544 omp->m_flag &= ~MHIDDEN;
4545 *nmp++ = *omp++;
4546 if (transparent && mb.mb_type == MB_CACHE)
4547 nmp[-1].m_flag |= hf;
4548 } else if (omp->m_uid < nmp->m_uid)
4549 ++omp;
4550 else
4551 ++nmp;
4552 }
4553 dot = newdot;
4554 setdot(newdot);
4555 prevdot = newprevdot;
4556 n_free(omessage);
4557 NYD_OU;
4558 }
4559
4560 FL time_t
imap_read_date_time(const char * cp)4561 imap_read_date_time(const char *cp)
4562 {
4563 char buf[3];
4564 time_t t;
4565 int i, year, month, day, hour, minute, second, sign = -1;
4566 NYD2_IN;
4567
4568 /* "25-Jul-2004 15:33:44 +0200"
4569 * | | | | | |
4570 * 0 5 10 15 20 25 */
4571 if (cp[0] != '"' || su_cs_len(cp) < 28 || cp[27] != '"')
4572 goto jinvalid;
4573 day = strtol(&cp[1], NULL, 10);
4574 for (i = 0;;) {
4575 if (su_cs_cmp_case_n(&cp[4], n_month_names[i], 3) == 0)
4576 break;
4577 if (n_month_names[++i][0] == '\0')
4578 goto jinvalid;
4579 }
4580 month = i + 1;
4581 year = strtol(&cp[8], NULL, 10);
4582 hour = strtol(&cp[13], NULL, 10);
4583 minute = strtol(&cp[16], NULL, 10);
4584 second = strtol(&cp[19], NULL, 10);
4585 if ((t = combinetime(year, month, day, hour, minute, second)) == (time_t)-1)
4586 goto jinvalid;
4587 switch (cp[22]) {
4588 case '-':
4589 sign = 1;
4590 break;
4591 case '+':
4592 break;
4593 default:
4594 goto jinvalid;
4595 }
4596 buf[2] = '\0';
4597 buf[0] = cp[23];
4598 buf[1] = cp[24];
4599 t += strtol(buf, NULL, 10) * sign * 3600;
4600 buf[0] = cp[25];
4601 buf[1] = cp[26];
4602 t += strtol(buf, NULL, 10) * sign * 60;
4603 jleave:
4604 NYD2_OU;
4605 return t;
4606 jinvalid:
4607 time(&t);
4608 goto jleave;
4609 }
4610
4611 FL const char *
imap_make_date_time(time_t t)4612 imap_make_date_time(time_t t)
4613 {
4614 static char s[40];
4615 char const *mn;
4616 s32 y, md, th, tm, ts;
4617 struct tm *tmp;
4618 int tzdiff_hour, tzdiff_min;
4619 NYD2_IN;
4620
4621 jredo:
4622 if((tmp = localtime(&t)) == NULL){
4623 t = 0;
4624 goto jredo;
4625 }
4626
4627 tzdiff_min = S(int,n_time_tzdiff(t, NIL, tmp));
4628 tzdiff_min /= 60; /* TODO su_TIME_MIN_SECS */
4629 tzdiff_hour = tzdiff_min / 60;
4630 tzdiff_min %= 60; /* TODO su_TIME_HOUR_MINS */
4631
4632 if(UNLIKELY((y = tmp->tm_year) < 0 || y >= 9999/*S32_MAX*/ - 1900)){
4633 y = 1970;
4634 mn = n_month_names[0];
4635 md = 1;
4636 th = tm = ts = 0;
4637 }else{
4638 y += 1900;
4639 mn = (tmp->tm_mon >= 0 && tmp->tm_mon <= 11)
4640 ? n_month_names[tmp->tm_mon] : n_qm;
4641
4642 if((md = tmp->tm_mday) < 1 || md > 31)
4643 md = 1;
4644
4645 if((th = tmp->tm_hour) < 0 || th > 23)
4646 th = 0;
4647 if((tm = tmp->tm_min) < 0 || tm > 59)
4648 tm = 0;
4649 if((ts = tmp->tm_sec) < 0 || ts > 60)
4650 ts = 0;
4651 }
4652
4653 snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4654 md, mn, y, th, tm, ts, tzdiff_hour, tzdiff_min);
4655
4656 NYD2_OU;
4657 return s;
4658 }
4659
4660 FL char *
4661 (protbase)(char const *cp su_DBG_LOC_ARGS_DECL)
4662 {
4663 char *n, *np;
4664 NYD2_IN;
4665
4666 np = n = su_MEM_BAG_SELF_AUTO_ALLOC_LOCOR(su_cs_len(cp) +1,
4667 su_DBG_LOC_ARGS_ORUSE);
4668
4669 /* Just ignore the `is-system-mailbox' prefix XXX */
4670 if (cp[0] == '%' && cp[1] == ':')
4671 cp += 2;
4672
4673 while (*cp != '\0') {
4674 if (cp[0] == ':' && cp[1] == '/' && cp[2] == '/') {
4675 *np++ = *cp++;
4676 *np++ = *cp++;
4677 *np++ = *cp++;
4678 } else if (cp[0] == '/')
4679 break;
4680 else
4681 *np++ = *cp++;
4682 }
4683 *np = '\0';
4684 NYD2_OU;
4685 return n;
4686 }
4687
4688 #include "su/code-ou.h"
4689 #endif /* mx_HAVE_IMAP */
4690 #undef mx_SOURCE_NET_IMAP
4691 #undef su_FILE
4692 /* s-it-mode */
4693