1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 * SPDX-License-Identifier: BSD-4-Clause
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 #define n_FILE obs_imap
43 #ifndef HAVE_AMALGAMATION
48 # include <sys/socket.h>
52 # include <netinet/in.h>
54 # ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
61 #define IMAP_ANSWER() \
63 if (mp->mb_type != MB_CACHE) {\
65 while (mp->mb_active & MB_COMD)\
66 ok = imap_answer(mp, 1);\
72 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
73 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
74 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
75 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
76 * TODO I.e., that should be a function, not a macro ... or so.
77 * TODO This entire module needs MASSIVE work! */
78 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
79 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
81 if (mp->mb_type != MB_CACHE) {\
82 if (imap_finish(mp) == STOP) {\
85 if (n_poption & n_PO_D_VV)\
88 if (swrite(&mp->mb_sock, X) == STOP) {\
97 static struct record
{
98 struct record
*rec_next
;
99 unsigned long rec_count
;
124 static char *responded_tag
;
125 static char *responded_text
;
126 static char *responded_other_text
;
127 static long responded_other_number
;
133 MAILBOX_DATA_MAILBOX
,
138 MESSAGE_DATA_EXPUNGE
,
141 RESPONSE_OTHER_UNKNOWN
144 static enum list_attributes
{
146 LIST_NOINFERIORS
= 001,
152 static int list_hierarchy_delimiter
;
153 static char *list_name
;
156 struct list_item
*l_next
;
159 enum list_attributes l_attr
;
165 static char *imapbuf
; /* TODO not static, use pool */
166 static size_t imapbufsize
;
167 static sigjmp_buf imapjmp
;
168 static sighandler_type savealrm
;
169 static int imapkeepalive
;
170 static long had_exists
= -1;
171 static long had_expunge
= -1;
172 static long expunged_messages
;
173 static int volatile imaplock
;
174 static int same_imap_account
;
175 static bool_t _imap_rdonly
;
177 static char *imap_quotestr(char const *s
);
178 static char *imap_unquotestr(char const *s
);
179 static void imap_delim_init(struct mailbox
*mp
, struct url
const *urlp
);
180 static char const *imap_path_normalize(struct mailbox
*mp
, char const *cp
);
181 /* Returns NULL on error */
182 static char *imap_path_quote(struct mailbox
*mp
, char const *cp
);
183 static void imap_other_get(char *pp
);
184 static void imap_response_get(const char **cp
);
185 static void imap_response_parse(void);
186 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
187 static enum okay
imap_parse_list(void);
188 static enum okay
imap_finish(struct mailbox
*mp
);
189 static void imap_timer_off(void);
190 static void imapcatch(int s
);
191 static void _imap_maincatch(int s
);
192 static enum okay
imap_noop1(struct mailbox
*mp
);
193 static void rec_queue(enum rec_type type
, unsigned long cnt
);
194 static enum okay
rec_dequeue(void);
195 static void rec_rmqueue(void);
196 static void imapalarm(int s
);
197 static enum okay
imap_preauth(struct mailbox
*mp
, struct url
*urlp
);
198 static enum okay
imap_capability(struct mailbox
*mp
);
199 static enum okay
imap_auth(struct mailbox
*mp
, struct ccred
*ccred
);
201 static enum okay
imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
);
203 static enum okay
imap_login(struct mailbox
*mp
, struct ccred
*ccred
);
205 static enum okay
_imap_gssapi(struct mailbox
*mp
, struct ccred
*ccred
);
207 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
208 static void imap_init(struct mailbox
*mp
, int n
);
209 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
211 static bool_t
_imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
,
213 static int _imap_setfile1(char const *who
, struct url
*urlp
,
214 enum fedit_mode fm
, int transparent
);
215 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
216 size_t expected
, int need
, const char *head
,
217 size_t headsize
, long headlines
);
218 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
219 const char *str
, const char *head
, size_t headsize
,
221 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
223 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
224 struct message
*from
, enum content_info content_info
);
225 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
227 static enum okay
imap_exit(struct mailbox
*mp
);
228 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
,
230 static enum okay
imap_close(struct mailbox
*mp
);
231 static enum okay
imap_update(struct mailbox
*mp
);
232 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
233 int c
, const char *sp
, int needstat
);
234 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
235 static const char *tag(int new);
236 static char * imap_putflags(int f
);
237 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
238 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
239 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
240 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
,
242 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
243 struct list_item
**list
, struct list_item
**lend
,
245 static enum okay
imap_list(struct mailbox
*mp
, const char *base
, int strip
,
247 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
249 static enum okay
imap_copyuid_parse(const char *cp
,
250 ui64_t
*uidvalidity
, ui64_t
*olduid
, ui64_t
*newuid
);
251 static enum okay
imap_appenduid_parse(const char *cp
,
252 ui64_t
*uidvalidity
, ui64_t
*uid
);
253 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
255 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
256 long off1
, long xsize
, long size
, long lines
, int flag
,
258 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
259 #ifdef HAVE_IMAP_SEARCH
260 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
,
261 const char *spec
, int f
);
263 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
264 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
266 static char * imap_strex(char const *cp
, char const **xp
);
267 static enum okay
check_expunged(void);
270 imap_quotestr(char const *s
)
275 np
= n
= n_autorec_alloc(2 * strlen(s
) + 3);
278 if (*s
== '"' || *s
== '\\')
289 imap_unquotestr(char const *s
)
299 np
= n
= n_autorec_alloc(strlen(s
) + 1);
314 imap_delim_init(struct mailbox
*mp
, struct url
const *urlp
){
319 mp
->mb_imap_delim
[0] = '\0';
321 if((cp
= xok_vlook(imap_delim
, urlp
, OXM_ALL
)) != NULL
){
326 i
= sizeof(n_IMAP_DELIM
) -1;
330 if(i
< n_NELEM(mp
->mb_imap_delim
))
332 memcpy(&mb
.mb_imap_delim
[0], cp
, i
+1);
334 n_err(_("*imap-delim* for %s is too long: %s\n"),
335 urlp
->url_input
, cp
);
341 imap_path_normalize(struct mailbox
*mp
, char const *cp
){
342 char *rv_base
, *rv
, dc2
, dc
, c
, lc
;
346 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
347 * exactly what i have specified" */
348 if(mp
== NULL
|| mp
->mb_imap_delim
[0] == '\0')
349 dcp
= &n_IMAP_DELIM
[0];
351 dcp
= &mp
->mb_imap_delim
[0];
352 dc2
= ((dc
= *dcp
) != '\0') ? *++dcp
: dc
;
354 /* Plain names don't need path quoting */
359 for(cpx
= cp
;; ++cpx
)
360 if((c
= *cpx
) == '\0')
363 if(strchr(n_IMAP_DELIM
, c
)){
369 else if(dc2
&& strchr(dcp
, c
) != NULL
)
372 /* And we don't need to reevaluate what we have seen yet */
373 i
= PTR2SIZE(cpx
- cp
);
374 rv
= rv_base
= n_autorec_alloc(i
+ (j
= strlen(cpx
) +1));
377 memcpy(&rv
[i
], cpx
, j
);
382 /* Squeeze adjacent delimiters, convert remain to dc */
383 for(lc
= '\0'; (c
= *cp
++) != '\0'; lc
= c
){
384 if(c
== dc
|| (lc
!= '\0' && dc2
&& strchr(dcp
, c
) != NULL
))
386 if(c
!= dc
|| lc
!= dc
)
398 imap_path_encode(char const *cp
, bool_t
*err_or_null
){
399 /* To a large extend inspired by dovecot(1) */
402 ui8_t
*be16p_base
, *be16p
;
408 if(err_or_null
== NULL
)
409 err_or_null
= &err_def
;
412 /* Is this a string that works out as "plain US-ASCII"? */
414 if((c
= cp
[l
]) == '\0')
416 else if(c
<= 0x1F || c
>= 0x7F || c
== '&')
421 /* We need to encode in mUTF-7! For that, we first have to convert the
422 * local charset to UTF-8, then convert all characters which need to be
423 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
424 * We can skip the UTF-8 conversion occasionally, however */
425 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
426 !defined HAVE_ALWAYS_UNICODE_LOCALE
427 if(!(n_psonce
& n_PSO_UNICODE
)){
430 emsg
= N_("iconv(3) from locale charset to UTF-8 failed");
431 if((x
= n_iconv_onetime_cp(n_ICONV_NONE
, "utf-8", ok_vlook(ttycharset
),
436 /* So: Why not start all over again?
437 * Is this a string that works out as "plain US-ASCII"? */
439 if((c
= cp
[l
]) == '\0')
441 else if(c
<= 0x1F || c
>= 0x7F || c
== '&')
446 /* We need to encode, save what we have, encode the rest */
449 for(cp
+= l
, l
= 0; cp
[l
] != '\0'; ++l
)
451 be16p_base
= n_autorec_alloc((l
<< 1) +1); /* XXX use n_string, resize */
453 out
.s
= n_autorec_alloc(l_plain
+ (l
<< 2) +1); /* XXX use n_string.. */
455 memcpy(out
.s
, &cp
[-l_plain
], out
.l
= l_plain
);
458 DBG( l_plain
+= (l
<< 2); )
465 out
.s
[out
.l
+ 0] = '&';
466 out
.s
[out
.l
+ 1] = '-';
468 }else if(c
> 0x1F && c
< 0x7F)
471 static char const mb64ct
[] =
472 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
475 /* Convert consecutive non-representables */
476 emsg
= N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
478 for(be16p
= be16p_base
, --cp
, ++l
;;){
479 if((utf32
= n_utf8_to_utf32(&cp
, &l
)) == UI32_MAX
)
482 /* TODO S-CText: magic utf16 conversions */
484 be16p
[1] = utf32
& 0xFF;
485 be16p
[0] = (utf32
>>= 8, utf32
&= 0xFF);
491 s7e
= 0xD800u
| (utf32
>> 10);
492 be16p
[1] = s7e
& 0xFF;
493 be16p
[0] = (s7e
>>= 8, s7e
&= 0xFF);
494 s7e
= 0xDC00u
| (utf32
&= 0x03FF);
495 be16p
[3] = s7e
& 0xFF;
496 be16p
[2] = (s7e
>>= 8, s7e
&= 0xFF);
502 if((c
= *cp
) > 0x1F && c
< 0x7F)
506 /* And then warp that UTF-16BE to mUTF-7 */
507 out
.s
[out
.l
++] = '&';
508 utf32
= (ui32_t
)PTR2SIZE(be16p
- be16p_base
);
511 for(; utf32
>= 3; be16p
+= 3, utf32
-= 3){
512 out
.s
[out
.l
+0] = mb64ct
[ be16p
[0] >> 2 ];
513 out
.s
[out
.l
+1] = mb64ct
[((be16p
[0] & 0x03) << 4) | (be16p
[1] >> 4)];
514 out
.s
[out
.l
+2] = mb64ct
[((be16p
[1] & 0x0F) << 2) | (be16p
[2] >> 6)];
515 out
.s
[out
.l
+3] = mb64ct
[ be16p
[2] & 0x3F];
519 out
.s
[out
.l
+ 0] = mb64ct
[be16p
[0] >> 2];
521 out
.s
[out
.l
+ 1] = mb64ct
[ (be16p
[0] & 0x03) << 4];
524 out
.s
[out
.l
+ 1] = mb64ct
[((be16p
[0] & 0x03) << 4) |
526 out
.s
[out
.l
+ 2] = mb64ct
[ (be16p
[1] & 0x0F) << 2];
530 out
.s
[out
.l
++] = '-';
534 assert(out
.l
<= l_plain
);
541 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp
, V_(emsg
));
546 imap_path_decode(char const *path
, bool_t
*err_or_null
){
547 /* To a large extend inspired by dovecot(1) TODO use string */
549 ui8_t
*mb64p_base
, *mb64p
, *mb64xp
;
550 char const *emsg
, *cp
;
551 char *rv_base
, *rv
, c
;
555 if(err_or_null
== NULL
)
556 err_or_null
= &err_def
;
559 l
= l_orig
= strlen(path
);
560 rv
= rv_base
= n_autorec_alloc(l
<< 1);
561 memcpy(rv
, path
, l
+1);
563 /* xxx Don't check for invalid characters from malicious servers */
564 if(l
== 0 || (cp
= memchr(path
, '&', l
)) == NULL
)
569 emsg
= N_("Invalid mUTF-7 encoding");
570 i
= PTR2SIZE(cp
- path
);
576 if((c
= *cp
) != '&'){
577 if(c
<= 0x1F || c
>= 0x7F){
578 emsg
= N_("Invalid mUTF-7: unencoded control or 8-bit byte");
586 else if(*++cp
== '-'){
592 emsg
= N_("Invalid mUTF-7: incomplete input");
595 /* mUTF-7 -> UTF-16BE -> UTF-8 */
596 static ui8_t
const mb64dt
[256] = {
599 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
600 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
601 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,62, 63,XX
,XX
,XX
,
602 52,53,54,55, 56,57,58,59, 60,61,XX
,XX
, XX
,XX
,XX
,XX
,
603 XX
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
604 15,16,17,18, 19,20,21,22, 23,24,25,XX
, XX
,XX
,XX
,XX
,
605 XX
,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
606 41,42,43,44, 45,46,47,48, 49,50,51,XX
, XX
,XX
,XX
,XX
,
607 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
608 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
609 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
610 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
611 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
612 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
613 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
614 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
617 if(mb64p_base
== NULL
)
618 mb64p_base
= n_autorec_alloc(l
);
620 /* Decode the mUTF-7 to what is indeed UTF-16BE */
621 for(mb64p
= mb64p_base
;;){
623 if((mb64p
[0] = mb64dt
[(ui8_t
)cp
[0]]) == XX
||
624 (mb64p
[1] = mb64dt
[(ui8_t
)cp
[1]]) == XX
)
633 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
639 if((c
= *cp
++) == '-')
641 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
645 if(l
> 0 && *cp
== '-'){
655 if(l
>= 2 && cp
[0] == '&' && cp
[1] != '-'){
656 emsg
= N_("Invalid mUTF-7, consecutive encoded sequences");
660 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
661 i
= PTR2SIZE(mb64p
- mb64p_base
);
662 mb64p
= mb64xp
= mb64p_base
;
665 ui8_t ul
, u0
, u1
, u2
, u3
;
667 ul
= (i
>= 4) ? 4 : i
& 0x3;
671 u2
= (ul
< 3) ? 0 : mb64xp
[2];
672 u3
= (ul
< 4) ? 0 : mb64xp
[3];
674 *mb64p
++ = (u0
<<= 2) | (u1
>> 4);
677 *mb64p
++ = (u1
<<= 4) | (u2
>> 2);
680 *mb64p
++ = (u2
<<= 6, u2
&= 0xC0) | u3
;
683 /* UTF-16BE we convert to UTF-8 */
684 i
= PTR2SIZE(mb64p
- mb64p_base
);
686 emsg
= N_("Odd bytecount for UTF-16BE input");
690 /* TODO S-CText: magic utf16 conversions */
691 emsg
= N_("Invalid UTF-16BE encoding");
693 for(mb64p
= mb64p_base
; i
> 0;){
701 /* Not a surrogate? */
702 if(uhi
< 0xD800 || uhi
> 0xDFFF){
706 }else if(uhi
> 0xDBFF)
709 emsg
= N_("Incomplete UTF-16BE surrogate pair");
715 if(ulo
< 0xDC00 || ulo
> 0xDFFF)
718 utf32
= (uhi
&= 0x03FF);
721 utf32
|= (ulo
&= 0x03FF);
726 utf32
= n_utf32_to_utf8(utf32
, rv
);
733 /* We can skip the UTF-8 conversion occasionally */
734 #if (defined HAVE_DEVEL && defined HAVE_ICONV) ||\
735 !defined HAVE_ALWAYS_UNICODE_LOCALE
736 if(!(n_psonce
& n_PSO_UNICODE
)){
737 emsg
= N_("iconv(3) from UTF-8 to locale charset failed");
738 if((rv
= n_iconv_onetime_cp(n_ICONV_NONE
, NULL
, NULL
, rv_base
)) == NULL
)
749 n_err(_("Cannot decode IMAP path %s\n %s\n"), path
, V_(emsg
));
750 memcpy(rv
= rv_base
, path
, ++l_orig
);
755 imap_path_quote(struct mailbox
*mp
, char const *cp
){
760 cp
= imap_path_normalize(mp
, cp
);
761 cp
= imap_path_encode(cp
, &err
);
762 rv
= err
? NULL
: imap_quotestr(cp
);
768 imap_other_get(char *pp
)
773 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
775 response_other
= MAILBOX_DATA_FLAGS
;
776 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
778 response_other
= MAILBOX_DATA_LIST
;
779 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
781 response_other
= MAILBOX_DATA_LSUB
;
782 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
784 response_other
= MAILBOX_DATA_MAILBOX
;
785 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
787 response_other
= MAILBOX_DATA_SEARCH
;
788 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
790 response_other
= MAILBOX_DATA_STATUS
;
791 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
793 response_other
= CAPABILITY_DATA
;
795 responded_other_number
= strtol(pp
, &xp
, 10);
798 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
799 response_other
= MAILBOX_DATA_EXISTS
;
800 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
801 response_other
= MAILBOX_DATA_RECENT
;
802 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
803 response_other
= MESSAGE_DATA_EXPUNGE
;
804 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
806 response_other
= MESSAGE_DATA_FETCH
;
808 response_other
= RESPONSE_OTHER_UNKNOWN
;
810 responded_other_text
= pp
;
815 imap_response_get(const char **cp
)
818 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
820 response_status
= RESPONSE_OK
;
821 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
823 response_status
= RESPONSE_NO
;
824 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
826 response_status
= RESPONSE_BAD
;
827 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
829 response_status
= RESPONSE_PREAUTH
;
830 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
832 response_status
= RESPONSE_BYE
;
834 response_status
= RESPONSE_OTHER
;
839 imap_response_parse(void)
841 static char *parsebuf
; /* TODO Use pool */
842 static size_t parsebufsize
;
844 const char *ip
= imapbuf
;
848 if (parsebufsize
< imapbufsize
+ 1)
849 parsebuf
= n_realloc(parsebuf
, parsebufsize
= imapbufsize
);
850 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
854 response_type
= RESPONSE_CONT
;
869 imap_response_get(&ip
);
870 pp
= &parsebuf
[ip
- imapbuf
];
871 switch (response_status
) {
873 response_type
= RESPONSE_FATAL
;
876 response_type
= RESPONSE_DATA
;
880 responded_tag
= parsebuf
;
881 while (*pp
&& *pp
!= ' ')
884 response_type
= RESPONSE_ILLEGAL
;
888 while (*pp
&& *pp
== ' ')
891 response_type
= RESPONSE_ILLEGAL
;
894 ip
= &imapbuf
[pp
- parsebuf
];
895 response_type
= RESPONSE_TAGGED
;
896 imap_response_get(&ip
);
897 pp
= &parsebuf
[ip
- imapbuf
];
900 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
901 response_status
== RESPONSE_OTHER
)
907 imap_answer(struct mailbox
*mp
, int errprnt
)
914 if (mp
->mb_type
== MB_CACHE
)
918 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
919 if (n_poption
& n_PO_D_VV
)
920 n_err(">>> SERVER: %s", imapbuf
);
921 imap_response_parse();
922 if (response_type
== RESPONSE_ILLEGAL
)
924 if (response_type
== RESPONSE_CONT
) {
928 if (response_status
== RESPONSE_OTHER
) {
929 if (response_other
== MAILBOX_DATA_EXISTS
) {
930 had_exists
= responded_other_number
;
931 rec_queue(REC_EXISTS
, responded_other_number
);
934 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
935 rec_queue(REC_EXPUNGE
, responded_other_number
);
943 if (response_type
== RESPONSE_TAGGED
) {
944 if (asccasecmp(responded_tag
, tag(0)) == 0)
949 switch (response_status
) {
950 case RESPONSE_PREAUTH
:
951 mp
->mb_active
&= ~MB_PREAUTH
;
963 n_err(_("IMAP error: %s"), responded_text
);
965 case RESPONSE_UNKNOWN
: /* does not happen */
968 mp
->mb_active
= MB_NONE
;
976 if (response_status
!= RESPONSE_OTHER
&&
977 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
978 n_err(_("IMAP alert: %s"), &responded_text
[8]);
980 mp
->mb_active
&= ~MB_COMD
;
982 mp
->mb_active
= MB_NONE
;
989 imap_parse_list(void)
997 cp
= responded_other_text
;
998 list_attributes
= LIST_NONE
;
1000 while (*cp
&& *cp
!= ')') {
1002 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
1003 list_attributes
|= LIST_NOINFERIORS
;
1005 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
1006 list_attributes
|= LIST_NOSELECT
;
1008 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
1009 list_attributes
|= LIST_MARKED
;
1011 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
1012 list_attributes
|= LIST_UNMARKED
;
1024 list_hierarchy_delimiter
= EOF
;
1028 list_hierarchy_delimiter
= *cp
++ & 0377;
1029 if (cp
[0] != '"' || cp
[1] != ' ')
1032 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
1033 list_hierarchy_delimiter
= EOF
;
1040 while (*cp
&& *cp
!= '\r')
1050 imap_finish(struct mailbox
*mp
)
1053 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
1060 imap_timer_off(void)
1063 if (imapkeepalive
> 0) {
1065 safe_signal(SIGALRM
, savealrm
);
1073 NYD_X
; /* Signal handler */
1076 n_err_sighdl(_("Interrupt\n"));
1077 siglongjmp(imapjmp
, 1);
1080 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1086 _imap_maincatch(int s
)
1088 NYD_X
; /* Signal handler */
1090 if (interrupts
++ == 0) {
1091 n_err_sighdl(_("Interrupt\n"));
1094 n_go_onintr_for_imap();
1098 imap_noop1(struct mailbox
*mp
)
1101 FILE *queuefp
= NULL
;
1104 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
1105 IMAP_OUT(o
, MB_COMD
, return STOP
)
1111 imap_fileof(char const *xcp
)
1113 char const *cp
= xcp
;
1118 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
1122 if (cp
[0] == '/' && state
== 1) {
1140 sighandler_type
volatile oldint
, oldpipe
;
1141 enum okay
volatile rv
= STOP
;
1144 if (mb
.mb_type
!= MB_IMAP
)
1148 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1149 safe_signal(SIGINT
, &_imap_maincatch
);
1150 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1151 if (sigsetjmp(imapjmp
, 1) == 0) {
1152 if (oldpipe
!= SIG_IGN
)
1153 safe_signal(SIGPIPE
, imapcatch
);
1155 rv
= imap_noop1(&mb
);
1157 safe_signal(SIGINT
, oldint
);
1158 safe_signal(SIGPIPE
, oldpipe
);
1163 n_go_onintr_for_imap();
1168 rec_queue(enum rec_type rt
, unsigned long cnt
)
1173 rp
= n_calloc(1, sizeof *rp
);
1175 rp
->rec_count
= cnt
;
1176 if (record
&& recend
) {
1177 recend
->rec_next
= rp
;
1180 record
= recend
= rp
;
1187 struct message
*omessage
;
1188 struct record
*rp
, *rq
;
1189 uiz_t exists
= 0, i
;
1190 enum okay rv
= STOP
;
1197 message
= n_alloc((msgCount
+1) * sizeof *message
);
1199 memcpy(message
, omessage
, msgCount
* sizeof *message
);
1200 memset(&message
[msgCount
], 0, sizeof *message
);
1202 rp
= record
, rq
= NULL
;
1204 while (rp
!= NULL
) {
1205 switch (rp
->rec_type
) {
1207 exists
= rp
->rec_count
;
1210 if (rp
->rec_count
== 0) {
1214 if (rp
->rec_count
> (unsigned long)msgCount
) {
1215 if (exists
== 0 || rp
->rec_count
> exists
--)
1221 delcache(&mb
, &message
[rp
->rec_count
-1]);
1222 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
1223 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
1225 /* If the message was part of a collapsed thread,
1226 * the m_collapsed field of one of its ancestors
1227 * should be incremented. It seems hardly possible
1228 * to do this with the current message structure,
1229 * though. The result is that a '+' may be shown
1230 * in the header summary even if no collapsed
1231 * children exists */
1242 record
= recend
= NULL
;
1243 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
1244 message
= n_realloc(message
, (exists
+ 1) * sizeof *message
);
1245 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
1246 for (i
= msgCount
; i
< exists
; ++i
)
1248 imap_flags(&mb
, msgCount
+1, exists
);
1267 for (rp
= record
; rp
!= NULL
;) {
1268 struct record
*tmp
= rp
;
1272 record
= recend
= NULL
;
1280 sighandler_type
volatile saveint
, savepipe
;
1281 NYD_X
; /* Signal handler */
1284 if (imaplock
++ == 0) {
1285 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1286 safe_signal(SIGINT
, &_imap_maincatch
);
1287 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1288 if (sigsetjmp(imapjmp
, 1)) {
1289 safe_signal(SIGINT
, saveint
);
1290 safe_signal(SIGPIPE
, savepipe
);
1293 if (savepipe
!= SIG_IGN
)
1294 safe_signal(SIGPIPE
, imapcatch
);
1295 if (imap_noop1(&mb
) != OKAY
) {
1296 safe_signal(SIGINT
, saveint
);
1297 safe_signal(SIGPIPE
, savepipe
);
1300 safe_signal(SIGINT
, saveint
);
1301 safe_signal(SIGPIPE
, savepipe
);
1304 alarm(imapkeepalive
);
1310 imap_preauth(struct mailbox
*mp
, struct url
*urlp
)
1314 mp
->mb_active
|= MB_PREAUTH
;
1318 if (!mp
->mb_sock
.s_use_tls
&& xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1319 FILE *queuefp
= NULL
;
1322 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
1323 IMAP_OUT(o
, MB_COMD
, return STOP
)
1325 if(!n_tls_open(urlp
, &mp
->mb_sock
))
1329 if (xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1330 n_err(_("No TLS support compiled in\n"));
1335 imap_capability(mp
);
1340 imap_capability(struct mailbox
*mp
)
1343 FILE *queuefp
= NULL
;
1344 enum okay ok
= STOP
;
1348 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
1349 IMAP_OUT(o
, MB_COMD
, return STOP
)
1350 while (mp
->mb_active
& MB_COMD
) {
1351 ok
= imap_answer(mp
, 0);
1352 if (response_status
== RESPONSE_OTHER
&&
1353 response_other
== CAPABILITY_DATA
) {
1354 cp
= responded_other_text
;
1356 while (spacechar(*cp
))
1358 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
1360 mp
->mb_flags
|= MB_UIDPLUS
;
1361 while (*cp
&& !spacechar(*cp
))
1370 imap_auth(struct mailbox
*mp
, struct ccred
*ccred
)
1375 if (!(mp
->mb_active
& MB_PREAUTH
)) {
1380 switch (ccred
->cc_authtype
) {
1381 case AUTHTYPE_LOGIN
:
1382 rv
= imap_login(mp
, ccred
);
1385 case AUTHTYPE_CRAM_MD5
:
1386 rv
= imap_cram_md5(mp
, ccred
);
1390 case AUTHTYPE_GSSAPI
:
1391 rv
= _imap_gssapi(mp
, ccred
);
1405 imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
)
1407 char o
[LINESIZE
], *cp
;
1408 FILE *queuefp
= NULL
;
1409 enum okay rv
= STOP
;
1412 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1413 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
1415 if (response_type
!= RESPONSE_CONT
)
1418 cp
= cram_md5_string(&ccred
->cc_user
, &ccred
->cc_pass
, responded_text
);
1421 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
1422 while (mp
->mb_active
& MB_COMD
)
1423 rv
= imap_answer(mp
, 1);
1428 #endif /* HAVE_MD5 */
1431 imap_login(struct mailbox
*mp
, struct ccred
*ccred
)
1434 FILE *queuefp
= NULL
;
1435 enum okay rv
= STOP
;
1438 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
1439 tag(1), imap_quotestr(ccred
->cc_user
.s
), imap_quotestr(ccred
->cc_pass
.s
));
1440 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
1441 while (mp
->mb_active
& MB_COMD
)
1442 rv
= imap_answer(mp
, 1);
1449 # include "obs-imap-gssapi.h"
1453 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
,
1457 char const *qname
, *cp
;
1466 if((qname
= imap_path_quote(mp
, mbx
)) == NULL
)
1471 mp
->mb_uidvalidity
= 0;
1472 snprintf(o
, sizeof o
, "%s %s %s\r\n", tag(1),
1473 (fm
& FEDIT_RDONLY
? "EXAMINE" : "SELECT"), qname
);
1474 IMAP_OUT(o
, MB_COMD
, ok
= STOP
;goto jleave
)
1475 while (mp
->mb_active
& MB_COMD
) {
1476 ok
= imap_answer(mp
, 1);
1477 if (response_status
!= RESPONSE_OTHER
&&
1478 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
1479 n_idec_ui64_cp(&mp
->mb_uidvalidity
, &cp
[13], 10, NULL
);/* TODO err? */
1481 *cnt
= (had_exists
> 0) ? had_exists
: 0;
1482 if (response_status
!= RESPONSE_OTHER
&&
1483 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
1490 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
1493 FILE *queuefp
= NULL
;
1496 unsigned x
= X
, y
= Y
, n
;
1499 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
1500 IMAP_OUT(o
, MB_COMD
, return STOP
)
1501 while (mp
->mb_active
& MB_COMD
) {
1503 if (response_status
== RESPONSE_OTHER
&&
1504 response_other
== MESSAGE_DATA_FETCH
) {
1505 n
= responded_other_number
;
1513 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1518 imap_getflags(cp
, &cp
, &m
->m_flag
);
1521 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1522 n_idec_ui64_cp(&m
->m_uid
, &cp
[4], 10, NULL
);/* TODO errors? */
1523 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1524 m
->m_flag
&= ~MHIDDEN
;
1527 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1529 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1532 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1534 IMAP_OUT(o
, MB_COMD
, return STOP
)
1535 while (mp
->mb_active
& MB_COMD
) {
1537 if (response_status
== RESPONSE_OTHER
&&
1538 response_other
== MESSAGE_DATA_FETCH
) {
1539 n
= responded_other_number
;
1545 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1546 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1547 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1548 m
->m_time
= imap_read_date_time(&cp
[13]);
1553 for (n
= X
; n
<= Y
; ++n
) {
1554 putcache(mp
, &message
[n
-1]);
1562 imap_init(struct mailbox
*mp
, int n
)
1569 m
->m_flag
= MUSED
| MNOFROM
;
1576 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1578 struct message
*omessage
= 0;
1579 int i
, omsgCount
= 0;
1580 enum okay dequeued
= STOP
;
1583 if (nmail
|| transparent
) {
1585 omsgCount
= msgCount
;
1588 dequeued
= rec_dequeue();
1590 if (had_exists
>= 0) {
1591 if (dequeued
!= OKAY
)
1592 msgCount
= had_exists
;
1595 if (had_expunge
>= 0) {
1596 if (dequeued
!= OKAY
)
1597 msgCount
-= had_expunge
;
1601 if (nmail
&& expunged_messages
)
1602 printf("Expunged %ld message%s.\n", expunged_messages
,
1603 (expunged_messages
!= 1 ? "s" : ""));
1604 *prevcount
= omsgCount
- expunged_messages
;
1605 expunged_messages
= 0;
1607 fputs("IMAP error: Negative message count\n", stderr
);
1611 if (dequeued
!= OKAY
) {
1612 message
= n_calloc(msgCount
+ 1, sizeof *message
);
1613 for (i
= 0; i
< msgCount
; i
++)
1615 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1618 imap_flags(mp
, 1, msgCount
);
1619 message
[msgCount
].m_size
= 0;
1620 message
[msgCount
].m_lines
= 0;
1623 if (nmail
|| transparent
)
1624 transflags(omessage
, omsgCount
, transparent
);
1631 imap_setfile(char const * volatile who
, const char *xserver
,
1638 if (!url_parse(&url
, CPROTO_IMAP
, xserver
)) {
1642 if (!ok_blook(v15_compat
) &&
1643 (!(url
.url_flags
& n_URL_HAD_USER
) || url
.url_pass
.s
!= NULL
))
1644 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1646 _imap_rdonly
= ((fm
& FEDIT_RDONLY
) != 0);
1647 rv
= _imap_setfile1(who
, &url
, fm
, 0);
1654 _imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
, struct url
*urlp
)
1659 if (ok_blook(v15_compat
))
1660 rv
= ccred_lookup(ccredp
, urlp
);
1663 *xuhp
= ((urlp
->url_flags
& n_URL_HAD_USER
) ? urlp
->url_eu_h_p
.s
1664 : urlp
->url_u_h_p
.s
);
1666 if ((var
= mbp
->mb_imap_pass
) != NULL
) {
1667 var
= savecat("password-", xuhp
);
1668 if ((old
= n_UNCONST(n_var_vlook(var
, FAL0
))) != NULL
)
1670 n_var_vset(var
, (uintptr_t)mbp
->mb_imap_pass
);
1672 rv
= ccred_lookup_old(ccredp
, CPROTO_IMAP
, xuhp
);
1675 n_var_vset(var
, (uintptr_t)old
);
1687 _imap_setfile1(char const * volatile who
, struct url
*urlp
,
1688 enum fedit_mode
volatile fm
, int volatile transparent
)
1692 sighandler_type
volatile saveint
, savepipe
;
1695 int volatile prevcount
= 0;
1696 enum mbflags same_flags
;
1699 if (fm
& FEDIT_NEWMAIL
) {
1700 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1701 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1702 if (saveint
!= SIG_IGN
)
1703 safe_signal(SIGINT
, imapcatch
);
1704 if (savepipe
!= SIG_IGN
)
1705 safe_signal(SIGPIPE
, imapcatch
);
1710 same_flags
= mb
.mb_flags
;
1711 same_imap_account
= 0;
1712 if (mb
.mb_imap_account
!= NULL
&&
1713 (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)) {
1714 if (mb
.mb_sock
.s_fd
> 0 && mb
.mb_sock
.s_rsz
>= 0 &&
1715 !strcmp(mb
.mb_imap_account
, urlp
->url_p_eu_h_p
) &&
1716 disconnected(mb
.mb_imap_account
) == 0) {
1717 same_imap_account
= 1;
1718 if (urlp
->url_pass
.s
== NULL
&& mb
.mb_imap_pass
!= NULL
)
1721 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1722 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1723 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1726 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= savestr(mb
.mb_imap_pass
));
1730 if (!same_imap_account
&& mb
.mb_imap_pass
!= NULL
) {
1731 n_free(mb
.mb_imap_pass
);
1732 mb
.mb_imap_pass
= NULL
;
1734 if (!_imap_getcred(&mb
, &ccred
, urlp
)) {
1739 memset(&so
, 0, sizeof so
);
1741 if (!same_imap_account
) {
1742 if (!disconnected(urlp
->url_p_eu_h_p
) && !sopen(&so
, urlp
)) {
1755 if (fm
& FEDIT_SYSBOX
)
1756 n_pstate
&= ~n_PS_EDIT
;
1758 n_pstate
|= n_PS_EDIT
;
1759 if (mb
.mb_imap_account
!= NULL
)
1760 n_free(mb
.mb_imap_account
);
1761 if (mb
.mb_imap_pass
!= NULL
)
1762 n_free(mb
.mb_imap_pass
);
1763 mb
.mb_imap_account
= sstrdup(urlp
->url_p_eu_h_p
);
1764 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1765 * TODO object, and mailbox will naturally have an URL and credentials */
1766 mb
.mb_imap_pass
= sbufdup(ccred
.cc_pass
.s
, ccred
.cc_pass
.l
);
1768 if (!same_imap_account
) {
1769 if (mb
.mb_sock
.s_fd
>= 0)
1770 sclose(&mb
.mb_sock
);
1772 same_imap_account
= 0;
1783 if (mb
.mb_imap_mailbox
!= NULL
)
1784 n_free(mb
.mb_imap_mailbox
);
1785 assert(urlp
->url_path
.s
!= NULL
);
1786 imap_delim_init(&mb
, urlp
);
1787 mb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mb
, urlp
->url_path
.s
));
1788 initbox(savecatsep(urlp
->url_p_eu_h_p
,
1789 (mb
.mb_imap_delim
[0] != '\0' ? mb
.mb_imap_delim
[0] : n_IMAP_DELIM
[0]),
1790 mb
.mb_imap_mailbox
));
1792 mb
.mb_type
= MB_VOID
;
1793 mb
.mb_active
= MB_NONE
;
1796 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1797 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1798 if (sigsetjmp(imapjmp
, 1)) {
1799 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1800 sclose(&mb
.mb_sock
);
1801 safe_signal(SIGINT
, saveint
);
1802 safe_signal(SIGPIPE
, savepipe
);
1805 mb
.mb_type
= MB_VOID
;
1806 mb
.mb_active
= MB_NONE
;
1807 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1810 if (saveint
!= SIG_IGN
)
1811 safe_signal(SIGINT
, imapcatch
);
1812 if (savepipe
!= SIG_IGN
)
1813 safe_signal(SIGPIPE
, imapcatch
);
1815 if (mb
.mb_sock
.s_fd
< 0) {
1816 if (disconnected(mb
.mb_imap_account
)) {
1817 if (cache_setptr(fm
, transparent
) == STOP
)
1818 n_err(_("Mailbox \"%s\" is not cached\n"), urlp
->url_p_eu_h_p_p
);
1821 if ((cp
= xok_vlook(imap_keepalive
, urlp
, OXM_ALL
)) != NULL
) {
1822 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1823 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1824 alarm(imapkeepalive
);
1829 mb
.mb_sock
.s_desc
= "IMAP";
1830 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1831 if (imap_preauth(&mb
, urlp
) != OKAY
|| imap_auth(&mb
, &ccred
) != OKAY
) {
1832 sclose(&mb
.mb_sock
);
1834 safe_signal(SIGINT
, saveint
);
1835 safe_signal(SIGPIPE
, savepipe
);
1837 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1840 } else /* same account */
1841 mb
.mb_flags
|= same_flags
;
1843 if (n_poption
& n_PO_R_FLAG
)
1845 mb
.mb_perm
= (fm
& FEDIT_RDONLY
) ? 0 : MB_DELE
;
1846 mb
.mb_type
= MB_IMAP
;
1848 assert(urlp
->url_path
.s
!= NULL
);
1849 if (imap_select(&mb
, &mailsize
, &msgCount
, urlp
->url_path
.s
, fm
) != OKAY
) {
1850 /*sclose(&mb.mb_sock);
1852 safe_signal(SIGINT
, saveint
);
1853 safe_signal(SIGPIPE
, savepipe
);
1855 mb
.mb_type
= MB_VOID
;
1856 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1861 imap_setptr(&mb
, ((fm
& FEDIT_NEWMAIL
) != 0), transparent
,
1862 n_UNVOLATILE(&prevcount
));
1865 safe_signal(SIGINT
, saveint
);
1866 safe_signal(SIGPIPE
, savepipe
);
1869 if (!(fm
& FEDIT_NEWMAIL
) && mb
.mb_type
== MB_IMAP
)
1870 purgecache(&mb
, message
, msgCount
);
1871 if (((fm
& FEDIT_NEWMAIL
) || transparent
) && mb
.mb_sorted
) {
1876 if (!(fm
& FEDIT_NEWMAIL
) && !transparent
) {
1877 n_pstate
&= ~n_PS_SAW_COMMAND
;
1878 n_pstate
|= n_PS_SETFILE_OPENED
;
1881 if ((n_poption
& n_PO_EXISTONLY
) && (mb
.mb_type
== MB_IMAP
||
1882 mb
.mb_type
== MB_CACHE
)) {
1883 rv
= (msgCount
== 0);
1887 if (!(fm
& FEDIT_NEWMAIL
) && !(n_pstate
& n_PS_EDIT
) && msgCount
== 0) {
1888 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1889 !ok_blook(emptystart
)){
1893 intro
= who
= n_empty
;
1896 n_err(_("No mail%s%s at %s\n"), intro
, who
, urlp
->url_p_eu_h_p_p
);
1902 if (fm
& FEDIT_NEWMAIL
)
1903 newmailinfo(prevcount
);
1911 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1912 int need
, const char *head
, size_t headsize
, long headlines
)
1914 char *line
= NULL
, *lp
;
1915 size_t linesize
= 0, linelen
, size
= 0;
1916 int emptyline
= 0, lines
= 0, excess
= 0;
1920 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1921 offset
= ftell(mp
->mb_otf
);
1924 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1926 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1928 if (linelen
> expected
) {
1929 excess
= linelen
- expected
;
1933 * Need to mask 'From ' lines. This cannot be done properly
1934 * since some servers pass them as 'From ' and others as
1935 * '>From '. Although one could identify the first kind of
1936 * server in principle, it is not possible to identify the
1937 * second as '>From ' may also come from a server of the
1938 * first type as actual data. So do what is absolutely
1939 * necessary only - mask 'From '.
1941 * If the line is the first line of the message header, it
1942 * is likely a real 'From ' line. In this case, it is just
1943 * ignored since it violates all standards.
1944 * TODO can the latter *really* happen??
1947 /* Since we simply copy over data without doing any transfer
1948 * encoding reclassification/adjustment we *have* to perform
1949 * RFC 4155 compliant From_ quoting here */
1950 if (emptyline
&& is_head(lp
, linelen
, FAL0
)) {
1951 fputc('>', mp
->mb_otf
);
1955 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1957 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1958 size
+= linelen
- 1;
1963 fputc('\n', mp
->mb_otf
);
1965 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1969 if ((expected
-= linelen
) <= 0)
1973 /* TODO This is very ugly; but some IMAP daemons don't end a
1974 * TODO message with \r\n\r\n, and we need \n\n for mbox format.
1975 * TODO That is to say we do it wrong here in order to get it right
1976 * TODO when send.c stuff or with MBOX handling, even though THIS
1977 * TODO line is solely a property of the MBOX database format! */
1978 fputc('\n', mp
->mb_otf
);
1985 m
->m_size
= size
+ headsize
;
1986 m
->m_lines
= lines
+ headlines
;
1987 m
->m_block
= mailx_blockof(offset
);
1988 m
->m_offset
= mailx_offsetof(offset
);
1991 m
->m_content_info
= CI_HAVE_HEADER
;
1994 m
->m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
1995 m
->m_xlines
= m
->m_lines
;
1996 m
->m_xsize
= m
->m_size
;
2006 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
2007 const char *head
, size_t headsize
, long headlines
)
2014 fseek(mp
->mb_otf
, 0L, SEEK_END
);
2015 offset
= ftell(mp
->mb_otf
);
2017 fwrite(head
, 1, headsize
, mp
->mb_otf
);
2019 fwrite(str
, 1, len
, mp
->mb_otf
);
2020 fputc('\n', mp
->mb_otf
);
2026 m
->m_size
= headsize
+ len
;
2027 m
->m_lines
= headlines
+ 1;
2028 m
->m_block
= mailx_blockof(offset
);
2029 m
->m_offset
= mailx_offsetof(offset
);
2030 m
->m_content_info
|= CI_HAVE_HEADER
| CI_HAVE_BODY
;
2031 m
->m_xlines
= m
->m_lines
;
2032 m
->m_xsize
= m
->m_size
;
2038 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
2042 sighandler_type
volatile saveint
, savepipe
;
2043 char * volatile head
;
2044 char const *cp
, *loc
, * volatile item
, * volatile resp
;
2046 size_t volatile headsize
;
2049 long volatile headlines
;
2054 saveint
= savepipe
= SIG_IGN
;
2056 cp
= loc
= item
= resp
= NULL
;
2058 number
= (int)PTR2SIZE(m
- message
+ 1);
2063 if (getcache(mp
, m
, need
) == OKAY
)
2065 if (mp
->mb_type
== MB_CACHE
) {
2066 n_err(_("Message %lu not available\n"), (ul_i
)number
);
2070 if (mp
->mb_sock
.s_fd
< 0) {
2071 n_err(_("IMAP connection closed\n"));
2077 resp
= item
= "RFC822.HEADER";
2080 item
= "BODY.PEEK[]";
2082 if ((m
->m_content_info
& CI_HAVE_HEADER
) && m
->m_size
) {
2083 char *hdr
= n_alloc(m
->m_size
);
2085 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
2087 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
2092 headsize
= m
->m_size
;
2093 headlines
= m
->m_lines
;
2094 item
= "BODY.PEEK[TEXT]";
2095 resp
= "BODY[TEXT]";
2103 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2104 if (sigsetjmp(imapjmp
, 1)) {
2105 safe_signal(SIGINT
, saveint
);
2106 safe_signal(SIGPIPE
, savepipe
);
2110 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2111 safe_signal(SIGINT
, &_imap_maincatch
);
2112 if (savepipe
!= SIG_IGN
)
2113 safe_signal(SIGPIPE
, imapcatch
);
2116 snprintf(o
, sizeof o
, "%s UID FETCH %" PRIu64
" (%s)\r\n",
2117 tag(1), m
->m_uid
, item
);
2119 if (check_expunged() == STOP
)
2121 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
2123 IMAP_OUT(o
, MB_COMD
, goto out
)
2127 ok
= imap_answer(mp
, 1);
2130 if (response_status
!= RESPONSE_OTHER
||
2131 response_other
!= MESSAGE_DATA_FETCH
)
2133 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
2137 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
2138 n_idec_ui64_cp(&uid
, &cp
[4], 10, NULL
);/* TODO errors? */
2143 n
= responded_other_number
;
2144 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
2145 if (m
->m_uid
? m
->m_uid
!= uid
: n
!= number
)
2147 if ((cp
= strchr(loc
, '"')) != NULL
) {
2148 cp
= imap_unquotestr(cp
);
2149 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
2151 m
->m_content_info
|= CI_HAVE_HEADER
| CI_HAVE_BODY
;
2152 m
->m_xlines
= m
->m_lines
;
2153 m
->m_xsize
= m
->m_size
;
2157 expected
= atol(&cp
[1]);
2158 if (m
->m_uid
? n
== 0 && m
->m_uid
!= uid
: n
!= number
) {
2159 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
2163 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
2165 commitmsg(mp
, m
, &mt
, mt
.m_content_info
);
2168 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2169 if (n_poption
& n_PO_VERBVERB
)
2170 fputs(imapbuf
, stderr
);
2171 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2172 n_idec_ui64_cp(&uid
, &cp
[4], 10, NULL
);/* TODO errors? */
2173 if (uid
== m
->m_uid
) {
2174 commitmsg(mp
, m
, &mt
, mt
.m_content_info
);
2181 while (mp
->mb_active
& MB_COMD
)
2182 ok
= imap_answer(mp
, 1);
2184 if (saveint
!= SIG_IGN
)
2185 safe_signal(SIGINT
, saveint
);
2186 if (savepipe
!= SIG_IGN
)
2187 safe_signal(SIGPIPE
, savepipe
);
2195 n_go_onintr_for_imap();
2200 imap_header(struct message
*m
)
2205 rv
= imap_get(&mb
, m
, NEED_HEADER
);
2212 imap_body(struct message
*m
)
2217 rv
= imap_get(&mb
, m
, NEED_BODY
);
2223 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
2224 enum content_info content_info
)
2227 tomp
->m_size
= frommp
->m_size
;
2228 tomp
->m_lines
= frommp
->m_lines
;
2229 tomp
->m_block
= frommp
->m_block
;
2230 tomp
->m_offset
= frommp
->m_offset
;
2231 tomp
->m_content_info
= content_info
& CI_HAVE_MASK
;
2232 if (content_info
& CI_HAVE_BODY
) {
2233 tomp
->m_xlines
= frommp
->m_lines
;
2234 tomp
->m_xsize
= frommp
->m_size
;
2241 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
2249 FILE *queuefp
= NULL
;
2254 snprintf(o
, sizeof o
,
2255 "%s UID FETCH %" PRIu64
":%" PRIu64
" (RFC822.HEADER)\r\n",
2256 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
2258 if (check_expunged() == STOP
)
2260 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2263 IMAP_OUT(o
, MB_COMD
, return STOP
)
2267 ok
= imap_answer(mp
, 1);
2268 if (response_status
!= RESPONSE_OTHER
)
2270 if (response_other
!= MESSAGE_DATA_FETCH
)
2272 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0) {
2276 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
2278 expected
= atol(&cp
[1]);
2279 if (m
[bot
-1].m_uid
) {
2280 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
2283 n_idec_ui64_cp(&uid
, &cp
[4], 10, NULL
);/* TODO errors? */
2284 for (n
= bot
; n
<= topp
; n
++)
2285 if (uid
== m
[n
-1].m_uid
)
2288 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2294 n
= responded_other_number
;
2295 if (n
<= 0 || n
> msgCount
) {
2296 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2300 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
2301 if (n
>= 0 && !(m
[n
-1].m_content_info
& CI_HAVE_HEADER
))
2302 commitmsg(mp
, &m
[n
-1], &mt
, CI_HAVE_HEADER
);
2303 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2304 if (n_poption
& n_PO_VERBVERB
)
2305 fputs(imapbuf
, stderr
);
2306 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2309 n_idec_ui64_cp(&uid
, &cp
[4], 10, NULL
);/* TODO errors? */
2310 for (n
= bot
; n
<= topp
; n
++)
2311 if (uid
== m
[n
-1].m_uid
)
2313 if (n
<= topp
&& !(m
[n
-1].m_content_info
& CI_HAVE_HEADER
))
2314 commitmsg(mp
, &m
[n
-1], &mt
, CI_HAVE_HEADER
);
2321 while (mp
->mb_active
& MB_COMD
)
2322 ok
= imap_answer(mp
, 1);
2327 imap_getheaders(int volatile bot
, int volatile topp
) /* TODO iterator!! */
2329 sighandler_type saveint
, savepipe
;
2330 /*enum okay ok = STOP;*/
2334 if (mb
.mb_type
== MB_CACHE
)
2338 if (topp
> msgCount
)
2340 for (i
= bot
; i
< topp
; i
++) {
2341 if ((message
[i
-1].m_content_info
& CI_HAVE_HEADER
) ||
2342 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2347 for (i
= topp
; i
> bot
; i
--) {
2348 if ((message
[i
-1].m_content_info
& CI_HAVE_HEADER
) ||
2349 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2358 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2359 safe_signal(SIGINT
, &_imap_maincatch
);
2360 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2361 if (sigsetjmp(imapjmp
, 1) == 0) {
2362 if (savepipe
!= SIG_IGN
)
2363 safe_signal(SIGPIPE
, imapcatch
);
2365 for (i
= bot
; i
<= topp
; i
+= chunk
) {
2366 int j
= i
+ chunk
- 1;
2368 if (visible(message
+ j
))
2369 /*ok = */imap_fetchheaders(&mb
, message
, i
, j
);
2371 n_go_onintr_for_imap(); /* XXX imaplock? */
2374 safe_signal(SIGINT
, saveint
);
2375 safe_signal(SIGPIPE
, savepipe
);
2380 __imap_exit(struct mailbox
*mp
)
2383 FILE *queuefp
= NULL
;
2386 mp
->mb_active
|= MB_BYE
;
2387 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
2388 IMAP_OUT(o
, MB_COMD
, return STOP
)
2394 imap_exit(struct mailbox
*mp
)
2399 rv
= __imap_exit(mp
);
2400 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2401 n_free(mp
->mb_imap_pass
);
2402 n_free(mp
->mb_imap_account
);
2403 n_free(mp
->mb_imap_mailbox
);
2404 if (mp
->mb_cache_directory
!= NULL
)
2405 n_free(mp
->mb_cache_directory
);
2406 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2407 mp
->mb_imap_account
=
2408 mp
->mb_imap_mailbox
=
2409 mp
->mb_cache_directory
= "";
2411 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
2412 mp
->mb_imap_mailbox
= NULL
;
2413 mp
->mb_cache_directory
= NULL
;
2416 sclose(&mp
->mb_sock
);
2422 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
2425 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
2426 if (mp
->mb_type
== MB_IMAP
)
2433 imap_close(struct mailbox
*mp
)
2436 FILE *queuefp
= NULL
;
2439 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
2440 IMAP_OUT(o
, MB_COMD
, return STOP
)
2446 imap_update(struct mailbox
*mp
)
2449 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
2452 if (!(n_pstate
& n_PS_EDIT
) && mp
->mb_perm
!= 0) {
2455 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2456 if (m
->m_flag
& MBOX
)
2459 if (makembox() == STOP
)
2464 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
2465 if (mp
->mb_perm
== 0)
2467 else if (n_pstate
& n_PS_EDIT
)
2468 dodel
= ((m
->m_flag
& MDELETED
) != 0);
2470 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
2472 /* Fetch the result after around each 800 STORE commands
2473 * sent (approx. 32k data sent). Otherwise, servers will
2474 * try to flush the return queue at some point, leading
2475 * to a deadlock if we are still writing commands but not
2476 * reading their results */
2477 needstat
= stored
> 0 && stored
% 800 == 0;
2478 /* Even if this message has been deleted, continue
2479 * to set further flags. This is necessary to support
2480 * Gmail semantics, where "delete" actually means
2481 * "archive", and the flags are applied to the copy
2483 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2484 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
2487 if (m
->m_flag
& MFLAG
) {
2488 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
2491 if (m
->m_flag
& MUNFLAG
) {
2492 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
2495 if (m
->m_flag
& MANSWER
) {
2496 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
2499 if (m
->m_flag
& MUNANSWER
) {
2500 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
2503 if (m
->m_flag
& MDRAFT
) {
2504 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
2507 if (m
->m_flag
& MUNDRAFT
) {
2508 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
2513 imap_delete(mp
, m
-message
+1, m
, needstat
);
2516 } else if (mp
->mb_type
!= MB_CACHE
||
2517 (!(n_pstate
& n_PS_EDIT
) &&
2518 !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
2519 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
2520 (MPRESERVE
| MTOUCH
) ||
2521 ((n_pstate
& n_PS_EDIT
) && !(m
->m_flag
& MDELETED
)))
2523 if (m
->m_flag
& MNEW
) {
2525 m
->m_flag
|= MSTATUS
;
2532 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2533 if (!(m
->m_flag
& MUNLINKED
) &&
2534 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
2535 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
2540 /* XXX should be readonly (but our IMAP code is weird...) */
2541 if (!(n_poption
& (n_PO_EXISTONLY
| n_PO_HEADERSONLY
| n_PO_HEADERLIST
)) &&
2543 if ((gotcha
|| modflags
) && (n_pstate
& n_PS_EDIT
)) {
2544 printf(_("\"%s\" "), displayname
);
2545 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2546 ? _("complete\n") : _("updated.\n"));
2547 } else if (held
&& !(n_pstate
& n_PS_EDIT
)) {
2549 printf(_("Held 1 message in %s\n"), displayname
);
2551 printf(_("Held %d messages in %s\n"), held
, displayname
);
2560 imap_quit(bool_t hold_sigs_on
)
2562 sighandler_type
volatile saveint
, savepipe
;
2569 if (mb
.mb_type
== MB_CACHE
) {
2570 rv
= (imap_update(&mb
) == OKAY
);
2576 if (mb
.mb_sock
.s_fd
< 0) {
2577 n_err(_("IMAP connection closed\n"));
2582 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2583 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2584 if (sigsetjmp(imapjmp
, 1)) {
2585 safe_signal(SIGINT
, saveint
);
2586 safe_signal(SIGPIPE
, saveint
);
2590 if (saveint
!= SIG_IGN
)
2591 safe_signal(SIGINT
, imapcatch
);
2592 if (savepipe
!= SIG_IGN
)
2593 safe_signal(SIGPIPE
, imapcatch
);
2595 rv
= (imap_update(&mb
) == OKAY
);
2596 if(!same_imap_account
&& imap_exit(&mb
) != OKAY
)
2599 safe_signal(SIGINT
, saveint
);
2600 safe_signal(SIGPIPE
, savepipe
);
2610 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2614 FILE *queuefp
= NULL
;
2617 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2620 snprintf(o
, sizeof o
, "%s UID STORE %" PRIu64
" %cFLAGS (%s)\r\n",
2621 tag(1), m
->m_uid
, c
, sp
);
2623 if (check_expunged() == STOP
)
2625 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2627 IMAP_OUT(o
, MB_COMD
, return STOP
)
2631 mb
.mb_active
&= ~MB_COMD
;
2632 if (queuefp
!= NULL
)
2638 imap_undelete(struct message
*m
, int n
)
2643 rv
= imap_unstore(m
, n
, "\\Deleted");
2649 imap_unread(struct message
*m
, int n
)
2654 rv
= imap_unstore(m
, n
, "\\Seen");
2660 imap_unstore(struct message
*m
, int n
, const char *flag
)
2662 sighandler_type saveint
, savepipe
;
2663 enum okay
volatile rv
= STOP
;
2667 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2668 safe_signal(SIGINT
, &_imap_maincatch
);
2669 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2670 if (sigsetjmp(imapjmp
, 1) == 0) {
2671 if (savepipe
!= SIG_IGN
)
2672 safe_signal(SIGPIPE
, imapcatch
);
2674 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2676 safe_signal(SIGINT
, saveint
);
2677 safe_signal(SIGPIPE
, savepipe
);
2682 n_go_onintr_for_imap();
2695 snprintf(ts
, sizeof ts
, "T%lu", n
);
2701 c_imapcodec(void *vp
){
2704 char const **argv
, *varname
, *varres
, *act
, *cp
;
2708 varname
= (n_pstate
& n_PS_ARGMOD_VPUT
) ? *argv
++ : NULL
;
2711 for(cp
= act
; *cp
!= '\0' && !blankspacechar(*cp
); ++cp
)
2715 alen
= PTR2SIZE(cp
- act
);
2719 n_pstate_err_no
= n_ERR_NONE
;
2720 varres
= imap_path_normalize(NULL
, cp
);
2722 if(is_ascncaseprefix(act
, "encode", alen
))
2723 varres
= imap_path_encode(varres
, &err
);
2724 else if(is_ascncaseprefix(act
, "decode", alen
))
2725 varres
= imap_path_decode(varres
, &err
);
2730 n_pstate_err_no
= n_ERR_CANCELED
;
2735 if(varname
!= NULL
){
2736 if(!n_var_vset(varname
, (uintptr_t)varres
)){
2737 n_pstate_err_no
= n_ERR_NOTSUP
;
2743 in
.l
= strlen(in
.s
= n_UNCONST(varres
));
2744 makeprint(&in
, &out
);
2745 if(fprintf(n_stdout
, "%s\n", out
.s
) < 0){
2746 n_pstate_err_no
= n_err_no
;
2754 return (vp
!= NULL
? 0 : 1);
2756 n_err(_("Synopsis: imapcodec: <e[ncode]|d[ecode]> <rest-of-line>\n"));
2757 n_pstate_err_no
= n_ERR_INVAL
;
2763 c_imap_imap(void *vp
)
2766 sighandler_type saveint
, savepipe
;
2767 struct mailbox
*mp
= &mb
;
2768 FILE *queuefp
= NULL
;
2769 enum okay
volatile ok
= STOP
;
2772 if (mp
->mb_type
!= MB_IMAP
) {
2773 printf("Not operating on an IMAP mailbox.\n");
2777 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2778 safe_signal(SIGINT
, &_imap_maincatch
);
2779 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2780 if (sigsetjmp(imapjmp
, 1) == 0) {
2781 if (savepipe
!= SIG_IGN
)
2782 safe_signal(SIGPIPE
, imapcatch
);
2784 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2785 IMAP_OUT(o
, MB_COMD
, goto out
)
2786 while (mp
->mb_active
& MB_COMD
) {
2787 ok
= imap_answer(mp
, 0);
2788 fputs(responded_text
, stdout
);
2792 safe_signal(SIGINT
, saveint
);
2793 safe_signal(SIGPIPE
, savepipe
);
2797 n_go_onintr_for_imap();
2802 imap_newmail(int nmail
)
2806 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2812 if (had_exists
== msgCount
&& had_expunge
< 0)
2813 /* Some servers always respond with EXISTS to NOOP. If
2814 * the mailbox has been changed but the number of messages
2815 * has not, an EXPUNGE must also had been sent; otherwise,
2816 * nothing has changed */
2819 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2823 imap_putflags(int f
)
2829 bp
= buf
= n_autorec_alloc(100);
2830 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2835 for (cp
= "\\Seen"; *cp
; cp
++)
2841 for (cp
= "\\Flagged"; *cp
; cp
++)
2844 if (f
& MANSWERED
) {
2847 for (cp
= "\\Answered"; *cp
; cp
++)
2853 for (cp
= "\\Draft"; *cp
; cp
++)
2865 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2868 while (*cp
!= ')') {
2870 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2872 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2874 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2876 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2878 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2880 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2892 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2893 long xsize
, enum mflag flag
, time_t t
)
2895 char o
[LINESIZE
], *buf
;
2896 size_t bufsize
, buflen
, cnt
;
2897 long size
, lines
, ysize
;
2909 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
2912 if (mp
->mb_type
== MB_CACHE
) {
2913 queuefp
= cache_queue(mp
);
2914 if (queuefp
== NULL
) {
2921 buf
= n_alloc(bufsize
= LINESIZE
);
2926 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2931 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2932 tag(1), qname
, imap_putflags(flag
), imap_make_date_time(t
), size
);
2933 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2934 while (mp
->mb_active
& MB_COMD
) {
2935 rv
= imap_answer(mp
, twice
);
2936 if (response_type
== RESPONSE_CONT
)
2940 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2949 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2952 buf
[buflen
- 1] = '\r';
2954 if (mp
->mb_type
!= MB_CACHE
)
2955 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2957 fwrite(buf
, 1, buflen
+1, queuefp
);
2960 if (mp
->mb_type
!= MB_CACHE
)
2961 swrite(&mp
->mb_sock
, "\r\n");
2963 fputs("\r\n", queuefp
);
2964 while (mp
->mb_active
& MB_COMD
) {
2965 rv
= imap_answer(mp
, 0);
2966 if (response_status
== RESPONSE_NO
/*&&
2967 ascncasecmp(responded_text,
2968 "[TRYCREATE] ", 12) == 0*/) {
2975 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2976 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2977 while (mp
->mb_active
& MB_COMD
)
2978 rv
= imap_answer(mp
, 1);
2981 imap_created_mailbox
++;
2983 } else if (rv
!= OKAY
)
2984 n_err(_("IMAP error: %s"), responded_text
);
2985 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2986 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2989 if (queuefp
!= NULL
)
2998 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
, long offset
)
3000 char *buf
, *bp
, *lp
;
3001 size_t bufsize
, buflen
, cnt
;
3002 off_t off1
= -1, offs
;
3004 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
3010 buf
= n_alloc(bufsize
= LINESIZE
);
3013 offs
= offset
/* BSD will move due to O_APPEND! ftell(fp) */;
3017 for (flag
= MNEW
, state
= _NLSEP
;;) {
3018 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
3021 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
3022 is_head(buf
, buflen
, FAL0
))) {
3023 if (off1
!= (off_t
)-1) {
3024 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
3027 fseek(fp
, offs
+buflen
, SEEK_SET
);
3029 off1
= offs
+ buflen
;
3035 tim
= unixtime(buf
);
3041 if (buf
[0] == '\n') {
3044 } else if (state
& _INHEAD
) {
3045 if (ascncasecmp(buf
, "status", 6) == 0) {
3047 while (whitechar(*lp
))
3050 while (*++lp
!= '\0')
3059 } else if (ascncasecmp(buf
, "x-status", 8) == 0) {
3061 while (whitechar(*lp
))
3064 while (*++lp
!= '\0')
3087 imap_append(const char *xserver
, FILE *fp
, long offset
)
3089 sighandler_type
volatile saveint
, savepipe
;
3092 enum okay rv
= STOP
;
3095 if (!url_parse(&url
, CPROTO_IMAP
, xserver
))
3097 if (!ok_blook(v15_compat
) &&
3098 (!(url
.url_flags
& n_URL_HAD_USER
) || url
.url_pass
.s
!= NULL
))
3099 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3100 assert(url
.url_path
.s
!= NULL
);
3103 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3104 safe_signal(SIGINT
, &_imap_maincatch
);
3105 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3106 if (sigsetjmp(imapjmp
, 1))
3108 if (savepipe
!= SIG_IGN
)
3109 safe_signal(SIGPIPE
, imapcatch
);
3111 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
3112 !strcmp(url
.url_p_eu_h_p
, mb
.mb_imap_account
)) {
3113 rv
= imap_append0(&mb
, url
.url_path
.s
, fp
, offset
);
3117 memset(&mx
, 0, sizeof mx
);
3119 if (!_imap_getcred(&mx
, &ccred
, &url
))
3122 imap_delim_init(&mx
, &url
);
3123 mx
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mx
, url
.url_path
.s
));
3125 if (disconnected(url
.url_p_eu_h_p
) == 0) {
3126 if (!sopen(&mx
.mb_sock
, &url
))
3128 mx
.mb_sock
.s_desc
= "IMAP";
3129 mx
.mb_type
= MB_IMAP
;
3130 mx
.mb_imap_account
= n_UNCONST(url
.url_p_eu_h_p
);
3131 /* TODO the code now did
3132 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3133 * TODO though imap_mailbox is sfree()d and mbx
3134 * TODO is possibly even a constant
3135 * TODO i changed this to sstrdup() sofar, as is used
3136 * TODO somewhere else in this file for this! */
3137 if (imap_preauth(&mx
, &url
) != OKAY
||
3138 imap_auth(&mx
, &ccred
) != OKAY
) {
3139 sclose(&mx
.mb_sock
);
3142 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
, offset
);
3145 mx
.mb_imap_account
= n_UNCONST(url
.url_p_eu_h_p
);
3146 mx
.mb_type
= MB_CACHE
;
3147 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
, offset
);
3154 safe_signal(SIGINT
, saveint
);
3155 safe_signal(SIGPIPE
, savepipe
);
3160 n_go_onintr_for_imap();
3165 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
3166 struct list_item
**lend
, int level
)
3168 char o
[LINESIZE
], *cp
;
3169 struct list_item
*lp
;
3170 const char *qname
, *bp
;
3178 if((qname
= imap_path_quote(mp
, base
)) == NULL
)
3181 *list
= *lend
= NULL
;
3182 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), qname
);
3183 IMAP_OUT(o
, MB_COMD
, goto jleave
)
3184 while (mp
->mb_active
& MB_COMD
) {
3185 ok
= imap_answer(mp
, 1);
3186 if (response_status
== RESPONSE_OTHER
&&
3187 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
3188 cp
= imap_path_decode(imap_unquotestr(list_name
), NULL
);
3189 lp
= n_autorec_calloc(1, sizeof *lp
);
3191 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
3193 lp
->l_base
= *cp
? cp
: savestr(base
);
3194 lp
->l_attr
= list_attributes
;
3195 lp
->l_level
= level
+1;
3196 lp
->l_delim
= list_hierarchy_delimiter
;
3197 if (*list
&& *lend
) {
3198 (*lend
)->l_next
= lp
;
3209 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
3211 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
3218 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
3219 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
3222 if (list
== NULL
|| lend
== NULL
)
3225 for (lp
= list
; lp
; lp
= lp
->l_next
)
3226 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
3227 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
3228 cp
= n_autorec_alloc((n
= strlen(lp
->l_name
)) + 2);
3229 memcpy(cp
, lp
->l_name
, n
);
3230 cp
[n
] = lp
->l_delim
;
3232 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
3233 lp
->l_has_children
= 1;
3234 if (strcmp(cp
, lx
->l_name
) == 0)
3243 for (lp
= list
; lp
; lp
= lp
->l_next
) {
3246 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
3250 if (!(lp
->l_attr
& LIST_NOSELECT
))
3251 fprintf(fp
, "%s\n", *cp
? cp
: base
);
3252 else if (lp
->l_has_children
== 0)
3253 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
3254 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
3262 imap_folders(const char * volatile name
, int strip
)
3264 sighandler_type saveint
, savepipe
;
3265 const char * volatile fold
, *cp
, *sp
;
3270 cp
= protbase(name
);
3271 sp
= mb
.mb_imap_account
;
3272 if (sp
== NULL
|| strcmp(cp
, sp
)) {
3274 _("Cannot perform `folders' but when on the very IMAP "
3275 "account; the current one is\n `%s' -- "
3276 "try `folders @'\n"),
3277 (sp
!= NULL
? sp
: _("[NONE]")));
3281 fold
= imap_fileof(name
);
3282 if (n_psonce
& n_PSO_TTYOUT
) {
3283 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
))
3285 n_perr(_("tmpfile"), 0);
3292 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3293 safe_signal(SIGINT
, &_imap_maincatch
);
3294 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3295 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
3297 if (savepipe
!= SIG_IGN
)
3298 safe_signal(SIGPIPE
, imapcatch
);
3300 if (mb
.mb_type
== MB_CACHE
)
3301 cache_list(&mb
, fold
, strip
, fp
);
3303 imap_list(&mb
, fold
, strip
, fp
);
3307 if (n_psonce
& n_PSO_TTYOUT
)
3314 if (n_psonce
& n_PSO_TTYOUT
) {
3317 page_or_print(fp
, 0);
3320 n_err(_("Folder not found\n"));
3324 safe_signal(SIGINT
, saveint
);
3325 safe_signal(SIGPIPE
, savepipe
);
3326 if (n_psonce
& n_PSO_TTYOUT
)
3331 n_go_onintr_for_imap();
3336 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
3340 bool_t twice
, stored
;
3347 twice
= stored
= FAL0
;
3352 i
= strlen(name
= imap_fileof(name
));
3353 if(i
== 0 || (i
> 0 && name
[i
- 1] == '/'))
3354 name
= savecat(name
, "INBOX");
3355 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
3359 if (mp
->mb_type
== MB_CACHE
) {
3360 if ((queuefp
= cache_queue(mp
)) == NULL
)
3365 /* Since it is not possible to set flags on the copy, recently
3366 * set flags must be set on the original to include it in the copy */
3367 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
3368 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
3369 if (m
->m_flag
&MFLAG
)
3370 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3371 if (m
->m_flag
&MUNFLAG
)
3372 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3373 if (m
->m_flag
&MANSWER
)
3374 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3375 if (m
->m_flag
&MUNANSWER
)
3376 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3377 if (m
->m_flag
&MDRAFT
)
3378 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3379 if (m
->m_flag
&MUNDRAFT
)
3380 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3383 snprintf(o
, sizeof o
, "%s UID COPY %" PRIu64
" %s\r\n",
3384 tag(1), m
->m_uid
, qname
);
3386 if (check_expunged() == STOP
)
3388 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
3390 IMAP_OUT(o
, MB_COMD
, goto out
)
3391 while (mp
->mb_active
& MB_COMD
)
3392 ok
= imap_answer(mp
, twice
);
3394 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
3395 response_status
== RESPONSE_OK
)
3396 imap_copyuid(mp
, m
, name
);
3398 if (response_status
== RESPONSE_NO
&& !twice
) {
3399 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
3400 IMAP_OUT(o
, MB_COMD
, goto out
)
3401 while (mp
->mb_active
& MB_COMD
)
3402 ok
= imap_answer(mp
, 1);
3404 imap_created_mailbox
++;
3409 if (queuefp
!= NULL
)
3412 /* ... and reset the flag to its initial value so that the 'exit'
3413 * command still leaves the message unread */
3415 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
3416 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
3419 if (m
->m_flag
& MFLAG
) {
3420 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3423 if (m
->m_flag
& MUNFLAG
) {
3424 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3427 if (m
->m_flag
& MANSWER
) {
3428 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
3431 if (m
->m_flag
& MUNANSWER
) {
3432 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3435 if (m
->m_flag
& MDRAFT
) {
3436 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3439 if (m
->m_flag
& MUNDRAFT
) {
3440 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3444 mp
->mb_active
|= MB_COMD
;
3445 (void)imap_finish(mp
);
3452 imap_copy(struct message
*m
, int n
, const char *name
)
3454 sighandler_type saveint
, savepipe
;
3455 enum okay
volatile rv
= STOP
;
3459 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3460 safe_signal(SIGINT
, &_imap_maincatch
);
3461 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3462 if (sigsetjmp(imapjmp
, 1) == 0) {
3463 if (savepipe
!= SIG_IGN
)
3464 safe_signal(SIGPIPE
, imapcatch
);
3466 rv
= imap_copy1(&mb
, m
, n
, name
);
3468 safe_signal(SIGINT
, saveint
);
3469 safe_signal(SIGPIPE
, savepipe
);
3474 n_go_onintr_for_imap();
3479 imap_copyuid_parse(const char *cp
, ui64_t
*uidvalidity
, ui64_t
*olduid
,
3482 char const *xp
, *yp
, *zp
;
3486 n_idec_ui64_cp(uidvalidity
, cp
, 10, &xp
); /* TODO errors */
3487 n_idec_ui64_cp(olduid
, xp
, 10, &yp
); /* TODO errors */
3488 n_idec_ui64_cp(newuid
, yp
, 10, &zp
); /* TODO errors */
3489 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
3490 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
3496 imap_appenduid_parse(const char *cp
, ui64_t
*uidvalidity
, ui64_t
*uid
)
3498 char const *xp
, *yp
;
3502 n_idec_ui64_cp(uidvalidity
, cp
, 10, &xp
); /* TODO errors */
3503 n_idec_ui64_cp(uid
, xp
, 10, &yp
); /* TODO errors */
3504 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
3511 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
3516 ui64_t uidvalidity
, olduid
, newuid
;
3522 memset(&xmb
, 0, sizeof xmb
);
3524 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
3525 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
3531 xmb
.mb_cache_directory
= NULL
;
3532 xmb
.mb_imap_account
= sstrdup(mp
->mb_imap_account
);
3533 xmb
.mb_imap_pass
= sstrdup(mp
->mb_imap_pass
);
3534 memcpy(&xmb
.mb_imap_delim
[0], &mp
->mb_imap_delim
[0],
3535 sizeof(xmb
.mb_imap_delim
));
3536 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3537 if (mp
->mb_cache_directory
!= NULL
)
3538 xmb
.mb_cache_directory
= sstrdup(mp
->mb_cache_directory
);
3539 xmb
.mb_uidvalidity
= uidvalidity
;
3543 memset(&xm
, 0, sizeof xm
);
3545 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
3547 getcache(mp
, &xm
, NEED_HEADER
);
3548 getcache(mp
, &xm
, NEED_BODY
);
3550 if ((m
->m_content_info
& CI_HAVE_HEADER
) == 0)
3551 getcache(mp
, m
, NEED_HEADER
);
3552 if ((m
->m_content_info
& CI_HAVE_BODY
) == 0)
3553 getcache(mp
, m
, NEED_BODY
);
3557 xm
.m_flag
&= ~MFULLYCACHED
;
3558 putcache(&xmb
, &xm
);
3560 if (xmb
.mb_cache_directory
!= NULL
)
3561 n_free(xmb
.mb_cache_directory
);
3562 if (xmb
.mb_imap_mailbox
!= NULL
)
3563 n_free(xmb
.mb_imap_mailbox
);
3564 if (xmb
.mb_imap_pass
!= NULL
)
3565 n_free(xmb
.mb_imap_pass
);
3566 if (xmb
.mb_imap_account
!= NULL
)
3567 n_free(xmb
.mb_imap_account
);
3573 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
3574 long size
, long lines
, int flag
, const char *name
)
3579 ui64_t uidvalidity
, uid
;
3585 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
3586 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
3592 xmb
.mb_cache_directory
= NULL
;
3593 /* XXX mb_imap_delim reused */
3594 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3595 xmb
.mb_uidvalidity
= uidvalidity
;
3596 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
3598 memset(&xm
, 0, sizeof xm
);
3599 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
3601 xm
.m_block
= mailx_blockof(off1
);
3602 xm
.m_offset
= mailx_offsetof(off1
);
3605 xm
.m_lines
= xm
.m_xlines
= lines
;
3607 xm
.m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
3608 putcache(&xmb
, &xm
);
3610 n_free(xmb
.mb_imap_mailbox
);
3617 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
3621 long size
, xsize
, ysize
, lines
;
3622 enum mflag flag
= MNEW
;
3623 char *name
, *buf
, *bp
;
3625 size_t bufsize
, buflen
, cnt
;
3626 enum okay rv
= STOP
;
3629 buf
= n_alloc(bufsize
= LINESIZE
);
3632 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3635 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3640 if ((cp
= strrchr(bp
, '{')) == NULL
)
3643 xsize
= atol(&cp
[1]) + 2;
3644 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3650 imap_getflags(cp
, &cp
, &flag
);
3651 while (*++cp
== ' ')
3654 t
= imap_read_date_time(cp
);
3656 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
))
3663 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3666 buf
[--buflen
] = '\0';
3667 buf
[buflen
-1] = '\n';
3668 fwrite(buf
, 1, buflen
, tp
);
3675 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3676 imap_unquotestr(name
));
3686 #ifdef HAVE_IMAP_SEARCH
3688 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3693 FILE *queuefp
= NULL
;
3695 const char *cp
, *xp
;
3696 enum okay ok
= STOP
;
3700 for (cp
= spec
; *cp
; cp
++)
3703 cp
= ok_vlook(ttycharset
);
3705 if(asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")){ /* XXX */
3708 if((nspec
= n_iconv_onetime_cp(n_ICONV_DEFAULT
, "utf-8", cp
, spec
)
3715 cp
= imap_quotestr(cp
);
3716 cs
= n_lofi_alloc(n
= strlen(cp
) + 10);
3717 snprintf(cs
, n
, "CHARSET %s ", cp
);
3719 cs
= n_UNCONST(n_empty
);
3721 o
= n_lofi_alloc(n
= strlen(spec
) + 60);
3722 snprintf(o
, n
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3723 IMAP_OUT(o
, MB_COMD
, goto out
)
3724 while (mp
->mb_active
& MB_COMD
) {
3725 ok
= imap_answer(mp
, 0);
3726 if (response_status
== RESPONSE_OTHER
&&
3727 response_other
== MAILBOX_DATA_SEARCH
) {
3728 xp
= responded_other_text
;
3729 while (*xp
&& *xp
!= '\r') {
3732 n_idec_ui64_cp(&uid
, xp
, 10, &xp
);/* TODO errors? */
3733 for (i
= 0; i
< cnt
; i
++)
3734 if (m
[i
].m_uid
== uid
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3735 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3748 imap_search1(const char * volatile spec
, int f
)
3750 sighandler_type saveint
, savepipe
;
3751 enum okay
volatile rv
= STOP
;
3754 if (mb
.mb_type
!= MB_IMAP
)
3758 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3759 safe_signal(SIGINT
, &_imap_maincatch
);
3760 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3761 if (sigsetjmp(imapjmp
, 1) == 0) {
3762 if (savepipe
!= SIG_IGN
)
3763 safe_signal(SIGPIPE
, imapcatch
);
3765 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3767 safe_signal(SIGINT
, saveint
);
3768 safe_signal(SIGPIPE
, savepipe
);
3773 n_go_onintr_for_imap();
3776 #endif /* HAVE_IMAP_SEARCH */
3779 imap_thisaccount(const char *cp
)
3784 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3786 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3787 mb
.mb_imap_account
== NULL
)
3790 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3796 imap_remove(const char * volatile name
)
3798 sighandler_type
volatile saveint
, savepipe
;
3799 enum okay
volatile rv
= STOP
;
3802 if (mb
.mb_type
!= MB_IMAP
) {
3803 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name
);
3807 if (!imap_thisaccount(name
)) {
3808 n_err(_("Can only remove mailboxes on current IMAP server: "
3809 "\"%s\" not removed\n"), name
);
3814 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3815 safe_signal(SIGINT
, &_imap_maincatch
);
3816 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3817 if (sigsetjmp(imapjmp
, 1) == 0) {
3818 if (savepipe
!= SIG_IGN
)
3819 safe_signal(SIGPIPE
, imapcatch
);
3821 rv
= imap_remove1(&mb
, imap_fileof(name
));
3823 safe_signal(SIGINT
, saveint
);
3824 safe_signal(SIGPIPE
, savepipe
);
3828 rv
= cache_remove(name
);
3832 n_go_onintr_for_imap();
3837 imap_remove1(struct mailbox
*mp
, const char *name
)
3849 if((qname
= imap_path_quote(mp
, name
)) != NULL
){
3850 o
= n_lofi_alloc(os
= strlen(qname
) + 100);
3851 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), qname
);
3852 IMAP_OUT(o
, MB_COMD
, goto out
)
3853 while (mp
->mb_active
& MB_COMD
)
3854 ok
= imap_answer(mp
, 1);
3862 imap_rename(const char *old
, const char *new)
3864 sighandler_type saveint
, savepipe
;
3865 enum okay
volatile rv
= STOP
;
3868 if (mb
.mb_type
!= MB_IMAP
) {
3869 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3873 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3874 n_err(_("Can only rename mailboxes on current IMAP "
3875 "server: \"%s\" not renamed to \"%s\"\n"), old
, new);
3880 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3881 safe_signal(SIGINT
, &_imap_maincatch
);
3882 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3883 if (sigsetjmp(imapjmp
, 1) == 0) {
3884 if (savepipe
!= SIG_IGN
)
3885 safe_signal(SIGPIPE
, imapcatch
);
3887 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3889 safe_signal(SIGINT
, saveint
);
3890 safe_signal(SIGPIPE
, savepipe
);
3894 rv
= cache_rename(old
, new);
3898 n_go_onintr_for_imap();
3903 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3907 char const *qoname
, *qnname
;
3915 if((qoname
= imap_path_quote(mp
, old
)) != NULL
&&
3916 (qnname
= imap_path_quote(mp
, new)) != NULL
){
3917 o
= n_lofi_alloc(os
= strlen(qoname
) + strlen(qnname
) + 100);
3918 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), qoname
, qnname
);
3919 IMAP_OUT(o
, MB_COMD
, goto out
)
3920 while (mp
->mb_active
& MB_COMD
)
3921 ok
= imap_answer(mp
, 1);
3929 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3931 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3932 size_t bufsize
, buflen
, cnt
;
3933 long offs
, offs1
, offs2
, octets
;
3934 int twice
, gotcha
= 0;
3935 FILE *queuefp
= NULL
;
3936 enum okay ok
= OKAY
, rok
= OKAY
;
3939 buf
= n_alloc(bufsize
= LINESIZE
);
3942 while ((offs1
= ftell(fp
)) >= 0 &&
3943 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3944 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3949 if ((offs
= ftell(fp
)) < 0)
3952 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3953 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3955 while (digitchar(*cp
))
3961 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3963 IMAP_OUT(o
, MB_COMD
, continue)
3964 while (mp
->mb_active
& MB_COMD
)
3965 ok
= imap_answer(mp
, twice
);
3966 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3968 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3969 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3971 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3972 IMAP_OUT(o
, MB_COMD
, continue)
3973 while (mp
->mb_active
& MB_COMD
)
3974 ok
= imap_answer(mp
, 1);
3977 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3978 if ((cp
= strrchr(bp
, '{')) == NULL
)
3980 octets
= atol(&cp
[1]) + 2;
3981 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3983 IMAP_OUT(o
, MB_COMD
, continue)
3984 while (mp
->mb_active
& MB_COMD
) {
3985 ok
= imap_answer(mp
, twice
);
3986 if (response_type
== RESPONSE_CONT
)
3990 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3994 while (octets
> 0) {
3995 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3996 ? sizeof iob
: (size_t)octets
);
3998 if (n
!= fread(iob
, 1, n
, fp
))
4000 swrite1(&mp
->mb_sock
, iob
, n
, 1);
4002 swrite(&mp
->mb_sock
, "");
4003 while (mp
->mb_active
& MB_COMD
) {
4004 ok
= imap_answer(mp
, 0);
4005 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
4006 if (fseek(fp
, offs
, SEEK_SET
) < 0)
4011 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
4012 if ((offs2
= ftell(fp
)) < 0)
4014 fseek(fp
, offs1
, SEEK_SET
);
4015 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
4016 (void)fseek(fp
, offs2
, SEEK_SET
);
4022 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp
);
4027 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
4028 IMAP_OUT(o
, MB_COMD
, continue)
4029 while (mp
->mb_active
& MB_COMD
)
4030 ok
= imap_answer(mp
, 1);
4036 ftruncate(fileno(fp
), 0);
4044 imap_strex(char const *cp
, char const **xp
)
4053 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
4056 else if (*cq
== '"')
4062 n
= n_autorec_alloc(cq
- cp
+ 2);
4063 memcpy(n
, cp
, cq
- cp
+1);
4064 n
[cq
- cp
+ 1] = '\0';
4073 check_expunged(void)
4078 if (expunged_messages
> 0) {
4079 n_err(_("Command not executed - messages have been expunged\n"));
4088 c_connect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4091 int rv
, omsgCount
= msgCount
;
4095 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
4096 n_err(_("Already connected\n"));
4101 if (!url_parse(&url
, CPROTO_IMAP
, mailname
)) {
4105 ok_bclear(disconnected
);
4106 n_var_vclear(savecat("disconnected-", url
.url_u_h_p
.s
));
4108 if (mb
.mb_type
== MB_CACHE
) {
4109 enum fedit_mode fm
= FEDIT_NONE
;
4112 if (!(n_pstate
& n_PS_EDIT
))
4114 _imap_setfile1(NULL
, &url
, fm
, 1);
4115 if (msgCount
> omsgCount
)
4116 newmailinfo(omsgCount
);
4125 c_disconnect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4128 int rv
= 1, *msgvec
= vp
;
4131 if (mb
.mb_type
== MB_CACHE
) {
4132 n_err(_("Not connected\n"));
4135 if (mb
.mb_type
!= MB_IMAP
|| cached_uidvalidity(&mb
) == 0) {
4136 n_err(_("The current mailbox is not cached\n"));
4140 if (!url_parse(&url
, CPROTO_IMAP
, mailname
))
4145 ok_bset(disconnected
);
4146 if (mb
.mb_type
== MB_IMAP
) {
4147 enum fedit_mode fm
= FEDIT_NONE
;
4150 if (!(n_pstate
& n_PS_EDIT
))
4152 sclose(&mb
.mb_sock
);
4153 _imap_setfile1(NULL
, &url
, fm
, 1);
4164 int rv
= 1, *msgvec
= vp
, *ip
;
4168 if (mb
.mb_type
!= MB_IMAP
) {
4169 n_err(_("Not connected to an IMAP server\n"));
4172 if (cached_uidvalidity(&mb
) == 0) {
4173 n_err(_("The current mailbox is not cached\n"));
4178 for (ip
= msgvec
; *ip
; ++ip
) {
4179 mp
= &message
[*ip
- 1];
4180 if (!(mp
->m_content_info
& CI_HAVE_BODY
)) {
4193 disconnected(const char *file
)
4199 if (ok_blook(disconnected
)) {
4204 if (!url_parse(&url
, CPROTO_IMAP
, file
)) {
4208 rv
= (n_var_vlook(savecat("disconnected-", url
.url_u_h_p
.s
), FAL0
) != NULL
);
4216 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
4218 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
4226 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
4227 PTRCMP(nmp
, <, message
+ msgCount
)) {
4228 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
4230 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
4232 if (omp
->m_uid
== nmp
->m_uid
) {
4233 hf
= nmp
->m_flag
& MHIDDEN
;
4234 if (transparent
&& mb
.mb_type
== MB_IMAP
)
4235 omp
->m_flag
&= ~MHIDDEN
;
4237 if (transparent
&& mb
.mb_type
== MB_CACHE
)
4238 nmp
[-1].m_flag
|= hf
;
4239 } else if (omp
->m_uid
< nmp
->m_uid
)
4246 prevdot
= newprevdot
;
4252 imap_read_date_time(const char *cp
)
4256 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
4259 /* "25-Jul-2004 15:33:44 +0200"
4261 * 0 5 10 15 20 25 */
4262 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
4264 day
= strtol(&cp
[1], NULL
, 10);
4266 if (ascncasecmp(&cp
[4], n_month_names
[i
], 3) == 0)
4268 if (n_month_names
[++i
][0] == '\0')
4272 year
= strtol(&cp
[8], NULL
, 10);
4273 hour
= strtol(&cp
[13], NULL
, 10);
4274 minute
= strtol(&cp
[16], NULL
, 10);
4275 second
= strtol(&cp
[19], NULL
, 10);
4276 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
4290 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
4293 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
4303 imap_make_date_time(time_t t
)
4307 si32_t y
, md
, th
, tm
, ts
;
4309 int tzdiff
, tzdiff_hour
, tzdiff_min
;
4314 if((t2
= mktime(gmtime(&t
))) == (time_t)-1){
4319 if((tmp
= localtime(&t
)) == NULL
){
4324 tzdiff_hour
= (int)(tzdiff
/ 60);
4325 tzdiff_min
= tzdiff_hour
% 60;
4327 if (tmp
->tm_isdst
> 0)
4330 if(n_UNLIKELY((y
= tmp
->tm_year
) < 0 || y
>= 9999/*SI32_MAX*/ - 1900)){
4332 mn
= n_month_names
[0];
4337 mn
= (tmp
->tm_mon
>= 0 && tmp
->tm_mon
<= 11)
4338 ? n_month_names
[tmp
->tm_mon
] : n_qm
;
4340 if((md
= tmp
->tm_mday
) < 1 || md
> 31)
4343 if((th
= tmp
->tm_hour
) < 0 || th
> 23)
4345 if((tm
= tmp
->tm_min
) < 0 || tm
> 59)
4347 if((ts
= tmp
->tm_sec
) < 0 || ts
> 60)
4351 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4352 md
, mn
, y
, th
, tm
, ts
, tzdiff_hour
, tzdiff_min
);
4358 (protbase
)(char const *cp n_MEMORY_DEBUG_ARGS
)
4363 np
= n
= (n_autorec_alloc_from_pool
)(NULL
, strlen(cp
) +1
4364 n_MEMORY_DEBUG_ARGSCALL
);
4366 /* Just ignore the `is-system-mailbox' prefix XXX */
4367 if (cp
[0] == '%' && cp
[1] == ':')
4370 while (*cp
!= '\0') {
4371 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
4375 } else if (cp
[0] == '/')
4384 #endif /* HAVE_IMAP */