1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
3 *@ CRAM-MD5 as of RFC 2195.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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 #ifndef HAVE_AMALGAMATION
46 # include <sys/socket.h>
50 # include <netinet/in.h>
52 # ifdef HAVE_ARPA_INET_H
53 # include <arpa/inet.h>
58 #define IMAP_ANSWER() \
60 if (mp->mb_type != MB_CACHE) {\
62 while (mp->mb_active & MB_COMD)\
63 ok = imap_answer(mp, 1);\
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
82 if (options & OPT_VERBVERB)\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
94 static struct record
{
95 struct record
*rec_next
;
96 unsigned long rec_count
;
121 static char *responded_tag
;
122 static char *responded_text
;
123 static char *responded_other_text
;
124 static long responded_other_number
;
130 MAILBOX_DATA_MAILBOX
,
135 MESSAGE_DATA_EXPUNGE
,
138 RESPONSE_OTHER_UNKNOWN
141 static enum list_attributes
{
143 LIST_NOINFERIORS
= 001,
149 static int list_hierarchy_delimiter
;
150 static char *list_name
;
153 struct list_item
*l_next
;
156 enum list_attributes l_attr
;
162 static char *imapbuf
; /* TODO not static, use pool */
163 static size_t imapbufsize
;
164 static sigjmp_buf imapjmp
;
165 static sighandler_type savealrm
;
166 static int imapkeepalive
;
167 static long had_exists
= -1;
168 static long had_expunge
= -1;
169 static long expunged_messages
;
170 static int volatile imaplock
;
171 static int same_imap_account
;
172 static bool_t _imap_rdonly
;
174 static void imap_delim_init(struct mailbox
*mp
, struct url
const *urlp
);
175 static char const *imap_path_normalize(struct mailbox
*mp
, char const *cp
);
176 /* Returns NULL on error */
177 static char *imap_path_quote(struct mailbox
*mp
, char const *cp
);
178 static void imap_other_get(char *pp
);
179 static void imap_response_get(const char **cp
);
180 static void imap_response_parse(void);
181 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
182 static enum okay
imap_parse_list(void);
183 static enum okay
imap_finish(struct mailbox
*mp
);
184 static void imap_timer_off(void);
185 static void imapcatch(int s
);
186 static void _imap_maincatch(int s
);
187 static enum okay
imap_noop1(struct mailbox
*mp
);
188 static void rec_queue(enum rec_type type
, unsigned long cnt
);
189 static enum okay
rec_dequeue(void);
190 static void rec_rmqueue(void);
191 static void imapalarm(int s
);
192 static enum okay
imap_preauth(struct mailbox
*mp
, struct url
const *urlp
);
193 static enum okay
imap_capability(struct mailbox
*mp
);
194 static enum okay
imap_auth(struct mailbox
*mp
, struct ccred
*ccred
);
196 static enum okay
imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
);
198 static enum okay
imap_login(struct mailbox
*mp
, struct ccred
*ccred
);
200 static enum okay
_imap_gssapi(struct mailbox
*mp
, struct ccred
*ccred
);
202 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
203 static void imap_init(struct mailbox
*mp
, int n
);
204 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
206 static bool_t
_imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
,
208 static int _imap_setfile1(struct url
*urlp
, enum fedit_mode fm
,
210 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
211 size_t expected
, int need
, const char *head
,
212 size_t headsize
, long headlines
);
213 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
214 const char *str
, const char *head
, size_t headsize
,
216 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
218 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
219 struct message
*from
, enum havespec have
);
220 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
222 static enum okay
imap_exit(struct mailbox
*mp
);
223 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
,
225 static enum okay
imap_close(struct mailbox
*mp
);
226 static enum okay
imap_update(struct mailbox
*mp
);
227 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
228 int c
, const char *sp
, int needstat
);
229 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
230 static const char *tag(int new);
231 static char * imap_putflags(int f
);
232 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
233 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
234 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
235 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
236 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
237 struct list_item
**list
, struct list_item
**lend
,
239 static enum okay
imap_list(struct mailbox
*mp
, const char *base
, int strip
,
241 static void dopr(FILE *fp
);
242 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
244 static enum okay
imap_copyuid_parse(const char *cp
,
245 unsigned long *uidvalidity
, unsigned long *olduid
,
246 unsigned long *newuid
);
247 static enum okay
imap_appenduid_parse(const char *cp
,
248 unsigned long *uidvalidity
, unsigned long *uid
);
249 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
251 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
252 long off1
, long xsize
, long size
, long lines
, int flag
,
254 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
255 #ifdef HAVE_IMAP_SEARCH
256 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
,
257 const char *spec
, int f
);
259 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
260 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
262 static char * imap_strex(char const *cp
, char const **xp
);
263 static enum okay
check_expunged(void);
266 imap_delim_init(struct mailbox
*mp
, struct url
const *urlp
){
271 mp
->mb_imap_delim
[0] = '\0';
273 if((cp
= xok_vlook(imap_delim
, urlp
, OXM_ALL
)) != NULL
){
278 i
= sizeof(n_IMAP_DELIM
) -1;
282 if(i
< NELEM(mp
->mb_imap_delim
))
284 memcpy(&mb
.mb_imap_delim
[0], cp
, i
+1);
286 n_err(_("*imap-delim* for %s is too long: %s\n"),
287 urlp
->url_input
, cp
);
293 imap_path_normalize(struct mailbox
*mp
, char const *cp
){
294 char *rv_base
, *rv
, dc2
, dc
, c
, lc
;
298 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
299 * exactly what i have specified" */
300 dcp
= (mp
== NULL
) ? &n_IMAP_DELIM
[0] : &mp
->mb_imap_delim
[0];
301 dc2
= ((dc
= *dcp
) != '\0') ? *++dcp
: dc
;
303 /* Plain names don't need path quoting */
308 for(cpx
= cp
;; ++cpx
)
309 if((c
= *cpx
) == '\0')
312 if(strchr(n_IMAP_DELIM
, c
)){
318 else if(dc2
&& strchr(dcp
, c
) != NULL
)
321 /* And we don't need to reevaluate what we have seen yet */
322 i
= PTR2SIZE(cpx
- cp
);
323 rv
= rv_base
= salloc(i
+ (j
= strlen(cpx
) +1));
326 memcpy(&rv
[i
], cpx
, j
);
331 /* Squeeze adjacent delimiters, convert remain to dc */
332 for(lc
= '\0'; (c
= *cp
++) != '\0'; lc
= c
){
333 if(c
== dc
|| (dc2
&& strchr(dcp
, c
) != NULL
))
335 if(c
!= dc
|| lc
!= dc
)
347 imap_path_encode(char const *cp
, bool_t
*err_or_null
){
348 /* To a large extend inspired by dovecot(1) */
351 ui8_t
*be16p_base
, *be16p
;
357 if(err_or_null
== NULL
)
358 err_or_null
= &err_def
;
361 /* Is this a string that works out as "plain US-ASCII"? */
363 if((c
= cp
[l
]) == '\0')
365 else if(c
<= 0x1F || c
>= 0x7F || c
== '&')
370 /* We need to encode in mUTF-7! For that, we first have to convert the
371 * local charset to UTF-8, then convert all characters which need to be
372 * encoded (except plain "&") to UTF-16BE first, then that to mUTF-7.
373 * We can skip the UTF-8 conversion occasionally, however */
374 if(!(options
& OPT_UNICODE
)){
378 emsg
= N_("iconv(3) from locale charset to UTF-8 failed");
380 if((icd
= iconv_open("utf-8", charset_get_lc())) == (iconv_t
)-1)
383 out
.s
= NULL
, out
.l
= 0;
384 in
.s
= UNCONST(cp
); /* logical */
387 if((ir
= n_iconv_str(icd
, &out
, &in
, NULL
, FAL0
)) == 0)
388 cp
= savestrbuf(out
.s
, out
.l
);
398 * So: Why not start all over again?
401 /* Is this a string that works out as "plain US-ASCII"? */
403 if((c
= cp
[l
]) == '\0')
405 else if(c
<= 0x1F || c
>= 0x7F || c
== '&')
409 /* We need to encode, save what we have, encode the rest */
412 for(cp
+= l
, l
= 0; cp
[l
] != '\0'; ++l
)
414 be16p_base
= salloc((l
<< 1) +1); /* XXX use n_string, resize */
416 out
.s
= salloc(l_plain
+ (l
<< 2) +1); /* XXX use n_string, resize */
418 memcpy(out
.s
, &cp
[-l_plain
], out
.l
= l_plain
);
421 DBG( l_plain
+= (l
<< 2); )
428 out
.s
[out
.l
+ 0] = '&';
429 out
.s
[out
.l
+ 1] = '-';
431 }else if(c
> 0x1F && c
< 0x7F)
434 static char const mb64ct
[] =
435 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
438 /* Convert consecutive non-representables */
439 emsg
= N_("Invalid UTF-8 sequence, cannot convert to UTF-32");
441 for(be16p
= be16p_base
, --cp
, ++l
;;){
442 if((utf32
= n_utf8_to_utf32(&cp
, &l
)) == UI32_MAX
)
445 /* TODO S-CText: magic utf16 conversions */
447 be16p
[1] = utf32
& 0xFF;
448 be16p
[0] = (utf32
>>= 8, utf32
&= 0xFF);
453 s7e
= 0xD800u
| ((utf32
- 0x10000) >> 10);
454 be16p
[1] = s7e
& 0xFF;
455 be16p
[0] = (s7e
>>= 8, s7e
&= 0xFF);
456 s7e
= 0xDC00u
| ((utf32
- 0x10000) & 0x03FF);
457 be16p
[3] = s7e
& 0xFF;
458 be16p
[2] = (s7e
>>= 8, s7e
&= 0xFF);
464 if((c
= *cp
) > 0x1F && c
< 0x7F)
468 /* And then warp that UTF-16BE to mUTF-7 */
469 out
.s
[out
.l
++] = '&';
470 utf32
= (ui32_t
)PTR2SIZE(be16p
- be16p_base
);
473 for(; utf32
>= 3; be16p
+= 3, utf32
-= 3){
474 out
.s
[out
.l
+0] = mb64ct
[ be16p
[0] >> 2 ];
475 out
.s
[out
.l
+1] = mb64ct
[((be16p
[0] & 0x03) << 4) | (be16p
[1] >> 4)];
476 out
.s
[out
.l
+2] = mb64ct
[((be16p
[1] & 0x0F) << 2) | (be16p
[2] >> 6)];
477 out
.s
[out
.l
+3] = mb64ct
[ be16p
[2] & 0x3F];
481 out
.s
[out
.l
+ 0] = mb64ct
[be16p
[0] >> 2];
483 out
.s
[out
.l
+ 1] = mb64ct
[ (be16p
[0] & 0x03) << 4];
486 out
.s
[out
.l
+ 1] = mb64ct
[((be16p
[0] & 0x03) << 4) |
488 out
.s
[out
.l
+ 2] = mb64ct
[ (be16p
[1] & 0x0F) << 2];
492 out
.s
[out
.l
++] = '-';
496 assert(out
.l
<= l_plain
);
503 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp
, V_(emsg
));
508 imap_path_decode(char const *path
, bool_t
*err_or_null
){
509 /* To a large extend inspired by dovecot(1) TODO use string */
512 ui8_t
*mb64p_base
, *mb64p
, *mb64xp
;
513 char const *emsg
, *cp
;
514 char *rv_base
, *rv
, c
;
518 if(err_or_null
== NULL
)
519 err_or_null
= &err_def
;
522 l
= l_orig
= strlen(path
);
523 rv
= rv_base
= savestrbuf(path
, l
<< 1);
525 /* xxx Don't check for invalid characters from malicious servers */
526 if(l
== 0 || (cp
= memchr(path
, '&', l
)) == NULL
)
531 emsg
= N_("Invalid mUTF-7 encoding");
532 i
= PTR2SIZE(cp
- path
);
538 if((c
= *cp
) != '&'){
539 if(c
<= 0x1F || c
>= 0x7F){
540 emsg
= N_("Invalid mUTF-7: unencoded control or 8-bit byte");
548 else if(*++cp
== '-'){
554 emsg
= N_("Invalid mUTF-7: incomplete input");
557 /* mUTF-7 -> UTF-16BE -> UTF-8 */
558 static ui8_t
const mb64dt
[256] = {
561 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
562 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
563 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,62, 63,XX
,XX
,XX
,
564 52,53,54,55, 56,57,58,59, 60,61,XX
,XX
, XX
,XX
,XX
,XX
,
565 XX
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
566 15,16,17,18, 19,20,21,22, 23,24,25,XX
, XX
,XX
,XX
,XX
,
567 XX
,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
568 41,42,43,44, 45,46,47,48, 49,50,51,XX
, XX
,XX
,XX
,XX
,
569 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
570 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
571 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
572 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
573 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
574 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
575 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
576 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
579 if(mb64p_base
== NULL
)
580 mb64p_base
= salloc(l
);
582 /* Decode the mUTF-7 to what is indeed UTF-16BE */
583 for(mb64p
= mb64p_base
;;){
585 if((mb64p
[0] = mb64dt
[(ui8_t
)cp
[0]]) == XX
||
586 (mb64p
[1] = mb64dt
[(ui8_t
)cp
[1]]) == XX
)
595 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
601 if((c
= *cp
++) == '-')
603 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
607 if(l
> 0 && *cp
== '-'){
617 if(l
>= 2 && cp
[0] == '&' && cp
[1] != '-'){
618 emsg
= N_("Invalid mUTF-7, consecutive encoded sequences");
622 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
623 i
= PTR2SIZE(mb64p
- mb64p_base
);
624 mb64p
= mb64xp
= mb64p_base
;
627 ui8_t ul
, u0
, u1
, u2
, u3
;
629 ul
= (i
>= 4) ? 4 : i
& 0x3;
633 u2
= (ul
< 3) ? 0 : mb64xp
[2];
634 u3
= (ul
< 4) ? 0 : mb64xp
[3];
636 *mb64p
++ = (u0
<<= 2) | (u1
>> 4);
639 *mb64p
++ = (u1
<<= 4) | (u2
>> 2);
642 *mb64p
++ = (u2
<<= 6, u2
&= 0xC0) | u3
;
645 /* UTF-16BE we convert to UTF-8 */
646 i
= PTR2SIZE(mb64p
- mb64p_base
);
648 emsg
= N_("Odd bytecount for UTF-16BE input");
652 /* TODO S-CText: magic utf16 conversions */
653 emsg
= N_("Invalid UTF-16BE encoding");
655 for(mb64p
= mb64p_base
; i
> 0;){
663 /* Not a surrogate? */
664 if(uhi
< 0xD800 || uhi
> 0xDFFF){
668 }else if(uhi
> 0xDBFF)
671 emsg
= N_("Incomplete UTF-16BE surrogate pair");
677 if(ulo
< 0xDC00 || ulo
> 0xDFFF){
686 utf32
= 0x10000u
+ uhi
;
691 utf32
= n_utf32_to_utf8(utf32
, rv
);
698 /* We can skip the UTF-8 conversion occasionally */
699 if(!(options
& OPT_UNICODE
)){
703 emsg
= N_("iconv(3) from UTF-8 to locale charset failed");
705 if((icd
= iconv_open(charset_get_lc(), "utf-8")) == (iconv_t
)-1)
708 out
.s
= NULL
, out
.l
= 0;
709 in
.l
= strlen(in
.s
= rv_base
);
710 if((ir
= n_iconv_str(icd
, &out
, &in
, NULL
, FAL0
)) == 0)
711 /* Because the length of this is unpredictable, copy*/
712 rv_base
= savestrbuf(out
.s
, out
.l
);
728 n_err(_("Cannot decode IMAP path %s\n %s\n"), path
, V_(emsg
));
729 memcpy(rv
= rv_base
, path
, ++l_orig
);
734 imap_path_quote(struct mailbox
*mp
, char const *cp
){
739 cp
= imap_path_normalize(mp
, cp
);
740 cp
= imap_path_encode(cp
, &err
);
741 rv
= err
? NULL
: imap_quotestr(cp
);
747 imap_other_get(char *pp
)
752 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
754 response_other
= MAILBOX_DATA_FLAGS
;
755 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
757 response_other
= MAILBOX_DATA_LIST
;
758 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
760 response_other
= MAILBOX_DATA_LSUB
;
761 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
763 response_other
= MAILBOX_DATA_MAILBOX
;
764 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
766 response_other
= MAILBOX_DATA_SEARCH
;
767 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
769 response_other
= MAILBOX_DATA_STATUS
;
770 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
772 response_other
= CAPABILITY_DATA
;
774 responded_other_number
= strtol(pp
, &xp
, 10);
777 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
778 response_other
= MAILBOX_DATA_EXISTS
;
779 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
780 response_other
= MAILBOX_DATA_RECENT
;
781 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
782 response_other
= MESSAGE_DATA_EXPUNGE
;
783 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
785 response_other
= MESSAGE_DATA_FETCH
;
787 response_other
= RESPONSE_OTHER_UNKNOWN
;
789 responded_other_text
= pp
;
794 imap_response_get(const char **cp
)
797 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
799 response_status
= RESPONSE_OK
;
800 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
802 response_status
= RESPONSE_NO
;
803 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
805 response_status
= RESPONSE_BAD
;
806 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
808 response_status
= RESPONSE_PREAUTH
;
809 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
811 response_status
= RESPONSE_BYE
;
813 response_status
= RESPONSE_OTHER
;
818 imap_response_parse(void)
820 static char *parsebuf
; /* TODO Use pool */
821 static size_t parsebufsize
;
823 const char *ip
= imapbuf
;
827 if (parsebufsize
< imapbufsize
)
828 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
829 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
833 response_type
= RESPONSE_CONT
;
848 imap_response_get(&ip
);
849 pp
= &parsebuf
[ip
- imapbuf
];
850 switch (response_status
) {
852 response_type
= RESPONSE_FATAL
;
855 response_type
= RESPONSE_DATA
;
859 responded_tag
= parsebuf
;
860 while (*pp
&& *pp
!= ' ')
863 response_type
= RESPONSE_ILLEGAL
;
867 while (*pp
&& *pp
== ' ')
870 response_type
= RESPONSE_ILLEGAL
;
873 ip
= &imapbuf
[pp
- parsebuf
];
874 response_type
= RESPONSE_TAGGED
;
875 imap_response_get(&ip
);
876 pp
= &parsebuf
[ip
- imapbuf
];
879 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
880 response_status
== RESPONSE_OTHER
)
886 imap_answer(struct mailbox
*mp
, int errprnt
)
893 if (mp
->mb_type
== MB_CACHE
)
897 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
898 if (options
& OPT_VERBVERB
)
899 fputs(imapbuf
, stderr
);
900 imap_response_parse();
901 if (response_type
== RESPONSE_ILLEGAL
)
903 if (response_type
== RESPONSE_CONT
) {
907 if (response_status
== RESPONSE_OTHER
) {
908 if (response_other
== MAILBOX_DATA_EXISTS
) {
909 had_exists
= responded_other_number
;
910 rec_queue(REC_EXISTS
, responded_other_number
);
913 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
914 rec_queue(REC_EXPUNGE
, responded_other_number
);
922 if (response_type
== RESPONSE_TAGGED
) {
923 if (asccasecmp(responded_tag
, tag(0)) == 0)
928 switch (response_status
) {
929 case RESPONSE_PREAUTH
:
930 mp
->mb_active
&= ~MB_PREAUTH
;
943 n_err(_("IMAP error: %s"), responded_text
);
945 case RESPONSE_UNKNOWN
: /* does not happen */
948 mp
->mb_active
= MB_NONE
;
956 if (response_status
!= RESPONSE_OTHER
&&
957 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
958 n_err(_("IMAP alert: %s"), &responded_text
[8]);
960 mp
->mb_active
&= ~MB_COMD
;
963 mp
->mb_active
= MB_NONE
;
971 imap_parse_list(void)
979 cp
= responded_other_text
;
980 list_attributes
= LIST_NONE
;
982 while (*cp
&& *cp
!= ')') {
984 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
985 list_attributes
|= LIST_NOINFERIORS
;
987 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
988 list_attributes
|= LIST_NOSELECT
;
990 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
991 list_attributes
|= LIST_MARKED
;
993 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
994 list_attributes
|= LIST_UNMARKED
;
1006 list_hierarchy_delimiter
= EOF
;
1010 list_hierarchy_delimiter
= *cp
++ & 0377;
1011 if (cp
[0] != '"' || cp
[1] != ' ')
1014 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
1015 list_hierarchy_delimiter
= EOF
;
1022 while (*cp
&& *cp
!= '\r')
1032 imap_finish(struct mailbox
*mp
)
1035 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
1042 imap_timer_off(void)
1045 if (imapkeepalive
> 0) {
1047 safe_signal(SIGALRM
, savealrm
);
1055 NYD_X
; /* Signal handler */
1058 n_err_sighdl(_("Interrupt\n"));
1059 siglongjmp(imapjmp
, 1);
1062 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1068 _imap_maincatch(int s
)
1070 NYD_X
; /* Signal handler */
1072 if (interrupts
++ == 0) {
1073 n_err_sighdl(_("Interrupt\n"));
1080 imap_noop1(struct mailbox
*mp
)
1083 FILE *queuefp
= NULL
;
1086 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
1087 IMAP_OUT(o
, MB_COMD
, return STOP
)
1093 imap_fileof(char const *xcp
)
1095 char const *cp
= xcp
;
1100 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
1104 if (cp
[0] == '/' && state
== 1) {
1122 sighandler_type
volatile oldint
, oldpipe
;
1123 enum okay rv
= STOP
;
1126 if (mb
.mb_type
!= MB_IMAP
)
1130 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1131 safe_signal(SIGINT
, &_imap_maincatch
);
1132 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1133 if (sigsetjmp(imapjmp
, 1) == 0) {
1134 if (oldpipe
!= SIG_IGN
)
1135 safe_signal(SIGPIPE
, imapcatch
);
1137 rv
= imap_noop1(&mb
);
1139 safe_signal(SIGINT
, oldint
);
1140 safe_signal(SIGPIPE
, oldpipe
);
1150 rec_queue(enum rec_type rt
, unsigned long cnt
)
1155 rp
= scalloc(1, sizeof *rp
);
1157 rp
->rec_count
= cnt
;
1158 if (record
&& recend
) {
1159 recend
->rec_next
= rp
;
1162 record
= recend
= rp
;
1169 struct message
*omessage
;
1170 struct record
*rp
, *rq
;
1171 uiz_t exists
= 0, i
;
1172 enum okay rv
= STOP
;
1179 message
= smalloc((msgCount
+1) * sizeof *message
);
1181 memcpy(message
, omessage
, msgCount
* sizeof *message
);
1182 memset(&message
[msgCount
], 0, sizeof *message
);
1184 rp
= record
, rq
= NULL
;
1186 while (rp
!= NULL
) {
1187 switch (rp
->rec_type
) {
1189 exists
= rp
->rec_count
;
1192 if (rp
->rec_count
== 0) {
1196 if (rp
->rec_count
> (unsigned long)msgCount
) {
1197 if (exists
== 0 || rp
->rec_count
> exists
--)
1203 delcache(&mb
, &message
[rp
->rec_count
-1]);
1204 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
1205 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
1207 /* If the message was part of a collapsed thread,
1208 * the m_collapsed field of one of its ancestors
1209 * should be incremented. It seems hardly possible
1210 * to do this with the current message structure,
1211 * though. The result is that a '+' may be shown
1212 * in the header summary even if no collapsed
1213 * children exists */
1224 record
= recend
= NULL
;
1225 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
1226 message
= srealloc(message
, (exists
+ 1) * sizeof *message
);
1227 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
1228 for (i
= msgCount
; i
< exists
; ++i
)
1230 imap_flags(&mb
, msgCount
+1, exists
);
1249 for (rp
= record
; rp
!= NULL
;) {
1250 struct record
*tmp
= rp
;
1254 record
= recend
= NULL
;
1262 sighandler_type
volatile saveint
, savepipe
;
1263 NYD_X
; /* Signal handler */
1266 if (imaplock
++ == 0) {
1267 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1268 safe_signal(SIGINT
, &_imap_maincatch
);
1269 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1270 if (sigsetjmp(imapjmp
, 1)) {
1271 safe_signal(SIGINT
, saveint
);
1272 safe_signal(SIGPIPE
, savepipe
);
1275 if (savepipe
!= SIG_IGN
)
1276 safe_signal(SIGPIPE
, imapcatch
);
1277 if (imap_noop1(&mb
) != OKAY
) {
1278 safe_signal(SIGINT
, saveint
);
1279 safe_signal(SIGPIPE
, savepipe
);
1282 safe_signal(SIGINT
, saveint
);
1283 safe_signal(SIGPIPE
, savepipe
);
1286 alarm(imapkeepalive
);
1292 imap_preauth(struct mailbox
*mp
, struct url
const *urlp
)
1296 mp
->mb_active
|= MB_PREAUTH
;
1300 if (!mp
->mb_sock
.s_use_ssl
&& xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1301 FILE *queuefp
= NULL
;
1304 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
1305 IMAP_OUT(o
, MB_COMD
, return STOP
)
1307 if (ssl_open(urlp
, &mp
->mb_sock
) != OKAY
)
1311 if (xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1312 n_err(_("No SSL support compiled in\n"));
1317 imap_capability(mp
);
1322 imap_capability(struct mailbox
*mp
)
1325 FILE *queuefp
= NULL
;
1326 enum okay ok
= STOP
;
1330 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
1331 IMAP_OUT(o
, MB_COMD
, return STOP
)
1332 while (mp
->mb_active
& MB_COMD
) {
1333 ok
= imap_answer(mp
, 0);
1334 if (response_status
== RESPONSE_OTHER
&&
1335 response_other
== CAPABILITY_DATA
) {
1336 cp
= responded_other_text
;
1338 while (spacechar(*cp
))
1340 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
1342 mp
->mb_flags
|= MB_UIDPLUS
;
1343 while (*cp
&& !spacechar(*cp
))
1352 imap_auth(struct mailbox
*mp
, struct ccred
*ccred
)
1357 if (!(mp
->mb_active
& MB_PREAUTH
)) {
1362 switch (ccred
->cc_authtype
) {
1363 case AUTHTYPE_LOGIN
:
1364 rv
= imap_login(mp
, ccred
);
1367 case AUTHTYPE_CRAM_MD5
:
1368 rv
= imap_cram_md5(mp
, ccred
);
1372 case AUTHTYPE_GSSAPI
:
1373 rv
= _imap_gssapi(mp
, ccred
);
1387 imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
)
1389 char o
[LINESIZE
], *cp
;
1390 FILE *queuefp
= NULL
;
1391 enum okay rv
= STOP
;
1394 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1395 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
1397 if (response_type
!= RESPONSE_CONT
)
1400 cp
= cram_md5_string(&ccred
->cc_user
, &ccred
->cc_pass
, responded_text
);
1401 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
1402 while (mp
->mb_active
& MB_COMD
)
1403 rv
= imap_answer(mp
, 1);
1408 #endif /* HAVE_MD5 */
1411 imap_login(struct mailbox
*mp
, struct ccred
*ccred
)
1414 FILE *queuefp
= NULL
;
1415 enum okay rv
= STOP
;
1418 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
1419 tag(1), imap_quotestr(ccred
->cc_user
.s
), imap_quotestr(ccred
->cc_pass
.s
));
1420 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
1421 while (mp
->mb_active
& MB_COMD
)
1422 rv
= imap_answer(mp
, 1);
1429 # include "imap_gssapi.h"
1433 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
,
1437 char const *qname
, *cp
;
1446 if((qname
= imap_path_quote(mp
, mbx
)) == NULL
)
1451 mp
->mb_uidvalidity
= 0;
1452 snprintf(o
, sizeof o
, "%s %s %s\r\n", tag(1),
1453 (fm
& FEDIT_RDONLY
? "EXAMINE" : "SELECT"), qname
);
1454 IMAP_OUT(o
, MB_COMD
, ok
= STOP
;goto jleave
)
1455 while (mp
->mb_active
& MB_COMD
) {
1456 ok
= imap_answer(mp
, 1);
1457 if (response_status
!= RESPONSE_OTHER
&&
1458 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
1459 mp
->mb_uidvalidity
= atol(&cp
[13]);
1461 *cnt
= (had_exists
> 0) ? had_exists
: 0;
1462 if (response_status
!= RESPONSE_OTHER
&&
1463 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
1470 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
1473 FILE *queuefp
= NULL
;
1476 unsigned x
= X
, y
= Y
, n
;
1479 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
1480 IMAP_OUT(o
, MB_COMD
, return STOP
)
1481 while (mp
->mb_active
& MB_COMD
) {
1483 if (response_status
== RESPONSE_OTHER
&&
1484 response_other
== MESSAGE_DATA_FETCH
) {
1485 n
= responded_other_number
;
1493 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1498 imap_getflags(cp
, &cp
, &m
->m_flag
);
1501 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1502 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1503 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1504 m
->m_flag
&= ~MHIDDEN
;
1507 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1509 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1512 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1514 IMAP_OUT(o
, MB_COMD
, return STOP
)
1515 while (mp
->mb_active
& MB_COMD
) {
1517 if (response_status
== RESPONSE_OTHER
&&
1518 response_other
== MESSAGE_DATA_FETCH
) {
1519 n
= responded_other_number
;
1525 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1526 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1527 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1528 m
->m_time
= imap_read_date_time(&cp
[13]);
1533 for (n
= X
; n
<= Y
; ++n
) {
1534 putcache(mp
, &message
[n
-1]);
1542 imap_init(struct mailbox
*mp
, int n
)
1549 m
->m_flag
= MUSED
| MNOFROM
;
1556 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1558 struct message
*omessage
= 0;
1559 int i
, omsgCount
= 0;
1560 enum okay dequeued
= STOP
;
1563 if (nmail
|| transparent
) {
1565 omsgCount
= msgCount
;
1568 dequeued
= rec_dequeue();
1570 if (had_exists
>= 0) {
1571 if (dequeued
!= OKAY
)
1572 msgCount
= had_exists
;
1575 if (had_expunge
>= 0) {
1576 if (dequeued
!= OKAY
)
1577 msgCount
-= had_expunge
;
1581 if (nmail
&& expunged_messages
)
1582 printf("Expunged %ld message%s.\n", expunged_messages
,
1583 (expunged_messages
!= 1 ? "s" : ""));
1584 *prevcount
= omsgCount
- expunged_messages
;
1585 expunged_messages
= 0;
1587 fputs("IMAP error: Negative message count\n", stderr
);
1591 if (dequeued
!= OKAY
) {
1592 message
= scalloc(msgCount
+ 1, sizeof *message
);
1593 for (i
= 0; i
< msgCount
; i
++)
1595 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1598 imap_flags(mp
, 1, msgCount
);
1599 message
[msgCount
].m_size
= 0;
1600 message
[msgCount
].m_lines
= 0;
1603 if (nmail
|| transparent
)
1604 transflags(omessage
, omsgCount
, transparent
);
1611 imap_setfile(const char *xserver
, enum fedit_mode fm
)
1617 if (!url_parse(&url
, CPROTO_IMAP
, xserver
)) {
1621 if (!ok_blook(v15_compat
) &&
1622 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
1623 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1625 _imap_rdonly
= ((fm
& FEDIT_RDONLY
) != 0);
1626 rv
= _imap_setfile1(&url
, fm
, 0);
1633 _imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
, struct url
*urlp
)
1638 if (ok_blook(v15_compat
))
1639 rv
= ccred_lookup(ccredp
, urlp
);
1642 *xuhp
= (urlp
->url_had_user
? urlp
->url_eu_h_p
.s
: urlp
->url_u_h_p
.s
);
1644 if ((var
= mbp
->mb_imap_pass
) != NULL
) {
1645 var
= savecat("password-", xuhp
);
1646 if ((old
= vok_vlook(var
)) != NULL
)
1648 vok_vset(var
, mbp
->mb_imap_pass
);
1650 rv
= ccred_lookup_old(ccredp
, CPROTO_IMAP
, xuhp
);
1665 _imap_setfile1(struct url
*urlp
, enum fedit_mode
volatile fm
,
1666 int volatile transparent
)
1670 sighandler_type
volatile saveint
, savepipe
;
1673 int volatile prevcount
= 0;
1674 enum mbflags same_flags
;
1677 if (fm
& FEDIT_NEWMAIL
) {
1678 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1679 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1680 if (saveint
!= SIG_IGN
)
1681 safe_signal(SIGINT
, imapcatch
);
1682 if (savepipe
!= SIG_IGN
)
1683 safe_signal(SIGPIPE
, imapcatch
);
1688 same_flags
= mb
.mb_flags
;
1689 same_imap_account
= 0;
1690 if (mb
.mb_imap_account
!= NULL
&&
1691 (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)) {
1692 if (mb
.mb_sock
.s_fd
> 0 && mb
.mb_sock
.s_rsz
>= 0 &&
1693 !strcmp(mb
.mb_imap_account
, urlp
->url_p_eu_h_p
) &&
1694 disconnected(mb
.mb_imap_account
) == 0) {
1695 same_imap_account
= 1;
1696 if (urlp
->url_pass
.s
== NULL
&& mb
.mb_imap_pass
!= NULL
)
1699 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1700 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1701 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1704 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= savestr(mb
.mb_imap_pass
));
1708 if (!same_imap_account
&& mb
.mb_imap_pass
!= NULL
) {
1709 free(mb
.mb_imap_pass
);
1710 mb
.mb_imap_pass
= NULL
;
1712 if (!_imap_getcred(&mb
, &ccred
, urlp
)) {
1718 if (!same_imap_account
) {
1719 if (!disconnected(urlp
->url_p_eu_h_p
) && !sopen(&so
, urlp
)) {
1728 if (fm
& FEDIT_SYSBOX
)
1732 if (mb
.mb_imap_account
!= NULL
)
1733 free(mb
.mb_imap_account
);
1734 if (mb
.mb_imap_pass
!= NULL
)
1735 free(mb
.mb_imap_pass
);
1736 mb
.mb_imap_account
= sstrdup(urlp
->url_p_eu_h_p
);
1737 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1738 * TODO object, and mailbox will naturally have an URL and credentials */
1739 mb
.mb_imap_pass
= sbufdup(ccred
.cc_pass
.s
, ccred
.cc_pass
.l
);
1741 if (!same_imap_account
) {
1742 if (mb
.mb_sock
.s_fd
>= 0)
1743 sclose(&mb
.mb_sock
);
1745 same_imap_account
= 0;
1756 if (mb
.mb_imap_mailbox
!= NULL
)
1757 free(mb
.mb_imap_mailbox
);
1758 assert(urlp
->url_path
.s
!= NULL
);
1759 imap_delim_init(&mb
, urlp
);
1760 mb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mb
, urlp
->url_path
.s
));
1761 initbox(savecatsep(urlp
->url_p_eu_h_p
,
1762 (mb
.mb_imap_delim
[0] != '\0' ? mb
.mb_imap_delim
[0] : n_IMAP_DELIM
[0]),
1763 mb
.mb_imap_mailbox
));
1765 mb
.mb_type
= MB_VOID
;
1766 mb
.mb_active
= MB_NONE
;
1769 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1770 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1771 if (sigsetjmp(imapjmp
, 1)) {
1772 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1773 sclose(&mb
.mb_sock
);
1774 safe_signal(SIGINT
, saveint
);
1775 safe_signal(SIGPIPE
, savepipe
);
1778 mb
.mb_type
= MB_VOID
;
1779 mb
.mb_active
= MB_NONE
;
1780 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1783 if (saveint
!= SIG_IGN
)
1784 safe_signal(SIGINT
, imapcatch
);
1785 if (savepipe
!= SIG_IGN
)
1786 safe_signal(SIGPIPE
, imapcatch
);
1788 if (mb
.mb_sock
.s_fd
< 0) {
1789 if (disconnected(mb
.mb_imap_account
)) {
1790 if (cache_setptr(fm
, transparent
) == STOP
)
1791 n_err(_("Mailbox \"%s\" is not cached\n"), urlp
->url_p_eu_h_p_p
);
1794 if ((cp
= xok_vlook(imap_keepalive
, urlp
, OXM_ALL
)) != NULL
) {
1795 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1796 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1797 alarm(imapkeepalive
);
1802 mb
.mb_sock
.s_desc
= "IMAP";
1803 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1804 if (imap_preauth(&mb
, urlp
) != OKAY
|| imap_auth(&mb
, &ccred
) != OKAY
) {
1805 sclose(&mb
.mb_sock
);
1807 safe_signal(SIGINT
, saveint
);
1808 safe_signal(SIGPIPE
, savepipe
);
1810 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1813 } else /* same account */
1814 mb
.mb_flags
|= same_flags
;
1816 if (options
& OPT_R_FLAG
)
1818 mb
.mb_perm
= (fm
& FEDIT_RDONLY
) ? 0 : MB_DELE
;
1819 mb
.mb_type
= MB_IMAP
;
1821 assert(urlp
->url_path
.s
!= NULL
);
1822 if (imap_select(&mb
, &mailsize
, &msgCount
, urlp
->url_path
.s
, fm
) != OKAY
) {
1823 /*sclose(&mb.mb_sock);
1825 safe_signal(SIGINT
, saveint
);
1826 safe_signal(SIGPIPE
, savepipe
);
1828 mb
.mb_type
= MB_VOID
;
1829 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1834 imap_setptr(&mb
, ((fm
& FEDIT_NEWMAIL
) != 0), transparent
,
1835 UNVOLATILE(&prevcount
));
1838 if (!(fm
& FEDIT_NEWMAIL
) && !transparent
)
1839 pstate
&= ~PS_SAW_COMMAND
;
1840 safe_signal(SIGINT
, saveint
);
1841 safe_signal(SIGPIPE
, savepipe
);
1844 if (!(fm
& FEDIT_NEWMAIL
) && mb
.mb_type
== MB_IMAP
)
1845 purgecache(&mb
, message
, msgCount
);
1846 if (((fm
& FEDIT_NEWMAIL
) || transparent
) && mb
.mb_sorted
) {
1851 if ((options
& OPT_EXISTONLY
) && (mb
.mb_type
== MB_IMAP
||
1852 mb
.mb_type
== MB_CACHE
)) {
1853 rv
= (msgCount
== 0);
1857 if (!(fm
& FEDIT_NEWMAIL
) && !(pstate
& PS_EDIT
) && msgCount
== 0) {
1858 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1859 !ok_blook(emptystart
))
1860 n_err(_("No mail at %s\n"), urlp
->url_p_eu_h_p_p
);
1865 if (fm
& FEDIT_NEWMAIL
)
1866 newmailinfo(prevcount
);
1874 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1875 int need
, const char *head
, size_t headsize
, long headlines
)
1877 char *line
= NULL
, *lp
;
1878 size_t linesize
= 0, linelen
, size
= 0;
1879 int emptyline
= 0, lines
= 0, excess
= 0;
1883 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1884 offset
= ftell(mp
->mb_otf
);
1887 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1889 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1891 if (linelen
> expected
) {
1892 excess
= linelen
- expected
;
1896 * Need to mask 'From ' lines. This cannot be done properly
1897 * since some servers pass them as 'From ' and others as
1898 * '>From '. Although one could identify the first kind of
1899 * server in principle, it is not possible to identify the
1900 * second as '>From ' may also come from a server of the
1901 * first type as actual data. So do what is absolutely
1902 * necessary only - mask 'From '.
1904 * If the line is the first line of the message header, it
1905 * is likely a real 'From ' line. In this case, it is just
1906 * ignored since it violates all standards.
1907 * TODO can the latter *really* happen??
1910 /* Since we simply copy over data without doing any transfer
1911 * encoding reclassification/adjustment we *have* to perform
1912 * RFC 4155 compliant From_ quoting here */
1913 if (emptyline
&& is_head(lp
, linelen
, FAL0
)) {
1914 fputc('>', mp
->mb_otf
);
1918 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1920 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1921 size
+= linelen
- 1;
1926 fputc('\n', mp
->mb_otf
);
1928 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1932 if ((expected
-= linelen
) <= 0)
1936 /* This is very ugly; but some IMAP daemons don't end a
1937 * message with \r\n\r\n, and we need \n\n for mbox format */
1938 fputc('\n', mp
->mb_otf
);
1945 m
->m_size
= size
+ headsize
;
1946 m
->m_lines
= lines
+ headlines
;
1947 m
->m_block
= mailx_blockof(offset
);
1948 m
->m_offset
= mailx_offsetof(offset
);
1951 m
->m_have
|= HAVE_HEADER
;
1954 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1955 m
->m_xlines
= m
->m_lines
;
1956 m
->m_xsize
= m
->m_size
;
1966 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1967 const char *head
, size_t headsize
, long headlines
)
1974 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1975 offset
= ftell(mp
->mb_otf
);
1977 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1979 fwrite(str
, 1, len
, mp
->mb_otf
);
1980 fputc('\n', mp
->mb_otf
);
1986 m
->m_size
= headsize
+ len
;
1987 m
->m_lines
= headlines
+ 1;
1988 m
->m_block
= mailx_blockof(offset
);
1989 m
->m_offset
= mailx_offsetof(offset
);
1990 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1991 m
->m_xlines
= m
->m_lines
;
1992 m
->m_xsize
= m
->m_size
;
1998 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
2002 sighandler_type
volatile saveint
, savepipe
;
2003 char * volatile head
;
2004 char const *cp
, *loc
, * volatile item
, * volatile resp
;
2006 size_t volatile headsize
;
2009 long volatile headlines
;
2015 saveint
= savepipe
= SIG_IGN
;
2017 cp
= loc
= item
= resp
= NULL
;
2019 number
= (int)PTR2SIZE(m
- message
+ 1);
2026 if (getcache(mp
, m
, need
) == OKAY
)
2028 if (mp
->mb_type
== MB_CACHE
) {
2029 n_err(_("Message %lu not available\n"), (ul_i
)number
);
2033 if (mp
->mb_sock
.s_fd
< 0) {
2034 n_err(_("IMAP connection closed\n"));
2040 resp
= item
= "RFC822.HEADER";
2043 item
= "BODY.PEEK[]";
2045 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
2046 char *hdr
= smalloc(m
->m_size
);
2048 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
2050 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
2055 headsize
= m
->m_size
;
2056 headlines
= m
->m_lines
;
2057 item
= "BODY.PEEK[TEXT]";
2058 resp
= "BODY[TEXT]";
2066 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2067 if (sigsetjmp(imapjmp
, 1)) {
2068 safe_signal(SIGINT
, saveint
);
2069 safe_signal(SIGPIPE
, savepipe
);
2073 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2074 safe_signal(SIGINT
, &_imap_maincatch
);
2075 if (savepipe
!= SIG_IGN
)
2076 safe_signal(SIGPIPE
, imapcatch
);
2079 snprintf(o
, sizeof o
, "%s UID FETCH %lu (%s)\r\n",
2080 tag(1), m
->m_uid
, item
);
2082 if (check_expunged() == STOP
)
2084 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
2086 IMAP_OUT(o
, MB_COMD
, goto out
)
2088 ok
= imap_answer(mp
, 1);
2091 if (response_status
!= RESPONSE_OTHER
||
2092 response_other
!= MESSAGE_DATA_FETCH
)
2094 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
2097 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
2105 n
= responded_other_number
;
2106 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
2107 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
2109 if ((cp
= strchr(loc
, '"')) != NULL
) {
2110 cp
= imap_unquotestr(cp
);
2111 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
2113 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
2114 m
->m_xlines
= m
->m_lines
;
2115 m
->m_xsize
= m
->m_size
;
2119 expected
= atol(&cp
[1]);
2120 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
2121 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
2125 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
2127 commitmsg(mp
, m
, &mt
, mt
.m_have
);
2130 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2131 if (options
& OPT_VERBVERB
)
2132 fputs(imapbuf
, stderr
);
2133 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2135 if (u
== m
->m_uid
) {
2136 commitmsg(mp
, m
, &mt
, mt
.m_have
);
2143 while (mp
->mb_active
& MB_COMD
)
2144 ok
= imap_answer(mp
, 1);
2146 if (saveint
!= SIG_IGN
)
2147 safe_signal(SIGINT
, saveint
);
2148 if (savepipe
!= SIG_IGN
)
2149 safe_signal(SIGPIPE
, savepipe
);
2162 imap_header(struct message
*m
)
2167 rv
= imap_get(&mb
, m
, NEED_HEADER
);
2174 imap_body(struct message
*m
)
2179 rv
= imap_get(&mb
, m
, NEED_BODY
);
2185 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
2189 tomp
->m_size
= frommp
->m_size
;
2190 tomp
->m_lines
= frommp
->m_lines
;
2191 tomp
->m_block
= frommp
->m_block
;
2192 tomp
->m_offset
= frommp
->m_offset
;
2193 tomp
->m_have
= have
;
2194 if (have
& HAVE_BODY
) {
2195 tomp
->m_xlines
= frommp
->m_lines
;
2196 tomp
->m_xsize
= frommp
->m_size
;
2203 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
2211 FILE *queuefp
= NULL
;
2216 snprintf(o
, sizeof o
, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2217 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
2219 if (check_expunged() == STOP
)
2221 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2224 IMAP_OUT(o
, MB_COMD
, return STOP
)
2228 ok
= imap_answer(mp
, 1);
2229 if (response_status
!= RESPONSE_OTHER
)
2231 if (response_other
!= MESSAGE_DATA_FETCH
)
2233 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0) {
2237 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
2239 expected
= atol(&cp
[1]);
2240 if (m
[bot
-1].m_uid
) {
2241 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
2243 for (n
= bot
; n
<= topp
; n
++)
2244 if ((unsigned long)u
== m
[n
-1].m_uid
)
2247 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2253 n
= responded_other_number
;
2254 if (n
<= 0 || n
> msgCount
) {
2255 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2259 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
2260 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
2261 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
2262 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2263 if (options
& OPT_VERBVERB
)
2264 fputs(imapbuf
, stderr
);
2265 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2267 for (n
= bot
; n
<= topp
; n
++)
2268 if ((unsigned long)u
== m
[n
-1].m_uid
)
2270 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
2271 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
2279 while (mp
->mb_active
& MB_COMD
)
2280 ok
= imap_answer(mp
, 1);
2285 imap_getheaders(int volatile bot
, int volatile topp
) /* TODO iterator!! */
2287 sighandler_type saveint
, savepipe
;
2288 /*enum okay ok = STOP;*/
2292 if (mb
.mb_type
== MB_CACHE
)
2296 if (topp
> msgCount
)
2298 for (i
= bot
; i
< topp
; i
++) {
2299 if (message
[i
-1].m_have
& HAVE_HEADER
||
2300 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2305 for (i
= topp
; i
> bot
; i
--) {
2306 if (message
[i
-1].m_have
& HAVE_HEADER
||
2307 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2316 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2317 safe_signal(SIGINT
, &_imap_maincatch
);
2318 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2319 if (sigsetjmp(imapjmp
, 1) == 0) {
2320 if (savepipe
!= SIG_IGN
)
2321 safe_signal(SIGPIPE
, imapcatch
);
2323 for (i
= bot
; i
<= topp
; i
+= chunk
) {
2324 int j
= i
+ chunk
- 1;
2326 if (visible(message
+ j
))
2327 /*ok = */imap_fetchheaders(&mb
, message
, i
, j
);
2329 onintr(0); /* XXX imaplock? */
2332 safe_signal(SIGINT
, saveint
);
2333 safe_signal(SIGPIPE
, savepipe
);
2338 __imap_exit(struct mailbox
*mp
)
2341 FILE *queuefp
= NULL
;
2344 mp
->mb_active
|= MB_BYE
;
2345 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
2346 IMAP_OUT(o
, MB_COMD
, return STOP
)
2352 imap_exit(struct mailbox
*mp
)
2357 rv
= __imap_exit(mp
);
2358 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2359 free(mp
->mb_imap_pass
);
2360 free(mp
->mb_imap_account
);
2361 free(mp
->mb_imap_mailbox
);
2362 if (mp
->mb_cache_directory
!= NULL
)
2363 free(mp
->mb_cache_directory
);
2364 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2365 mp
->mb_imap_account
=
2366 mp
->mb_imap_mailbox
=
2367 mp
->mb_cache_directory
= "";
2369 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
2370 mp
->mb_imap_mailbox
= NULL
;
2371 mp
->mb_cache_directory
= NULL
;
2374 sclose(&mp
->mb_sock
);
2380 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
2383 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
2384 if (mp
->mb_type
== MB_IMAP
)
2391 imap_close(struct mailbox
*mp
)
2394 FILE *queuefp
= NULL
;
2397 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
2398 IMAP_OUT(o
, MB_COMD
, return STOP
)
2404 imap_update(struct mailbox
*mp
)
2407 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
2410 if (!(pstate
& PS_EDIT
) && mp
->mb_perm
!= 0) {
2413 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2414 if (m
->m_flag
& MBOX
)
2417 if (makembox() == STOP
)
2422 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
2423 if (mp
->mb_perm
== 0)
2425 else if (pstate
& PS_EDIT
)
2426 dodel
= ((m
->m_flag
& MDELETED
) != 0);
2428 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
2430 /* Fetch the result after around each 800 STORE commands
2431 * sent (approx. 32k data sent). Otherwise, servers will
2432 * try to flush the return queue at some point, leading
2433 * to a deadlock if we are still writing commands but not
2434 * reading their results */
2435 needstat
= stored
> 0 && stored
% 800 == 0;
2436 /* Even if this message has been deleted, continue
2437 * to set further flags. This is necessary to support
2438 * Gmail semantics, where "delete" actually means
2439 * "archive", and the flags are applied to the copy
2441 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2442 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
2445 if (m
->m_flag
& MFLAG
) {
2446 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
2449 if (m
->m_flag
& MUNFLAG
) {
2450 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
2453 if (m
->m_flag
& MANSWER
) {
2454 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
2457 if (m
->m_flag
& MUNANSWER
) {
2458 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
2461 if (m
->m_flag
& MDRAFT
) {
2462 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
2465 if (m
->m_flag
& MUNDRAFT
) {
2466 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
2471 imap_delete(mp
, m
-message
+1, m
, needstat
);
2474 } else if (mp
->mb_type
!= MB_CACHE
||
2475 (!(pstate
& PS_EDIT
) &&
2476 !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
2477 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
2478 (MPRESERVE
| MTOUCH
) ||
2479 ((pstate
& PS_EDIT
) && !(m
->m_flag
& MDELETED
)))
2481 if (m
->m_flag
& MNEW
) {
2483 m
->m_flag
|= MSTATUS
;
2490 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2491 if (!(m
->m_flag
& MUNLINKED
) &&
2492 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
2493 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
2498 /* XXX should be readonly (but our IMAP code is weird...) */
2499 if (!(options
& (OPT_EXISTONLY
| OPT_HEADERSONLY
| OPT_HEADERLIST
)) &&
2501 if ((gotcha
|| modflags
) && (pstate
& PS_EDIT
)) {
2502 printf(_("\"%s\" "), displayname
);
2503 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2504 ? _("complete\n") : _("updated.\n"));
2505 } else if (held
&& !(pstate
& PS_EDIT
)) {
2507 printf(_("Held 1 message in %s\n"), displayname
);
2509 printf(_("Held %d messages in %s\n"), held
, displayname
);
2520 sighandler_type
volatile saveint
, savepipe
;
2523 if (mb
.mb_type
== MB_CACHE
) {
2528 if (mb
.mb_sock
.s_fd
< 0) {
2529 n_err(_("IMAP connection closed\n"));
2534 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2535 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2536 if (sigsetjmp(imapjmp
, 1)) {
2537 safe_signal(SIGINT
, saveint
);
2538 safe_signal(SIGPIPE
, saveint
);
2542 if (saveint
!= SIG_IGN
)
2543 safe_signal(SIGINT
, imapcatch
);
2544 if (savepipe
!= SIG_IGN
)
2545 safe_signal(SIGPIPE
, imapcatch
);
2548 if (!same_imap_account
)
2551 safe_signal(SIGINT
, saveint
);
2552 safe_signal(SIGPIPE
, savepipe
);
2559 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2563 FILE *queuefp
= NULL
;
2566 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2569 snprintf(o
, sizeof o
, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2570 tag(1), m
->m_uid
, c
, sp
);
2572 if (check_expunged() == STOP
)
2574 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2576 IMAP_OUT(o
, MB_COMD
, return STOP
)
2580 mb
.mb_active
&= ~MB_COMD
;
2581 if (queuefp
!= NULL
)
2587 imap_undelete(struct message
*m
, int n
)
2592 rv
= imap_unstore(m
, n
, "\\Deleted");
2598 imap_unread(struct message
*m
, int n
)
2603 rv
= imap_unstore(m
, n
, "\\Seen");
2609 imap_unstore(struct message
*m
, int n
, const char *flag
)
2611 sighandler_type saveint
, savepipe
;
2612 enum okay rv
= STOP
;
2616 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2617 safe_signal(SIGINT
, &_imap_maincatch
);
2618 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2619 if (sigsetjmp(imapjmp
, 1) == 0) {
2620 if (savepipe
!= SIG_IGN
)
2621 safe_signal(SIGPIPE
, imapcatch
);
2623 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2625 safe_signal(SIGINT
, saveint
);
2626 safe_signal(SIGPIPE
, savepipe
);
2644 snprintf(ts
, sizeof ts
, "T%lu", n
);
2650 c_imapcodec(void *v
){
2652 char const **argv
, *cp
, *res
;
2655 if(is_prefix(cp
= *(argv
= v
), "encode")){
2656 while((cp
= *++argv
) != NULL
){
2657 res
= imap_path_normalize(NULL
, cp
);
2658 res
= imap_path_encode(res
, &err
);
2659 printf(" in: %s (%" PRIuZ
" bytes)\nout: %s%s (%" PRIuZ
" bytes)\n",
2660 cp
, strlen(cp
), (err
? "ERROR " : ""), res
, strlen(res
));
2662 }else if(is_prefix(cp
, "decode")){
2665 while((cp
= *++argv
) != NULL
){
2666 res
= imap_path_normalize(NULL
, cp
);
2667 res
= imap_path_decode(res
, &err
);
2668 in
.l
= strlen(in
.s
= UNCONST(res
)); /* logical */
2669 makeprint(&in
, &out
);
2670 printf(" in: %s (%" PRIuZ
" bytes)\nout: %s%s (%" PRIuZ
" bytes)\n",
2671 cp
, strlen(cp
), (err
? "ERROR " : ""), out
.s
, in
.l
);
2675 n_err(_("`imapcodec': invalid subcommand: %s\n"), *argv
);
2679 return (cp
!= NULL
? OKAY
: STOP
);
2683 c_imap_imap(void *vp
)
2686 sighandler_type saveint
, savepipe
;
2687 struct mailbox
*mp
= &mb
;
2688 FILE *queuefp
= NULL
;
2689 enum okay
volatile ok
= STOP
;
2692 if (mp
->mb_type
!= MB_IMAP
) {
2693 printf("Not operating on an IMAP mailbox.\n");
2697 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2698 safe_signal(SIGINT
, &_imap_maincatch
);
2699 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2700 if (sigsetjmp(imapjmp
, 1) == 0) {
2701 if (savepipe
!= SIG_IGN
)
2702 safe_signal(SIGPIPE
, imapcatch
);
2704 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2705 IMAP_OUT(o
, MB_COMD
, goto out
)
2706 while (mp
->mb_active
& MB_COMD
) {
2707 ok
= imap_answer(mp
, 0);
2708 fputs(responded_text
, stdout
);
2712 safe_signal(SIGINT
, saveint
);
2713 safe_signal(SIGPIPE
, savepipe
);
2722 imap_newmail(int nmail
)
2726 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2732 if (had_exists
== msgCount
&& had_expunge
< 0)
2733 /* Some servers always respond with EXISTS to NOOP. If
2734 * the mailbox has been changed but the number of messages
2735 * has not, an EXPUNGE must also had been sent; otherwise,
2736 * nothing has changed */
2739 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2743 imap_putflags(int f
)
2749 bp
= buf
= salloc(100);
2750 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2755 for (cp
= "\\Seen"; *cp
; cp
++)
2761 for (cp
= "\\Flagged"; *cp
; cp
++)
2764 if (f
& MANSWERED
) {
2767 for (cp
= "\\Answered"; *cp
; cp
++)
2773 for (cp
= "\\Draft"; *cp
; cp
++)
2785 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2788 while (*cp
!= ')') {
2790 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2792 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2794 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2796 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2798 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2800 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2812 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2813 long xsize
, enum mflag flag
, time_t t
)
2815 char o
[LINESIZE
], *buf
;
2816 size_t bufsize
, buflen
, cnt
;
2817 long size
, lines
, ysize
;
2829 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
2832 if (mp
->mb_type
== MB_CACHE
) {
2833 queuefp
= cache_queue(mp
);
2834 if (queuefp
== NULL
) {
2841 buf
= smalloc(bufsize
= LINESIZE
);
2846 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2851 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2852 tag(1), qname
, imap_putflags(flag
), imap_make_date_time(t
), size
);
2853 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2854 while (mp
->mb_active
& MB_COMD
) {
2855 rv
= imap_answer(mp
, twice
);
2856 if (response_type
== RESPONSE_CONT
)
2860 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2869 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2872 buf
[buflen
- 1] = '\r';
2874 if (mp
->mb_type
!= MB_CACHE
)
2875 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2877 fwrite(buf
, 1, buflen
+1, queuefp
);
2880 if (mp
->mb_type
!= MB_CACHE
)
2881 swrite(&mp
->mb_sock
, "\r\n");
2883 fputs("\r\n", queuefp
);
2884 while (mp
->mb_active
& MB_COMD
) {
2885 rv
= imap_answer(mp
, 0);
2886 if (response_status
== RESPONSE_NO
/*&&
2887 ascncasecmp(responded_text,
2888 "[TRYCREATE] ", 12) == 0*/) {
2895 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2896 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2897 while (mp
->mb_active
& MB_COMD
)
2898 rv
= imap_answer(mp
, 1);
2901 imap_created_mailbox
++;
2903 } else if (rv
!= OKAY
)
2904 n_err(_("IMAP error: %s"), responded_text
);
2905 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2906 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2909 if (queuefp
!= NULL
)
2918 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2920 char *buf
, *bp
, *lp
;
2921 size_t bufsize
, buflen
, cnt
;
2922 off_t off1
= -1, offs
;
2924 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
2930 buf
= smalloc(bufsize
= LINESIZE
);
2937 for (flag
= MNEW
, state
= _NLSEP
;;) {
2938 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2941 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
2942 is_head(buf
, buflen
, FAL0
))) {
2943 if (off1
!= (off_t
)-1) {
2944 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
2947 fseek(fp
, offs
+buflen
, SEEK_SET
);
2949 off1
= offs
+ buflen
;
2955 tim
= unixtime(buf
);
2961 if (buf
[0] == '\n') {
2964 } else if (state
& _INHEAD
) {
2965 if (ascncasecmp(buf
, "status", 6) == 0) {
2967 while (whitechar(*lp
))
2970 while (*++lp
!= '\0')
2979 } else if (ascncasecmp(buf
, "x-status", 8) == 0) {
2981 while (whitechar(*lp
))
2984 while (*++lp
!= '\0')
3007 imap_append(const char *xserver
, FILE *fp
)
3009 sighandler_type
volatile saveint
, savepipe
;
3012 enum okay rv
= STOP
;
3015 if (!url_parse(&url
, CPROTO_IMAP
, xserver
))
3017 if (!ok_blook(v15_compat
) &&
3018 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
3019 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3020 assert(url
.url_path
.s
!= NULL
);
3023 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3024 safe_signal(SIGINT
, &_imap_maincatch
);
3025 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3026 if (sigsetjmp(imapjmp
, 1))
3028 if (savepipe
!= SIG_IGN
)
3029 safe_signal(SIGPIPE
, imapcatch
);
3031 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
3032 !strcmp(url
.url_p_eu_h_p
, mb
.mb_imap_account
)) {
3033 rv
= imap_append0(&mb
, url
.url_path
.s
, fp
);
3037 memset(&mx
, 0, sizeof mx
);
3039 if (!_imap_getcred(&mx
, &ccred
, &url
))
3042 imap_delim_init(&mx
, &url
);
3043 mx
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mx
, url
.url_path
.s
));
3045 if (disconnected(url
.url_p_eu_h_p
) == 0) {
3046 if (!sopen(&mx
.mb_sock
, &url
))
3048 mx
.mb_sock
.s_desc
= "IMAP";
3049 mx
.mb_type
= MB_IMAP
;
3050 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
3051 /* TODO the code now did
3052 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3053 * TODO though imap_mailbox is sfree()d and mbx
3054 * TODO is possibly even a constant
3055 * TODO i changed this to sstrdup() sofar, as is used
3056 * TODO somewhere else in this file for this! */
3057 if (imap_preauth(&mx
, &url
) != OKAY
||
3058 imap_auth(&mx
, &ccred
) != OKAY
) {
3059 sclose(&mx
.mb_sock
);
3062 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
3065 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
3066 mx
.mb_type
= MB_CACHE
;
3067 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
3074 safe_signal(SIGINT
, saveint
);
3075 safe_signal(SIGPIPE
, savepipe
);
3085 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
3086 struct list_item
**lend
, int level
)
3088 char o
[LINESIZE
], *cp
;
3089 struct list_item
*lp
;
3090 const char *qname
, *bp
;
3098 if((qname
= imap_path_quote(mp
, base
)) == NULL
)
3101 *list
= *lend
= NULL
;
3102 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), qname
);
3103 IMAP_OUT(o
, MB_COMD
, goto jleave
)
3104 while (mp
->mb_active
& MB_COMD
) {
3105 ok
= imap_answer(mp
, 1);
3106 if (response_status
== RESPONSE_OTHER
&&
3107 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
3108 cp
= imap_path_decode(imap_unquotestr(list_name
), NULL
);
3109 lp
= csalloc(1, sizeof *lp
);
3111 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
3113 lp
->l_base
= *cp
? cp
: savestr(base
);
3114 lp
->l_attr
= list_attributes
;
3115 lp
->l_level
= level
+1;
3116 lp
->l_delim
= list_hierarchy_delimiter
;
3117 if (*list
&& *lend
) {
3118 (*lend
)->l_next
= lp
;
3129 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
3131 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
3138 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
3139 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
3142 if (list
== NULL
|| lend
== NULL
)
3145 for (lp
= list
; lp
; lp
= lp
->l_next
)
3146 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
3147 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
3148 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
3149 memcpy(cp
, lp
->l_name
, n
);
3150 cp
[n
] = lp
->l_delim
;
3152 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
3153 lp
->l_has_children
= 1;
3154 if (strcmp(cp
, lx
->l_name
) == 0)
3163 for (lp
= list
; lp
; lp
= lp
->l_next
) {
3166 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
3170 if (!(lp
->l_attr
& LIST_NOSELECT
))
3171 fprintf(fp
, "%s\n", *cp
? cp
: base
);
3172 else if (lp
->l_has_children
== 0)
3173 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
3174 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
3182 imap_folders(const char * volatile name
, int strip
)
3184 sighandler_type saveint
, savepipe
;
3185 const char *fold
, *cp
, *sp
;
3189 cp
= protbase(name
);
3190 sp
= mb
.mb_imap_account
;
3191 if (sp
== NULL
|| strcmp(cp
, sp
)) {
3193 _("Cannot perform `folders' but when on the very IMAP "
3194 "account; the current one is\n `%s' -- "
3195 "try `folders @'\n"),
3196 (sp
!= NULL
? sp
: _("[NONE]")));
3200 fold
= imap_fileof(name
);
3201 if (options
& OPT_TTYOUT
) {
3202 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
3204 n_perr(_("tmpfile"), 0);
3211 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3212 safe_signal(SIGINT
, &_imap_maincatch
);
3213 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3214 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
3216 if (savepipe
!= SIG_IGN
)
3217 safe_signal(SIGPIPE
, imapcatch
);
3219 if (mb
.mb_type
== MB_CACHE
)
3220 cache_list(&mb
, fold
, strip
, fp
);
3222 imap_list(&mb
, fold
, strip
, fp
);
3226 if (options
& OPT_TTYOUT
)
3232 if (options
& OPT_TTYOUT
) {
3237 n_err(_("Folder not found\n"));
3240 safe_signal(SIGINT
, saveint
);
3241 safe_signal(SIGPIPE
, savepipe
);
3242 if (options
& OPT_TTYOUT
)
3255 long n
= 0, mx
= 0, columns
, width
;
3259 if ((out
= Ftmp(NULL
, "imapdopr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600))
3261 n_perr(_("tmpfile"), 0);
3265 while ((c
= getc(fp
)) != EOF
) {
3276 if (mx
< width
/ 2) {
3277 columns
= width
/ (mx
+2);
3278 snprintf(o
, sizeof o
, "sort | pr -%lu -w%lu -t", columns
, width
);
3280 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
3281 run_command(XSHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
3282 page_or_print(out
, 0);
3289 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
3293 bool_t twice
, stored
;
3300 twice
= stored
= FAL0
;
3305 i
= strlen(name
= imap_fileof(name
));
3306 if(i
== 0 || (i
> 0 && name
[i
- 1] == '/'))
3307 name
= savecat(name
, "INBOX");
3308 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
3312 if (mp
->mb_type
== MB_CACHE
) {
3313 if ((queuefp
= cache_queue(mp
)) == NULL
)
3318 /* Since it is not possible to set flags on the copy, recently
3319 * set flags must be set on the original to include it in the copy */
3320 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
3321 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
3322 if (m
->m_flag
&MFLAG
)
3323 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3324 if (m
->m_flag
&MUNFLAG
)
3325 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3326 if (m
->m_flag
&MANSWER
)
3327 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3328 if (m
->m_flag
&MUNANSWER
)
3329 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3330 if (m
->m_flag
&MDRAFT
)
3331 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3332 if (m
->m_flag
&MUNDRAFT
)
3333 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3336 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n", tag(1), m
->m_uid
, qname
);
3338 if (check_expunged() == STOP
)
3340 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
3342 IMAP_OUT(o
, MB_COMD
, goto out
)
3343 while (mp
->mb_active
& MB_COMD
)
3344 ok
= imap_answer(mp
, twice
);
3346 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
3347 response_status
== RESPONSE_OK
)
3348 imap_copyuid(mp
, m
, name
);
3350 if (response_status
== RESPONSE_NO
&& !twice
) {
3351 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
3352 IMAP_OUT(o
, MB_COMD
, goto out
)
3353 while (mp
->mb_active
& MB_COMD
)
3354 ok
= imap_answer(mp
, 1);
3356 imap_created_mailbox
++;
3362 if (queuefp
!= NULL
)
3365 /* ... and reset the flag to its initial value so that the 'exit'
3366 * command still leaves the message unread */
3368 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
3369 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
3372 if (m
->m_flag
& MFLAG
) {
3373 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3376 if (m
->m_flag
& MUNFLAG
) {
3377 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3380 if (m
->m_flag
& MANSWER
) {
3381 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
3384 if (m
->m_flag
& MUNANSWER
) {
3385 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3388 if (m
->m_flag
& MDRAFT
) {
3389 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3392 if (m
->m_flag
& MUNDRAFT
) {
3393 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3397 mp
->mb_active
|= MB_COMD
;
3398 (void)imap_finish(mp
);
3405 imap_copy(struct message
*m
, int n
, const char *name
)
3407 sighandler_type saveint
, savepipe
;
3408 enum okay rv
= STOP
;
3412 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3413 safe_signal(SIGINT
, &_imap_maincatch
);
3414 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3415 if (sigsetjmp(imapjmp
, 1) == 0) {
3416 if (savepipe
!= SIG_IGN
)
3417 safe_signal(SIGPIPE
, imapcatch
);
3419 rv
= imap_copy1(&mb
, m
, n
, name
);
3421 safe_signal(SIGINT
, saveint
);
3422 safe_signal(SIGPIPE
, savepipe
);
3432 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
3433 unsigned long *olduid
, unsigned long *newuid
)
3439 *uidvalidity
= strtoul(cp
, &xp
, 10);
3440 *olduid
= strtoul(xp
, &yp
, 10);
3441 *newuid
= strtoul(yp
, &zp
, 10);
3442 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
3443 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
3449 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
3456 *uidvalidity
= strtoul(cp
, &xp
, 10);
3457 *uid
= strtoul(xp
, &yp
, 10);
3458 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
3465 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
3470 unsigned long uidvalidity
, olduid
, newuid
;
3476 memset(&xmb
, 0, sizeof xmb
);
3478 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
3479 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
3485 xmb
.mb_cache_directory
= NULL
;
3486 xmb
.mb_imap_account
= sstrdup(mp
->mb_imap_account
);
3487 xmb
.mb_imap_pass
= sstrdup(mp
->mb_imap_pass
);
3488 memcpy(&xmb
.mb_imap_delim
[0], &mp
->mb_imap_delim
[0],
3489 sizeof(xmb
.mb_imap_delim
));
3490 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3491 if (mp
->mb_cache_directory
!= NULL
)
3492 xmb
.mb_cache_directory
= sstrdup(mp
->mb_cache_directory
);
3493 xmb
.mb_uidvalidity
= uidvalidity
;
3497 memset(&xm
, 0, sizeof xm
);
3499 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
3501 getcache(mp
, &xm
, NEED_HEADER
);
3502 getcache(mp
, &xm
, NEED_BODY
);
3504 if ((m
->m_flag
& HAVE_HEADER
) == 0)
3505 getcache(mp
, m
, NEED_HEADER
);
3506 if ((m
->m_flag
& HAVE_BODY
) == 0)
3507 getcache(mp
, m
, NEED_BODY
);
3511 xm
.m_flag
&= ~MFULLYCACHED
;
3512 putcache(&xmb
, &xm
);
3514 if (xmb
.mb_cache_directory
!= NULL
)
3515 free(xmb
.mb_cache_directory
);
3516 if (xmb
.mb_imap_mailbox
!= NULL
)
3517 free(xmb
.mb_imap_mailbox
);
3518 if (xmb
.mb_imap_pass
!= NULL
)
3519 free(xmb
.mb_imap_pass
);
3520 if (xmb
.mb_imap_account
!= NULL
)
3521 free(xmb
.mb_imap_account
);
3527 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
3528 long size
, long lines
, int flag
, const char *name
)
3533 unsigned long uidvalidity
, uid
;
3539 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
3540 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
3546 xmb
.mb_cache_directory
= NULL
;
3547 /* XXX mb_imap_delim reused */
3548 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3549 xmb
.mb_uidvalidity
= uidvalidity
;
3550 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
3552 memset(&xm
, 0, sizeof xm
);
3553 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
3555 xm
.m_block
= mailx_blockof(off1
);
3556 xm
.m_offset
= mailx_offsetof(off1
);
3559 xm
.m_lines
= xm
.m_xlines
= lines
;
3561 xm
.m_have
= HAVE_HEADER
| HAVE_BODY
;
3562 putcache(&xmb
, &xm
);
3564 free(xmb
.mb_imap_mailbox
);
3571 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
3575 long size
, xsize
, ysize
, lines
;
3576 enum mflag flag
= MNEW
;
3577 char *name
, *buf
, *bp
;
3579 size_t bufsize
, buflen
, cnt
;
3580 enum okay rv
= STOP
;
3583 buf
= smalloc(bufsize
= LINESIZE
);
3586 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3589 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3594 if ((cp
= strrchr(bp
, '{')) == NULL
)
3597 xsize
= atol(&cp
[1]) + 2;
3598 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3604 imap_getflags(cp
, &cp
, &flag
);
3605 while (*++cp
== ' ')
3608 t
= imap_read_date_time(cp
);
3610 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
3617 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3620 buf
[--buflen
] = '\0';
3621 buf
[buflen
-1] = '\n';
3622 fwrite(buf
, 1, buflen
, tp
);
3629 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3630 imap_unquotestr(name
));
3640 #ifdef HAVE_IMAP_SEARCH
3642 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3645 char *o
, *xp
, *cs
, c
;
3647 FILE *queuefp
= NULL
;
3651 enum okay ok
= STOP
;
3655 for (cp
= spec
; *cp
; cp
++)
3658 cp
= charset_get_lc();
3660 if (asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")) {
3665 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
3666 sz
= strlen(spec
) + 1;
3667 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
3668 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
, FAL0
) == 0 &&
3677 cp
= imap_quotestr(cp
);
3678 cs
= salloc(n
= strlen(cp
) + 10);
3679 snprintf(cs
, n
, "CHARSET %s ", cp
);
3683 o
= ac_alloc(osize
= strlen(spec
) + 60);
3684 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3685 IMAP_OUT(o
, MB_COMD
, goto out
)
3686 while (mp
->mb_active
& MB_COMD
) {
3687 ok
= imap_answer(mp
, 0);
3688 if (response_status
== RESPONSE_OTHER
&&
3689 response_other
== MAILBOX_DATA_SEARCH
) {
3690 xp
= responded_other_text
;
3691 while (*xp
&& *xp
!= '\r') {
3692 n
= strtoul(xp
, &xp
, 10);
3693 for (i
= 0; i
< cnt
; i
++)
3694 if (m
[i
].m_uid
== n
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3695 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3706 imap_search1(const char * volatile spec
, int f
)
3708 sighandler_type saveint
, savepipe
;
3709 enum okay
volatile rv
= STOP
;
3712 if (mb
.mb_type
!= MB_IMAP
)
3716 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3717 safe_signal(SIGINT
, &_imap_maincatch
);
3718 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3719 if (sigsetjmp(imapjmp
, 1) == 0) {
3720 if (savepipe
!= SIG_IGN
)
3721 safe_signal(SIGPIPE
, imapcatch
);
3723 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3725 safe_signal(SIGINT
, saveint
);
3726 safe_signal(SIGPIPE
, savepipe
);
3734 #endif /* HAVE_IMAP_SEARCH */
3737 imap_thisaccount(const char *cp
)
3742 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3744 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3745 mb
.mb_imap_account
== NULL
)
3748 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3754 imap_remove(const char * volatile name
)
3756 sighandler_type
volatile saveint
, savepipe
;
3757 enum okay
volatile rv
= STOP
;
3760 if (mb
.mb_type
!= MB_IMAP
) {
3761 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name
);
3765 if (!imap_thisaccount(name
)) {
3766 n_err(_("Can only remove mailboxes on current IMAP server: "
3767 "\"%s\" not removed\n"), name
);
3772 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3773 safe_signal(SIGINT
, &_imap_maincatch
);
3774 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3775 if (sigsetjmp(imapjmp
, 1) == 0) {
3776 if (savepipe
!= SIG_IGN
)
3777 safe_signal(SIGPIPE
, imapcatch
);
3779 rv
= imap_remove1(&mb
, imap_fileof(name
));
3781 safe_signal(SIGINT
, saveint
);
3782 safe_signal(SIGPIPE
, savepipe
);
3786 rv
= cache_remove(name
);
3795 imap_remove1(struct mailbox
*mp
, const char *name
)
3807 if((qname
= imap_path_quote(mp
, name
)) != NULL
){
3808 o
= ac_alloc(os
= strlen(qname
) + 100);
3809 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), qname
);
3810 IMAP_OUT(o
, MB_COMD
, goto out
)
3811 while (mp
->mb_active
& MB_COMD
)
3812 ok
= imap_answer(mp
, 1);
3820 imap_rename(const char *old
, const char *new)
3822 sighandler_type saveint
, savepipe
;
3823 enum okay rv
= STOP
;
3826 if (mb
.mb_type
!= MB_IMAP
) {
3827 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3831 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3832 n_err(_("Can only rename mailboxes on current IMAP "
3833 "server: \"%s\" not renamed to \"%s\"\n"), old
, new);
3838 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3839 safe_signal(SIGINT
, &_imap_maincatch
);
3840 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3841 if (sigsetjmp(imapjmp
, 1) == 0) {
3842 if (savepipe
!= SIG_IGN
)
3843 safe_signal(SIGPIPE
, imapcatch
);
3845 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3847 safe_signal(SIGINT
, saveint
);
3848 safe_signal(SIGPIPE
, savepipe
);
3852 rv
= cache_rename(old
, new);
3861 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3865 char const *qoname
, *qnname
;
3873 if((qoname
= imap_path_quote(mp
, old
)) != NULL
&&
3874 (qnname
= imap_path_quote(mp
, new)) != NULL
){
3875 o
= ac_alloc(os
= strlen(qoname
) + strlen(qnname
) + 100);
3876 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), qoname
, qnname
);
3877 IMAP_OUT(o
, MB_COMD
, goto out
)
3878 while (mp
->mb_active
& MB_COMD
)
3879 ok
= imap_answer(mp
, 1);
3887 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3889 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3890 size_t bufsize
, buflen
, cnt
;
3891 long offs
, offs1
, offs2
, octets
;
3892 int twice
, gotcha
= 0;
3893 FILE *queuefp
= NULL
;
3894 enum okay ok
= OKAY
, rok
= OKAY
;
3897 buf
= smalloc(bufsize
= LINESIZE
);
3900 while ((offs1
= ftell(fp
)) >= 0 &&
3901 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3902 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3907 if ((offs
= ftell(fp
)) < 0)
3910 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3911 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3913 while (digitchar(*cp
))
3919 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3921 IMAP_OUT(o
, MB_COMD
, continue)
3922 while (mp
->mb_active
& MB_COMD
)
3923 ok
= imap_answer(mp
, twice
);
3924 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3926 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3927 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3929 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3930 IMAP_OUT(o
, MB_COMD
, continue)
3931 while (mp
->mb_active
& MB_COMD
)
3932 ok
= imap_answer(mp
, 1);
3935 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3936 if ((cp
= strrchr(bp
, '{')) == NULL
)
3938 octets
= atol(&cp
[1]) + 2;
3939 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3941 IMAP_OUT(o
, MB_COMD
, continue)
3942 while (mp
->mb_active
& MB_COMD
) {
3943 ok
= imap_answer(mp
, twice
);
3944 if (response_type
== RESPONSE_CONT
)
3948 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3952 while (octets
> 0) {
3953 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3954 ? sizeof iob
: (size_t)octets
);
3956 if (n
!= fread(iob
, 1, n
, fp
))
3958 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3960 swrite(&mp
->mb_sock
, "");
3961 while (mp
->mb_active
& MB_COMD
) {
3962 ok
= imap_answer(mp
, 0);
3963 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
3964 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3969 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3970 if ((offs2
= ftell(fp
)) < 0)
3972 fseek(fp
, offs1
, SEEK_SET
);
3973 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3974 (void)fseek(fp
, offs2
, SEEK_SET
);
3980 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp
);
3985 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
3986 IMAP_OUT(o
, MB_COMD
, continue)
3987 while (mp
->mb_active
& MB_COMD
)
3988 ok
= imap_answer(mp
, 1);
3994 ftruncate(fileno(fp
), 0);
4002 imap_strex(char const *cp
, char const **xp
)
4011 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
4014 else if (*cq
== '"')
4020 n
= salloc(cq
- cp
+ 2);
4021 memcpy(n
, cp
, cq
- cp
+1);
4022 n
[cq
- cp
+ 1] = '\0';
4031 check_expunged(void)
4036 if (expunged_messages
> 0) {
4037 n_err(_("Command not executed - messages have been expunged\n"));
4046 c_connect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4049 int rv
, omsgCount
= msgCount
;
4053 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
4054 n_err(_("Already connected\n"));
4059 if (!url_parse(&url
, CPROTO_IMAP
, mailname
)) {
4063 ok_bclear(disconnected
);
4064 vok_bclear(savecat("disconnected-", url
.url_u_h_p
.s
));
4066 if (mb
.mb_type
== MB_CACHE
) {
4067 enum fedit_mode fm
= FEDIT_NONE
;
4070 if (!(pstate
& PS_EDIT
))
4072 _imap_setfile1(&url
, fm
, 1);
4073 if (msgCount
> omsgCount
)
4074 newmailinfo(omsgCount
);
4083 c_disconnect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4086 int rv
= 1, *msgvec
= vp
;
4089 if (mb
.mb_type
== MB_CACHE
) {
4090 n_err(_("Not connected\n"));
4093 if (mb
.mb_type
!= MB_IMAP
|| cached_uidvalidity(&mb
) == 0) {
4094 n_err(_("The current mailbox is not cached\n"));
4098 if (!url_parse(&url
, CPROTO_IMAP
, mailname
))
4103 ok_bset(disconnected
, TRU1
);
4104 if (mb
.mb_type
== MB_IMAP
) {
4105 enum fedit_mode fm
= FEDIT_NONE
;
4108 if (!(pstate
& PS_EDIT
))
4110 sclose(&mb
.mb_sock
);
4111 _imap_setfile1(&url
, fm
, 1);
4122 int rv
= 1, *msgvec
= vp
, *ip
;
4126 if (mb
.mb_type
!= MB_IMAP
) {
4127 n_err(_("Not connected to an IMAP server\n"));
4130 if (cached_uidvalidity(&mb
) == 0) {
4131 n_err(_("The current mailbox is not cached\n"));
4136 for (ip
= msgvec
; *ip
; ++ip
) {
4137 mp
= &message
[*ip
- 1];
4138 if (!(mp
->m_have
& HAVE_BODY
)) {
4151 disconnected(const char *file
)
4157 if (ok_blook(disconnected
)) {
4162 if (!url_parse(&url
, CPROTO_IMAP
, file
)) {
4166 rv
= vok_blook(savecat("disconnected-", url
.url_u_h_p
.s
));
4174 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
4176 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
4184 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
4185 PTRCMP(nmp
, <, message
+ msgCount
)) {
4186 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
4188 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
4190 if (omp
->m_uid
== nmp
->m_uid
) {
4191 hf
= nmp
->m_flag
& MHIDDEN
;
4192 if (transparent
&& mb
.mb_type
== MB_IMAP
)
4193 omp
->m_flag
&= ~MHIDDEN
;
4195 if (transparent
&& mb
.mb_type
== MB_CACHE
)
4196 nmp
[-1].m_flag
|= hf
;
4197 } else if (omp
->m_uid
< nmp
->m_uid
)
4204 prevdot
= newprevdot
;
4210 imap_read_date_time(const char *cp
)
4214 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
4217 /* "25-Jul-2004 15:33:44 +0200"
4219 * 0 5 10 15 20 25 */
4220 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
4222 day
= strtol(&cp
[1], NULL
, 10);
4224 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
4226 if (month_names
[++i
][0] == '\0')
4230 year
= strtol(&cp
[8], NULL
, 10);
4231 hour
= strtol(&cp
[13], NULL
, 10);
4232 minute
= strtol(&cp
[16], NULL
, 10);
4233 second
= strtol(&cp
[19], NULL
, 10);
4234 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
4248 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
4251 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
4261 imap_make_date_time(time_t t
)
4265 int tzdiff
, tzdiff_hour
, tzdiff_min
;
4268 tzdiff
= t
- mktime(gmtime(&t
));
4269 tzdiff_hour
= (int)(tzdiff
/ 60);
4270 tzdiff_min
= tzdiff_hour
% 60;
4272 tmptr
= localtime(&t
);
4273 if (tmptr
->tm_isdst
> 0)
4275 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4276 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
], tmptr
->tm_year
+ 1900,
4277 tmptr
->tm_hour
, tmptr
->tm_min
, tmptr
->tm_sec
, tzdiff_hour
, tzdiff_min
);
4281 #endif /* HAVE_IMAP */
4283 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
4285 imap_quotestr(char const *s
)
4290 np
= n
= salloc(2 * strlen(s
) + 3);
4293 if (*s
== '"' || *s
== '\\')
4304 imap_unquotestr(char const *s
)
4314 np
= n
= salloc(strlen(s
) + 1);
4327 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */