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);
454 s7e
= 0xD800u
| (utf32
>> 10);
455 be16p
[1] = s7e
& 0xFF;
456 be16p
[0] = (s7e
>>= 8, s7e
&= 0xFF);
457 s7e
= 0xDC00u
| (utf32
&= 0x03FF);
458 be16p
[3] = s7e
& 0xFF;
459 be16p
[2] = (s7e
>>= 8, s7e
&= 0xFF);
465 if((c
= *cp
) > 0x1F && c
< 0x7F)
469 /* And then warp that UTF-16BE to mUTF-7 */
470 out
.s
[out
.l
++] = '&';
471 utf32
= (ui32_t
)PTR2SIZE(be16p
- be16p_base
);
474 for(; utf32
>= 3; be16p
+= 3, utf32
-= 3){
475 out
.s
[out
.l
+0] = mb64ct
[ be16p
[0] >> 2 ];
476 out
.s
[out
.l
+1] = mb64ct
[((be16p
[0] & 0x03) << 4) | (be16p
[1] >> 4)];
477 out
.s
[out
.l
+2] = mb64ct
[((be16p
[1] & 0x0F) << 2) | (be16p
[2] >> 6)];
478 out
.s
[out
.l
+3] = mb64ct
[ be16p
[2] & 0x3F];
482 out
.s
[out
.l
+ 0] = mb64ct
[be16p
[0] >> 2];
484 out
.s
[out
.l
+ 1] = mb64ct
[ (be16p
[0] & 0x03) << 4];
487 out
.s
[out
.l
+ 1] = mb64ct
[((be16p
[0] & 0x03) << 4) |
489 out
.s
[out
.l
+ 2] = mb64ct
[ (be16p
[1] & 0x0F) << 2];
493 out
.s
[out
.l
++] = '-';
497 assert(out
.l
<= l_plain
);
504 n_err(_("Cannot encode IMAP path %s\n %s\n"), cp
, V_(emsg
));
509 imap_path_decode(char const *path
, bool_t
*err_or_null
){
510 /* To a large extend inspired by dovecot(1) TODO use string */
513 ui8_t
*mb64p_base
, *mb64p
, *mb64xp
;
514 char const *emsg
, *cp
;
515 char *rv_base
, *rv
, c
;
519 if(err_or_null
== NULL
)
520 err_or_null
= &err_def
;
523 l
= l_orig
= strlen(path
);
524 rv
= rv_base
= salloc(l
<< 1);
525 memcpy(rv
, path
, l
+1);
527 /* xxx Don't check for invalid characters from malicious servers */
528 if(l
== 0 || (cp
= memchr(path
, '&', l
)) == NULL
)
533 emsg
= N_("Invalid mUTF-7 encoding");
534 i
= PTR2SIZE(cp
- path
);
540 if((c
= *cp
) != '&'){
541 if(c
<= 0x1F || c
>= 0x7F){
542 emsg
= N_("Invalid mUTF-7: unencoded control or 8-bit byte");
550 else if(*++cp
== '-'){
556 emsg
= N_("Invalid mUTF-7: incomplete input");
559 /* mUTF-7 -> UTF-16BE -> UTF-8 */
560 static ui8_t
const mb64dt
[256] = {
563 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
564 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
565 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,62, 63,XX
,XX
,XX
,
566 52,53,54,55, 56,57,58,59, 60,61,XX
,XX
, XX
,XX
,XX
,XX
,
567 XX
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
568 15,16,17,18, 19,20,21,22, 23,24,25,XX
, XX
,XX
,XX
,XX
,
569 XX
,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
570 41,42,43,44, 45,46,47,48, 49,50,51,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
,
577 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
,
578 XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
, XX
,XX
,XX
,XX
581 if(mb64p_base
== NULL
)
582 mb64p_base
= salloc(l
);
584 /* Decode the mUTF-7 to what is indeed UTF-16BE */
585 for(mb64p
= mb64p_base
;;){
587 if((mb64p
[0] = mb64dt
[(ui8_t
)cp
[0]]) == XX
||
588 (mb64p
[1] = mb64dt
[(ui8_t
)cp
[1]]) == XX
)
597 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
603 if((c
= *cp
++) == '-')
605 if((*mb64p
++ = mb64dt
[(ui8_t
)c
]) == XX
)
609 if(l
> 0 && *cp
== '-'){
619 if(l
>= 2 && cp
[0] == '&' && cp
[1] != '-'){
620 emsg
= N_("Invalid mUTF-7, consecutive encoded sequences");
624 /* Yet halfway decoded mUTF-7, go remaining way to gain UTF-16BE */
625 i
= PTR2SIZE(mb64p
- mb64p_base
);
626 mb64p
= mb64xp
= mb64p_base
;
629 ui8_t ul
, u0
, u1
, u2
, u3
;
631 ul
= (i
>= 4) ? 4 : i
& 0x3;
635 u2
= (ul
< 3) ? 0 : mb64xp
[2];
636 u3
= (ul
< 4) ? 0 : mb64xp
[3];
638 *mb64p
++ = (u0
<<= 2) | (u1
>> 4);
641 *mb64p
++ = (u1
<<= 4) | (u2
>> 2);
644 *mb64p
++ = (u2
<<= 6, u2
&= 0xC0) | u3
;
647 /* UTF-16BE we convert to UTF-8 */
648 i
= PTR2SIZE(mb64p
- mb64p_base
);
650 emsg
= N_("Odd bytecount for UTF-16BE input");
654 /* TODO S-CText: magic utf16 conversions */
655 emsg
= N_("Invalid UTF-16BE encoding");
657 for(mb64p
= mb64p_base
; i
> 0;){
665 /* Not a surrogate? */
666 if(uhi
< 0xD800 || uhi
> 0xDFFF){
670 }else if(uhi
> 0xDBFF)
673 emsg
= N_("Incomplete UTF-16BE surrogate pair");
679 if(ulo
< 0xDC00 || ulo
> 0xDFFF)
682 utf32
= (uhi
&= 0x03FF);
685 utf32
|= (ulo
&= 0x03FF);
690 utf32
= n_utf32_to_utf8(utf32
, rv
);
697 /* We can skip the UTF-8 conversion occasionally */
698 if(!(options
& OPT_UNICODE
)){
702 emsg
= N_("iconv(3) from UTF-8 to locale charset failed");
704 if((icd
= iconv_open(charset_get_lc(), "utf-8")) == (iconv_t
)-1)
707 out
.s
= NULL
, out
.l
= 0;
708 in
.l
= strlen(in
.s
= rv_base
);
709 if((ir
= n_iconv_str(icd
, &out
, &in
, NULL
, FAL0
)) == 0)
710 /* Because the length of this is unpredictable, copy*/
711 rv_base
= savestrbuf(out
.s
, out
.l
);
727 n_err(_("Cannot decode IMAP path %s\n %s\n"), path
, V_(emsg
));
728 memcpy(rv
= rv_base
, path
, ++l_orig
);
733 imap_path_quote(struct mailbox
*mp
, char const *cp
){
738 cp
= imap_path_normalize(mp
, cp
);
739 cp
= imap_path_encode(cp
, &err
);
740 rv
= err
? NULL
: imap_quotestr(cp
);
746 imap_other_get(char *pp
)
751 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
753 response_other
= MAILBOX_DATA_FLAGS
;
754 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
756 response_other
= MAILBOX_DATA_LIST
;
757 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
759 response_other
= MAILBOX_DATA_LSUB
;
760 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
762 response_other
= MAILBOX_DATA_MAILBOX
;
763 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
765 response_other
= MAILBOX_DATA_SEARCH
;
766 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
768 response_other
= MAILBOX_DATA_STATUS
;
769 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
771 response_other
= CAPABILITY_DATA
;
773 responded_other_number
= strtol(pp
, &xp
, 10);
776 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
777 response_other
= MAILBOX_DATA_EXISTS
;
778 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
779 response_other
= MAILBOX_DATA_RECENT
;
780 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
781 response_other
= MESSAGE_DATA_EXPUNGE
;
782 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
784 response_other
= MESSAGE_DATA_FETCH
;
786 response_other
= RESPONSE_OTHER_UNKNOWN
;
788 responded_other_text
= pp
;
793 imap_response_get(const char **cp
)
796 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
798 response_status
= RESPONSE_OK
;
799 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
801 response_status
= RESPONSE_NO
;
802 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
804 response_status
= RESPONSE_BAD
;
805 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
807 response_status
= RESPONSE_PREAUTH
;
808 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
810 response_status
= RESPONSE_BYE
;
812 response_status
= RESPONSE_OTHER
;
817 imap_response_parse(void)
819 static char *parsebuf
; /* TODO Use pool */
820 static size_t parsebufsize
;
822 const char *ip
= imapbuf
;
826 if (parsebufsize
< imapbufsize
)
827 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
828 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
832 response_type
= RESPONSE_CONT
;
847 imap_response_get(&ip
);
848 pp
= &parsebuf
[ip
- imapbuf
];
849 switch (response_status
) {
851 response_type
= RESPONSE_FATAL
;
854 response_type
= RESPONSE_DATA
;
858 responded_tag
= parsebuf
;
859 while (*pp
&& *pp
!= ' ')
862 response_type
= RESPONSE_ILLEGAL
;
866 while (*pp
&& *pp
== ' ')
869 response_type
= RESPONSE_ILLEGAL
;
872 ip
= &imapbuf
[pp
- parsebuf
];
873 response_type
= RESPONSE_TAGGED
;
874 imap_response_get(&ip
);
875 pp
= &parsebuf
[ip
- imapbuf
];
878 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
879 response_status
== RESPONSE_OTHER
)
885 imap_answer(struct mailbox
*mp
, int errprnt
)
892 if (mp
->mb_type
== MB_CACHE
)
896 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
897 if (options
& OPT_VERBVERB
)
898 fputs(imapbuf
, stderr
);
899 imap_response_parse();
900 if (response_type
== RESPONSE_ILLEGAL
)
902 if (response_type
== RESPONSE_CONT
) {
906 if (response_status
== RESPONSE_OTHER
) {
907 if (response_other
== MAILBOX_DATA_EXISTS
) {
908 had_exists
= responded_other_number
;
909 rec_queue(REC_EXISTS
, responded_other_number
);
912 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
913 rec_queue(REC_EXPUNGE
, responded_other_number
);
921 if (response_type
== RESPONSE_TAGGED
) {
922 if (asccasecmp(responded_tag
, tag(0)) == 0)
927 switch (response_status
) {
928 case RESPONSE_PREAUTH
:
929 mp
->mb_active
&= ~MB_PREAUTH
;
942 n_err(_("IMAP error: %s"), responded_text
);
944 case RESPONSE_UNKNOWN
: /* does not happen */
947 mp
->mb_active
= MB_NONE
;
955 if (response_status
!= RESPONSE_OTHER
&&
956 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
957 n_err(_("IMAP alert: %s"), &responded_text
[8]);
959 mp
->mb_active
&= ~MB_COMD
;
962 mp
->mb_active
= MB_NONE
;
970 imap_parse_list(void)
978 cp
= responded_other_text
;
979 list_attributes
= LIST_NONE
;
981 while (*cp
&& *cp
!= ')') {
983 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
984 list_attributes
|= LIST_NOINFERIORS
;
986 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
987 list_attributes
|= LIST_NOSELECT
;
989 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
990 list_attributes
|= LIST_MARKED
;
992 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
993 list_attributes
|= LIST_UNMARKED
;
1005 list_hierarchy_delimiter
= EOF
;
1009 list_hierarchy_delimiter
= *cp
++ & 0377;
1010 if (cp
[0] != '"' || cp
[1] != ' ')
1013 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
1014 list_hierarchy_delimiter
= EOF
;
1021 while (*cp
&& *cp
!= '\r')
1031 imap_finish(struct mailbox
*mp
)
1034 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
1041 imap_timer_off(void)
1044 if (imapkeepalive
> 0) {
1046 safe_signal(SIGALRM
, savealrm
);
1054 NYD_X
; /* Signal handler */
1057 n_err_sighdl(_("Interrupt\n"));
1058 siglongjmp(imapjmp
, 1);
1061 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
1067 _imap_maincatch(int s
)
1069 NYD_X
; /* Signal handler */
1071 if (interrupts
++ == 0) {
1072 n_err_sighdl(_("Interrupt\n"));
1079 imap_noop1(struct mailbox
*mp
)
1082 FILE *queuefp
= NULL
;
1085 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
1086 IMAP_OUT(o
, MB_COMD
, return STOP
)
1092 imap_fileof(char const *xcp
)
1094 char const *cp
= xcp
;
1099 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
1103 if (cp
[0] == '/' && state
== 1) {
1121 sighandler_type
volatile oldint
, oldpipe
;
1122 enum okay rv
= STOP
;
1125 if (mb
.mb_type
!= MB_IMAP
)
1129 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1130 safe_signal(SIGINT
, &_imap_maincatch
);
1131 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1132 if (sigsetjmp(imapjmp
, 1) == 0) {
1133 if (oldpipe
!= SIG_IGN
)
1134 safe_signal(SIGPIPE
, imapcatch
);
1136 rv
= imap_noop1(&mb
);
1138 safe_signal(SIGINT
, oldint
);
1139 safe_signal(SIGPIPE
, oldpipe
);
1149 rec_queue(enum rec_type rt
, unsigned long cnt
)
1154 rp
= scalloc(1, sizeof *rp
);
1156 rp
->rec_count
= cnt
;
1157 if (record
&& recend
) {
1158 recend
->rec_next
= rp
;
1161 record
= recend
= rp
;
1168 struct message
*omessage
;
1169 struct record
*rp
, *rq
;
1170 uiz_t exists
= 0, i
;
1171 enum okay rv
= STOP
;
1178 message
= smalloc((msgCount
+1) * sizeof *message
);
1180 memcpy(message
, omessage
, msgCount
* sizeof *message
);
1181 memset(&message
[msgCount
], 0, sizeof *message
);
1183 rp
= record
, rq
= NULL
;
1185 while (rp
!= NULL
) {
1186 switch (rp
->rec_type
) {
1188 exists
= rp
->rec_count
;
1191 if (rp
->rec_count
== 0) {
1195 if (rp
->rec_count
> (unsigned long)msgCount
) {
1196 if (exists
== 0 || rp
->rec_count
> exists
--)
1202 delcache(&mb
, &message
[rp
->rec_count
-1]);
1203 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
1204 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
1206 /* If the message was part of a collapsed thread,
1207 * the m_collapsed field of one of its ancestors
1208 * should be incremented. It seems hardly possible
1209 * to do this with the current message structure,
1210 * though. The result is that a '+' may be shown
1211 * in the header summary even if no collapsed
1212 * children exists */
1223 record
= recend
= NULL
;
1224 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
1225 message
= srealloc(message
, (exists
+ 1) * sizeof *message
);
1226 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
1227 for (i
= msgCount
; i
< exists
; ++i
)
1229 imap_flags(&mb
, msgCount
+1, exists
);
1248 for (rp
= record
; rp
!= NULL
;) {
1249 struct record
*tmp
= rp
;
1253 record
= recend
= NULL
;
1261 sighandler_type
volatile saveint
, savepipe
;
1262 NYD_X
; /* Signal handler */
1265 if (imaplock
++ == 0) {
1266 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1267 safe_signal(SIGINT
, &_imap_maincatch
);
1268 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1269 if (sigsetjmp(imapjmp
, 1)) {
1270 safe_signal(SIGINT
, saveint
);
1271 safe_signal(SIGPIPE
, savepipe
);
1274 if (savepipe
!= SIG_IGN
)
1275 safe_signal(SIGPIPE
, imapcatch
);
1276 if (imap_noop1(&mb
) != OKAY
) {
1277 safe_signal(SIGINT
, saveint
);
1278 safe_signal(SIGPIPE
, savepipe
);
1281 safe_signal(SIGINT
, saveint
);
1282 safe_signal(SIGPIPE
, savepipe
);
1285 alarm(imapkeepalive
);
1291 imap_preauth(struct mailbox
*mp
, struct url
const *urlp
)
1295 mp
->mb_active
|= MB_PREAUTH
;
1299 if (!mp
->mb_sock
.s_use_ssl
&& xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1300 FILE *queuefp
= NULL
;
1303 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
1304 IMAP_OUT(o
, MB_COMD
, return STOP
)
1306 if (ssl_open(urlp
, &mp
->mb_sock
) != OKAY
)
1310 if (xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
1311 n_err(_("No SSL support compiled in\n"));
1316 imap_capability(mp
);
1321 imap_capability(struct mailbox
*mp
)
1324 FILE *queuefp
= NULL
;
1325 enum okay ok
= STOP
;
1329 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
1330 IMAP_OUT(o
, MB_COMD
, return STOP
)
1331 while (mp
->mb_active
& MB_COMD
) {
1332 ok
= imap_answer(mp
, 0);
1333 if (response_status
== RESPONSE_OTHER
&&
1334 response_other
== CAPABILITY_DATA
) {
1335 cp
= responded_other_text
;
1337 while (spacechar(*cp
))
1339 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
1341 mp
->mb_flags
|= MB_UIDPLUS
;
1342 while (*cp
&& !spacechar(*cp
))
1351 imap_auth(struct mailbox
*mp
, struct ccred
*ccred
)
1356 if (!(mp
->mb_active
& MB_PREAUTH
)) {
1361 switch (ccred
->cc_authtype
) {
1362 case AUTHTYPE_LOGIN
:
1363 rv
= imap_login(mp
, ccred
);
1366 case AUTHTYPE_CRAM_MD5
:
1367 rv
= imap_cram_md5(mp
, ccred
);
1371 case AUTHTYPE_GSSAPI
:
1372 rv
= _imap_gssapi(mp
, ccred
);
1386 imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
)
1388 char o
[LINESIZE
], *cp
;
1389 FILE *queuefp
= NULL
;
1390 enum okay rv
= STOP
;
1393 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1394 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
1396 if (response_type
!= RESPONSE_CONT
)
1399 cp
= cram_md5_string(&ccred
->cc_user
, &ccred
->cc_pass
, responded_text
);
1400 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
1401 while (mp
->mb_active
& MB_COMD
)
1402 rv
= imap_answer(mp
, 1);
1407 #endif /* HAVE_MD5 */
1410 imap_login(struct mailbox
*mp
, struct ccred
*ccred
)
1413 FILE *queuefp
= NULL
;
1414 enum okay rv
= STOP
;
1417 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
1418 tag(1), imap_quotestr(ccred
->cc_user
.s
), imap_quotestr(ccred
->cc_pass
.s
));
1419 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
1420 while (mp
->mb_active
& MB_COMD
)
1421 rv
= imap_answer(mp
, 1);
1428 # include "imap_gssapi.h"
1432 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
,
1436 char const *qname
, *cp
;
1445 if((qname
= imap_path_quote(mp
, mbx
)) == NULL
)
1450 mp
->mb_uidvalidity
= 0;
1451 snprintf(o
, sizeof o
, "%s %s %s\r\n", tag(1),
1452 (fm
& FEDIT_RDONLY
? "EXAMINE" : "SELECT"), qname
);
1453 IMAP_OUT(o
, MB_COMD
, ok
= STOP
;goto jleave
)
1454 while (mp
->mb_active
& MB_COMD
) {
1455 ok
= imap_answer(mp
, 1);
1456 if (response_status
!= RESPONSE_OTHER
&&
1457 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
1458 mp
->mb_uidvalidity
= atol(&cp
[13]);
1460 *cnt
= (had_exists
> 0) ? had_exists
: 0;
1461 if (response_status
!= RESPONSE_OTHER
&&
1462 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
1469 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
1472 FILE *queuefp
= NULL
;
1475 unsigned x
= X
, y
= Y
, n
;
1478 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
1479 IMAP_OUT(o
, MB_COMD
, return STOP
)
1480 while (mp
->mb_active
& MB_COMD
) {
1482 if (response_status
== RESPONSE_OTHER
&&
1483 response_other
== MESSAGE_DATA_FETCH
) {
1484 n
= responded_other_number
;
1492 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1497 imap_getflags(cp
, &cp
, &m
->m_flag
);
1500 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1501 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1502 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1503 m
->m_flag
&= ~MHIDDEN
;
1506 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1508 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1511 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1513 IMAP_OUT(o
, MB_COMD
, return STOP
)
1514 while (mp
->mb_active
& MB_COMD
) {
1516 if (response_status
== RESPONSE_OTHER
&&
1517 response_other
== MESSAGE_DATA_FETCH
) {
1518 n
= responded_other_number
;
1524 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1525 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1526 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1527 m
->m_time
= imap_read_date_time(&cp
[13]);
1532 for (n
= X
; n
<= Y
; ++n
) {
1533 putcache(mp
, &message
[n
-1]);
1541 imap_init(struct mailbox
*mp
, int n
)
1548 m
->m_flag
= MUSED
| MNOFROM
;
1555 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1557 struct message
*omessage
= 0;
1558 int i
, omsgCount
= 0;
1559 enum okay dequeued
= STOP
;
1562 if (nmail
|| transparent
) {
1564 omsgCount
= msgCount
;
1567 dequeued
= rec_dequeue();
1569 if (had_exists
>= 0) {
1570 if (dequeued
!= OKAY
)
1571 msgCount
= had_exists
;
1574 if (had_expunge
>= 0) {
1575 if (dequeued
!= OKAY
)
1576 msgCount
-= had_expunge
;
1580 if (nmail
&& expunged_messages
)
1581 printf("Expunged %ld message%s.\n", expunged_messages
,
1582 (expunged_messages
!= 1 ? "s" : ""));
1583 *prevcount
= omsgCount
- expunged_messages
;
1584 expunged_messages
= 0;
1586 fputs("IMAP error: Negative message count\n", stderr
);
1590 if (dequeued
!= OKAY
) {
1591 message
= scalloc(msgCount
+ 1, sizeof *message
);
1592 for (i
= 0; i
< msgCount
; i
++)
1594 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1597 imap_flags(mp
, 1, msgCount
);
1598 message
[msgCount
].m_size
= 0;
1599 message
[msgCount
].m_lines
= 0;
1602 if (nmail
|| transparent
)
1603 transflags(omessage
, omsgCount
, transparent
);
1610 imap_setfile(const char *xserver
, enum fedit_mode fm
)
1616 if (!url_parse(&url
, CPROTO_IMAP
, xserver
)) {
1620 if (!ok_blook(v15_compat
) &&
1621 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
1622 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1624 _imap_rdonly
= ((fm
& FEDIT_RDONLY
) != 0);
1625 rv
= _imap_setfile1(&url
, fm
, 0);
1632 _imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
, struct url
*urlp
)
1637 if (ok_blook(v15_compat
))
1638 rv
= ccred_lookup(ccredp
, urlp
);
1641 *xuhp
= (urlp
->url_had_user
? urlp
->url_eu_h_p
.s
: urlp
->url_u_h_p
.s
);
1643 if ((var
= mbp
->mb_imap_pass
) != NULL
) {
1644 var
= savecat("password-", xuhp
);
1645 if ((old
= vok_vlook(var
)) != NULL
)
1647 vok_vset(var
, mbp
->mb_imap_pass
);
1649 rv
= ccred_lookup_old(ccredp
, CPROTO_IMAP
, xuhp
);
1664 _imap_setfile1(struct url
*urlp
, enum fedit_mode
volatile fm
,
1665 int volatile transparent
)
1669 sighandler_type
volatile saveint
, savepipe
;
1672 int volatile prevcount
= 0;
1673 enum mbflags same_flags
;
1676 if (fm
& FEDIT_NEWMAIL
) {
1677 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1678 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1679 if (saveint
!= SIG_IGN
)
1680 safe_signal(SIGINT
, imapcatch
);
1681 if (savepipe
!= SIG_IGN
)
1682 safe_signal(SIGPIPE
, imapcatch
);
1687 same_flags
= mb
.mb_flags
;
1688 same_imap_account
= 0;
1689 if (mb
.mb_imap_account
!= NULL
&&
1690 (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)) {
1691 if (mb
.mb_sock
.s_fd
> 0 && mb
.mb_sock
.s_rsz
>= 0 &&
1692 !strcmp(mb
.mb_imap_account
, urlp
->url_p_eu_h_p
) &&
1693 disconnected(mb
.mb_imap_account
) == 0) {
1694 same_imap_account
= 1;
1695 if (urlp
->url_pass
.s
== NULL
&& mb
.mb_imap_pass
!= NULL
)
1698 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1699 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1700 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1703 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= savestr(mb
.mb_imap_pass
));
1707 if (!same_imap_account
&& mb
.mb_imap_pass
!= NULL
) {
1708 free(mb
.mb_imap_pass
);
1709 mb
.mb_imap_pass
= NULL
;
1711 if (!_imap_getcred(&mb
, &ccred
, urlp
)) {
1717 if (!same_imap_account
) {
1718 if (!disconnected(urlp
->url_p_eu_h_p
) && !sopen(&so
, urlp
)) {
1727 if (fm
& FEDIT_SYSBOX
)
1731 if (mb
.mb_imap_account
!= NULL
)
1732 free(mb
.mb_imap_account
);
1733 if (mb
.mb_imap_pass
!= NULL
)
1734 free(mb
.mb_imap_pass
);
1735 mb
.mb_imap_account
= sstrdup(urlp
->url_p_eu_h_p
);
1736 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1737 * TODO object, and mailbox will naturally have an URL and credentials */
1738 mb
.mb_imap_pass
= sbufdup(ccred
.cc_pass
.s
, ccred
.cc_pass
.l
);
1740 if (!same_imap_account
) {
1741 if (mb
.mb_sock
.s_fd
>= 0)
1742 sclose(&mb
.mb_sock
);
1744 same_imap_account
= 0;
1755 if (mb
.mb_imap_mailbox
!= NULL
)
1756 free(mb
.mb_imap_mailbox
);
1757 assert(urlp
->url_path
.s
!= NULL
);
1758 imap_delim_init(&mb
, urlp
);
1759 mb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mb
, urlp
->url_path
.s
));
1760 initbox(savecatsep(urlp
->url_p_eu_h_p
,
1761 (mb
.mb_imap_delim
[0] != '\0' ? mb
.mb_imap_delim
[0] : n_IMAP_DELIM
[0]),
1762 mb
.mb_imap_mailbox
));
1764 mb
.mb_type
= MB_VOID
;
1765 mb
.mb_active
= MB_NONE
;
1768 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1769 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1770 if (sigsetjmp(imapjmp
, 1)) {
1771 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1772 sclose(&mb
.mb_sock
);
1773 safe_signal(SIGINT
, saveint
);
1774 safe_signal(SIGPIPE
, savepipe
);
1777 mb
.mb_type
= MB_VOID
;
1778 mb
.mb_active
= MB_NONE
;
1779 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1782 if (saveint
!= SIG_IGN
)
1783 safe_signal(SIGINT
, imapcatch
);
1784 if (savepipe
!= SIG_IGN
)
1785 safe_signal(SIGPIPE
, imapcatch
);
1787 if (mb
.mb_sock
.s_fd
< 0) {
1788 if (disconnected(mb
.mb_imap_account
)) {
1789 if (cache_setptr(fm
, transparent
) == STOP
)
1790 n_err(_("Mailbox \"%s\" is not cached\n"), urlp
->url_p_eu_h_p_p
);
1793 if ((cp
= xok_vlook(imap_keepalive
, urlp
, OXM_ALL
)) != NULL
) {
1794 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1795 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1796 alarm(imapkeepalive
);
1801 mb
.mb_sock
.s_desc
= "IMAP";
1802 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1803 if (imap_preauth(&mb
, urlp
) != OKAY
|| imap_auth(&mb
, &ccred
) != OKAY
) {
1804 sclose(&mb
.mb_sock
);
1806 safe_signal(SIGINT
, saveint
);
1807 safe_signal(SIGPIPE
, savepipe
);
1809 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1812 } else /* same account */
1813 mb
.mb_flags
|= same_flags
;
1815 if (options
& OPT_R_FLAG
)
1817 mb
.mb_perm
= (fm
& FEDIT_RDONLY
) ? 0 : MB_DELE
;
1818 mb
.mb_type
= MB_IMAP
;
1820 assert(urlp
->url_path
.s
!= NULL
);
1821 if (imap_select(&mb
, &mailsize
, &msgCount
, urlp
->url_path
.s
, fm
) != OKAY
) {
1822 /*sclose(&mb.mb_sock);
1824 safe_signal(SIGINT
, saveint
);
1825 safe_signal(SIGPIPE
, savepipe
);
1827 mb
.mb_type
= MB_VOID
;
1828 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1833 imap_setptr(&mb
, ((fm
& FEDIT_NEWMAIL
) != 0), transparent
,
1834 UNVOLATILE(&prevcount
));
1837 if (!(fm
& FEDIT_NEWMAIL
) && !transparent
)
1838 pstate
&= ~PS_SAW_COMMAND
;
1839 safe_signal(SIGINT
, saveint
);
1840 safe_signal(SIGPIPE
, savepipe
);
1843 if (!(fm
& FEDIT_NEWMAIL
) && mb
.mb_type
== MB_IMAP
)
1844 purgecache(&mb
, message
, msgCount
);
1845 if (((fm
& FEDIT_NEWMAIL
) || transparent
) && mb
.mb_sorted
) {
1850 if ((options
& OPT_EXISTONLY
) && (mb
.mb_type
== MB_IMAP
||
1851 mb
.mb_type
== MB_CACHE
)) {
1852 rv
= (msgCount
== 0);
1856 if (!(fm
& FEDIT_NEWMAIL
) && !(pstate
& PS_EDIT
) && msgCount
== 0) {
1857 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1858 !ok_blook(emptystart
))
1859 n_err(_("No mail at %s\n"), urlp
->url_p_eu_h_p_p
);
1864 if (fm
& FEDIT_NEWMAIL
)
1865 newmailinfo(prevcount
);
1873 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1874 int need
, const char *head
, size_t headsize
, long headlines
)
1876 char *line
= NULL
, *lp
;
1877 size_t linesize
= 0, linelen
, size
= 0;
1878 int emptyline
= 0, lines
= 0, excess
= 0;
1882 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1883 offset
= ftell(mp
->mb_otf
);
1886 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1888 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1890 if (linelen
> expected
) {
1891 excess
= linelen
- expected
;
1895 * Need to mask 'From ' lines. This cannot be done properly
1896 * since some servers pass them as 'From ' and others as
1897 * '>From '. Although one could identify the first kind of
1898 * server in principle, it is not possible to identify the
1899 * second as '>From ' may also come from a server of the
1900 * first type as actual data. So do what is absolutely
1901 * necessary only - mask 'From '.
1903 * If the line is the first line of the message header, it
1904 * is likely a real 'From ' line. In this case, it is just
1905 * ignored since it violates all standards.
1906 * TODO can the latter *really* happen??
1909 /* Since we simply copy over data without doing any transfer
1910 * encoding reclassification/adjustment we *have* to perform
1911 * RFC 4155 compliant From_ quoting here */
1912 if (emptyline
&& is_head(lp
, linelen
, FAL0
)) {
1913 fputc('>', mp
->mb_otf
);
1917 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1919 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1920 size
+= linelen
- 1;
1925 fputc('\n', mp
->mb_otf
);
1927 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1931 if ((expected
-= linelen
) <= 0)
1935 /* This is very ugly; but some IMAP daemons don't end a
1936 * message with \r\n\r\n, and we need \n\n for mbox format */
1937 fputc('\n', mp
->mb_otf
);
1944 m
->m_size
= size
+ headsize
;
1945 m
->m_lines
= lines
+ headlines
;
1946 m
->m_block
= mailx_blockof(offset
);
1947 m
->m_offset
= mailx_offsetof(offset
);
1950 m
->m_have
|= HAVE_HEADER
;
1953 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1954 m
->m_xlines
= m
->m_lines
;
1955 m
->m_xsize
= m
->m_size
;
1965 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1966 const char *head
, size_t headsize
, long headlines
)
1973 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1974 offset
= ftell(mp
->mb_otf
);
1976 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1978 fwrite(str
, 1, len
, mp
->mb_otf
);
1979 fputc('\n', mp
->mb_otf
);
1985 m
->m_size
= headsize
+ len
;
1986 m
->m_lines
= headlines
+ 1;
1987 m
->m_block
= mailx_blockof(offset
);
1988 m
->m_offset
= mailx_offsetof(offset
);
1989 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1990 m
->m_xlines
= m
->m_lines
;
1991 m
->m_xsize
= m
->m_size
;
1997 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
2001 sighandler_type
volatile saveint
, savepipe
;
2002 char * volatile head
;
2003 char const *cp
, *loc
, * volatile item
, * volatile resp
;
2005 size_t volatile headsize
;
2008 long volatile headlines
;
2014 saveint
= savepipe
= SIG_IGN
;
2016 cp
= loc
= item
= resp
= NULL
;
2018 number
= (int)PTR2SIZE(m
- message
+ 1);
2025 if (getcache(mp
, m
, need
) == OKAY
)
2027 if (mp
->mb_type
== MB_CACHE
) {
2028 n_err(_("Message %lu not available\n"), (ul_i
)number
);
2032 if (mp
->mb_sock
.s_fd
< 0) {
2033 n_err(_("IMAP connection closed\n"));
2039 resp
= item
= "RFC822.HEADER";
2042 item
= "BODY.PEEK[]";
2044 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
2045 char *hdr
= smalloc(m
->m_size
);
2047 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
2049 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
2054 headsize
= m
->m_size
;
2055 headlines
= m
->m_lines
;
2056 item
= "BODY.PEEK[TEXT]";
2057 resp
= "BODY[TEXT]";
2065 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2066 if (sigsetjmp(imapjmp
, 1)) {
2067 safe_signal(SIGINT
, saveint
);
2068 safe_signal(SIGPIPE
, savepipe
);
2072 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2073 safe_signal(SIGINT
, &_imap_maincatch
);
2074 if (savepipe
!= SIG_IGN
)
2075 safe_signal(SIGPIPE
, imapcatch
);
2078 snprintf(o
, sizeof o
, "%s UID FETCH %lu (%s)\r\n",
2079 tag(1), m
->m_uid
, item
);
2081 if (check_expunged() == STOP
)
2083 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
2085 IMAP_OUT(o
, MB_COMD
, goto out
)
2087 ok
= imap_answer(mp
, 1);
2090 if (response_status
!= RESPONSE_OTHER
||
2091 response_other
!= MESSAGE_DATA_FETCH
)
2093 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
2096 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
2104 n
= responded_other_number
;
2105 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
2106 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
2108 if ((cp
= strchr(loc
, '"')) != NULL
) {
2109 cp
= imap_unquotestr(cp
);
2110 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
2112 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
2113 m
->m_xlines
= m
->m_lines
;
2114 m
->m_xsize
= m
->m_size
;
2118 expected
= atol(&cp
[1]);
2119 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
2120 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
2124 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
2126 commitmsg(mp
, m
, &mt
, mt
.m_have
);
2129 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2130 if (options
& OPT_VERBVERB
)
2131 fputs(imapbuf
, stderr
);
2132 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2134 if (u
== m
->m_uid
) {
2135 commitmsg(mp
, m
, &mt
, mt
.m_have
);
2142 while (mp
->mb_active
& MB_COMD
)
2143 ok
= imap_answer(mp
, 1);
2145 if (saveint
!= SIG_IGN
)
2146 safe_signal(SIGINT
, saveint
);
2147 if (savepipe
!= SIG_IGN
)
2148 safe_signal(SIGPIPE
, savepipe
);
2161 imap_header(struct message
*m
)
2166 rv
= imap_get(&mb
, m
, NEED_HEADER
);
2173 imap_body(struct message
*m
)
2178 rv
= imap_get(&mb
, m
, NEED_BODY
);
2184 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
2188 tomp
->m_size
= frommp
->m_size
;
2189 tomp
->m_lines
= frommp
->m_lines
;
2190 tomp
->m_block
= frommp
->m_block
;
2191 tomp
->m_offset
= frommp
->m_offset
;
2192 tomp
->m_have
= have
;
2193 if (have
& HAVE_BODY
) {
2194 tomp
->m_xlines
= frommp
->m_lines
;
2195 tomp
->m_xsize
= frommp
->m_size
;
2202 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
2210 FILE *queuefp
= NULL
;
2215 snprintf(o
, sizeof o
, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
2216 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
2218 if (check_expunged() == STOP
)
2220 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
2223 IMAP_OUT(o
, MB_COMD
, return STOP
)
2227 ok
= imap_answer(mp
, 1);
2228 if (response_status
!= RESPONSE_OTHER
)
2230 if (response_other
!= MESSAGE_DATA_FETCH
)
2232 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0) {
2236 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
2238 expected
= atol(&cp
[1]);
2239 if (m
[bot
-1].m_uid
) {
2240 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
2242 for (n
= bot
; n
<= topp
; n
++)
2243 if ((unsigned long)u
== m
[n
-1].m_uid
)
2246 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2252 n
= responded_other_number
;
2253 if (n
<= 0 || n
> msgCount
) {
2254 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
2258 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
2259 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
2260 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
2261 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
2262 if (options
& OPT_VERBVERB
)
2263 fputs(imapbuf
, stderr
);
2264 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
2266 for (n
= bot
; n
<= topp
; n
++)
2267 if ((unsigned long)u
== m
[n
-1].m_uid
)
2269 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
2270 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
2278 while (mp
->mb_active
& MB_COMD
)
2279 ok
= imap_answer(mp
, 1);
2284 imap_getheaders(int volatile bot
, int volatile topp
) /* TODO iterator!! */
2286 sighandler_type saveint
, savepipe
;
2287 /*enum okay ok = STOP;*/
2291 if (mb
.mb_type
== MB_CACHE
)
2295 if (topp
> msgCount
)
2297 for (i
= bot
; i
< topp
; i
++) {
2298 if (message
[i
-1].m_have
& HAVE_HEADER
||
2299 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2304 for (i
= topp
; i
> bot
; i
--) {
2305 if (message
[i
-1].m_have
& HAVE_HEADER
||
2306 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
2315 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2316 safe_signal(SIGINT
, &_imap_maincatch
);
2317 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2318 if (sigsetjmp(imapjmp
, 1) == 0) {
2319 if (savepipe
!= SIG_IGN
)
2320 safe_signal(SIGPIPE
, imapcatch
);
2322 for (i
= bot
; i
<= topp
; i
+= chunk
) {
2323 int j
= i
+ chunk
- 1;
2325 if (visible(message
+ j
))
2326 /*ok = */imap_fetchheaders(&mb
, message
, i
, j
);
2328 onintr(0); /* XXX imaplock? */
2331 safe_signal(SIGINT
, saveint
);
2332 safe_signal(SIGPIPE
, savepipe
);
2337 __imap_exit(struct mailbox
*mp
)
2340 FILE *queuefp
= NULL
;
2343 mp
->mb_active
|= MB_BYE
;
2344 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
2345 IMAP_OUT(o
, MB_COMD
, return STOP
)
2351 imap_exit(struct mailbox
*mp
)
2356 rv
= __imap_exit(mp
);
2357 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
2358 free(mp
->mb_imap_pass
);
2359 free(mp
->mb_imap_account
);
2360 free(mp
->mb_imap_mailbox
);
2361 if (mp
->mb_cache_directory
!= NULL
)
2362 free(mp
->mb_cache_directory
);
2363 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
2364 mp
->mb_imap_account
=
2365 mp
->mb_imap_mailbox
=
2366 mp
->mb_cache_directory
= "";
2368 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
2369 mp
->mb_imap_mailbox
= NULL
;
2370 mp
->mb_cache_directory
= NULL
;
2373 sclose(&mp
->mb_sock
);
2379 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
2382 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
2383 if (mp
->mb_type
== MB_IMAP
)
2390 imap_close(struct mailbox
*mp
)
2393 FILE *queuefp
= NULL
;
2396 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
2397 IMAP_OUT(o
, MB_COMD
, return STOP
)
2403 imap_update(struct mailbox
*mp
)
2406 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
2409 if (!(pstate
& PS_EDIT
) && mp
->mb_perm
!= 0) {
2412 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2413 if (m
->m_flag
& MBOX
)
2416 if (makembox() == STOP
)
2421 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
2422 if (mp
->mb_perm
== 0)
2424 else if (pstate
& PS_EDIT
)
2425 dodel
= ((m
->m_flag
& MDELETED
) != 0);
2427 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
2429 /* Fetch the result after around each 800 STORE commands
2430 * sent (approx. 32k data sent). Otherwise, servers will
2431 * try to flush the return queue at some point, leading
2432 * to a deadlock if we are still writing commands but not
2433 * reading their results */
2434 needstat
= stored
> 0 && stored
% 800 == 0;
2435 /* Even if this message has been deleted, continue
2436 * to set further flags. This is necessary to support
2437 * Gmail semantics, where "delete" actually means
2438 * "archive", and the flags are applied to the copy
2440 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2441 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
2444 if (m
->m_flag
& MFLAG
) {
2445 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
2448 if (m
->m_flag
& MUNFLAG
) {
2449 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
2452 if (m
->m_flag
& MANSWER
) {
2453 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
2456 if (m
->m_flag
& MUNANSWER
) {
2457 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
2460 if (m
->m_flag
& MDRAFT
) {
2461 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
2464 if (m
->m_flag
& MUNDRAFT
) {
2465 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
2470 imap_delete(mp
, m
-message
+1, m
, needstat
);
2473 } else if (mp
->mb_type
!= MB_CACHE
||
2474 (!(pstate
& PS_EDIT
) &&
2475 !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
2476 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
2477 (MPRESERVE
| MTOUCH
) ||
2478 ((pstate
& PS_EDIT
) && !(m
->m_flag
& MDELETED
)))
2480 if (m
->m_flag
& MNEW
) {
2482 m
->m_flag
|= MSTATUS
;
2489 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2490 if (!(m
->m_flag
& MUNLINKED
) &&
2491 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
2492 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
2497 /* XXX should be readonly (but our IMAP code is weird...) */
2498 if (!(options
& (OPT_EXISTONLY
| OPT_HEADERSONLY
| OPT_HEADERLIST
)) &&
2500 if ((gotcha
|| modflags
) && (pstate
& PS_EDIT
)) {
2501 printf(_("\"%s\" "), displayname
);
2502 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2503 ? _("complete\n") : _("updated.\n"));
2504 } else if (held
&& !(pstate
& PS_EDIT
)) {
2506 printf(_("Held 1 message in %s\n"), displayname
);
2508 printf(_("Held %d messages in %s\n"), held
, displayname
);
2519 sighandler_type
volatile saveint
, savepipe
;
2522 if (mb
.mb_type
== MB_CACHE
) {
2527 if (mb
.mb_sock
.s_fd
< 0) {
2528 n_err(_("IMAP connection closed\n"));
2533 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2534 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2535 if (sigsetjmp(imapjmp
, 1)) {
2536 safe_signal(SIGINT
, saveint
);
2537 safe_signal(SIGPIPE
, saveint
);
2541 if (saveint
!= SIG_IGN
)
2542 safe_signal(SIGINT
, imapcatch
);
2543 if (savepipe
!= SIG_IGN
)
2544 safe_signal(SIGPIPE
, imapcatch
);
2547 if (!same_imap_account
)
2550 safe_signal(SIGINT
, saveint
);
2551 safe_signal(SIGPIPE
, savepipe
);
2558 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2562 FILE *queuefp
= NULL
;
2565 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2568 snprintf(o
, sizeof o
, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2569 tag(1), m
->m_uid
, c
, sp
);
2571 if (check_expunged() == STOP
)
2573 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2575 IMAP_OUT(o
, MB_COMD
, return STOP
)
2579 mb
.mb_active
&= ~MB_COMD
;
2580 if (queuefp
!= NULL
)
2586 imap_undelete(struct message
*m
, int n
)
2591 rv
= imap_unstore(m
, n
, "\\Deleted");
2597 imap_unread(struct message
*m
, int n
)
2602 rv
= imap_unstore(m
, n
, "\\Seen");
2608 imap_unstore(struct message
*m
, int n
, const char *flag
)
2610 sighandler_type saveint
, savepipe
;
2611 enum okay rv
= STOP
;
2615 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2616 safe_signal(SIGINT
, &_imap_maincatch
);
2617 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2618 if (sigsetjmp(imapjmp
, 1) == 0) {
2619 if (savepipe
!= SIG_IGN
)
2620 safe_signal(SIGPIPE
, imapcatch
);
2622 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2624 safe_signal(SIGINT
, saveint
);
2625 safe_signal(SIGPIPE
, savepipe
);
2643 snprintf(ts
, sizeof ts
, "T%lu", n
);
2649 c_imapcodec(void *v
){
2651 char const **argv
, *cp
, *res
;
2654 if(is_prefix(cp
= *(argv
= v
), "encode")){
2655 while((cp
= *++argv
) != NULL
){
2656 res
= imap_path_normalize(NULL
, cp
);
2657 res
= imap_path_encode(res
, &err
);
2658 printf(" in: %s (%" PRIuZ
" bytes)\nout: %s%s (%" PRIuZ
" bytes)\n",
2659 cp
, strlen(cp
), (err
? "ERROR " : ""), res
, strlen(res
));
2661 }else if(is_prefix(cp
, "decode")){
2664 while((cp
= *++argv
) != NULL
){
2665 res
= imap_path_normalize(NULL
, cp
);
2666 res
= imap_path_decode(res
, &err
);
2667 in
.l
= strlen(in
.s
= UNCONST(res
)); /* logical */
2668 makeprint(&in
, &out
);
2669 printf(" in: %s (%" PRIuZ
" bytes)\nout: %s%s (%" PRIuZ
" bytes)\n",
2670 cp
, strlen(cp
), (err
? "ERROR " : ""), out
.s
, in
.l
);
2674 n_err(_("`imapcodec': invalid subcommand: %s\n"), *argv
);
2678 return (cp
!= NULL
? OKAY
: STOP
);
2682 c_imap_imap(void *vp
)
2685 sighandler_type saveint
, savepipe
;
2686 struct mailbox
*mp
= &mb
;
2687 FILE *queuefp
= NULL
;
2688 enum okay
volatile ok
= STOP
;
2691 if (mp
->mb_type
!= MB_IMAP
) {
2692 printf("Not operating on an IMAP mailbox.\n");
2696 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2697 safe_signal(SIGINT
, &_imap_maincatch
);
2698 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2699 if (sigsetjmp(imapjmp
, 1) == 0) {
2700 if (savepipe
!= SIG_IGN
)
2701 safe_signal(SIGPIPE
, imapcatch
);
2703 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2704 IMAP_OUT(o
, MB_COMD
, goto out
)
2705 while (mp
->mb_active
& MB_COMD
) {
2706 ok
= imap_answer(mp
, 0);
2707 fputs(responded_text
, stdout
);
2711 safe_signal(SIGINT
, saveint
);
2712 safe_signal(SIGPIPE
, savepipe
);
2721 imap_newmail(int nmail
)
2725 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2731 if (had_exists
== msgCount
&& had_expunge
< 0)
2732 /* Some servers always respond with EXISTS to NOOP. If
2733 * the mailbox has been changed but the number of messages
2734 * has not, an EXPUNGE must also had been sent; otherwise,
2735 * nothing has changed */
2738 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2742 imap_putflags(int f
)
2748 bp
= buf
= salloc(100);
2749 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2754 for (cp
= "\\Seen"; *cp
; cp
++)
2760 for (cp
= "\\Flagged"; *cp
; cp
++)
2763 if (f
& MANSWERED
) {
2766 for (cp
= "\\Answered"; *cp
; cp
++)
2772 for (cp
= "\\Draft"; *cp
; cp
++)
2784 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2787 while (*cp
!= ')') {
2789 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2791 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2793 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2795 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2797 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2799 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2811 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2812 long xsize
, enum mflag flag
, time_t t
)
2814 char o
[LINESIZE
], *buf
;
2815 size_t bufsize
, buflen
, cnt
;
2816 long size
, lines
, ysize
;
2828 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
2831 if (mp
->mb_type
== MB_CACHE
) {
2832 queuefp
= cache_queue(mp
);
2833 if (queuefp
== NULL
) {
2840 buf
= smalloc(bufsize
= LINESIZE
);
2845 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2850 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2851 tag(1), qname
, imap_putflags(flag
), imap_make_date_time(t
), size
);
2852 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2853 while (mp
->mb_active
& MB_COMD
) {
2854 rv
= imap_answer(mp
, twice
);
2855 if (response_type
== RESPONSE_CONT
)
2859 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2868 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2871 buf
[buflen
- 1] = '\r';
2873 if (mp
->mb_type
!= MB_CACHE
)
2874 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2876 fwrite(buf
, 1, buflen
+1, queuefp
);
2879 if (mp
->mb_type
!= MB_CACHE
)
2880 swrite(&mp
->mb_sock
, "\r\n");
2882 fputs("\r\n", queuefp
);
2883 while (mp
->mb_active
& MB_COMD
) {
2884 rv
= imap_answer(mp
, 0);
2885 if (response_status
== RESPONSE_NO
/*&&
2886 ascncasecmp(responded_text,
2887 "[TRYCREATE] ", 12) == 0*/) {
2894 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2895 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2896 while (mp
->mb_active
& MB_COMD
)
2897 rv
= imap_answer(mp
, 1);
2900 imap_created_mailbox
++;
2902 } else if (rv
!= OKAY
)
2903 n_err(_("IMAP error: %s"), responded_text
);
2904 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2905 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2908 if (queuefp
!= NULL
)
2917 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2919 char *buf
, *bp
, *lp
;
2920 size_t bufsize
, buflen
, cnt
;
2921 off_t off1
= -1, offs
;
2923 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
2929 buf
= smalloc(bufsize
= LINESIZE
);
2936 for (flag
= MNEW
, state
= _NLSEP
;;) {
2937 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2940 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
2941 is_head(buf
, buflen
, FAL0
))) {
2942 if (off1
!= (off_t
)-1) {
2943 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
2946 fseek(fp
, offs
+buflen
, SEEK_SET
);
2948 off1
= offs
+ buflen
;
2954 tim
= unixtime(buf
);
2960 if (buf
[0] == '\n') {
2963 } else if (state
& _INHEAD
) {
2964 if (ascncasecmp(buf
, "status", 6) == 0) {
2966 while (whitechar(*lp
))
2969 while (*++lp
!= '\0')
2978 } else if (ascncasecmp(buf
, "x-status", 8) == 0) {
2980 while (whitechar(*lp
))
2983 while (*++lp
!= '\0')
3006 imap_append(const char *xserver
, FILE *fp
)
3008 sighandler_type
volatile saveint
, savepipe
;
3011 enum okay rv
= STOP
;
3014 if (!url_parse(&url
, CPROTO_IMAP
, xserver
))
3016 if (!ok_blook(v15_compat
) &&
3017 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
3018 n_err(_("New-style URL used without *v15-compat* being set!\n"));
3019 assert(url
.url_path
.s
!= NULL
);
3022 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3023 safe_signal(SIGINT
, &_imap_maincatch
);
3024 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3025 if (sigsetjmp(imapjmp
, 1))
3027 if (savepipe
!= SIG_IGN
)
3028 safe_signal(SIGPIPE
, imapcatch
);
3030 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
3031 !strcmp(url
.url_p_eu_h_p
, mb
.mb_imap_account
)) {
3032 rv
= imap_append0(&mb
, url
.url_path
.s
, fp
);
3036 memset(&mx
, 0, sizeof mx
);
3038 if (!_imap_getcred(&mx
, &ccred
, &url
))
3041 imap_delim_init(&mx
, &url
);
3042 mx
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&mx
, url
.url_path
.s
));
3044 if (disconnected(url
.url_p_eu_h_p
) == 0) {
3045 if (!sopen(&mx
.mb_sock
, &url
))
3047 mx
.mb_sock
.s_desc
= "IMAP";
3048 mx
.mb_type
= MB_IMAP
;
3049 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
3050 /* TODO the code now did
3051 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
3052 * TODO though imap_mailbox is sfree()d and mbx
3053 * TODO is possibly even a constant
3054 * TODO i changed this to sstrdup() sofar, as is used
3055 * TODO somewhere else in this file for this! */
3056 if (imap_preauth(&mx
, &url
) != OKAY
||
3057 imap_auth(&mx
, &ccred
) != OKAY
) {
3058 sclose(&mx
.mb_sock
);
3061 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
3064 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
3065 mx
.mb_type
= MB_CACHE
;
3066 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
3073 safe_signal(SIGINT
, saveint
);
3074 safe_signal(SIGPIPE
, savepipe
);
3084 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
3085 struct list_item
**lend
, int level
)
3087 char o
[LINESIZE
], *cp
;
3088 struct list_item
*lp
;
3089 const char *qname
, *bp
;
3097 if((qname
= imap_path_quote(mp
, base
)) == NULL
)
3100 *list
= *lend
= NULL
;
3101 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), qname
);
3102 IMAP_OUT(o
, MB_COMD
, goto jleave
)
3103 while (mp
->mb_active
& MB_COMD
) {
3104 ok
= imap_answer(mp
, 1);
3105 if (response_status
== RESPONSE_OTHER
&&
3106 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
3107 cp
= imap_path_decode(imap_unquotestr(list_name
), NULL
);
3108 lp
= csalloc(1, sizeof *lp
);
3110 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
3112 lp
->l_base
= *cp
? cp
: savestr(base
);
3113 lp
->l_attr
= list_attributes
;
3114 lp
->l_level
= level
+1;
3115 lp
->l_delim
= list_hierarchy_delimiter
;
3116 if (*list
&& *lend
) {
3117 (*lend
)->l_next
= lp
;
3128 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
3130 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
3137 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
3138 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
3141 if (list
== NULL
|| lend
== NULL
)
3144 for (lp
= list
; lp
; lp
= lp
->l_next
)
3145 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
3146 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
3147 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
3148 memcpy(cp
, lp
->l_name
, n
);
3149 cp
[n
] = lp
->l_delim
;
3151 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
3152 lp
->l_has_children
= 1;
3153 if (strcmp(cp
, lx
->l_name
) == 0)
3162 for (lp
= list
; lp
; lp
= lp
->l_next
) {
3165 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
3169 if (!(lp
->l_attr
& LIST_NOSELECT
))
3170 fprintf(fp
, "%s\n", *cp
? cp
: base
);
3171 else if (lp
->l_has_children
== 0)
3172 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
3173 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
3181 imap_folders(const char * volatile name
, int strip
)
3183 sighandler_type saveint
, savepipe
;
3184 const char *fold
, *cp
, *sp
;
3188 cp
= protbase(name
);
3189 sp
= mb
.mb_imap_account
;
3190 if (sp
== NULL
|| strcmp(cp
, sp
)) {
3192 _("Cannot perform `folders' but when on the very IMAP "
3193 "account; the current one is\n `%s' -- "
3194 "try `folders @'\n"),
3195 (sp
!= NULL
? sp
: _("[NONE]")));
3199 fold
= imap_fileof(name
);
3200 if (options
& OPT_TTYOUT
) {
3201 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
3203 n_perr(_("tmpfile"), 0);
3210 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3211 safe_signal(SIGINT
, &_imap_maincatch
);
3212 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3213 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
3215 if (savepipe
!= SIG_IGN
)
3216 safe_signal(SIGPIPE
, imapcatch
);
3218 if (mb
.mb_type
== MB_CACHE
)
3219 cache_list(&mb
, fold
, strip
, fp
);
3221 imap_list(&mb
, fold
, strip
, fp
);
3225 if (options
& OPT_TTYOUT
)
3231 if (options
& OPT_TTYOUT
) {
3236 n_err(_("Folder not found\n"));
3239 safe_signal(SIGINT
, saveint
);
3240 safe_signal(SIGPIPE
, savepipe
);
3241 if (options
& OPT_TTYOUT
)
3254 long n
= 0, mx
= 0, columns
, width
;
3258 if ((out
= Ftmp(NULL
, "imapdopr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600))
3260 n_perr(_("tmpfile"), 0);
3264 while ((c
= getc(fp
)) != EOF
) {
3275 if (mx
< width
/ 2) {
3276 columns
= width
/ (mx
+2);
3277 snprintf(o
, sizeof o
, "sort | pr -%lu -w%lu -t", columns
, width
);
3279 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
3280 run_command(XSHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
3281 page_or_print(out
, 0);
3288 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
3292 bool_t twice
, stored
;
3299 twice
= stored
= FAL0
;
3304 i
= strlen(name
= imap_fileof(name
));
3305 if(i
== 0 || (i
> 0 && name
[i
- 1] == '/'))
3306 name
= savecat(name
, "INBOX");
3307 if((qname
= imap_path_quote(mp
, name
)) == NULL
)
3311 if (mp
->mb_type
== MB_CACHE
) {
3312 if ((queuefp
= cache_queue(mp
)) == NULL
)
3317 /* Since it is not possible to set flags on the copy, recently
3318 * set flags must be set on the original to include it in the copy */
3319 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
3320 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
3321 if (m
->m_flag
&MFLAG
)
3322 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3323 if (m
->m_flag
&MUNFLAG
)
3324 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3325 if (m
->m_flag
&MANSWER
)
3326 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3327 if (m
->m_flag
&MUNANSWER
)
3328 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3329 if (m
->m_flag
&MDRAFT
)
3330 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3331 if (m
->m_flag
&MUNDRAFT
)
3332 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3335 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n", tag(1), m
->m_uid
, qname
);
3337 if (check_expunged() == STOP
)
3339 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
3341 IMAP_OUT(o
, MB_COMD
, goto out
)
3342 while (mp
->mb_active
& MB_COMD
)
3343 ok
= imap_answer(mp
, twice
);
3345 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
3346 response_status
== RESPONSE_OK
)
3347 imap_copyuid(mp
, m
, name
);
3349 if (response_status
== RESPONSE_NO
&& !twice
) {
3350 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
3351 IMAP_OUT(o
, MB_COMD
, goto out
)
3352 while (mp
->mb_active
& MB_COMD
)
3353 ok
= imap_answer(mp
, 1);
3355 imap_created_mailbox
++;
3361 if (queuefp
!= NULL
)
3364 /* ... and reset the flag to its initial value so that the 'exit'
3365 * command still leaves the message unread */
3367 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
3368 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
3371 if (m
->m_flag
& MFLAG
) {
3372 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
3375 if (m
->m_flag
& MUNFLAG
) {
3376 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
3379 if (m
->m_flag
& MANSWER
) {
3380 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
3383 if (m
->m_flag
& MUNANSWER
) {
3384 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
3387 if (m
->m_flag
& MDRAFT
) {
3388 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
3391 if (m
->m_flag
& MUNDRAFT
) {
3392 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
3396 mp
->mb_active
|= MB_COMD
;
3397 (void)imap_finish(mp
);
3404 imap_copy(struct message
*m
, int n
, const char *name
)
3406 sighandler_type saveint
, savepipe
;
3407 enum okay rv
= STOP
;
3411 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3412 safe_signal(SIGINT
, &_imap_maincatch
);
3413 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3414 if (sigsetjmp(imapjmp
, 1) == 0) {
3415 if (savepipe
!= SIG_IGN
)
3416 safe_signal(SIGPIPE
, imapcatch
);
3418 rv
= imap_copy1(&mb
, m
, n
, name
);
3420 safe_signal(SIGINT
, saveint
);
3421 safe_signal(SIGPIPE
, savepipe
);
3431 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
3432 unsigned long *olduid
, unsigned long *newuid
)
3438 *uidvalidity
= strtoul(cp
, &xp
, 10);
3439 *olduid
= strtoul(xp
, &yp
, 10);
3440 *newuid
= strtoul(yp
, &zp
, 10);
3441 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
3442 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
3448 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
3455 *uidvalidity
= strtoul(cp
, &xp
, 10);
3456 *uid
= strtoul(xp
, &yp
, 10);
3457 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
3464 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
3469 unsigned long uidvalidity
, olduid
, newuid
;
3475 memset(&xmb
, 0, sizeof xmb
);
3477 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
3478 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
3484 xmb
.mb_cache_directory
= NULL
;
3485 xmb
.mb_imap_account
= sstrdup(mp
->mb_imap_account
);
3486 xmb
.mb_imap_pass
= sstrdup(mp
->mb_imap_pass
);
3487 memcpy(&xmb
.mb_imap_delim
[0], &mp
->mb_imap_delim
[0],
3488 sizeof(xmb
.mb_imap_delim
));
3489 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3490 if (mp
->mb_cache_directory
!= NULL
)
3491 xmb
.mb_cache_directory
= sstrdup(mp
->mb_cache_directory
);
3492 xmb
.mb_uidvalidity
= uidvalidity
;
3496 memset(&xm
, 0, sizeof xm
);
3498 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
3500 getcache(mp
, &xm
, NEED_HEADER
);
3501 getcache(mp
, &xm
, NEED_BODY
);
3503 if ((m
->m_flag
& HAVE_HEADER
) == 0)
3504 getcache(mp
, m
, NEED_HEADER
);
3505 if ((m
->m_flag
& HAVE_BODY
) == 0)
3506 getcache(mp
, m
, NEED_BODY
);
3510 xm
.m_flag
&= ~MFULLYCACHED
;
3511 putcache(&xmb
, &xm
);
3513 if (xmb
.mb_cache_directory
!= NULL
)
3514 free(xmb
.mb_cache_directory
);
3515 if (xmb
.mb_imap_mailbox
!= NULL
)
3516 free(xmb
.mb_imap_mailbox
);
3517 if (xmb
.mb_imap_pass
!= NULL
)
3518 free(xmb
.mb_imap_pass
);
3519 if (xmb
.mb_imap_account
!= NULL
)
3520 free(xmb
.mb_imap_account
);
3526 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
3527 long size
, long lines
, int flag
, const char *name
)
3532 unsigned long uidvalidity
, uid
;
3538 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
3539 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
3545 xmb
.mb_cache_directory
= NULL
;
3546 /* XXX mb_imap_delim reused */
3547 xmb
.mb_imap_mailbox
= sstrdup(imap_path_normalize(&xmb
, name
));
3548 xmb
.mb_uidvalidity
= uidvalidity
;
3549 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
3551 memset(&xm
, 0, sizeof xm
);
3552 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
3554 xm
.m_block
= mailx_blockof(off1
);
3555 xm
.m_offset
= mailx_offsetof(off1
);
3558 xm
.m_lines
= xm
.m_xlines
= lines
;
3560 xm
.m_have
= HAVE_HEADER
| HAVE_BODY
;
3561 putcache(&xmb
, &xm
);
3563 free(xmb
.mb_imap_mailbox
);
3570 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
3574 long size
, xsize
, ysize
, lines
;
3575 enum mflag flag
= MNEW
;
3576 char *name
, *buf
, *bp
;
3578 size_t bufsize
, buflen
, cnt
;
3579 enum okay rv
= STOP
;
3582 buf
= smalloc(bufsize
= LINESIZE
);
3585 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3588 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3593 if ((cp
= strrchr(bp
, '{')) == NULL
)
3596 xsize
= atol(&cp
[1]) + 2;
3597 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3603 imap_getflags(cp
, &cp
, &flag
);
3604 while (*++cp
== ' ')
3607 t
= imap_read_date_time(cp
);
3609 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
3616 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3619 buf
[--buflen
] = '\0';
3620 buf
[buflen
-1] = '\n';
3621 fwrite(buf
, 1, buflen
, tp
);
3628 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3629 imap_unquotestr(name
));
3639 #ifdef HAVE_IMAP_SEARCH
3641 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3644 char *o
, *xp
, *cs
, c
;
3646 FILE *queuefp
= NULL
;
3650 enum okay ok
= STOP
;
3654 for (cp
= spec
; *cp
; cp
++)
3657 cp
= charset_get_lc();
3659 if (asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")) {
3664 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
3665 sz
= strlen(spec
) + 1;
3666 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
3667 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
, FAL0
) == 0 &&
3676 cp
= imap_quotestr(cp
);
3677 cs
= salloc(n
= strlen(cp
) + 10);
3678 snprintf(cs
, n
, "CHARSET %s ", cp
);
3682 o
= ac_alloc(osize
= strlen(spec
) + 60);
3683 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3684 IMAP_OUT(o
, MB_COMD
, goto out
)
3685 while (mp
->mb_active
& MB_COMD
) {
3686 ok
= imap_answer(mp
, 0);
3687 if (response_status
== RESPONSE_OTHER
&&
3688 response_other
== MAILBOX_DATA_SEARCH
) {
3689 xp
= responded_other_text
;
3690 while (*xp
&& *xp
!= '\r') {
3691 n
= strtoul(xp
, &xp
, 10);
3692 for (i
= 0; i
< cnt
; i
++)
3693 if (m
[i
].m_uid
== n
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3694 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3705 imap_search1(const char * volatile spec
, int f
)
3707 sighandler_type saveint
, savepipe
;
3708 enum okay
volatile rv
= STOP
;
3711 if (mb
.mb_type
!= MB_IMAP
)
3715 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3716 safe_signal(SIGINT
, &_imap_maincatch
);
3717 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3718 if (sigsetjmp(imapjmp
, 1) == 0) {
3719 if (savepipe
!= SIG_IGN
)
3720 safe_signal(SIGPIPE
, imapcatch
);
3722 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3724 safe_signal(SIGINT
, saveint
);
3725 safe_signal(SIGPIPE
, savepipe
);
3733 #endif /* HAVE_IMAP_SEARCH */
3736 imap_thisaccount(const char *cp
)
3741 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3743 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3744 mb
.mb_imap_account
== NULL
)
3747 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3753 imap_remove(const char * volatile name
)
3755 sighandler_type
volatile saveint
, savepipe
;
3756 enum okay
volatile rv
= STOP
;
3759 if (mb
.mb_type
!= MB_IMAP
) {
3760 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name
);
3764 if (!imap_thisaccount(name
)) {
3765 n_err(_("Can only remove mailboxes on current IMAP server: "
3766 "\"%s\" not removed\n"), name
);
3771 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3772 safe_signal(SIGINT
, &_imap_maincatch
);
3773 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3774 if (sigsetjmp(imapjmp
, 1) == 0) {
3775 if (savepipe
!= SIG_IGN
)
3776 safe_signal(SIGPIPE
, imapcatch
);
3778 rv
= imap_remove1(&mb
, imap_fileof(name
));
3780 safe_signal(SIGINT
, saveint
);
3781 safe_signal(SIGPIPE
, savepipe
);
3785 rv
= cache_remove(name
);
3794 imap_remove1(struct mailbox
*mp
, const char *name
)
3806 if((qname
= imap_path_quote(mp
, name
)) != NULL
){
3807 o
= ac_alloc(os
= strlen(qname
) + 100);
3808 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), qname
);
3809 IMAP_OUT(o
, MB_COMD
, goto out
)
3810 while (mp
->mb_active
& MB_COMD
)
3811 ok
= imap_answer(mp
, 1);
3819 imap_rename(const char *old
, const char *new)
3821 sighandler_type saveint
, savepipe
;
3822 enum okay rv
= STOP
;
3825 if (mb
.mb_type
!= MB_IMAP
) {
3826 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3830 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3831 n_err(_("Can only rename mailboxes on current IMAP "
3832 "server: \"%s\" not renamed to \"%s\"\n"), old
, new);
3837 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3838 safe_signal(SIGINT
, &_imap_maincatch
);
3839 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3840 if (sigsetjmp(imapjmp
, 1) == 0) {
3841 if (savepipe
!= SIG_IGN
)
3842 safe_signal(SIGPIPE
, imapcatch
);
3844 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3846 safe_signal(SIGINT
, saveint
);
3847 safe_signal(SIGPIPE
, savepipe
);
3851 rv
= cache_rename(old
, new);
3860 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3864 char const *qoname
, *qnname
;
3872 if((qoname
= imap_path_quote(mp
, old
)) != NULL
&&
3873 (qnname
= imap_path_quote(mp
, new)) != NULL
){
3874 o
= ac_alloc(os
= strlen(qoname
) + strlen(qnname
) + 100);
3875 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), qoname
, qnname
);
3876 IMAP_OUT(o
, MB_COMD
, goto out
)
3877 while (mp
->mb_active
& MB_COMD
)
3878 ok
= imap_answer(mp
, 1);
3886 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3888 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3889 size_t bufsize
, buflen
, cnt
;
3890 long offs
, offs1
, offs2
, octets
;
3891 int twice
, gotcha
= 0;
3892 FILE *queuefp
= NULL
;
3893 enum okay ok
= OKAY
, rok
= OKAY
;
3896 buf
= smalloc(bufsize
= LINESIZE
);
3899 while ((offs1
= ftell(fp
)) >= 0 &&
3900 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3901 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3906 if ((offs
= ftell(fp
)) < 0)
3909 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3910 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3912 while (digitchar(*cp
))
3918 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3920 IMAP_OUT(o
, MB_COMD
, continue)
3921 while (mp
->mb_active
& MB_COMD
)
3922 ok
= imap_answer(mp
, twice
);
3923 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3925 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3926 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3928 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3929 IMAP_OUT(o
, MB_COMD
, continue)
3930 while (mp
->mb_active
& MB_COMD
)
3931 ok
= imap_answer(mp
, 1);
3934 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3935 if ((cp
= strrchr(bp
, '{')) == NULL
)
3937 octets
= atol(&cp
[1]) + 2;
3938 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3940 IMAP_OUT(o
, MB_COMD
, continue)
3941 while (mp
->mb_active
& MB_COMD
) {
3942 ok
= imap_answer(mp
, twice
);
3943 if (response_type
== RESPONSE_CONT
)
3947 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3951 while (octets
> 0) {
3952 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3953 ? sizeof iob
: (size_t)octets
);
3955 if (n
!= fread(iob
, 1, n
, fp
))
3957 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3959 swrite(&mp
->mb_sock
, "");
3960 while (mp
->mb_active
& MB_COMD
) {
3961 ok
= imap_answer(mp
, 0);
3962 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
3963 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3968 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3969 if ((offs2
= ftell(fp
)) < 0)
3971 fseek(fp
, offs1
, SEEK_SET
);
3972 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3973 (void)fseek(fp
, offs2
, SEEK_SET
);
3979 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp
);
3984 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
3985 IMAP_OUT(o
, MB_COMD
, continue)
3986 while (mp
->mb_active
& MB_COMD
)
3987 ok
= imap_answer(mp
, 1);
3993 ftruncate(fileno(fp
), 0);
4001 imap_strex(char const *cp
, char const **xp
)
4010 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
4013 else if (*cq
== '"')
4019 n
= salloc(cq
- cp
+ 2);
4020 memcpy(n
, cp
, cq
- cp
+1);
4021 n
[cq
- cp
+ 1] = '\0';
4030 check_expunged(void)
4035 if (expunged_messages
> 0) {
4036 n_err(_("Command not executed - messages have been expunged\n"));
4045 c_connect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4048 int rv
, omsgCount
= msgCount
;
4052 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
4053 n_err(_("Already connected\n"));
4058 if (!url_parse(&url
, CPROTO_IMAP
, mailname
)) {
4062 ok_bclear(disconnected
);
4063 vok_bclear(savecat("disconnected-", url
.url_u_h_p
.s
));
4065 if (mb
.mb_type
== MB_CACHE
) {
4066 enum fedit_mode fm
= FEDIT_NONE
;
4069 if (!(pstate
& PS_EDIT
))
4071 _imap_setfile1(&url
, fm
, 1);
4072 if (msgCount
> omsgCount
)
4073 newmailinfo(omsgCount
);
4082 c_disconnect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
4085 int rv
= 1, *msgvec
= vp
;
4088 if (mb
.mb_type
== MB_CACHE
) {
4089 n_err(_("Not connected\n"));
4092 if (mb
.mb_type
!= MB_IMAP
|| cached_uidvalidity(&mb
) == 0) {
4093 n_err(_("The current mailbox is not cached\n"));
4097 if (!url_parse(&url
, CPROTO_IMAP
, mailname
))
4102 ok_bset(disconnected
, TRU1
);
4103 if (mb
.mb_type
== MB_IMAP
) {
4104 enum fedit_mode fm
= FEDIT_NONE
;
4107 if (!(pstate
& PS_EDIT
))
4109 sclose(&mb
.mb_sock
);
4110 _imap_setfile1(&url
, fm
, 1);
4121 int rv
= 1, *msgvec
= vp
, *ip
;
4125 if (mb
.mb_type
!= MB_IMAP
) {
4126 n_err(_("Not connected to an IMAP server\n"));
4129 if (cached_uidvalidity(&mb
) == 0) {
4130 n_err(_("The current mailbox is not cached\n"));
4135 for (ip
= msgvec
; *ip
; ++ip
) {
4136 mp
= &message
[*ip
- 1];
4137 if (!(mp
->m_have
& HAVE_BODY
)) {
4150 disconnected(const char *file
)
4156 if (ok_blook(disconnected
)) {
4161 if (!url_parse(&url
, CPROTO_IMAP
, file
)) {
4165 rv
= vok_blook(savecat("disconnected-", url
.url_u_h_p
.s
));
4173 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
4175 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
4183 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
4184 PTRCMP(nmp
, <, message
+ msgCount
)) {
4185 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
4187 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
4189 if (omp
->m_uid
== nmp
->m_uid
) {
4190 hf
= nmp
->m_flag
& MHIDDEN
;
4191 if (transparent
&& mb
.mb_type
== MB_IMAP
)
4192 omp
->m_flag
&= ~MHIDDEN
;
4194 if (transparent
&& mb
.mb_type
== MB_CACHE
)
4195 nmp
[-1].m_flag
|= hf
;
4196 } else if (omp
->m_uid
< nmp
->m_uid
)
4203 prevdot
= newprevdot
;
4209 imap_read_date_time(const char *cp
)
4213 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
4216 /* "25-Jul-2004 15:33:44 +0200"
4218 * 0 5 10 15 20 25 */
4219 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
4221 day
= strtol(&cp
[1], NULL
, 10);
4223 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
4225 if (month_names
[++i
][0] == '\0')
4229 year
= strtol(&cp
[8], NULL
, 10);
4230 hour
= strtol(&cp
[13], NULL
, 10);
4231 minute
= strtol(&cp
[16], NULL
, 10);
4232 second
= strtol(&cp
[19], NULL
, 10);
4233 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
4247 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
4250 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
4260 imap_make_date_time(time_t t
)
4264 int tzdiff
, tzdiff_hour
, tzdiff_min
;
4267 tzdiff
= t
- mktime(gmtime(&t
));
4268 tzdiff_hour
= (int)(tzdiff
/ 60);
4269 tzdiff_min
= tzdiff_hour
% 60;
4271 tmptr
= localtime(&t
);
4272 if (tmptr
->tm_isdst
> 0)
4274 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
4275 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
], tmptr
->tm_year
+ 1900,
4276 tmptr
->tm_hour
, tmptr
->tm_min
, tmptr
->tm_sec
, tzdiff_hour
, tzdiff_min
);
4280 #endif /* HAVE_IMAP */
4282 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
4284 imap_quotestr(char const *s
)
4289 np
= n
= salloc(2 * strlen(s
) + 3);
4292 if (*s
== '"' || *s
== '\\')
4303 imap_unquotestr(char const *s
)
4313 np
= n
= salloc(strlen(s
) + 1);
4326 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */