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_other_get(char *pp
);
175 static void imap_response_get(const char **cp
);
176 static void imap_response_parse(void);
177 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
178 static enum okay
imap_parse_list(void);
179 static enum okay
imap_finish(struct mailbox
*mp
);
180 static void imap_timer_off(void);
181 static void imapcatch(int s
);
182 static void _imap_maincatch(int s
);
183 static enum okay
imap_noop1(struct mailbox
*mp
);
184 static void rec_queue(enum rec_type type
, unsigned long cnt
);
185 static enum okay
rec_dequeue(void);
186 static void rec_rmqueue(void);
187 static void imapalarm(int s
);
188 static enum okay
imap_preauth(struct mailbox
*mp
, struct url
const *urlp
);
189 static enum okay
imap_capability(struct mailbox
*mp
);
190 static enum okay
imap_auth(struct mailbox
*mp
, struct ccred
*ccred
);
192 static enum okay
imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
);
194 static enum okay
imap_login(struct mailbox
*mp
, struct ccred
*ccred
);
196 static enum okay
_imap_gssapi(struct mailbox
*mp
, struct ccred
*ccred
);
198 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
199 static void imap_init(struct mailbox
*mp
, int n
);
200 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
202 static bool_t
_imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
,
204 static int _imap_setfile1(struct url
*urlp
, enum fedit_mode fm
,
206 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
207 size_t expected
, int need
, const char *head
,
208 size_t headsize
, long headlines
);
209 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
210 const char *str
, const char *head
, size_t headsize
,
212 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
214 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
215 struct message
*from
, enum havespec have
);
216 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
218 static enum okay
imap_exit(struct mailbox
*mp
);
219 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
,
221 static enum okay
imap_close(struct mailbox
*mp
);
222 static enum okay
imap_update(struct mailbox
*mp
);
223 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
224 int c
, const char *sp
, int needstat
);
225 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
226 static const char *tag(int new);
227 static char * imap_putflags(int f
);
228 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
229 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
230 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
231 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
232 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
233 struct list_item
**list
, struct list_item
**lend
,
235 static enum okay
imap_list(struct mailbox
*mp
, const char *base
, int strip
,
237 static void dopr(FILE *fp
);
238 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
240 static enum okay
imap_copyuid_parse(const char *cp
,
241 unsigned long *uidvalidity
, unsigned long *olduid
,
242 unsigned long *newuid
);
243 static enum okay
imap_appenduid_parse(const char *cp
,
244 unsigned long *uidvalidity
, unsigned long *uid
);
245 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
247 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
248 long off1
, long xsize
, long size
, long lines
, int flag
,
250 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
251 #ifdef HAVE_IMAP_SEARCH
252 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
,
253 const char *spec
, int f
);
255 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
256 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
258 static char * imap_strex(char const *cp
, char const **xp
);
259 static enum okay
check_expunged(void);
262 imap_other_get(char *pp
)
267 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
269 response_other
= MAILBOX_DATA_FLAGS
;
270 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
272 response_other
= MAILBOX_DATA_LIST
;
273 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
275 response_other
= MAILBOX_DATA_LSUB
;
276 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
278 response_other
= MAILBOX_DATA_MAILBOX
;
279 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
281 response_other
= MAILBOX_DATA_SEARCH
;
282 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
284 response_other
= MAILBOX_DATA_STATUS
;
285 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
287 response_other
= CAPABILITY_DATA
;
289 responded_other_number
= strtol(pp
, &xp
, 10);
292 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
293 response_other
= MAILBOX_DATA_EXISTS
;
294 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
295 response_other
= MAILBOX_DATA_RECENT
;
296 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
297 response_other
= MESSAGE_DATA_EXPUNGE
;
298 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
300 response_other
= MESSAGE_DATA_FETCH
;
302 response_other
= RESPONSE_OTHER_UNKNOWN
;
304 responded_other_text
= pp
;
309 imap_response_get(const char **cp
)
312 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
314 response_status
= RESPONSE_OK
;
315 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
317 response_status
= RESPONSE_NO
;
318 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
320 response_status
= RESPONSE_BAD
;
321 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
323 response_status
= RESPONSE_PREAUTH
;
324 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
326 response_status
= RESPONSE_BYE
;
328 response_status
= RESPONSE_OTHER
;
333 imap_response_parse(void)
335 static char *parsebuf
; /* TODO Use pool */
336 static size_t parsebufsize
;
338 const char *ip
= imapbuf
;
342 if (parsebufsize
< imapbufsize
)
343 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
344 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
348 response_type
= RESPONSE_CONT
;
363 imap_response_get(&ip
);
364 pp
= &parsebuf
[ip
- imapbuf
];
365 switch (response_status
) {
367 response_type
= RESPONSE_FATAL
;
370 response_type
= RESPONSE_DATA
;
374 responded_tag
= parsebuf
;
375 while (*pp
&& *pp
!= ' ')
378 response_type
= RESPONSE_ILLEGAL
;
382 while (*pp
&& *pp
== ' ')
385 response_type
= RESPONSE_ILLEGAL
;
388 ip
= &imapbuf
[pp
- parsebuf
];
389 response_type
= RESPONSE_TAGGED
;
390 imap_response_get(&ip
);
391 pp
= &parsebuf
[ip
- imapbuf
];
394 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
395 response_status
== RESPONSE_OTHER
)
401 imap_answer(struct mailbox
*mp
, int errprnt
)
408 if (mp
->mb_type
== MB_CACHE
)
412 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
413 if (options
& OPT_VERBVERB
)
414 fputs(imapbuf
, stderr
);
415 imap_response_parse();
416 if (response_type
== RESPONSE_ILLEGAL
)
418 if (response_type
== RESPONSE_CONT
) {
422 if (response_status
== RESPONSE_OTHER
) {
423 if (response_other
== MAILBOX_DATA_EXISTS
) {
424 had_exists
= responded_other_number
;
425 rec_queue(REC_EXISTS
, responded_other_number
);
428 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
429 rec_queue(REC_EXPUNGE
, responded_other_number
);
437 if (response_type
== RESPONSE_TAGGED
) {
438 if (asccasecmp(responded_tag
, tag(0)) == 0)
443 switch (response_status
) {
444 case RESPONSE_PREAUTH
:
445 mp
->mb_active
&= ~MB_PREAUTH
;
458 n_err(_("IMAP error: %s"), responded_text
);
460 case RESPONSE_UNKNOWN
: /* does not happen */
463 mp
->mb_active
= MB_NONE
;
471 if (response_status
!= RESPONSE_OTHER
&&
472 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
473 n_err(_("IMAP alert: %s"), &responded_text
[8]);
475 mp
->mb_active
&= ~MB_COMD
;
478 mp
->mb_active
= MB_NONE
;
486 imap_parse_list(void)
494 cp
= responded_other_text
;
495 list_attributes
= LIST_NONE
;
497 while (*cp
&& *cp
!= ')') {
499 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
500 list_attributes
|= LIST_NOINFERIORS
;
502 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
503 list_attributes
|= LIST_NOSELECT
;
505 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
506 list_attributes
|= LIST_MARKED
;
508 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
509 list_attributes
|= LIST_UNMARKED
;
521 list_hierarchy_delimiter
= EOF
;
525 list_hierarchy_delimiter
= *cp
++ & 0377;
526 if (cp
[0] != '"' || cp
[1] != ' ')
529 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
530 list_hierarchy_delimiter
= EOF
;
537 while (*cp
&& *cp
!= '\r')
547 imap_finish(struct mailbox
*mp
)
550 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
560 if (imapkeepalive
> 0) {
562 safe_signal(SIGALRM
, savealrm
);
570 NYD_X
; /* Signal handler */
573 n_err_sighdl(_("Interrupt\n"));
574 siglongjmp(imapjmp
, 1);
577 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
583 _imap_maincatch(int s
)
585 NYD_X
; /* Signal handler */
587 if (interrupts
++ == 0) {
588 n_err_sighdl(_("Interrupt\n"));
595 imap_noop1(struct mailbox
*mp
)
598 FILE *queuefp
= NULL
;
601 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
602 IMAP_OUT(o
, MB_COMD
, return STOP
)
608 imap_fileof(char const *xcp
)
610 char const *cp
= xcp
;
615 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
619 if (cp
[0] == '/' && state
== 1) {
637 sighandler_type
volatile oldint
, oldpipe
;
641 if (mb
.mb_type
!= MB_IMAP
)
645 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
646 safe_signal(SIGINT
, &_imap_maincatch
);
647 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
648 if (sigsetjmp(imapjmp
, 1) == 0) {
649 if (oldpipe
!= SIG_IGN
)
650 safe_signal(SIGPIPE
, imapcatch
);
652 rv
= imap_noop1(&mb
);
654 safe_signal(SIGINT
, oldint
);
655 safe_signal(SIGPIPE
, oldpipe
);
665 rec_queue(enum rec_type rt
, unsigned long cnt
)
670 rp
= scalloc(1, sizeof *rp
);
673 if (record
&& recend
) {
674 recend
->rec_next
= rp
;
677 record
= recend
= rp
;
684 struct message
*omessage
;
685 struct record
*rp
, *rq
;
694 message
= smalloc((msgCount
+1) * sizeof *message
);
696 memcpy(message
, omessage
, msgCount
* sizeof *message
);
697 memset(&message
[msgCount
], 0, sizeof *message
);
699 rp
= record
, rq
= NULL
;
702 switch (rp
->rec_type
) {
704 exists
= rp
->rec_count
;
707 if (rp
->rec_count
== 0) {
711 if (rp
->rec_count
> (unsigned long)msgCount
) {
712 if (exists
== 0 || rp
->rec_count
> exists
--)
718 delcache(&mb
, &message
[rp
->rec_count
-1]);
719 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
720 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
722 /* If the message was part of a collapsed thread,
723 * the m_collapsed field of one of its ancestors
724 * should be incremented. It seems hardly possible
725 * to do this with the current message structure,
726 * though. The result is that a '+' may be shown
727 * in the header summary even if no collapsed
739 record
= recend
= NULL
;
740 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
741 message
= srealloc(message
, (exists
+ 1) * sizeof *message
);
742 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
743 for (i
= msgCount
; i
< exists
; ++i
)
745 imap_flags(&mb
, msgCount
+1, exists
);
764 for (rp
= record
; rp
!= NULL
;) {
765 struct record
*tmp
= rp
;
769 record
= recend
= NULL
;
777 sighandler_type
volatile saveint
, savepipe
;
778 NYD_X
; /* Signal handler */
781 if (imaplock
++ == 0) {
782 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
783 safe_signal(SIGINT
, &_imap_maincatch
);
784 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
785 if (sigsetjmp(imapjmp
, 1)) {
786 safe_signal(SIGINT
, saveint
);
787 safe_signal(SIGPIPE
, savepipe
);
790 if (savepipe
!= SIG_IGN
)
791 safe_signal(SIGPIPE
, imapcatch
);
792 if (imap_noop1(&mb
) != OKAY
) {
793 safe_signal(SIGINT
, saveint
);
794 safe_signal(SIGPIPE
, savepipe
);
797 safe_signal(SIGINT
, saveint
);
798 safe_signal(SIGPIPE
, savepipe
);
801 alarm(imapkeepalive
);
807 imap_preauth(struct mailbox
*mp
, struct url
const *urlp
)
811 mp
->mb_active
|= MB_PREAUTH
;
815 if (!mp
->mb_sock
.s_use_ssl
&& xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
816 FILE *queuefp
= NULL
;
819 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
820 IMAP_OUT(o
, MB_COMD
, return STOP
)
822 if (ssl_open(urlp
, &mp
->mb_sock
) != OKAY
)
826 if (xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
827 n_err(_("No SSL support compiled in\n"));
837 imap_capability(struct mailbox
*mp
)
840 FILE *queuefp
= NULL
;
845 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
846 IMAP_OUT(o
, MB_COMD
, return STOP
)
847 while (mp
->mb_active
& MB_COMD
) {
848 ok
= imap_answer(mp
, 0);
849 if (response_status
== RESPONSE_OTHER
&&
850 response_other
== CAPABILITY_DATA
) {
851 cp
= responded_other_text
;
853 while (spacechar(*cp
))
855 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
857 mp
->mb_flags
|= MB_UIDPLUS
;
858 while (*cp
&& !spacechar(*cp
))
867 imap_auth(struct mailbox
*mp
, struct ccred
*ccred
)
872 if (!(mp
->mb_active
& MB_PREAUTH
)) {
877 switch (ccred
->cc_authtype
) {
879 rv
= imap_login(mp
, ccred
);
882 case AUTHTYPE_CRAM_MD5
:
883 rv
= imap_cram_md5(mp
, ccred
);
887 case AUTHTYPE_GSSAPI
:
888 rv
= _imap_gssapi(mp
, ccred
);
902 imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
)
904 char o
[LINESIZE
], *cp
;
905 FILE *queuefp
= NULL
;
909 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
910 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
912 if (response_type
!= RESPONSE_CONT
)
915 cp
= cram_md5_string(&ccred
->cc_user
, &ccred
->cc_pass
, responded_text
);
916 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
917 while (mp
->mb_active
& MB_COMD
)
918 rv
= imap_answer(mp
, 1);
923 #endif /* HAVE_MD5 */
926 imap_login(struct mailbox
*mp
, struct ccred
*ccred
)
929 FILE *queuefp
= NULL
;
933 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
934 tag(1), imap_quotestr(ccred
->cc_user
.s
), imap_quotestr(ccred
->cc_pass
.s
));
935 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
936 while (mp
->mb_active
& MB_COMD
)
937 rv
= imap_answer(mp
, 1);
944 # include "imap_gssapi.h"
948 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
,
954 FILE *queuefp
= NULL
;
958 mp
->mb_uidvalidity
= 0;
959 snprintf(o
, sizeof o
, "%s %s %s\r\n", tag(1),
960 (fm
& FEDIT_RDONLY
? "EXAMINE" : "SELECT"), imap_quotestr(mbx
));
961 IMAP_OUT(o
, MB_COMD
, return STOP
)
962 while (mp
->mb_active
& MB_COMD
) {
963 ok
= imap_answer(mp
, 1);
964 if (response_status
!= RESPONSE_OTHER
&&
965 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
966 mp
->mb_uidvalidity
= atol(&cp
[13]);
968 *cnt
= (had_exists
> 0) ? had_exists
: 0;
969 if (response_status
!= RESPONSE_OTHER
&&
970 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
976 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
979 FILE *queuefp
= NULL
;
982 unsigned x
= X
, y
= Y
, n
;
985 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
986 IMAP_OUT(o
, MB_COMD
, return STOP
)
987 while (mp
->mb_active
& MB_COMD
) {
989 if (response_status
== RESPONSE_OTHER
&&
990 response_other
== MESSAGE_DATA_FETCH
) {
991 n
= responded_other_number
;
999 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1004 imap_getflags(cp
, &cp
, &m
->m_flag
);
1007 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1008 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1009 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1010 m
->m_flag
&= ~MHIDDEN
;
1013 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1015 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1018 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1020 IMAP_OUT(o
, MB_COMD
, return STOP
)
1021 while (mp
->mb_active
& MB_COMD
) {
1023 if (response_status
== RESPONSE_OTHER
&&
1024 response_other
== MESSAGE_DATA_FETCH
) {
1025 n
= responded_other_number
;
1031 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1032 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1033 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1034 m
->m_time
= imap_read_date_time(&cp
[13]);
1039 for (n
= X
; n
<= Y
; ++n
) {
1040 putcache(mp
, &message
[n
-1]);
1048 imap_init(struct mailbox
*mp
, int n
)
1055 m
->m_flag
= MUSED
| MNOFROM
;
1062 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1064 struct message
*omessage
= 0;
1065 int i
, omsgCount
= 0;
1066 enum okay dequeued
= STOP
;
1069 if (nmail
|| transparent
) {
1071 omsgCount
= msgCount
;
1074 dequeued
= rec_dequeue();
1076 if (had_exists
>= 0) {
1077 if (dequeued
!= OKAY
)
1078 msgCount
= had_exists
;
1081 if (had_expunge
>= 0) {
1082 if (dequeued
!= OKAY
)
1083 msgCount
-= had_expunge
;
1087 if (nmail
&& expunged_messages
)
1088 printf("Expunged %ld message%s.\n", expunged_messages
,
1089 (expunged_messages
!= 1 ? "s" : ""));
1090 *prevcount
= omsgCount
- expunged_messages
;
1091 expunged_messages
= 0;
1093 fputs("IMAP error: Negative message count\n", stderr
);
1097 if (dequeued
!= OKAY
) {
1098 message
= scalloc(msgCount
+ 1, sizeof *message
);
1099 for (i
= 0; i
< msgCount
; i
++)
1101 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1104 imap_flags(mp
, 1, msgCount
);
1105 message
[msgCount
].m_size
= 0;
1106 message
[msgCount
].m_lines
= 0;
1109 if (nmail
|| transparent
)
1110 transflags(omessage
, omsgCount
, transparent
);
1117 imap_setfile(const char *xserver
, enum fedit_mode fm
)
1123 if (!url_parse(&url
, CPROTO_IMAP
, xserver
)) {
1127 if (!ok_blook(v15_compat
) &&
1128 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
1129 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1131 _imap_rdonly
= ((fm
& FEDIT_RDONLY
) != 0);
1132 rv
= _imap_setfile1(&url
, fm
, 0);
1139 _imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
, struct url
*urlp
)
1144 if (ok_blook(v15_compat
))
1145 rv
= ccred_lookup(ccredp
, urlp
);
1148 *xuhp
= (urlp
->url_had_user
? urlp
->url_eu_h_p
.s
: urlp
->url_u_h_p
.s
);
1150 if ((var
= mbp
->mb_imap_pass
) != NULL
) {
1151 var
= savecat("password-", xuhp
);
1152 if ((old
= vok_vlook(var
)) != NULL
)
1154 vok_vset(var
, mbp
->mb_imap_pass
);
1156 rv
= ccred_lookup_old(ccredp
, CPROTO_IMAP
, xuhp
);
1171 _imap_setfile1(struct url
*urlp
, enum fedit_mode fm
, int volatile transparent
)
1175 sighandler_type
volatile saveint
, savepipe
;
1178 int volatile prevcount
= 0;
1179 enum mbflags same_flags
;
1182 if (fm
& FEDIT_NEWMAIL
) {
1183 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1184 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1185 if (saveint
!= SIG_IGN
)
1186 safe_signal(SIGINT
, imapcatch
);
1187 if (savepipe
!= SIG_IGN
)
1188 safe_signal(SIGPIPE
, imapcatch
);
1193 same_flags
= mb
.mb_flags
;
1194 same_imap_account
= 0;
1195 if (mb
.mb_imap_account
!= NULL
&&
1196 (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)) {
1197 if (mb
.mb_sock
.s_fd
> 0 && mb
.mb_sock
.s_rsz
>= 0 &&
1198 !strcmp(mb
.mb_imap_account
, urlp
->url_p_eu_h_p
) &&
1199 disconnected(mb
.mb_imap_account
) == 0) {
1200 same_imap_account
= 1;
1201 if (urlp
->url_pass
.s
== NULL
&& mb
.mb_imap_pass
!= NULL
)
1204 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1205 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1206 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1209 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= savestr(mb
.mb_imap_pass
));
1213 if (!same_imap_account
&& mb
.mb_imap_pass
!= NULL
) {
1214 free(mb
.mb_imap_pass
);
1215 mb
.mb_imap_pass
= NULL
;
1217 if (!_imap_getcred(&mb
, &ccred
, urlp
)) {
1223 if (!same_imap_account
) {
1224 if (!disconnected(urlp
->url_p_eu_h_p
) && !sopen(&so
, urlp
)) {
1233 if (fm
& FEDIT_SYSBOX
)
1237 if (mb
.mb_imap_account
!= NULL
)
1238 free(mb
.mb_imap_account
);
1239 if (mb
.mb_imap_pass
!= NULL
)
1240 free(mb
.mb_imap_pass
);
1241 mb
.mb_imap_account
= sstrdup(urlp
->url_p_eu_h_p
);
1242 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1243 * TODO object, and mailbox will naturally have an URL and credentials */
1244 mb
.mb_imap_pass
= sbufdup(ccred
.cc_pass
.s
, ccred
.cc_pass
.l
);
1246 if (!same_imap_account
) {
1247 if (mb
.mb_sock
.s_fd
>= 0)
1248 sclose(&mb
.mb_sock
);
1250 same_imap_account
= 0;
1261 if (mb
.mb_imap_mailbox
!= NULL
)
1262 free(mb
.mb_imap_mailbox
);
1263 mb
.mb_imap_mailbox
= sstrdup((urlp
->url_path
.s
!= NULL
)
1264 ? urlp
->url_path
.s
: "INBOX");
1265 initbox(urlp
->url_p_eu_h_p_p
);
1267 mb
.mb_type
= MB_VOID
;
1268 mb
.mb_active
= MB_NONE
;
1271 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1272 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1273 if (sigsetjmp(imapjmp
, 1)) {
1274 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1275 sclose(&mb
.mb_sock
);
1276 safe_signal(SIGINT
, saveint
);
1277 safe_signal(SIGPIPE
, savepipe
);
1280 mb
.mb_type
= MB_VOID
;
1281 mb
.mb_active
= MB_NONE
;
1285 if (saveint
!= SIG_IGN
)
1286 safe_signal(SIGINT
, imapcatch
);
1287 if (savepipe
!= SIG_IGN
)
1288 safe_signal(SIGPIPE
, imapcatch
);
1290 if (mb
.mb_sock
.s_fd
< 0) {
1291 if (disconnected(mb
.mb_imap_account
)) {
1292 if (cache_setptr(fm
, transparent
) == STOP
)
1293 n_err(_("Mailbox \"%s\" is not cached\n"), urlp
->url_p_eu_h_p_p
);
1296 if ((cp
= xok_vlook(imap_keepalive
, urlp
, OXM_ALL
)) != NULL
) {
1297 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1298 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1299 alarm(imapkeepalive
);
1304 mb
.mb_sock
.s_desc
= "IMAP";
1305 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1306 if (imap_preauth(&mb
, urlp
) != OKAY
|| imap_auth(&mb
, &ccred
) != OKAY
) {
1307 sclose(&mb
.mb_sock
);
1309 safe_signal(SIGINT
, saveint
);
1310 safe_signal(SIGPIPE
, savepipe
);
1315 } else /* same account */
1316 mb
.mb_flags
|= same_flags
;
1318 mb
.mb_perm
= ((options
& OPT_R_FLAG
) || (fm
& FEDIT_RDONLY
)) ? 0 : MB_DELE
;
1319 mb
.mb_type
= MB_IMAP
;
1321 if (imap_select(&mb
, &mailsize
, &msgCount
,
1322 (urlp
->url_path
.s
!= NULL
? urlp
->url_path
.s
: "INBOX"), fm
) != OKAY
) {
1323 /*sclose(&mb.mb_sock);
1325 safe_signal(SIGINT
, saveint
);
1326 safe_signal(SIGPIPE
, savepipe
);
1328 mb
.mb_type
= MB_VOID
;
1334 imap_setptr(&mb
, ((fm
& FEDIT_NEWMAIL
) != 0), transparent
,
1335 UNVOLATILE(&prevcount
));
1338 if (!(fm
& FEDIT_NEWMAIL
) && !transparent
)
1339 pstate
&= ~PS_SAW_COMMAND
;
1340 safe_signal(SIGINT
, saveint
);
1341 safe_signal(SIGPIPE
, savepipe
);
1344 if (!(fm
& FEDIT_NEWMAIL
) && mb
.mb_type
== MB_IMAP
)
1345 purgecache(&mb
, message
, msgCount
);
1346 if (((fm
& FEDIT_NEWMAIL
) || transparent
) && mb
.mb_sorted
) {
1351 if ((options
& OPT_EXISTONLY
) && (mb
.mb_type
== MB_IMAP
||
1352 mb
.mb_type
== MB_CACHE
)) {
1353 rv
= (msgCount
== 0);
1357 if (!(fm
& FEDIT_NEWMAIL
) && !(pstate
& PS_EDIT
) && msgCount
== 0) {
1358 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1359 !ok_blook(emptystart
))
1360 n_err(_("No mail at %s\n"), urlp
->url_p_eu_h_p_p
);
1365 if (fm
& FEDIT_NEWMAIL
)
1366 newmailinfo(prevcount
);
1374 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1375 int need
, const char *head
, size_t headsize
, long headlines
)
1377 char *line
= NULL
, *lp
;
1378 size_t linesize
= 0, linelen
, size
= 0;
1379 int emptyline
= 0, lines
= 0, excess
= 0;
1383 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1384 offset
= ftell(mp
->mb_otf
);
1387 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1389 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1391 if (linelen
> expected
) {
1392 excess
= linelen
- expected
;
1396 * Need to mask 'From ' lines. This cannot be done properly
1397 * since some servers pass them as 'From ' and others as
1398 * '>From '. Although one could identify the first kind of
1399 * server in principle, it is not possible to identify the
1400 * second as '>From ' may also come from a server of the
1401 * first type as actual data. So do what is absolutely
1402 * necessary only - mask 'From '.
1404 * If the line is the first line of the message header, it
1405 * is likely a real 'From ' line. In this case, it is just
1406 * ignored since it violates all standards.
1407 * TODO can the latter *really* happen??
1410 /* Since we simply copy over data without doing any transfer
1411 * encoding reclassification/adjustment we *have* to perform
1412 * RFC 4155 compliant From_ quoting here */
1413 if (emptyline
&& is_head(lp
, linelen
, TRU1
)) {
1414 fputc('>', mp
->mb_otf
);
1418 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1420 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1421 size
+= linelen
- 1;
1426 fputc('\n', mp
->mb_otf
);
1428 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1432 if ((expected
-= linelen
) <= 0)
1436 /* This is very ugly; but some IMAP daemons don't end a
1437 * message with \r\n\r\n, and we need \n\n for mbox format */
1438 fputc('\n', mp
->mb_otf
);
1445 m
->m_size
= size
+ headsize
;
1446 m
->m_lines
= lines
+ headlines
;
1447 m
->m_block
= mailx_blockof(offset
);
1448 m
->m_offset
= mailx_offsetof(offset
);
1451 m
->m_have
|= HAVE_HEADER
;
1454 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1455 m
->m_xlines
= m
->m_lines
;
1456 m
->m_xsize
= m
->m_size
;
1466 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1467 const char *head
, size_t headsize
, long headlines
)
1474 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1475 offset
= ftell(mp
->mb_otf
);
1477 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1479 fwrite(str
, 1, len
, mp
->mb_otf
);
1480 fputc('\n', mp
->mb_otf
);
1486 m
->m_size
= headsize
+ len
;
1487 m
->m_lines
= headlines
+ 1;
1488 m
->m_block
= mailx_blockof(offset
);
1489 m
->m_offset
= mailx_offsetof(offset
);
1490 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1491 m
->m_xlines
= m
->m_lines
;
1492 m
->m_xsize
= m
->m_size
;
1498 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1502 sighandler_type
volatile saveint
, savepipe
;
1503 char * volatile head
;
1504 char const *cp
, *loc
, * volatile item
, * volatile resp
;
1506 size_t volatile headsize
;
1509 long volatile headlines
;
1515 saveint
= savepipe
= SIG_IGN
;
1517 cp
= loc
= item
= resp
= NULL
;
1519 number
= (int)PTR2SIZE(m
- message
+ 1);
1526 if (getcache(mp
, m
, need
) == OKAY
)
1528 if (mp
->mb_type
== MB_CACHE
) {
1529 n_err(_("Message %lu not available\n"), (ul_i
)number
);
1533 if (mp
->mb_sock
.s_fd
< 0) {
1534 n_err(_("IMAP connection closed\n"));
1540 resp
= item
= "RFC822.HEADER";
1543 item
= "BODY.PEEK[]";
1545 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1546 char *hdr
= smalloc(m
->m_size
);
1548 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
1550 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
1555 headsize
= m
->m_size
;
1556 headlines
= m
->m_lines
;
1557 item
= "BODY.PEEK[TEXT]";
1558 resp
= "BODY[TEXT]";
1566 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1567 if (sigsetjmp(imapjmp
, 1)) {
1568 safe_signal(SIGINT
, saveint
);
1569 safe_signal(SIGPIPE
, savepipe
);
1573 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1574 safe_signal(SIGINT
, &_imap_maincatch
);
1575 if (savepipe
!= SIG_IGN
)
1576 safe_signal(SIGPIPE
, imapcatch
);
1579 snprintf(o
, sizeof o
, "%s UID FETCH %lu (%s)\r\n",
1580 tag(1), m
->m_uid
, item
);
1582 if (check_expunged() == STOP
)
1584 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
1586 IMAP_OUT(o
, MB_COMD
, goto out
)
1588 ok
= imap_answer(mp
, 1);
1591 if (response_status
!= RESPONSE_OTHER
||
1592 response_other
!= MESSAGE_DATA_FETCH
)
1594 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1597 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1605 n
= responded_other_number
;
1606 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1607 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1609 if ((cp
= strchr(loc
, '"')) != NULL
) {
1610 cp
= imap_unquotestr(cp
);
1611 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
1613 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1614 m
->m_xlines
= m
->m_lines
;
1615 m
->m_xsize
= m
->m_size
;
1619 expected
= atol(&cp
[1]);
1620 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1621 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1625 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
1627 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1630 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1631 if (options
& OPT_VERBVERB
)
1632 fputs(imapbuf
, stderr
);
1633 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1635 if (u
== m
->m_uid
) {
1636 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1643 while (mp
->mb_active
& MB_COMD
)
1644 ok
= imap_answer(mp
, 1);
1646 if (saveint
!= SIG_IGN
)
1647 safe_signal(SIGINT
, saveint
);
1648 if (savepipe
!= SIG_IGN
)
1649 safe_signal(SIGPIPE
, savepipe
);
1662 imap_header(struct message
*m
)
1667 rv
= imap_get(&mb
, m
, NEED_HEADER
);
1674 imap_body(struct message
*m
)
1679 rv
= imap_get(&mb
, m
, NEED_BODY
);
1685 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
1689 tomp
->m_size
= frommp
->m_size
;
1690 tomp
->m_lines
= frommp
->m_lines
;
1691 tomp
->m_block
= frommp
->m_block
;
1692 tomp
->m_offset
= frommp
->m_offset
;
1693 tomp
->m_have
= have
;
1694 if (have
& HAVE_BODY
) {
1695 tomp
->m_xlines
= frommp
->m_lines
;
1696 tomp
->m_xsize
= frommp
->m_size
;
1703 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
1711 FILE *queuefp
= NULL
;
1716 snprintf(o
, sizeof o
, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1717 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
1719 if (check_expunged() == STOP
)
1721 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1724 IMAP_OUT(o
, MB_COMD
, return STOP
)
1728 ok
= imap_answer(mp
, 1);
1729 if (response_status
!= RESPONSE_OTHER
)
1731 if (response_other
!= MESSAGE_DATA_FETCH
)
1733 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0) {
1737 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1739 expected
= atol(&cp
[1]);
1740 if (m
[bot
-1].m_uid
) {
1741 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
1743 for (n
= bot
; n
<= topp
; n
++)
1744 if ((unsigned long)u
== m
[n
-1].m_uid
)
1747 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1753 n
= responded_other_number
;
1754 if (n
<= 0 || n
> msgCount
) {
1755 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1759 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1760 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1761 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
1762 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1763 if (options
& OPT_VERBVERB
)
1764 fputs(imapbuf
, stderr
);
1765 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1767 for (n
= bot
; n
<= topp
; n
++)
1768 if ((unsigned long)u
== m
[n
-1].m_uid
)
1770 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1771 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
1779 while (mp
->mb_active
& MB_COMD
)
1780 ok
= imap_answer(mp
, 1);
1785 imap_getheaders(int volatile bot
, int volatile topp
) /* TODO iterator!! */
1787 sighandler_type saveint
, savepipe
;
1788 /*enum okay ok = STOP;*/
1792 if (mb
.mb_type
== MB_CACHE
)
1796 if (topp
> msgCount
)
1798 for (i
= bot
; i
< topp
; i
++) {
1799 if (message
[i
-1].m_have
& HAVE_HEADER
||
1800 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1805 for (i
= topp
; i
> bot
; i
--) {
1806 if (message
[i
-1].m_have
& HAVE_HEADER
||
1807 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1816 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1817 safe_signal(SIGINT
, &_imap_maincatch
);
1818 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1819 if (sigsetjmp(imapjmp
, 1) == 0) {
1820 if (savepipe
!= SIG_IGN
)
1821 safe_signal(SIGPIPE
, imapcatch
);
1823 for (i
= bot
; i
<= topp
; i
+= chunk
) {
1824 int j
= i
+ chunk
- 1;
1826 if (visible(message
+ j
))
1827 /*ok = */imap_fetchheaders(&mb
, message
, i
, j
);
1829 onintr(0); /* XXX imaplock? */
1832 safe_signal(SIGINT
, saveint
);
1833 safe_signal(SIGPIPE
, savepipe
);
1838 __imap_exit(struct mailbox
*mp
)
1841 FILE *queuefp
= NULL
;
1844 mp
->mb_active
|= MB_BYE
;
1845 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1846 IMAP_OUT(o
, MB_COMD
, return STOP
)
1852 imap_exit(struct mailbox
*mp
)
1857 rv
= __imap_exit(mp
);
1858 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1859 free(mp
->mb_imap_pass
);
1860 free(mp
->mb_imap_account
);
1861 free(mp
->mb_imap_mailbox
);
1862 if (mp
->mb_cache_directory
!= NULL
)
1863 free(mp
->mb_cache_directory
);
1864 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1865 mp
->mb_imap_account
=
1866 mp
->mb_imap_mailbox
=
1867 mp
->mb_cache_directory
= "";
1869 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
1870 mp
->mb_imap_mailbox
= NULL
;
1871 mp
->mb_cache_directory
= NULL
;
1874 sclose(&mp
->mb_sock
);
1880 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1883 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1884 if (mp
->mb_type
== MB_IMAP
)
1891 imap_close(struct mailbox
*mp
)
1894 FILE *queuefp
= NULL
;
1897 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1898 IMAP_OUT(o
, MB_COMD
, return STOP
)
1904 imap_update(struct mailbox
*mp
)
1907 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1910 if (!(pstate
& PS_EDIT
) && mp
->mb_perm
!= 0) {
1913 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
1914 if (m
->m_flag
& MBOX
)
1917 if (makembox() == STOP
)
1922 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
1923 if (mp
->mb_perm
== 0)
1925 else if (pstate
& PS_EDIT
)
1926 dodel
= ((m
->m_flag
& MDELETED
) != 0);
1928 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
1930 /* Fetch the result after around each 800 STORE commands
1931 * sent (approx. 32k data sent). Otherwise, servers will
1932 * try to flush the return queue at some point, leading
1933 * to a deadlock if we are still writing commands but not
1934 * reading their results */
1935 needstat
= stored
> 0 && stored
% 800 == 0;
1936 /* Even if this message has been deleted, continue
1937 * to set further flags. This is necessary to support
1938 * Gmail semantics, where "delete" actually means
1939 * "archive", and the flags are applied to the copy
1941 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
1942 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
1945 if (m
->m_flag
& MFLAG
) {
1946 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
1949 if (m
->m_flag
& MUNFLAG
) {
1950 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
1953 if (m
->m_flag
& MANSWER
) {
1954 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
1957 if (m
->m_flag
& MUNANSWER
) {
1958 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
1961 if (m
->m_flag
& MDRAFT
) {
1962 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
1965 if (m
->m_flag
& MUNDRAFT
) {
1966 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
1971 imap_delete(mp
, m
-message
+1, m
, needstat
);
1974 } else if (mp
->mb_type
!= MB_CACHE
||
1975 (!(pstate
& PS_EDIT
) &&
1976 !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
1977 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
1978 (MPRESERVE
| MTOUCH
) ||
1979 ((pstate
& PS_EDIT
) && !(m
->m_flag
& MDELETED
)))
1981 if (m
->m_flag
& MNEW
) {
1983 m
->m_flag
|= MSTATUS
;
1990 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
1991 if (!(m
->m_flag
& MUNLINKED
) &&
1992 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
1993 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
1998 /* XXX should be readonly (but our IMAP code is weird...) */
1999 if (!(options
& (OPT_EXISTONLY
| OPT_HEADERSONLY
| OPT_HEADERLIST
))) {
2000 if ((gotcha
|| modflags
) && (pstate
& PS_EDIT
)) {
2001 printf(_("\"%s\" "), displayname
);
2002 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2003 ? _("complete\n") : _("updated.\n"));
2004 } else if (held
&& !(pstate
& PS_EDIT
) && mp
->mb_perm
!= 0) {
2006 printf(_("Held 1 message in %s\n"), displayname
);
2008 printf(_("Held %d messages in %s\n"), held
, displayname
);
2019 sighandler_type
volatile saveint
, savepipe
;
2022 if (mb
.mb_type
== MB_CACHE
) {
2027 if (mb
.mb_sock
.s_fd
< 0) {
2028 n_err(_("IMAP connection closed\n"));
2033 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2034 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2035 if (sigsetjmp(imapjmp
, 1)) {
2036 safe_signal(SIGINT
, saveint
);
2037 safe_signal(SIGPIPE
, saveint
);
2041 if (saveint
!= SIG_IGN
)
2042 safe_signal(SIGINT
, imapcatch
);
2043 if (savepipe
!= SIG_IGN
)
2044 safe_signal(SIGPIPE
, imapcatch
);
2047 if (!same_imap_account
)
2050 safe_signal(SIGINT
, saveint
);
2051 safe_signal(SIGPIPE
, savepipe
);
2058 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2062 FILE *queuefp
= NULL
;
2065 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2068 snprintf(o
, sizeof o
, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2069 tag(1), m
->m_uid
, c
, sp
);
2071 if (check_expunged() == STOP
)
2073 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2075 IMAP_OUT(o
, MB_COMD
, return STOP
)
2079 mb
.mb_active
&= ~MB_COMD
;
2080 if (queuefp
!= NULL
)
2086 imap_undelete(struct message
*m
, int n
)
2091 rv
= imap_unstore(m
, n
, "\\Deleted");
2097 imap_unread(struct message
*m
, int n
)
2102 rv
= imap_unstore(m
, n
, "\\Seen");
2108 imap_unstore(struct message
*m
, int n
, const char *flag
)
2110 sighandler_type saveint
, savepipe
;
2111 enum okay rv
= STOP
;
2115 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2116 safe_signal(SIGINT
, &_imap_maincatch
);
2117 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2118 if (sigsetjmp(imapjmp
, 1) == 0) {
2119 if (savepipe
!= SIG_IGN
)
2120 safe_signal(SIGPIPE
, imapcatch
);
2122 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2124 safe_signal(SIGINT
, saveint
);
2125 safe_signal(SIGPIPE
, savepipe
);
2143 snprintf(ts
, sizeof ts
, "T%lu", n
);
2149 c_imap_imap(void *vp
)
2152 sighandler_type saveint
, savepipe
;
2153 struct mailbox
*mp
= &mb
;
2154 FILE *queuefp
= NULL
;
2155 enum okay
volatile ok
= STOP
;
2158 if (mp
->mb_type
!= MB_IMAP
) {
2159 printf("Not operating on an IMAP mailbox.\n");
2163 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2164 safe_signal(SIGINT
, &_imap_maincatch
);
2165 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2166 if (sigsetjmp(imapjmp
, 1) == 0) {
2167 if (savepipe
!= SIG_IGN
)
2168 safe_signal(SIGPIPE
, imapcatch
);
2170 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2171 IMAP_OUT(o
, MB_COMD
, goto out
)
2172 while (mp
->mb_active
& MB_COMD
) {
2173 ok
= imap_answer(mp
, 0);
2174 fputs(responded_text
, stdout
);
2178 safe_signal(SIGINT
, saveint
);
2179 safe_signal(SIGPIPE
, savepipe
);
2188 imap_newmail(int nmail
)
2192 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2198 if (had_exists
== msgCount
&& had_expunge
< 0)
2199 /* Some servers always respond with EXISTS to NOOP. If
2200 * the mailbox has been changed but the number of messages
2201 * has not, an EXPUNGE must also had been sent; otherwise,
2202 * nothing has changed */
2205 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2209 imap_putflags(int f
)
2215 bp
= buf
= salloc(100);
2216 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2221 for (cp
= "\\Seen"; *cp
; cp
++)
2227 for (cp
= "\\Flagged"; *cp
; cp
++)
2230 if (f
& MANSWERED
) {
2233 for (cp
= "\\Answered"; *cp
; cp
++)
2239 for (cp
= "\\Draft"; *cp
; cp
++)
2251 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2254 while (*cp
!= ')') {
2256 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2258 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2260 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2262 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2264 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2266 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2278 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2279 long xsize
, enum mflag flag
, time_t t
)
2281 char o
[LINESIZE
], *buf
;
2282 size_t bufsize
, buflen
, cnt
;
2283 long size
, lines
, ysize
;
2285 FILE *queuefp
= NULL
;
2289 if (mp
->mb_type
== MB_CACHE
) {
2290 queuefp
= cache_queue(mp
);
2291 if (queuefp
== NULL
) {
2300 buf
= smalloc(bufsize
= LINESIZE
);
2305 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2310 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2311 tag(1), imap_quotestr(name
), imap_putflags(flag
),
2312 imap_make_date_time(t
), size
);
2313 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2314 while (mp
->mb_active
& MB_COMD
) {
2315 rv
= imap_answer(mp
, twice
);
2316 if (response_type
== RESPONSE_CONT
)
2320 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2329 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2332 buf
[buflen
- 1] = '\r';
2334 if (mp
->mb_type
!= MB_CACHE
)
2335 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2337 fwrite(buf
, 1, buflen
+1, queuefp
);
2340 if (mp
->mb_type
!= MB_CACHE
)
2341 swrite(&mp
->mb_sock
, "\r\n");
2343 fputs("\r\n", queuefp
);
2344 while (mp
->mb_active
& MB_COMD
) {
2345 rv
= imap_answer(mp
, 0);
2346 if (response_status
== RESPONSE_NO
/*&&
2347 ascncasecmp(responded_text,
2348 "[TRYCREATE] ", 12) == 0*/) {
2354 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), imap_quotestr(name
));
2355 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2356 while (mp
->mb_active
& MB_COMD
)
2357 rv
= imap_answer(mp
, 1);
2360 imap_created_mailbox
++;
2362 } else if (rv
!= OKAY
)
2363 n_err(_("IMAP error: %s"), responded_text
);
2364 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2365 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2368 if (queuefp
!= NULL
)
2377 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2379 char *buf
, *bp
, *lp
;
2380 size_t bufsize
, buflen
, cnt
;
2381 off_t off1
= -1, offs
;
2383 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
2389 buf
= smalloc(bufsize
= LINESIZE
);
2396 for (flag
= MNEW
, state
= _NLSEP
;;) {
2397 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2400 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
2401 is_head(buf
, buflen
, FAL0
))) {
2402 if (off1
!= (off_t
)-1) {
2403 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
2406 fseek(fp
, offs
+buflen
, SEEK_SET
);
2408 off1
= offs
+ buflen
;
2414 tim
= unixtime(buf
);
2420 if (buf
[0] == '\n') {
2423 } else if (state
& _INHEAD
) {
2424 if (ascncasecmp(buf
, "status", 6) == 0) {
2426 while (whitechar(*lp
))
2429 while (*++lp
!= '\0')
2438 } else if (ascncasecmp(buf
, "x-status", 8) == 0) {
2440 while (whitechar(*lp
))
2443 while (*++lp
!= '\0')
2466 imap_append(const char *xserver
, FILE *fp
)
2468 sighandler_type
volatile saveint
, savepipe
;
2471 char const * volatile mbx
;
2472 enum okay rv
= STOP
;
2475 if (!url_parse(&url
, CPROTO_IMAP
, xserver
))
2477 if (!ok_blook(v15_compat
) &&
2478 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
2479 n_err(_("New-style URL used without *v15-compat* being set!\n"));
2480 mbx
= (url
.url_path
.s
!= NULL
) ? url
.url_path
.s
: "INBOX";
2483 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2484 safe_signal(SIGINT
, &_imap_maincatch
);
2485 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2486 if (sigsetjmp(imapjmp
, 1))
2488 if (savepipe
!= SIG_IGN
)
2489 safe_signal(SIGPIPE
, imapcatch
);
2491 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
2492 !strcmp(url
.url_p_eu_h_p
, mb
.mb_imap_account
)) {
2493 rv
= imap_append0(&mb
, mbx
, fp
);
2497 memset(&mx
, 0, sizeof mx
);
2499 if (!_imap_getcred(&mx
, &ccred
, &url
))
2502 if (disconnected(url
.url_p_eu_h_p
) == 0) {
2503 if (!sopen(&mx
.mb_sock
, &url
))
2505 mx
.mb_sock
.s_desc
= "IMAP";
2506 mx
.mb_type
= MB_IMAP
;
2507 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
2508 /* TODO the code now did
2509 * TODO mx.mb_imap_mailbox = mbx;
2510 * TODO though imap_mailbox is sfree()d and mbx
2511 * TODO is possibly even a constant
2512 * TODO i changed this to sstrdup() sofar, as is used
2513 * TODO somewhere else in this file for this! */
2514 mx
.mb_imap_mailbox
= sstrdup(mbx
);
2515 if (imap_preauth(&mx
, &url
) != OKAY
||
2516 imap_auth(&mx
, &ccred
) != OKAY
) {
2517 sclose(&mx
.mb_sock
);
2520 rv
= imap_append0(&mx
, mbx
, fp
);
2523 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
2524 mx
.mb_imap_mailbox
= sstrdup(mbx
); /* TODO as above */
2525 mx
.mb_type
= MB_CACHE
;
2526 rv
= imap_append0(&mx
, mbx
, fp
);
2533 safe_signal(SIGINT
, saveint
);
2534 safe_signal(SIGPIPE
, savepipe
);
2544 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2545 struct list_item
**lend
, int level
)
2547 char o
[LINESIZE
], *cp
;
2549 FILE *queuefp
= NULL
;
2550 struct list_item
*lp
;
2551 enum okay ok
= STOP
;
2554 *list
= *lend
= NULL
;
2555 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base
));
2556 IMAP_OUT(o
, MB_COMD
, return STOP
)
2557 while (mp
->mb_active
& MB_COMD
) {
2558 ok
= imap_answer(mp
, 1);
2559 if (response_status
== RESPONSE_OTHER
&&
2560 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
2561 cp
= imap_unquotestr(list_name
);
2562 lp
= csalloc(1, sizeof *lp
);
2564 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
2566 lp
->l_base
= *cp
? cp
: savestr(base
);
2567 lp
->l_attr
= list_attributes
;
2568 lp
->l_level
= level
+1;
2569 lp
->l_delim
= list_hierarchy_delimiter
;
2570 if (*list
&& *lend
) {
2571 (*lend
)->l_next
= lp
;
2581 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2583 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2590 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
2591 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
2594 if (list
== NULL
|| lend
== NULL
)
2597 for (lp
= list
; lp
; lp
= lp
->l_next
)
2598 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
2599 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
2600 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2601 memcpy(cp
, lp
->l_name
, n
);
2602 cp
[n
] = lp
->l_delim
;
2604 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
2605 lp
->l_has_children
= 1;
2606 if (strcmp(cp
, lx
->l_name
) == 0)
2615 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2618 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2622 if (!(lp
->l_attr
& LIST_NOSELECT
))
2623 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2624 else if (lp
->l_has_children
== 0)
2625 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2626 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
2634 imap_folders(const char * volatile name
, int strip
)
2636 sighandler_type saveint
, savepipe
;
2637 const char *fold
, *cp
, *sp
;
2641 cp
= protbase(name
);
2642 sp
= mb
.mb_imap_account
;
2643 if (sp
== NULL
|| strcmp(cp
, sp
)) {
2645 _("Cannot perform `folders' but when on the very IMAP "
2646 "account; the current one is\n `%s' -- "
2647 "try `folders @'\n"),
2648 (sp
!= NULL
? sp
: _("[NONE]")));
2652 fold
= imap_fileof(name
);
2653 if (options
& OPT_TTYOUT
) {
2654 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
2656 n_perr(_("tmpfile"), 0);
2663 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2664 safe_signal(SIGINT
, &_imap_maincatch
);
2665 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2666 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
2668 if (savepipe
!= SIG_IGN
)
2669 safe_signal(SIGPIPE
, imapcatch
);
2671 if (mb
.mb_type
== MB_CACHE
)
2672 cache_list(&mb
, fold
, strip
, fp
);
2674 imap_list(&mb
, fold
, strip
, fp
);
2678 if (options
& OPT_TTYOUT
)
2684 if (options
& OPT_TTYOUT
) {
2689 n_err(_("Folder not found\n"));
2692 safe_signal(SIGINT
, saveint
);
2693 safe_signal(SIGPIPE
, savepipe
);
2694 if (options
& OPT_TTYOUT
)
2707 long n
= 0, mx
= 0, columns
, width
;
2711 if ((out
= Ftmp(NULL
, "imapdopr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600))
2713 n_perr(_("tmpfile"), 0);
2717 while ((c
= getc(fp
)) != EOF
) {
2728 if (mx
< width
/ 2) {
2729 columns
= width
/ (mx
+2);
2730 snprintf(o
, sizeof o
, "sort | pr -%lu -w%lu -t", columns
, width
);
2732 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2733 run_command(XSHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2734 page_or_print(out
, 0);
2741 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2745 int twice
= 0, stored
= 0;
2746 FILE *queuefp
= NULL
;
2747 enum okay ok
= STOP
;
2750 if (mp
->mb_type
== MB_CACHE
) {
2751 if ((queuefp
= cache_queue(mp
)) == NULL
)
2755 qname
= imap_quotestr(name
= imap_fileof(name
));
2756 /* Since it is not possible to set flags on the copy, recently
2757 * set flags must be set on the original to include it in the copy */
2758 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
2759 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2760 if (m
->m_flag
&MFLAG
)
2761 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2762 if (m
->m_flag
&MUNFLAG
)
2763 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2764 if (m
->m_flag
&MANSWER
)
2765 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2766 if (m
->m_flag
&MUNANSWER
)
2767 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2768 if (m
->m_flag
&MDRAFT
)
2769 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2770 if (m
->m_flag
&MUNDRAFT
)
2771 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2774 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n", tag(1), m
->m_uid
, qname
);
2776 if (check_expunged() == STOP
)
2778 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
2780 IMAP_OUT(o
, MB_COMD
, goto out
)
2781 while (mp
->mb_active
& MB_COMD
)
2782 ok
= imap_answer(mp
, twice
);
2784 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
2785 response_status
== RESPONSE_OK
)
2786 imap_copyuid(mp
, m
, name
);
2788 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2789 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2790 IMAP_OUT(o
, MB_COMD
, goto out
)
2791 while (mp
->mb_active
& MB_COMD
)
2792 ok
= imap_answer(mp
, 1);
2794 imap_created_mailbox
++;
2799 if (queuefp
!= NULL
)
2802 /* ... and reset the flag to its initial value so that the 'exit'
2803 * command still leaves the message unread */
2805 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2806 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2809 if (m
->m_flag
& MFLAG
) {
2810 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2813 if (m
->m_flag
& MUNFLAG
) {
2814 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2817 if (m
->m_flag
& MANSWER
) {
2818 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2821 if (m
->m_flag
& MUNANSWER
) {
2822 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2825 if (m
->m_flag
& MDRAFT
) {
2826 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2829 if (m
->m_flag
& MUNDRAFT
) {
2830 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2834 mp
->mb_active
|= MB_COMD
;
2835 (void)imap_finish(mp
);
2841 imap_copy(struct message
*m
, int n
, const char *name
)
2843 sighandler_type saveint
, savepipe
;
2844 enum okay rv
= STOP
;
2848 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2849 safe_signal(SIGINT
, &_imap_maincatch
);
2850 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2851 if (sigsetjmp(imapjmp
, 1) == 0) {
2852 if (savepipe
!= SIG_IGN
)
2853 safe_signal(SIGPIPE
, imapcatch
);
2855 rv
= imap_copy1(&mb
, m
, n
, name
);
2857 safe_signal(SIGINT
, saveint
);
2858 safe_signal(SIGPIPE
, savepipe
);
2868 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2869 unsigned long *olduid
, unsigned long *newuid
)
2875 *uidvalidity
= strtoul(cp
, &xp
, 10);
2876 *olduid
= strtoul(xp
, &yp
, 10);
2877 *newuid
= strtoul(yp
, &zp
, 10);
2878 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2879 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
2885 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2892 *uidvalidity
= strtoul(cp
, &xp
, 10);
2893 *uid
= strtoul(xp
, &yp
, 10);
2894 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
2901 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2906 unsigned long uidvalidity
, olduid
, newuid
;
2910 memset(&xmb
, 0, sizeof xmb
);
2913 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2914 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
2919 xmb
.mb_cache_directory
= NULL
;
2920 xmb
.mb_imap_account
= sstrdup(mp
->mb_imap_account
);
2921 xmb
.mb_imap_pass
= sstrdup(mp
->mb_imap_pass
);
2922 xmb
.mb_imap_mailbox
= sstrdup(name
);
2923 if (mp
->mb_cache_directory
!= NULL
)
2924 xmb
.mb_cache_directory
= sstrdup(mp
->mb_cache_directory
);
2925 xmb
.mb_uidvalidity
= uidvalidity
;
2929 memset(&xm
, 0, sizeof xm
);
2931 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
2933 getcache(mp
, &xm
, NEED_HEADER
);
2934 getcache(mp
, &xm
, NEED_BODY
);
2936 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2937 getcache(mp
, m
, NEED_HEADER
);
2938 if ((m
->m_flag
& HAVE_BODY
) == 0)
2939 getcache(mp
, m
, NEED_BODY
);
2943 xm
.m_flag
&= ~MFULLYCACHED
;
2944 putcache(&xmb
, &xm
);
2946 if (xmb
.mb_cache_directory
!= NULL
)
2947 free(xmb
.mb_cache_directory
);
2948 if (xmb
.mb_imap_mailbox
!= NULL
)
2949 free(xmb
.mb_imap_mailbox
);
2950 if (xmb
.mb_imap_pass
!= NULL
)
2951 free(xmb
.mb_imap_pass
);
2952 if (xmb
.mb_imap_account
!= NULL
)
2953 free(xmb
.mb_imap_account
);
2959 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
2960 long size
, long lines
, int flag
, const char *name
)
2965 unsigned long uidvalidity
, uid
;
2969 xmb
.mb_imap_mailbox
= NULL
;
2971 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2972 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
2977 xmb
.mb_cache_directory
= NULL
;
2978 xmb
.mb_imap_mailbox
= sstrdup(name
);
2979 xmb
.mb_uidvalidity
= uidvalidity
;
2980 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2982 memset(&xm
, 0, sizeof xm
);
2983 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2985 xm
.m_block
= mailx_blockof(off1
);
2986 xm
.m_offset
= mailx_offsetof(off1
);
2989 xm
.m_lines
= xm
.m_xlines
= lines
;
2991 xm
.m_have
= HAVE_HEADER
| HAVE_BODY
;
2992 putcache(&xmb
, &xm
);
2994 if (xmb
.mb_imap_mailbox
!= NULL
)
2995 free(xmb
.mb_imap_mailbox
);
3001 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
3005 long size
, xsize
, ysize
, lines
;
3006 enum mflag flag
= MNEW
;
3007 char *name
, *buf
, *bp
;
3009 size_t bufsize
, buflen
, cnt
;
3010 enum okay rv
= STOP
;
3013 buf
= smalloc(bufsize
= LINESIZE
);
3016 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3019 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3024 if ((cp
= strrchr(bp
, '{')) == NULL
)
3027 xsize
= atol(&cp
[1]) + 2;
3028 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3034 imap_getflags(cp
, &cp
, &flag
);
3035 while (*++cp
== ' ')
3038 t
= imap_read_date_time(cp
);
3040 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
3047 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3050 buf
[--buflen
] = '\0';
3051 buf
[buflen
-1] = '\n';
3052 fwrite(buf
, 1, buflen
, tp
);
3059 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3060 imap_unquotestr(name
));
3070 #ifdef HAVE_IMAP_SEARCH
3072 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3075 char *o
, *xp
, *cs
, c
;
3077 FILE *queuefp
= NULL
;
3081 enum okay ok
= STOP
;
3085 for (cp
= spec
; *cp
; cp
++)
3088 cp
= charset_get_lc();
3090 if (asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")) {
3095 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
3096 sz
= strlen(spec
) + 1;
3097 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
3098 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
, FAL0
) == 0 &&
3107 cp
= imap_quotestr(cp
);
3108 cs
= salloc(n
= strlen(cp
) + 10);
3109 snprintf(cs
, n
, "CHARSET %s ", cp
);
3113 o
= ac_alloc(osize
= strlen(spec
) + 60);
3114 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3115 IMAP_OUT(o
, MB_COMD
, goto out
)
3116 while (mp
->mb_active
& MB_COMD
) {
3117 ok
= imap_answer(mp
, 0);
3118 if (response_status
== RESPONSE_OTHER
&&
3119 response_other
== MAILBOX_DATA_SEARCH
) {
3120 xp
= responded_other_text
;
3121 while (*xp
&& *xp
!= '\r') {
3122 n
= strtoul(xp
, &xp
, 10);
3123 for (i
= 0; i
< cnt
; i
++)
3124 if (m
[i
].m_uid
== n
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3125 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3136 imap_search1(const char * volatile spec
, int f
)
3138 sighandler_type saveint
, savepipe
;
3139 enum okay
volatile rv
= STOP
;
3142 if (mb
.mb_type
!= MB_IMAP
)
3146 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3147 safe_signal(SIGINT
, &_imap_maincatch
);
3148 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3149 if (sigsetjmp(imapjmp
, 1) == 0) {
3150 if (savepipe
!= SIG_IGN
)
3151 safe_signal(SIGPIPE
, imapcatch
);
3153 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3155 safe_signal(SIGINT
, saveint
);
3156 safe_signal(SIGPIPE
, savepipe
);
3164 #endif /* HAVE_IMAP_SEARCH */
3167 imap_thisaccount(const char *cp
)
3172 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3174 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3175 mb
.mb_imap_account
== NULL
)
3178 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3184 imap_remove(const char * volatile name
)
3186 sighandler_type
volatile saveint
, savepipe
;
3187 enum okay
volatile rv
= STOP
;
3190 if (mb
.mb_type
!= MB_IMAP
) {
3191 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name
);
3195 if (!imap_thisaccount(name
)) {
3196 n_err(_("Can only remove mailboxes on current IMAP server: "
3197 "\"%s\" not removed\n"), name
);
3202 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3203 safe_signal(SIGINT
, &_imap_maincatch
);
3204 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3205 if (sigsetjmp(imapjmp
, 1) == 0) {
3206 if (savepipe
!= SIG_IGN
)
3207 safe_signal(SIGPIPE
, imapcatch
);
3209 rv
= imap_remove1(&mb
, imap_fileof(name
));
3211 safe_signal(SIGINT
, saveint
);
3212 safe_signal(SIGPIPE
, savepipe
);
3216 rv
= cache_remove(name
);
3225 imap_remove1(struct mailbox
*mp
, const char *name
)
3227 FILE *queuefp
= NULL
;
3230 enum okay ok
= STOP
;
3233 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3234 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3235 IMAP_OUT(o
, MB_COMD
, goto out
)
3236 while (mp
->mb_active
& MB_COMD
)
3237 ok
= imap_answer(mp
, 1);
3244 imap_rename(const char *old
, const char *new)
3246 sighandler_type saveint
, savepipe
;
3247 enum okay rv
= STOP
;
3250 if (mb
.mb_type
!= MB_IMAP
) {
3251 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3255 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3256 n_err(_("Can only rename mailboxes on current IMAP "
3257 "server: \"%s\" not renamed to \"%s\"\n"), old
, new);
3262 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3263 safe_signal(SIGINT
, &_imap_maincatch
);
3264 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3265 if (sigsetjmp(imapjmp
, 1) == 0) {
3266 if (savepipe
!= SIG_IGN
)
3267 safe_signal(SIGPIPE
, imapcatch
);
3269 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3271 safe_signal(SIGINT
, saveint
);
3272 safe_signal(SIGPIPE
, savepipe
);
3276 rv
= cache_rename(old
, new);
3285 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3287 FILE *queuefp
= NULL
;
3290 enum okay ok
= STOP
;
3293 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3294 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old
),
3295 imap_quotestr(new));
3296 IMAP_OUT(o
, MB_COMD
, goto out
)
3297 while (mp
->mb_active
& MB_COMD
)
3298 ok
= imap_answer(mp
, 1);
3305 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3307 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3308 size_t bufsize
, buflen
, cnt
;
3309 long offs
, offs1
, offs2
, octets
;
3310 int twice
, gotcha
= 0;
3311 FILE *queuefp
= NULL
;
3312 enum okay ok
= OKAY
, rok
= OKAY
;
3315 buf
= smalloc(bufsize
= LINESIZE
);
3318 while ((offs1
= ftell(fp
)) >= 0 &&
3319 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3320 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3325 if ((offs
= ftell(fp
)) < 0)
3328 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3329 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3331 while (digitchar(*cp
))
3337 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3339 IMAP_OUT(o
, MB_COMD
, continue)
3340 while (mp
->mb_active
& MB_COMD
)
3341 ok
= imap_answer(mp
, twice
);
3342 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3344 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3345 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3347 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3348 IMAP_OUT(o
, MB_COMD
, continue)
3349 while (mp
->mb_active
& MB_COMD
)
3350 ok
= imap_answer(mp
, 1);
3353 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3354 if ((cp
= strrchr(bp
, '{')) == NULL
)
3356 octets
= atol(&cp
[1]) + 2;
3357 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3359 IMAP_OUT(o
, MB_COMD
, continue)
3360 while (mp
->mb_active
& MB_COMD
) {
3361 ok
= imap_answer(mp
, twice
);
3362 if (response_type
== RESPONSE_CONT
)
3366 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3370 while (octets
> 0) {
3371 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3372 ? sizeof iob
: (size_t)octets
);
3374 if (n
!= fread(iob
, 1, n
, fp
))
3376 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3378 swrite(&mp
->mb_sock
, "");
3379 while (mp
->mb_active
& MB_COMD
) {
3380 ok
= imap_answer(mp
, 0);
3381 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
3382 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3387 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3388 if ((offs2
= ftell(fp
)) < 0)
3390 fseek(fp
, offs1
, SEEK_SET
);
3391 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3392 (void)fseek(fp
, offs2
, SEEK_SET
);
3398 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp
);
3403 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
3404 IMAP_OUT(o
, MB_COMD
, continue)
3405 while (mp
->mb_active
& MB_COMD
)
3406 ok
= imap_answer(mp
, 1);
3412 ftruncate(fileno(fp
), 0);
3420 imap_strex(char const *cp
, char const **xp
)
3429 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
3432 else if (*cq
== '"')
3438 n
= salloc(cq
- cp
+ 2);
3439 memcpy(n
, cp
, cq
- cp
+1);
3440 n
[cq
- cp
+ 1] = '\0';
3449 check_expunged(void)
3454 if (expunged_messages
> 0) {
3455 n_err(_("Command not executed - messages have been expunged\n"));
3464 c_connect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
3467 int rv
, omsgCount
= msgCount
;
3471 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3472 n_err(_("Already connected\n"));
3477 if (!url_parse(&url
, CPROTO_IMAP
, mailname
)) {
3481 ok_bclear(disconnected
);
3482 vok_bclear(savecat("disconnected-", url
.url_u_h_p
.s
));
3484 if (mb
.mb_type
== MB_CACHE
) {
3485 enum fedit_mode fm
= FEDIT_NONE
;
3488 if (!(pstate
& PS_EDIT
))
3490 _imap_setfile1(&url
, fm
, 1);
3491 if (msgCount
> omsgCount
)
3492 newmailinfo(omsgCount
);
3501 c_disconnect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
3504 int rv
= 1, *msgvec
= vp
;
3507 if (mb
.mb_type
== MB_CACHE
) {
3508 n_err(_("Not connected\n"));
3511 if (mb
.mb_type
!= MB_IMAP
|| cached_uidvalidity(&mb
) == 0) {
3512 n_err(_("The current mailbox is not cached\n"));
3516 if (!url_parse(&url
, CPROTO_IMAP
, mailname
))
3521 ok_bset(disconnected
, TRU1
);
3522 if (mb
.mb_type
== MB_IMAP
) {
3523 enum fedit_mode fm
= FEDIT_NONE
;
3526 if (!(pstate
& PS_EDIT
))
3528 sclose(&mb
.mb_sock
);
3529 _imap_setfile1(&url
, fm
, 1);
3540 int rv
= 1, *msgvec
= vp
, *ip
;
3544 if (mb
.mb_type
!= MB_IMAP
) {
3545 n_err(_("Not connected to an IMAP server\n"));
3548 if (cached_uidvalidity(&mb
) == 0) {
3549 n_err(_("The current mailbox is not cached\n"));
3554 for (ip
= msgvec
; *ip
; ++ip
) {
3555 mp
= &message
[*ip
- 1];
3556 if (!(mp
->m_have
& HAVE_BODY
)) {
3569 disconnected(const char *file
)
3575 if (ok_blook(disconnected
)) {
3580 if (!url_parse(&url
, CPROTO_IMAP
, file
)) {
3584 rv
= vok_blook(savecat("disconnected-", url
.url_u_h_p
.s
));
3592 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
3594 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
3602 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
3603 PTRCMP(nmp
, <, message
+ msgCount
)) {
3604 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
3606 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
3608 if (omp
->m_uid
== nmp
->m_uid
) {
3609 hf
= nmp
->m_flag
& MHIDDEN
;
3610 if (transparent
&& mb
.mb_type
== MB_IMAP
)
3611 omp
->m_flag
&= ~MHIDDEN
;
3613 if (transparent
&& mb
.mb_type
== MB_CACHE
)
3614 nmp
[-1].m_flag
|= hf
;
3615 } else if (omp
->m_uid
< nmp
->m_uid
)
3622 prevdot
= newprevdot
;
3628 imap_read_date_time(const char *cp
)
3632 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
3635 /* "25-Jul-2004 15:33:44 +0200"
3637 * 0 5 10 15 20 25 */
3638 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3640 day
= strtol(&cp
[1], NULL
, 10);
3642 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3644 if (month_names
[++i
][0] == '\0')
3648 year
= strtol(&cp
[8], NULL
, 10);
3649 hour
= strtol(&cp
[13], NULL
, 10);
3650 minute
= strtol(&cp
[16], NULL
, 10);
3651 second
= strtol(&cp
[19], NULL
, 10);
3652 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
3666 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3669 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3679 imap_make_date_time(time_t t
)
3683 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3686 tzdiff
= t
- mktime(gmtime(&t
));
3687 tzdiff_hour
= (int)(tzdiff
/ 60);
3688 tzdiff_min
= tzdiff_hour
% 60;
3690 tmptr
= localtime(&t
);
3691 if (tmptr
->tm_isdst
> 0)
3693 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3694 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
], tmptr
->tm_year
+ 1900,
3695 tmptr
->tm_hour
, tmptr
->tm_min
, tmptr
->tm_sec
, tzdiff_hour
, tzdiff_min
);
3699 #endif /* HAVE_IMAP */
3701 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3703 imap_quotestr(char const *s
)
3708 np
= n
= salloc(2 * strlen(s
) + 3);
3711 if (*s
== '"' || *s
== '\\')
3722 imap_unquotestr(char const *s
)
3732 np
= n
= salloc(strlen(s
) + 1);
3745 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */