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