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_normalize_path(struct mailbox
*mp
, char const *cp
);
176 static char *imap_quotepath(struct mailbox
*mp
, char const *cp
);
177 static void imap_other_get(char *pp
);
178 static void imap_response_get(const char **cp
);
179 static void imap_response_parse(void);
180 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
181 static enum okay
imap_parse_list(void);
182 static enum okay
imap_finish(struct mailbox
*mp
);
183 static void imap_timer_off(void);
184 static void imapcatch(int s
);
185 static void _imap_maincatch(int s
);
186 static enum okay
imap_noop1(struct mailbox
*mp
);
187 static void rec_queue(enum rec_type type
, unsigned long cnt
);
188 static enum okay
rec_dequeue(void);
189 static void rec_rmqueue(void);
190 static void imapalarm(int s
);
191 static enum okay
imap_preauth(struct mailbox
*mp
, struct url
const *urlp
);
192 static enum okay
imap_capability(struct mailbox
*mp
);
193 static enum okay
imap_auth(struct mailbox
*mp
, struct ccred
*ccred
);
195 static enum okay
imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
);
197 static enum okay
imap_login(struct mailbox
*mp
, struct ccred
*ccred
);
199 static enum okay
_imap_gssapi(struct mailbox
*mp
, struct ccred
*ccred
);
201 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
202 static void imap_init(struct mailbox
*mp
, int n
);
203 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
205 static bool_t
_imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
,
207 static int _imap_setfile1(struct url
*urlp
, enum fedit_mode fm
,
209 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
210 size_t expected
, int need
, const char *head
,
211 size_t headsize
, long headlines
);
212 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
213 const char *str
, const char *head
, size_t headsize
,
215 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
217 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
218 struct message
*from
, enum havespec have
);
219 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
221 static enum okay
imap_exit(struct mailbox
*mp
);
222 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
,
224 static enum okay
imap_close(struct mailbox
*mp
);
225 static enum okay
imap_update(struct mailbox
*mp
);
226 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
227 int c
, const char *sp
, int needstat
);
228 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
229 static const char *tag(int new);
230 static char * imap_putflags(int f
);
231 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
232 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
233 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
234 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
235 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
236 struct list_item
**list
, struct list_item
**lend
,
238 static enum okay
imap_list(struct mailbox
*mp
, const char *base
, int strip
,
240 static void dopr(FILE *fp
);
241 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
243 static enum okay
imap_copyuid_parse(const char *cp
,
244 unsigned long *uidvalidity
, unsigned long *olduid
,
245 unsigned long *newuid
);
246 static enum okay
imap_appenduid_parse(const char *cp
,
247 unsigned long *uidvalidity
, unsigned long *uid
);
248 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
250 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
251 long off1
, long xsize
, long size
, long lines
, int flag
,
253 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
254 #ifdef HAVE_IMAP_SEARCH
255 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
,
256 const char *spec
, int f
);
258 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
259 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
261 static char * imap_strex(char const *cp
, char const **xp
);
262 static enum okay
check_expunged(void);
265 imap_delim_init(struct mailbox
*mp
, struct url
const *urlp
){
270 mp
->mb_imap_delim
[0] = '\0';
272 if((cp
= xok_vlook(imap_delim
, urlp
, OXM_ALL
)) != NULL
){
277 i
= sizeof(n_IMAP_DELIM
) -1;
281 if(i
< NELEM(mp
->mb_imap_delim
))
283 memcpy(&mb
.mb_imap_delim
[0], cp
, i
+1);
285 n_err(_("*imap-delim* for %s too long: %s\n"),
286 urlp
->url_input
, cp
);
292 imap_normalize_path(struct mailbox
*mp
, char const *cp
){ /* TODO btw: no utf7 */
293 char *rv_base
, *rv
, dc2
, dc
, c
, lc
;
297 /* Unless we operate in free fly, honour a non-set *imap-delim* to mean "use
298 * exactly what i have specified" */
302 else if((dc
= *(dcp
= &mp
->mb_imap_delim
[0])) != '\0')
305 /* Plain names don't need path quoting */
310 for(cpx
= cp
;; ++cpx
)
311 if((c
= *cpx
) == '\0')
314 if(strchr(n_IMAP_DELIM
, c
)){
320 else if(dc2
&& strchr(dcp
, c
) != NULL
)
323 /* And we don't need to reevaluate what we have seen yet */
324 i
= PTR2SIZE(cpx
- cp
);
325 rv
= rv_base
= salloc(i
+ (j
= strlen(cpx
) +1));
328 memcpy(&rv
[i
], cpx
, j
);
333 /* Squeeze adjacent delimiters, convert remain to dc */
334 for(lc
= '\0'; (c
= *cp
++) != '\0'; lc
= c
){
335 if(c
== dc
|| (dc2
&& strchr(dcp
, c
) != NULL
))
337 if(c
!= dc
|| lc
!= dc
)
349 imap_quotepath(struct mailbox
*mp
, char const *cp
){
353 rv
= imap_quotestr(imap_normalize_path(mp
, cp
));
359 imap_other_get(char *pp
)
364 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
366 response_other
= MAILBOX_DATA_FLAGS
;
367 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
369 response_other
= MAILBOX_DATA_LIST
;
370 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
372 response_other
= MAILBOX_DATA_LSUB
;
373 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
375 response_other
= MAILBOX_DATA_MAILBOX
;
376 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
378 response_other
= MAILBOX_DATA_SEARCH
;
379 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
381 response_other
= MAILBOX_DATA_STATUS
;
382 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
384 response_other
= CAPABILITY_DATA
;
386 responded_other_number
= strtol(pp
, &xp
, 10);
389 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
390 response_other
= MAILBOX_DATA_EXISTS
;
391 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
392 response_other
= MAILBOX_DATA_RECENT
;
393 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
394 response_other
= MESSAGE_DATA_EXPUNGE
;
395 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
397 response_other
= MESSAGE_DATA_FETCH
;
399 response_other
= RESPONSE_OTHER_UNKNOWN
;
401 responded_other_text
= pp
;
406 imap_response_get(const char **cp
)
409 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
411 response_status
= RESPONSE_OK
;
412 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
414 response_status
= RESPONSE_NO
;
415 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
417 response_status
= RESPONSE_BAD
;
418 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
420 response_status
= RESPONSE_PREAUTH
;
421 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
423 response_status
= RESPONSE_BYE
;
425 response_status
= RESPONSE_OTHER
;
430 imap_response_parse(void)
432 static char *parsebuf
; /* TODO Use pool */
433 static size_t parsebufsize
;
435 const char *ip
= imapbuf
;
439 if (parsebufsize
< imapbufsize
)
440 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
441 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
445 response_type
= RESPONSE_CONT
;
460 imap_response_get(&ip
);
461 pp
= &parsebuf
[ip
- imapbuf
];
462 switch (response_status
) {
464 response_type
= RESPONSE_FATAL
;
467 response_type
= RESPONSE_DATA
;
471 responded_tag
= parsebuf
;
472 while (*pp
&& *pp
!= ' ')
475 response_type
= RESPONSE_ILLEGAL
;
479 while (*pp
&& *pp
== ' ')
482 response_type
= RESPONSE_ILLEGAL
;
485 ip
= &imapbuf
[pp
- parsebuf
];
486 response_type
= RESPONSE_TAGGED
;
487 imap_response_get(&ip
);
488 pp
= &parsebuf
[ip
- imapbuf
];
491 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
492 response_status
== RESPONSE_OTHER
)
498 imap_answer(struct mailbox
*mp
, int errprnt
)
505 if (mp
->mb_type
== MB_CACHE
)
509 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
510 if (options
& OPT_VERBVERB
)
511 fputs(imapbuf
, stderr
);
512 imap_response_parse();
513 if (response_type
== RESPONSE_ILLEGAL
)
515 if (response_type
== RESPONSE_CONT
) {
519 if (response_status
== RESPONSE_OTHER
) {
520 if (response_other
== MAILBOX_DATA_EXISTS
) {
521 had_exists
= responded_other_number
;
522 rec_queue(REC_EXISTS
, responded_other_number
);
525 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
526 rec_queue(REC_EXPUNGE
, responded_other_number
);
534 if (response_type
== RESPONSE_TAGGED
) {
535 if (asccasecmp(responded_tag
, tag(0)) == 0)
540 switch (response_status
) {
541 case RESPONSE_PREAUTH
:
542 mp
->mb_active
&= ~MB_PREAUTH
;
555 n_err(_("IMAP error: %s"), responded_text
);
557 case RESPONSE_UNKNOWN
: /* does not happen */
560 mp
->mb_active
= MB_NONE
;
568 if (response_status
!= RESPONSE_OTHER
&&
569 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
570 n_err(_("IMAP alert: %s"), &responded_text
[8]);
572 mp
->mb_active
&= ~MB_COMD
;
575 mp
->mb_active
= MB_NONE
;
583 imap_parse_list(void)
591 cp
= responded_other_text
;
592 list_attributes
= LIST_NONE
;
594 while (*cp
&& *cp
!= ')') {
596 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
597 list_attributes
|= LIST_NOINFERIORS
;
599 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
600 list_attributes
|= LIST_NOSELECT
;
602 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
603 list_attributes
|= LIST_MARKED
;
605 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
606 list_attributes
|= LIST_UNMARKED
;
618 list_hierarchy_delimiter
= EOF
;
622 list_hierarchy_delimiter
= *cp
++ & 0377;
623 if (cp
[0] != '"' || cp
[1] != ' ')
626 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
627 list_hierarchy_delimiter
= EOF
;
634 while (*cp
&& *cp
!= '\r')
644 imap_finish(struct mailbox
*mp
)
647 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
657 if (imapkeepalive
> 0) {
659 safe_signal(SIGALRM
, savealrm
);
667 NYD_X
; /* Signal handler */
670 n_err_sighdl(_("Interrupt\n"));
671 siglongjmp(imapjmp
, 1);
674 n_err_sighdl(_("Received SIGPIPE during IMAP operation\n"));
680 _imap_maincatch(int s
)
682 NYD_X
; /* Signal handler */
684 if (interrupts
++ == 0) {
685 n_err_sighdl(_("Interrupt\n"));
692 imap_noop1(struct mailbox
*mp
)
695 FILE *queuefp
= NULL
;
698 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
699 IMAP_OUT(o
, MB_COMD
, return STOP
)
705 imap_fileof(char const *xcp
)
707 char const *cp
= xcp
;
712 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
716 if (cp
[0] == '/' && state
== 1) {
734 sighandler_type
volatile oldint
, oldpipe
;
738 if (mb
.mb_type
!= MB_IMAP
)
742 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
743 safe_signal(SIGINT
, &_imap_maincatch
);
744 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
745 if (sigsetjmp(imapjmp
, 1) == 0) {
746 if (oldpipe
!= SIG_IGN
)
747 safe_signal(SIGPIPE
, imapcatch
);
749 rv
= imap_noop1(&mb
);
751 safe_signal(SIGINT
, oldint
);
752 safe_signal(SIGPIPE
, oldpipe
);
762 rec_queue(enum rec_type rt
, unsigned long cnt
)
767 rp
= scalloc(1, sizeof *rp
);
770 if (record
&& recend
) {
771 recend
->rec_next
= rp
;
774 record
= recend
= rp
;
781 struct message
*omessage
;
782 struct record
*rp
, *rq
;
791 message
= smalloc((msgCount
+1) * sizeof *message
);
793 memcpy(message
, omessage
, msgCount
* sizeof *message
);
794 memset(&message
[msgCount
], 0, sizeof *message
);
796 rp
= record
, rq
= NULL
;
799 switch (rp
->rec_type
) {
801 exists
= rp
->rec_count
;
804 if (rp
->rec_count
== 0) {
808 if (rp
->rec_count
> (unsigned long)msgCount
) {
809 if (exists
== 0 || rp
->rec_count
> exists
--)
815 delcache(&mb
, &message
[rp
->rec_count
-1]);
816 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
817 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
819 /* If the message was part of a collapsed thread,
820 * the m_collapsed field of one of its ancestors
821 * should be incremented. It seems hardly possible
822 * to do this with the current message structure,
823 * though. The result is that a '+' may be shown
824 * in the header summary even if no collapsed
836 record
= recend
= NULL
;
837 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
838 message
= srealloc(message
, (exists
+ 1) * sizeof *message
);
839 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
840 for (i
= msgCount
; i
< exists
; ++i
)
842 imap_flags(&mb
, msgCount
+1, exists
);
861 for (rp
= record
; rp
!= NULL
;) {
862 struct record
*tmp
= rp
;
866 record
= recend
= NULL
;
874 sighandler_type
volatile saveint
, savepipe
;
875 NYD_X
; /* Signal handler */
878 if (imaplock
++ == 0) {
879 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
880 safe_signal(SIGINT
, &_imap_maincatch
);
881 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
882 if (sigsetjmp(imapjmp
, 1)) {
883 safe_signal(SIGINT
, saveint
);
884 safe_signal(SIGPIPE
, savepipe
);
887 if (savepipe
!= SIG_IGN
)
888 safe_signal(SIGPIPE
, imapcatch
);
889 if (imap_noop1(&mb
) != OKAY
) {
890 safe_signal(SIGINT
, saveint
);
891 safe_signal(SIGPIPE
, savepipe
);
894 safe_signal(SIGINT
, saveint
);
895 safe_signal(SIGPIPE
, savepipe
);
898 alarm(imapkeepalive
);
904 imap_preauth(struct mailbox
*mp
, struct url
const *urlp
)
908 mp
->mb_active
|= MB_PREAUTH
;
912 if (!mp
->mb_sock
.s_use_ssl
&& xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
913 FILE *queuefp
= NULL
;
916 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
917 IMAP_OUT(o
, MB_COMD
, return STOP
)
919 if (ssl_open(urlp
, &mp
->mb_sock
) != OKAY
)
923 if (xok_blook(imap_use_starttls
, urlp
, OXM_ALL
)) {
924 n_err(_("No SSL support compiled in\n"));
934 imap_capability(struct mailbox
*mp
)
937 FILE *queuefp
= NULL
;
942 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
943 IMAP_OUT(o
, MB_COMD
, return STOP
)
944 while (mp
->mb_active
& MB_COMD
) {
945 ok
= imap_answer(mp
, 0);
946 if (response_status
== RESPONSE_OTHER
&&
947 response_other
== CAPABILITY_DATA
) {
948 cp
= responded_other_text
;
950 while (spacechar(*cp
))
952 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
954 mp
->mb_flags
|= MB_UIDPLUS
;
955 while (*cp
&& !spacechar(*cp
))
964 imap_auth(struct mailbox
*mp
, struct ccred
*ccred
)
969 if (!(mp
->mb_active
& MB_PREAUTH
)) {
974 switch (ccred
->cc_authtype
) {
976 rv
= imap_login(mp
, ccred
);
979 case AUTHTYPE_CRAM_MD5
:
980 rv
= imap_cram_md5(mp
, ccred
);
984 case AUTHTYPE_GSSAPI
:
985 rv
= _imap_gssapi(mp
, ccred
);
999 imap_cram_md5(struct mailbox
*mp
, struct ccred
*ccred
)
1001 char o
[LINESIZE
], *cp
;
1002 FILE *queuefp
= NULL
;
1003 enum okay rv
= STOP
;
1006 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
1007 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
1009 if (response_type
!= RESPONSE_CONT
)
1012 cp
= cram_md5_string(&ccred
->cc_user
, &ccred
->cc_pass
, responded_text
);
1013 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
1014 while (mp
->mb_active
& MB_COMD
)
1015 rv
= imap_answer(mp
, 1);
1020 #endif /* HAVE_MD5 */
1023 imap_login(struct mailbox
*mp
, struct ccred
*ccred
)
1026 FILE *queuefp
= NULL
;
1027 enum okay rv
= STOP
;
1030 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
1031 tag(1), imap_quotestr(ccred
->cc_user
.s
), imap_quotestr(ccred
->cc_pass
.s
));
1032 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
1033 while (mp
->mb_active
& MB_COMD
)
1034 rv
= imap_answer(mp
, 1);
1041 # include "imap_gssapi.h"
1045 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
,
1048 enum okay ok
= OKAY
;
1051 FILE *queuefp
= NULL
;
1055 mp
->mb_uidvalidity
= 0;
1056 snprintf(o
, sizeof o
, "%s %s %s\r\n", tag(1),
1057 (fm
& FEDIT_RDONLY
? "EXAMINE" : "SELECT"), imap_quotepath(mp
, mbx
));
1058 IMAP_OUT(o
, MB_COMD
, return STOP
)
1059 while (mp
->mb_active
& MB_COMD
) {
1060 ok
= imap_answer(mp
, 1);
1061 if (response_status
!= RESPONSE_OTHER
&&
1062 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
1063 mp
->mb_uidvalidity
= atol(&cp
[13]);
1065 *cnt
= (had_exists
> 0) ? had_exists
: 0;
1066 if (response_status
!= RESPONSE_OTHER
&&
1067 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
1073 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
1076 FILE *queuefp
= NULL
;
1079 unsigned x
= X
, y
= Y
, n
;
1082 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
1083 IMAP_OUT(o
, MB_COMD
, return STOP
)
1084 while (mp
->mb_active
& MB_COMD
) {
1086 if (response_status
== RESPONSE_OTHER
&&
1087 response_other
== MESSAGE_DATA_FETCH
) {
1088 n
= responded_other_number
;
1096 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1101 imap_getflags(cp
, &cp
, &m
->m_flag
);
1104 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1105 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1106 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1107 m
->m_flag
&= ~MHIDDEN
;
1110 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1112 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1115 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1117 IMAP_OUT(o
, MB_COMD
, return STOP
)
1118 while (mp
->mb_active
& MB_COMD
) {
1120 if (response_status
== RESPONSE_OTHER
&&
1121 response_other
== MESSAGE_DATA_FETCH
) {
1122 n
= responded_other_number
;
1128 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1129 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1130 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1131 m
->m_time
= imap_read_date_time(&cp
[13]);
1136 for (n
= X
; n
<= Y
; ++n
) {
1137 putcache(mp
, &message
[n
-1]);
1145 imap_init(struct mailbox
*mp
, int n
)
1152 m
->m_flag
= MUSED
| MNOFROM
;
1159 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1161 struct message
*omessage
= 0;
1162 int i
, omsgCount
= 0;
1163 enum okay dequeued
= STOP
;
1166 if (nmail
|| transparent
) {
1168 omsgCount
= msgCount
;
1171 dequeued
= rec_dequeue();
1173 if (had_exists
>= 0) {
1174 if (dequeued
!= OKAY
)
1175 msgCount
= had_exists
;
1178 if (had_expunge
>= 0) {
1179 if (dequeued
!= OKAY
)
1180 msgCount
-= had_expunge
;
1184 if (nmail
&& expunged_messages
)
1185 printf("Expunged %ld message%s.\n", expunged_messages
,
1186 (expunged_messages
!= 1 ? "s" : ""));
1187 *prevcount
= omsgCount
- expunged_messages
;
1188 expunged_messages
= 0;
1190 fputs("IMAP error: Negative message count\n", stderr
);
1194 if (dequeued
!= OKAY
) {
1195 message
= scalloc(msgCount
+ 1, sizeof *message
);
1196 for (i
= 0; i
< msgCount
; i
++)
1198 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1201 imap_flags(mp
, 1, msgCount
);
1202 message
[msgCount
].m_size
= 0;
1203 message
[msgCount
].m_lines
= 0;
1206 if (nmail
|| transparent
)
1207 transflags(omessage
, omsgCount
, transparent
);
1214 imap_setfile(const char *xserver
, enum fedit_mode fm
)
1220 if (!url_parse(&url
, CPROTO_IMAP
, xserver
)) {
1224 if (!ok_blook(v15_compat
) &&
1225 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
1226 n_err(_("New-style URL used without *v15-compat* being set!\n"));
1228 _imap_rdonly
= ((fm
& FEDIT_RDONLY
) != 0);
1229 rv
= _imap_setfile1(&url
, fm
, 0);
1236 _imap_getcred(struct mailbox
*mbp
, struct ccred
*ccredp
, struct url
*urlp
)
1241 if (ok_blook(v15_compat
))
1242 rv
= ccred_lookup(ccredp
, urlp
);
1245 *xuhp
= (urlp
->url_had_user
? urlp
->url_eu_h_p
.s
: urlp
->url_u_h_p
.s
);
1247 if ((var
= mbp
->mb_imap_pass
) != NULL
) {
1248 var
= savecat("password-", xuhp
);
1249 if ((old
= vok_vlook(var
)) != NULL
)
1251 vok_vset(var
, mbp
->mb_imap_pass
);
1253 rv
= ccred_lookup_old(ccredp
, CPROTO_IMAP
, xuhp
);
1268 _imap_setfile1(struct url
*urlp
, enum fedit_mode fm
, int volatile transparent
)
1272 sighandler_type
volatile saveint
, savepipe
;
1275 int volatile prevcount
= 0;
1276 enum mbflags same_flags
;
1279 if (fm
& FEDIT_NEWMAIL
) {
1280 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1281 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1282 if (saveint
!= SIG_IGN
)
1283 safe_signal(SIGINT
, imapcatch
);
1284 if (savepipe
!= SIG_IGN
)
1285 safe_signal(SIGPIPE
, imapcatch
);
1290 same_flags
= mb
.mb_flags
;
1291 same_imap_account
= 0;
1292 if (mb
.mb_imap_account
!= NULL
&&
1293 (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)) {
1294 if (mb
.mb_sock
.s_fd
> 0 && mb
.mb_sock
.s_rsz
>= 0 &&
1295 !strcmp(mb
.mb_imap_account
, urlp
->url_p_eu_h_p
) &&
1296 disconnected(mb
.mb_imap_account
) == 0) {
1297 same_imap_account
= 1;
1298 if (urlp
->url_pass
.s
== NULL
&& mb
.mb_imap_pass
!= NULL
)
1301 } else if ((transparent || mb.mb_type == MB_CACHE) &&
1302 !strcmp(mb.mb_imap_account, urlp->url_p_eu_h_p) &&
1303 urlp->url_pass.s == NULL && mb.mb_imap_pass != NULL)
1306 urlp
->url_pass
.l
= strlen(urlp
->url_pass
.s
= savestr(mb
.mb_imap_pass
));
1310 if (!same_imap_account
&& mb
.mb_imap_pass
!= NULL
) {
1311 free(mb
.mb_imap_pass
);
1312 mb
.mb_imap_pass
= NULL
;
1314 if (!_imap_getcred(&mb
, &ccred
, urlp
)) {
1320 if (!same_imap_account
) {
1321 if (!disconnected(urlp
->url_p_eu_h_p
) && !sopen(&so
, urlp
)) {
1330 if (fm
& FEDIT_SYSBOX
)
1334 if (mb
.mb_imap_account
!= NULL
)
1335 free(mb
.mb_imap_account
);
1336 if (mb
.mb_imap_pass
!= NULL
)
1337 free(mb
.mb_imap_pass
);
1338 mb
.mb_imap_account
= sstrdup(urlp
->url_p_eu_h_p
);
1339 /* TODO This is a hack to allow '@boxname'; in the end everything will be an
1340 * TODO object, and mailbox will naturally have an URL and credentials */
1341 mb
.mb_imap_pass
= sbufdup(ccred
.cc_pass
.s
, ccred
.cc_pass
.l
);
1343 if (!same_imap_account
) {
1344 if (mb
.mb_sock
.s_fd
>= 0)
1345 sclose(&mb
.mb_sock
);
1347 same_imap_account
= 0;
1358 if (mb
.mb_imap_mailbox
!= NULL
)
1359 free(mb
.mb_imap_mailbox
);
1360 assert(urlp
->url_path
.s
!= NULL
);
1361 imap_delim_init(&mb
, urlp
);
1362 mb
.mb_imap_mailbox
= sstrdup(imap_normalize_path(&mb
, urlp
->url_path
.s
));
1363 initbox(savecatsep(urlp
->url_p_eu_h_p
,
1364 (mb
.mb_imap_delim
[0] != '\0' ? mb
.mb_imap_delim
[0] : n_IMAP_DELIM
[0]),
1365 mb
.mb_imap_mailbox
));
1367 mb
.mb_type
= MB_VOID
;
1368 mb
.mb_active
= MB_NONE
;
1371 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1372 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1373 if (sigsetjmp(imapjmp
, 1)) {
1374 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1375 sclose(&mb
.mb_sock
);
1376 safe_signal(SIGINT
, saveint
);
1377 safe_signal(SIGPIPE
, savepipe
);
1380 mb
.mb_type
= MB_VOID
;
1381 mb
.mb_active
= MB_NONE
;
1382 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1385 if (saveint
!= SIG_IGN
)
1386 safe_signal(SIGINT
, imapcatch
);
1387 if (savepipe
!= SIG_IGN
)
1388 safe_signal(SIGPIPE
, imapcatch
);
1390 if (mb
.mb_sock
.s_fd
< 0) {
1391 if (disconnected(mb
.mb_imap_account
)) {
1392 if (cache_setptr(fm
, transparent
) == STOP
)
1393 n_err(_("Mailbox \"%s\" is not cached\n"), urlp
->url_p_eu_h_p_p
);
1396 if ((cp
= xok_vlook(imap_keepalive
, urlp
, OXM_ALL
)) != NULL
) {
1397 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1398 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1399 alarm(imapkeepalive
);
1404 mb
.mb_sock
.s_desc
= "IMAP";
1405 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1406 if (imap_preauth(&mb
, urlp
) != OKAY
|| imap_auth(&mb
, &ccred
) != OKAY
) {
1407 sclose(&mb
.mb_sock
);
1409 safe_signal(SIGINT
, saveint
);
1410 safe_signal(SIGPIPE
, savepipe
);
1412 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1415 } else /* same account */
1416 mb
.mb_flags
|= same_flags
;
1418 if (options
& OPT_R_FLAG
)
1420 mb
.mb_perm
= (fm
& FEDIT_RDONLY
) ? 0 : MB_DELE
;
1421 mb
.mb_type
= MB_IMAP
;
1423 assert(urlp
->url_path
.s
!= NULL
);
1424 if (imap_select(&mb
, &mailsize
, &msgCount
, urlp
->url_path
.s
, fm
) != OKAY
) {
1425 /*sclose(&mb.mb_sock);
1427 safe_signal(SIGINT
, saveint
);
1428 safe_signal(SIGPIPE
, savepipe
);
1430 mb
.mb_type
= MB_VOID
;
1431 rv
= (fm
& (FEDIT_SYSBOX
| FEDIT_NEWMAIL
)) ? 1 : -1;
1436 imap_setptr(&mb
, ((fm
& FEDIT_NEWMAIL
) != 0), transparent
,
1437 UNVOLATILE(&prevcount
));
1440 if (!(fm
& FEDIT_NEWMAIL
) && !transparent
)
1441 pstate
&= ~PS_SAW_COMMAND
;
1442 safe_signal(SIGINT
, saveint
);
1443 safe_signal(SIGPIPE
, savepipe
);
1446 if (!(fm
& FEDIT_NEWMAIL
) && mb
.mb_type
== MB_IMAP
)
1447 purgecache(&mb
, message
, msgCount
);
1448 if (((fm
& FEDIT_NEWMAIL
) || transparent
) && mb
.mb_sorted
) {
1453 if ((options
& OPT_EXISTONLY
) && (mb
.mb_type
== MB_IMAP
||
1454 mb
.mb_type
== MB_CACHE
)) {
1455 rv
= (msgCount
== 0);
1459 if (!(fm
& FEDIT_NEWMAIL
) && !(pstate
& PS_EDIT
) && msgCount
== 0) {
1460 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1461 !ok_blook(emptystart
))
1462 n_err(_("No mail at %s\n"), urlp
->url_p_eu_h_p_p
);
1467 if (fm
& FEDIT_NEWMAIL
)
1468 newmailinfo(prevcount
);
1476 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1477 int need
, const char *head
, size_t headsize
, long headlines
)
1479 char *line
= NULL
, *lp
;
1480 size_t linesize
= 0, linelen
, size
= 0;
1481 int emptyline
= 0, lines
= 0, excess
= 0;
1485 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1486 offset
= ftell(mp
->mb_otf
);
1489 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1491 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1493 if (linelen
> expected
) {
1494 excess
= linelen
- expected
;
1498 * Need to mask 'From ' lines. This cannot be done properly
1499 * since some servers pass them as 'From ' and others as
1500 * '>From '. Although one could identify the first kind of
1501 * server in principle, it is not possible to identify the
1502 * second as '>From ' may also come from a server of the
1503 * first type as actual data. So do what is absolutely
1504 * necessary only - mask 'From '.
1506 * If the line is the first line of the message header, it
1507 * is likely a real 'From ' line. In this case, it is just
1508 * ignored since it violates all standards.
1509 * TODO can the latter *really* happen??
1512 /* Since we simply copy over data without doing any transfer
1513 * encoding reclassification/adjustment we *have* to perform
1514 * RFC 4155 compliant From_ quoting here */
1515 if (emptyline
&& is_head(lp
, linelen
, FAL0
)) {
1516 fputc('>', mp
->mb_otf
);
1520 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1522 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1523 size
+= linelen
- 1;
1528 fputc('\n', mp
->mb_otf
);
1530 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1534 if ((expected
-= linelen
) <= 0)
1538 /* This is very ugly; but some IMAP daemons don't end a
1539 * message with \r\n\r\n, and we need \n\n for mbox format */
1540 fputc('\n', mp
->mb_otf
);
1547 m
->m_size
= size
+ headsize
;
1548 m
->m_lines
= lines
+ headlines
;
1549 m
->m_block
= mailx_blockof(offset
);
1550 m
->m_offset
= mailx_offsetof(offset
);
1553 m
->m_have
|= HAVE_HEADER
;
1556 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1557 m
->m_xlines
= m
->m_lines
;
1558 m
->m_xsize
= m
->m_size
;
1568 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1569 const char *head
, size_t headsize
, long headlines
)
1576 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1577 offset
= ftell(mp
->mb_otf
);
1579 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1581 fwrite(str
, 1, len
, mp
->mb_otf
);
1582 fputc('\n', mp
->mb_otf
);
1588 m
->m_size
= headsize
+ len
;
1589 m
->m_lines
= headlines
+ 1;
1590 m
->m_block
= mailx_blockof(offset
);
1591 m
->m_offset
= mailx_offsetof(offset
);
1592 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1593 m
->m_xlines
= m
->m_lines
;
1594 m
->m_xsize
= m
->m_size
;
1600 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1604 sighandler_type
volatile saveint
, savepipe
;
1605 char * volatile head
;
1606 char const *cp
, *loc
, * volatile item
, * volatile resp
;
1608 size_t volatile headsize
;
1611 long volatile headlines
;
1617 saveint
= savepipe
= SIG_IGN
;
1619 cp
= loc
= item
= resp
= NULL
;
1621 number
= (int)PTR2SIZE(m
- message
+ 1);
1628 if (getcache(mp
, m
, need
) == OKAY
)
1630 if (mp
->mb_type
== MB_CACHE
) {
1631 n_err(_("Message %lu not available\n"), (ul_i
)number
);
1635 if (mp
->mb_sock
.s_fd
< 0) {
1636 n_err(_("IMAP connection closed\n"));
1642 resp
= item
= "RFC822.HEADER";
1645 item
= "BODY.PEEK[]";
1647 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1648 char *hdr
= smalloc(m
->m_size
);
1650 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
1652 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
1657 headsize
= m
->m_size
;
1658 headlines
= m
->m_lines
;
1659 item
= "BODY.PEEK[TEXT]";
1660 resp
= "BODY[TEXT]";
1668 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1669 if (sigsetjmp(imapjmp
, 1)) {
1670 safe_signal(SIGINT
, saveint
);
1671 safe_signal(SIGPIPE
, savepipe
);
1675 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1676 safe_signal(SIGINT
, &_imap_maincatch
);
1677 if (savepipe
!= SIG_IGN
)
1678 safe_signal(SIGPIPE
, imapcatch
);
1681 snprintf(o
, sizeof o
, "%s UID FETCH %lu (%s)\r\n",
1682 tag(1), m
->m_uid
, item
);
1684 if (check_expunged() == STOP
)
1686 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
1688 IMAP_OUT(o
, MB_COMD
, goto out
)
1690 ok
= imap_answer(mp
, 1);
1693 if (response_status
!= RESPONSE_OTHER
||
1694 response_other
!= MESSAGE_DATA_FETCH
)
1696 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1699 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1707 n
= responded_other_number
;
1708 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1709 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1711 if ((cp
= strchr(loc
, '"')) != NULL
) {
1712 cp
= imap_unquotestr(cp
);
1713 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
1715 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1716 m
->m_xlines
= m
->m_lines
;
1717 m
->m_xsize
= m
->m_size
;
1721 expected
= atol(&cp
[1]);
1722 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1723 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1727 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
1729 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1732 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1733 if (options
& OPT_VERBVERB
)
1734 fputs(imapbuf
, stderr
);
1735 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1737 if (u
== m
->m_uid
) {
1738 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1745 while (mp
->mb_active
& MB_COMD
)
1746 ok
= imap_answer(mp
, 1);
1748 if (saveint
!= SIG_IGN
)
1749 safe_signal(SIGINT
, saveint
);
1750 if (savepipe
!= SIG_IGN
)
1751 safe_signal(SIGPIPE
, savepipe
);
1764 imap_header(struct message
*m
)
1769 rv
= imap_get(&mb
, m
, NEED_HEADER
);
1776 imap_body(struct message
*m
)
1781 rv
= imap_get(&mb
, m
, NEED_BODY
);
1787 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
1791 tomp
->m_size
= frommp
->m_size
;
1792 tomp
->m_lines
= frommp
->m_lines
;
1793 tomp
->m_block
= frommp
->m_block
;
1794 tomp
->m_offset
= frommp
->m_offset
;
1795 tomp
->m_have
= have
;
1796 if (have
& HAVE_BODY
) {
1797 tomp
->m_xlines
= frommp
->m_lines
;
1798 tomp
->m_xsize
= frommp
->m_size
;
1805 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
1813 FILE *queuefp
= NULL
;
1818 snprintf(o
, sizeof o
, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1819 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
1821 if (check_expunged() == STOP
)
1823 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1826 IMAP_OUT(o
, MB_COMD
, return STOP
)
1830 ok
= imap_answer(mp
, 1);
1831 if (response_status
!= RESPONSE_OTHER
)
1833 if (response_other
!= MESSAGE_DATA_FETCH
)
1835 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0) {
1839 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1841 expected
= atol(&cp
[1]);
1842 if (m
[bot
-1].m_uid
) {
1843 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
1845 for (n
= bot
; n
<= topp
; n
++)
1846 if ((unsigned long)u
== m
[n
-1].m_uid
)
1849 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1855 n
= responded_other_number
;
1856 if (n
<= 0 || n
> msgCount
) {
1857 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1861 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1862 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1863 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
1864 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1865 if (options
& OPT_VERBVERB
)
1866 fputs(imapbuf
, stderr
);
1867 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1869 for (n
= bot
; n
<= topp
; n
++)
1870 if ((unsigned long)u
== m
[n
-1].m_uid
)
1872 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1873 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
1881 while (mp
->mb_active
& MB_COMD
)
1882 ok
= imap_answer(mp
, 1);
1887 imap_getheaders(int volatile bot
, int volatile topp
) /* TODO iterator!! */
1889 sighandler_type saveint
, savepipe
;
1890 /*enum okay ok = STOP;*/
1894 if (mb
.mb_type
== MB_CACHE
)
1898 if (topp
> msgCount
)
1900 for (i
= bot
; i
< topp
; i
++) {
1901 if (message
[i
-1].m_have
& HAVE_HEADER
||
1902 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1907 for (i
= topp
; i
> bot
; i
--) {
1908 if (message
[i
-1].m_have
& HAVE_HEADER
||
1909 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1918 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1919 safe_signal(SIGINT
, &_imap_maincatch
);
1920 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1921 if (sigsetjmp(imapjmp
, 1) == 0) {
1922 if (savepipe
!= SIG_IGN
)
1923 safe_signal(SIGPIPE
, imapcatch
);
1925 for (i
= bot
; i
<= topp
; i
+= chunk
) {
1926 int j
= i
+ chunk
- 1;
1928 if (visible(message
+ j
))
1929 /*ok = */imap_fetchheaders(&mb
, message
, i
, j
);
1931 onintr(0); /* XXX imaplock? */
1934 safe_signal(SIGINT
, saveint
);
1935 safe_signal(SIGPIPE
, savepipe
);
1940 __imap_exit(struct mailbox
*mp
)
1943 FILE *queuefp
= NULL
;
1946 mp
->mb_active
|= MB_BYE
;
1947 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1948 IMAP_OUT(o
, MB_COMD
, return STOP
)
1954 imap_exit(struct mailbox
*mp
)
1959 rv
= __imap_exit(mp
);
1960 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1961 free(mp
->mb_imap_pass
);
1962 free(mp
->mb_imap_account
);
1963 free(mp
->mb_imap_mailbox
);
1964 if (mp
->mb_cache_directory
!= NULL
)
1965 free(mp
->mb_cache_directory
);
1966 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1967 mp
->mb_imap_account
=
1968 mp
->mb_imap_mailbox
=
1969 mp
->mb_cache_directory
= "";
1971 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
1972 mp
->mb_imap_mailbox
= NULL
;
1973 mp
->mb_cache_directory
= NULL
;
1976 sclose(&mp
->mb_sock
);
1982 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1985 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1986 if (mp
->mb_type
== MB_IMAP
)
1993 imap_close(struct mailbox
*mp
)
1996 FILE *queuefp
= NULL
;
1999 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
2000 IMAP_OUT(o
, MB_COMD
, return STOP
)
2006 imap_update(struct mailbox
*mp
)
2009 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
2012 if (!(pstate
& PS_EDIT
) && mp
->mb_perm
!= 0) {
2015 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2016 if (m
->m_flag
& MBOX
)
2019 if (makembox() == STOP
)
2024 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
2025 if (mp
->mb_perm
== 0)
2027 else if (pstate
& PS_EDIT
)
2028 dodel
= ((m
->m_flag
& MDELETED
) != 0);
2030 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
2032 /* Fetch the result after around each 800 STORE commands
2033 * sent (approx. 32k data sent). Otherwise, servers will
2034 * try to flush the return queue at some point, leading
2035 * to a deadlock if we are still writing commands but not
2036 * reading their results */
2037 needstat
= stored
> 0 && stored
% 800 == 0;
2038 /* Even if this message has been deleted, continue
2039 * to set further flags. This is necessary to support
2040 * Gmail semantics, where "delete" actually means
2041 * "archive", and the flags are applied to the copy
2043 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2044 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
2047 if (m
->m_flag
& MFLAG
) {
2048 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
2051 if (m
->m_flag
& MUNFLAG
) {
2052 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
2055 if (m
->m_flag
& MANSWER
) {
2056 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
2059 if (m
->m_flag
& MUNANSWER
) {
2060 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
2063 if (m
->m_flag
& MDRAFT
) {
2064 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
2067 if (m
->m_flag
& MUNDRAFT
) {
2068 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
2073 imap_delete(mp
, m
-message
+1, m
, needstat
);
2076 } else if (mp
->mb_type
!= MB_CACHE
||
2077 (!(pstate
& PS_EDIT
) &&
2078 !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
2079 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
2080 (MPRESERVE
| MTOUCH
) ||
2081 ((pstate
& PS_EDIT
) && !(m
->m_flag
& MDELETED
)))
2083 if (m
->m_flag
& MNEW
) {
2085 m
->m_flag
|= MSTATUS
;
2092 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
2093 if (!(m
->m_flag
& MUNLINKED
) &&
2094 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
2095 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
2100 /* XXX should be readonly (but our IMAP code is weird...) */
2101 if (!(options
& (OPT_EXISTONLY
| OPT_HEADERSONLY
| OPT_HEADERLIST
)) &&
2103 if ((gotcha
|| modflags
) && (pstate
& PS_EDIT
)) {
2104 printf(_("\"%s\" "), displayname
);
2105 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2106 ? _("complete\n") : _("updated.\n"));
2107 } else if (held
&& !(pstate
& PS_EDIT
)) {
2109 printf(_("Held 1 message in %s\n"), displayname
);
2111 printf(_("Held %d messages in %s\n"), held
, displayname
);
2122 sighandler_type
volatile saveint
, savepipe
;
2125 if (mb
.mb_type
== MB_CACHE
) {
2130 if (mb
.mb_sock
.s_fd
< 0) {
2131 n_err(_("IMAP connection closed\n"));
2136 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2137 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2138 if (sigsetjmp(imapjmp
, 1)) {
2139 safe_signal(SIGINT
, saveint
);
2140 safe_signal(SIGPIPE
, saveint
);
2144 if (saveint
!= SIG_IGN
)
2145 safe_signal(SIGINT
, imapcatch
);
2146 if (savepipe
!= SIG_IGN
)
2147 safe_signal(SIGPIPE
, imapcatch
);
2150 if (!same_imap_account
)
2153 safe_signal(SIGINT
, saveint
);
2154 safe_signal(SIGPIPE
, savepipe
);
2161 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2165 FILE *queuefp
= NULL
;
2168 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2171 snprintf(o
, sizeof o
, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2172 tag(1), m
->m_uid
, c
, sp
);
2174 if (check_expunged() == STOP
)
2176 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2178 IMAP_OUT(o
, MB_COMD
, return STOP
)
2182 mb
.mb_active
&= ~MB_COMD
;
2183 if (queuefp
!= NULL
)
2189 imap_undelete(struct message
*m
, int n
)
2194 rv
= imap_unstore(m
, n
, "\\Deleted");
2200 imap_unread(struct message
*m
, int n
)
2205 rv
= imap_unstore(m
, n
, "\\Seen");
2211 imap_unstore(struct message
*m
, int n
, const char *flag
)
2213 sighandler_type saveint
, savepipe
;
2214 enum okay rv
= STOP
;
2218 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2219 safe_signal(SIGINT
, &_imap_maincatch
);
2220 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2221 if (sigsetjmp(imapjmp
, 1) == 0) {
2222 if (savepipe
!= SIG_IGN
)
2223 safe_signal(SIGPIPE
, imapcatch
);
2225 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2227 safe_signal(SIGINT
, saveint
);
2228 safe_signal(SIGPIPE
, savepipe
);
2246 snprintf(ts
, sizeof ts
, "T%lu", n
);
2252 c_imap_imap(void *vp
)
2255 sighandler_type saveint
, savepipe
;
2256 struct mailbox
*mp
= &mb
;
2257 FILE *queuefp
= NULL
;
2258 enum okay
volatile ok
= STOP
;
2261 if (mp
->mb_type
!= MB_IMAP
) {
2262 printf("Not operating on an IMAP mailbox.\n");
2266 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2267 safe_signal(SIGINT
, &_imap_maincatch
);
2268 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2269 if (sigsetjmp(imapjmp
, 1) == 0) {
2270 if (savepipe
!= SIG_IGN
)
2271 safe_signal(SIGPIPE
, imapcatch
);
2273 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2274 IMAP_OUT(o
, MB_COMD
, goto out
)
2275 while (mp
->mb_active
& MB_COMD
) {
2276 ok
= imap_answer(mp
, 0);
2277 fputs(responded_text
, stdout
);
2281 safe_signal(SIGINT
, saveint
);
2282 safe_signal(SIGPIPE
, savepipe
);
2291 imap_newmail(int nmail
)
2295 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2301 if (had_exists
== msgCount
&& had_expunge
< 0)
2302 /* Some servers always respond with EXISTS to NOOP. If
2303 * the mailbox has been changed but the number of messages
2304 * has not, an EXPUNGE must also had been sent; otherwise,
2305 * nothing has changed */
2308 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2312 imap_putflags(int f
)
2318 bp
= buf
= salloc(100);
2319 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2324 for (cp
= "\\Seen"; *cp
; cp
++)
2330 for (cp
= "\\Flagged"; *cp
; cp
++)
2333 if (f
& MANSWERED
) {
2336 for (cp
= "\\Answered"; *cp
; cp
++)
2342 for (cp
= "\\Draft"; *cp
; cp
++)
2354 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2357 while (*cp
!= ')') {
2359 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2361 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2363 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2365 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2367 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2369 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2381 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2382 long xsize
, enum mflag flag
, time_t t
)
2384 char o
[LINESIZE
], *buf
;
2385 size_t bufsize
, buflen
, cnt
;
2386 long size
, lines
, ysize
;
2388 FILE *queuefp
= NULL
;
2392 if (mp
->mb_type
== MB_CACHE
) {
2393 queuefp
= cache_queue(mp
);
2394 if (queuefp
== NULL
) {
2403 buf
= smalloc(bufsize
= LINESIZE
);
2408 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2413 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2414 tag(1), imap_quotepath(mp
, name
), imap_putflags(flag
),
2415 imap_make_date_time(t
), size
);
2416 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2417 while (mp
->mb_active
& MB_COMD
) {
2418 rv
= imap_answer(mp
, twice
);
2419 if (response_type
== RESPONSE_CONT
)
2423 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2432 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2435 buf
[buflen
- 1] = '\r';
2437 if (mp
->mb_type
!= MB_CACHE
)
2438 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2440 fwrite(buf
, 1, buflen
+1, queuefp
);
2443 if (mp
->mb_type
!= MB_CACHE
)
2444 swrite(&mp
->mb_sock
, "\r\n");
2446 fputs("\r\n", queuefp
);
2447 while (mp
->mb_active
& MB_COMD
) {
2448 rv
= imap_answer(mp
, 0);
2449 if (response_status
== RESPONSE_NO
/*&&
2450 ascncasecmp(responded_text,
2451 "[TRYCREATE] ", 12) == 0*/) {
2457 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2458 tag(1), imap_quotepath(mp
, name
));
2459 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2460 while (mp
->mb_active
& MB_COMD
)
2461 rv
= imap_answer(mp
, 1);
2464 imap_created_mailbox
++;
2466 } else if (rv
!= OKAY
)
2467 n_err(_("IMAP error: %s"), responded_text
);
2468 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2469 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2472 if (queuefp
!= NULL
)
2481 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2483 char *buf
, *bp
, *lp
;
2484 size_t bufsize
, buflen
, cnt
;
2485 off_t off1
= -1, offs
;
2487 enum {_NONE
= 0, _INHEAD
= 1<<0, _NLSEP
= 1<<1} state
;
2493 buf
= smalloc(bufsize
= LINESIZE
);
2500 for (flag
= MNEW
, state
= _NLSEP
;;) {
2501 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2504 ((state
& (_INHEAD
| _NLSEP
)) == _NLSEP
&&
2505 is_head(buf
, buflen
, FAL0
))) {
2506 if (off1
!= (off_t
)-1) {
2507 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
2510 fseek(fp
, offs
+buflen
, SEEK_SET
);
2512 off1
= offs
+ buflen
;
2518 tim
= unixtime(buf
);
2524 if (buf
[0] == '\n') {
2527 } else if (state
& _INHEAD
) {
2528 if (ascncasecmp(buf
, "status", 6) == 0) {
2530 while (whitechar(*lp
))
2533 while (*++lp
!= '\0')
2542 } else if (ascncasecmp(buf
, "x-status", 8) == 0) {
2544 while (whitechar(*lp
))
2547 while (*++lp
!= '\0')
2570 imap_append(const char *xserver
, FILE *fp
)
2572 sighandler_type
volatile saveint
, savepipe
;
2575 char const * volatile mbx
;
2576 enum okay rv
= STOP
;
2579 if (!url_parse(&url
, CPROTO_IMAP
, xserver
))
2581 if (!ok_blook(v15_compat
) &&
2582 (!url
.url_had_user
|| url
.url_pass
.s
!= NULL
))
2583 n_err(_("New-style URL used without *v15-compat* being set!\n"));
2584 assert(url
.url_path
.s
!= NULL
);
2587 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2588 safe_signal(SIGINT
, &_imap_maincatch
);
2589 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2590 if (sigsetjmp(imapjmp
, 1))
2592 if (savepipe
!= SIG_IGN
)
2593 safe_signal(SIGPIPE
, imapcatch
);
2595 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
2596 !strcmp(url
.url_p_eu_h_p
, mb
.mb_imap_account
)) {
2597 rv
= imap_append0(&mb
, url
.url_path
.s
, fp
);
2601 memset(&mx
, 0, sizeof mx
);
2603 if (!_imap_getcred(&mx
, &ccred
, &url
))
2606 imap_delim_init(&mx
, &url
);
2607 mx
.mb_imap_mailbox
= sstrdup(imap_normalize_path(&mx
, url
.url_path
.s
));
2609 if (disconnected(url
.url_p_eu_h_p
) == 0) {
2610 if (!sopen(&mx
.mb_sock
, &url
))
2612 mx
.mb_sock
.s_desc
= "IMAP";
2613 mx
.mb_type
= MB_IMAP
;
2614 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
2615 /* TODO the code now did
2616 * TODO mx.mb_imap_mailbox = mbx->url.url_patth.s;
2617 * TODO though imap_mailbox is sfree()d and mbx
2618 * TODO is possibly even a constant
2619 * TODO i changed this to sstrdup() sofar, as is used
2620 * TODO somewhere else in this file for this! */
2621 if (imap_preauth(&mx
, &url
) != OKAY
||
2622 imap_auth(&mx
, &ccred
) != OKAY
) {
2623 sclose(&mx
.mb_sock
);
2626 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
2629 mx
.mb_imap_account
= UNCONST(url
.url_p_eu_h_p
);
2630 mx
.mb_type
= MB_CACHE
;
2631 rv
= imap_append0(&mx
, url
.url_path
.s
, fp
);
2638 safe_signal(SIGINT
, saveint
);
2639 safe_signal(SIGPIPE
, savepipe
);
2649 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2650 struct list_item
**lend
, int level
)
2652 char o
[LINESIZE
], *cp
;
2654 FILE *queuefp
= NULL
;
2655 struct list_item
*lp
;
2656 enum okay ok
= STOP
;
2659 *list
= *lend
= NULL
;
2660 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), imap_quotepath(mp
, base
));
2661 IMAP_OUT(o
, MB_COMD
, return STOP
)
2662 while (mp
->mb_active
& MB_COMD
) {
2663 ok
= imap_answer(mp
, 1);
2664 if (response_status
== RESPONSE_OTHER
&&
2665 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
2666 cp
= imap_unquotestr(list_name
);
2667 lp
= csalloc(1, sizeof *lp
);
2669 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
2671 lp
->l_base
= *cp
? cp
: savestr(base
);
2672 lp
->l_attr
= list_attributes
;
2673 lp
->l_level
= level
+1;
2674 lp
->l_delim
= list_hierarchy_delimiter
;
2675 if (*list
&& *lend
) {
2676 (*lend
)->l_next
= lp
;
2686 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2688 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2695 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
2696 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
2699 if (list
== NULL
|| lend
== NULL
)
2702 for (lp
= list
; lp
; lp
= lp
->l_next
)
2703 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
2704 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
2705 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2706 memcpy(cp
, lp
->l_name
, n
);
2707 cp
[n
] = lp
->l_delim
;
2709 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
2710 lp
->l_has_children
= 1;
2711 if (strcmp(cp
, lx
->l_name
) == 0)
2720 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2723 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2727 if (!(lp
->l_attr
& LIST_NOSELECT
))
2728 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2729 else if (lp
->l_has_children
== 0)
2730 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2731 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
2739 imap_folders(const char * volatile name
, int strip
)
2741 sighandler_type saveint
, savepipe
;
2742 const char *fold
, *cp
, *sp
;
2746 cp
= protbase(name
);
2747 sp
= mb
.mb_imap_account
;
2748 if (sp
== NULL
|| strcmp(cp
, sp
)) {
2750 _("Cannot perform `folders' but when on the very IMAP "
2751 "account; the current one is\n `%s' -- "
2752 "try `folders @'\n"),
2753 (sp
!= NULL
? sp
: _("[NONE]")));
2757 fold
= imap_fileof(name
);
2758 if (options
& OPT_TTYOUT
) {
2759 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
2761 n_perr(_("tmpfile"), 0);
2768 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2769 safe_signal(SIGINT
, &_imap_maincatch
);
2770 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2771 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
2773 if (savepipe
!= SIG_IGN
)
2774 safe_signal(SIGPIPE
, imapcatch
);
2776 if (mb
.mb_type
== MB_CACHE
)
2777 cache_list(&mb
, fold
, strip
, fp
);
2779 imap_list(&mb
, fold
, strip
, fp
);
2783 if (options
& OPT_TTYOUT
)
2789 if (options
& OPT_TTYOUT
) {
2794 n_err(_("Folder not found\n"));
2797 safe_signal(SIGINT
, saveint
);
2798 safe_signal(SIGPIPE
, savepipe
);
2799 if (options
& OPT_TTYOUT
)
2812 long n
= 0, mx
= 0, columns
, width
;
2816 if ((out
= Ftmp(NULL
, "imapdopr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600))
2818 n_perr(_("tmpfile"), 0);
2822 while ((c
= getc(fp
)) != EOF
) {
2833 if (mx
< width
/ 2) {
2834 columns
= width
/ (mx
+2);
2835 snprintf(o
, sizeof o
, "sort | pr -%lu -w%lu -t", columns
, width
);
2837 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2838 run_command(XSHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2839 page_or_print(out
, 0);
2846 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2850 int twice
= 0, stored
= 0;
2851 FILE *queuefp
= NULL
;
2852 enum okay ok
= STOP
;
2855 if (mp
->mb_type
== MB_CACHE
) {
2856 if ((queuefp
= cache_queue(mp
)) == NULL
)
2864 i
= strlen(name
= imap_fileof(name
));
2865 if(i
== 0 || (i
> 0 && name
[i
- 1] == '/'))
2866 name
= savecat(name
, "INBOX");
2867 qname
= imap_quotepath(mp
, name
);
2870 /* Since it is not possible to set flags on the copy, recently
2871 * set flags must be set on the original to include it in the copy */
2872 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
2873 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2874 if (m
->m_flag
&MFLAG
)
2875 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2876 if (m
->m_flag
&MUNFLAG
)
2877 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2878 if (m
->m_flag
&MANSWER
)
2879 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2880 if (m
->m_flag
&MUNANSWER
)
2881 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2882 if (m
->m_flag
&MDRAFT
)
2883 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2884 if (m
->m_flag
&MUNDRAFT
)
2885 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2888 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n", tag(1), m
->m_uid
, qname
);
2890 if (check_expunged() == STOP
)
2892 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
2894 IMAP_OUT(o
, MB_COMD
, goto out
)
2895 while (mp
->mb_active
& MB_COMD
)
2896 ok
= imap_answer(mp
, twice
);
2898 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
2899 response_status
== RESPONSE_OK
)
2900 imap_copyuid(mp
, m
, name
);
2902 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2903 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2904 IMAP_OUT(o
, MB_COMD
, goto out
)
2905 while (mp
->mb_active
& MB_COMD
)
2906 ok
= imap_answer(mp
, 1);
2908 imap_created_mailbox
++;
2913 if (queuefp
!= NULL
)
2916 /* ... and reset the flag to its initial value so that the 'exit'
2917 * command still leaves the message unread */
2919 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2920 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2923 if (m
->m_flag
& MFLAG
) {
2924 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2927 if (m
->m_flag
& MUNFLAG
) {
2928 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2931 if (m
->m_flag
& MANSWER
) {
2932 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2935 if (m
->m_flag
& MUNANSWER
) {
2936 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2939 if (m
->m_flag
& MDRAFT
) {
2940 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2943 if (m
->m_flag
& MUNDRAFT
) {
2944 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2948 mp
->mb_active
|= MB_COMD
;
2949 (void)imap_finish(mp
);
2955 imap_copy(struct message
*m
, int n
, const char *name
)
2957 sighandler_type saveint
, savepipe
;
2958 enum okay rv
= STOP
;
2962 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2963 safe_signal(SIGINT
, &_imap_maincatch
);
2964 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2965 if (sigsetjmp(imapjmp
, 1) == 0) {
2966 if (savepipe
!= SIG_IGN
)
2967 safe_signal(SIGPIPE
, imapcatch
);
2969 rv
= imap_copy1(&mb
, m
, n
, name
);
2971 safe_signal(SIGINT
, saveint
);
2972 safe_signal(SIGPIPE
, savepipe
);
2982 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2983 unsigned long *olduid
, unsigned long *newuid
)
2989 *uidvalidity
= strtoul(cp
, &xp
, 10);
2990 *olduid
= strtoul(xp
, &yp
, 10);
2991 *newuid
= strtoul(yp
, &zp
, 10);
2992 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2993 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
2999 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
3006 *uidvalidity
= strtoul(cp
, &xp
, 10);
3007 *uid
= strtoul(xp
, &yp
, 10);
3008 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
3015 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
3020 unsigned long uidvalidity
, olduid
, newuid
;
3026 memset(&xmb
, 0, sizeof xmb
);
3028 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
3029 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
3035 xmb
.mb_cache_directory
= NULL
;
3036 xmb
.mb_imap_account
= sstrdup(mp
->mb_imap_account
);
3037 xmb
.mb_imap_pass
= sstrdup(mp
->mb_imap_pass
);
3038 memcpy(&xmb
.mb_imap_delim
[0], &mp
->mb_imap_delim
[0],
3039 sizeof(xmb
.mb_imap_delim
));
3040 xmb
.mb_imap_mailbox
= sstrdup(imap_normalize_path(&xmb
, name
));
3041 if (mp
->mb_cache_directory
!= NULL
)
3042 xmb
.mb_cache_directory
= sstrdup(mp
->mb_cache_directory
);
3043 xmb
.mb_uidvalidity
= uidvalidity
;
3047 memset(&xm
, 0, sizeof xm
);
3049 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
3051 getcache(mp
, &xm
, NEED_HEADER
);
3052 getcache(mp
, &xm
, NEED_BODY
);
3054 if ((m
->m_flag
& HAVE_HEADER
) == 0)
3055 getcache(mp
, m
, NEED_HEADER
);
3056 if ((m
->m_flag
& HAVE_BODY
) == 0)
3057 getcache(mp
, m
, NEED_BODY
);
3061 xm
.m_flag
&= ~MFULLYCACHED
;
3062 putcache(&xmb
, &xm
);
3064 if (xmb
.mb_cache_directory
!= NULL
)
3065 free(xmb
.mb_cache_directory
);
3066 if (xmb
.mb_imap_mailbox
!= NULL
)
3067 free(xmb
.mb_imap_mailbox
);
3068 if (xmb
.mb_imap_pass
!= NULL
)
3069 free(xmb
.mb_imap_pass
);
3070 if (xmb
.mb_imap_account
!= NULL
)
3071 free(xmb
.mb_imap_account
);
3077 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
3078 long size
, long lines
, int flag
, const char *name
)
3083 unsigned long uidvalidity
, uid
;
3089 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
3090 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
3096 xmb
.mb_cache_directory
= NULL
;
3097 /* XXX mb_imap_delim reused */
3098 xmb
.mb_imap_mailbox
= sstrdup(imap_normalize_path(&xmb
, name
));
3099 xmb
.mb_uidvalidity
= uidvalidity
;
3100 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
3102 memset(&xm
, 0, sizeof xm
);
3103 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
3105 xm
.m_block
= mailx_blockof(off1
);
3106 xm
.m_offset
= mailx_offsetof(off1
);
3109 xm
.m_lines
= xm
.m_xlines
= lines
;
3111 xm
.m_have
= HAVE_HEADER
| HAVE_BODY
;
3112 putcache(&xmb
, &xm
);
3114 free(xmb
.mb_imap_mailbox
);
3121 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
3125 long size
, xsize
, ysize
, lines
;
3126 enum mflag flag
= MNEW
;
3127 char *name
, *buf
, *bp
;
3129 size_t bufsize
, buflen
, cnt
;
3130 enum okay rv
= STOP
;
3133 buf
= smalloc(bufsize
= LINESIZE
);
3136 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3139 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3144 if ((cp
= strrchr(bp
, '{')) == NULL
)
3147 xsize
= atol(&cp
[1]) + 2;
3148 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3154 imap_getflags(cp
, &cp
, &flag
);
3155 while (*++cp
== ' ')
3158 t
= imap_read_date_time(cp
);
3160 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
3167 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3170 buf
[--buflen
] = '\0';
3171 buf
[buflen
-1] = '\n';
3172 fwrite(buf
, 1, buflen
, tp
);
3179 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3180 imap_unquotestr(name
));
3190 #ifdef HAVE_IMAP_SEARCH
3192 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3195 char *o
, *xp
, *cs
, c
;
3197 FILE *queuefp
= NULL
;
3201 enum okay ok
= STOP
;
3205 for (cp
= spec
; *cp
; cp
++)
3208 cp
= charset_get_lc();
3210 if (asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")) {
3215 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
3216 sz
= strlen(spec
) + 1;
3217 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
3218 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
, FAL0
) == 0 &&
3227 cp
= imap_quotestr(cp
);
3228 cs
= salloc(n
= strlen(cp
) + 10);
3229 snprintf(cs
, n
, "CHARSET %s ", cp
);
3233 o
= ac_alloc(osize
= strlen(spec
) + 60);
3234 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3235 IMAP_OUT(o
, MB_COMD
, goto out
)
3236 while (mp
->mb_active
& MB_COMD
) {
3237 ok
= imap_answer(mp
, 0);
3238 if (response_status
== RESPONSE_OTHER
&&
3239 response_other
== MAILBOX_DATA_SEARCH
) {
3240 xp
= responded_other_text
;
3241 while (*xp
&& *xp
!= '\r') {
3242 n
= strtoul(xp
, &xp
, 10);
3243 for (i
= 0; i
< cnt
; i
++)
3244 if (m
[i
].m_uid
== n
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3245 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3256 imap_search1(const char * volatile spec
, int f
)
3258 sighandler_type saveint
, savepipe
;
3259 enum okay
volatile rv
= STOP
;
3262 if (mb
.mb_type
!= MB_IMAP
)
3266 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3267 safe_signal(SIGINT
, &_imap_maincatch
);
3268 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3269 if (sigsetjmp(imapjmp
, 1) == 0) {
3270 if (savepipe
!= SIG_IGN
)
3271 safe_signal(SIGPIPE
, imapcatch
);
3273 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3275 safe_signal(SIGINT
, saveint
);
3276 safe_signal(SIGPIPE
, savepipe
);
3284 #endif /* HAVE_IMAP_SEARCH */
3287 imap_thisaccount(const char *cp
)
3292 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3294 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3295 mb
.mb_imap_account
== NULL
)
3298 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3304 imap_remove(const char * volatile name
)
3306 sighandler_type
volatile saveint
, savepipe
;
3307 enum okay
volatile rv
= STOP
;
3310 if (mb
.mb_type
!= MB_IMAP
) {
3311 n_err(_("Refusing to remove \"%s\" in disconnected mode\n"), name
);
3315 if (!imap_thisaccount(name
)) {
3316 n_err(_("Can only remove mailboxes on current IMAP server: "
3317 "\"%s\" not removed\n"), name
);
3322 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3323 safe_signal(SIGINT
, &_imap_maincatch
);
3324 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3325 if (sigsetjmp(imapjmp
, 1) == 0) {
3326 if (savepipe
!= SIG_IGN
)
3327 safe_signal(SIGPIPE
, imapcatch
);
3329 rv
= imap_remove1(&mb
, imap_fileof(name
));
3331 safe_signal(SIGINT
, saveint
);
3332 safe_signal(SIGPIPE
, savepipe
);
3336 rv
= cache_remove(name
);
3345 imap_remove1(struct mailbox
*mp
, const char *name
)
3347 FILE *queuefp
= NULL
;
3350 enum okay ok
= STOP
;
3353 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3354 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotepath(mp
, name
));
3355 IMAP_OUT(o
, MB_COMD
, goto out
)
3356 while (mp
->mb_active
& MB_COMD
)
3357 ok
= imap_answer(mp
, 1);
3364 imap_rename(const char *old
, const char *new)
3366 sighandler_type saveint
, savepipe
;
3367 enum okay rv
= STOP
;
3370 if (mb
.mb_type
!= MB_IMAP
) {
3371 n_err(_("Refusing to rename mailboxes in disconnected mode\n"));
3375 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3376 n_err(_("Can only rename mailboxes on current IMAP "
3377 "server: \"%s\" not renamed to \"%s\"\n"), old
, new);
3382 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3383 safe_signal(SIGINT
, &_imap_maincatch
);
3384 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3385 if (sigsetjmp(imapjmp
, 1) == 0) {
3386 if (savepipe
!= SIG_IGN
)
3387 safe_signal(SIGPIPE
, imapcatch
);
3389 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3391 safe_signal(SIGINT
, saveint
);
3392 safe_signal(SIGPIPE
, savepipe
);
3396 rv
= cache_rename(old
, new);
3405 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3407 FILE *queuefp
= NULL
;
3410 enum okay ok
= STOP
;
3413 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3414 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), imap_quotepath(mp
, old
),
3415 imap_quotepath(mp
, new));
3416 IMAP_OUT(o
, MB_COMD
, goto out
)
3417 while (mp
->mb_active
& MB_COMD
)
3418 ok
= imap_answer(mp
, 1);
3425 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3427 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3428 size_t bufsize
, buflen
, cnt
;
3429 long offs
, offs1
, offs2
, octets
;
3430 int twice
, gotcha
= 0;
3431 FILE *queuefp
= NULL
;
3432 enum okay ok
= OKAY
, rok
= OKAY
;
3435 buf
= smalloc(bufsize
= LINESIZE
);
3438 while ((offs1
= ftell(fp
)) >= 0 &&
3439 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3440 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3445 if ((offs
= ftell(fp
)) < 0)
3448 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3449 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3451 while (digitchar(*cp
))
3457 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3459 IMAP_OUT(o
, MB_COMD
, continue)
3460 while (mp
->mb_active
& MB_COMD
)
3461 ok
= imap_answer(mp
, twice
);
3462 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3464 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3465 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3467 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3468 IMAP_OUT(o
, MB_COMD
, continue)
3469 while (mp
->mb_active
& MB_COMD
)
3470 ok
= imap_answer(mp
, 1);
3473 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3474 if ((cp
= strrchr(bp
, '{')) == NULL
)
3476 octets
= atol(&cp
[1]) + 2;
3477 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3479 IMAP_OUT(o
, MB_COMD
, continue)
3480 while (mp
->mb_active
& MB_COMD
) {
3481 ok
= imap_answer(mp
, twice
);
3482 if (response_type
== RESPONSE_CONT
)
3486 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3490 while (octets
> 0) {
3491 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3492 ? sizeof iob
: (size_t)octets
);
3494 if (n
!= fread(iob
, 1, n
, fp
))
3496 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3498 swrite(&mp
->mb_sock
, "");
3499 while (mp
->mb_active
& MB_COMD
) {
3500 ok
= imap_answer(mp
, 0);
3501 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
3502 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3507 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3508 if ((offs2
= ftell(fp
)) < 0)
3510 fseek(fp
, offs1
, SEEK_SET
);
3511 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3512 (void)fseek(fp
, offs2
, SEEK_SET
);
3518 n_err(_("Invalid command in IMAP cache queue: \"%s\"\n"), bp
);
3523 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
3524 IMAP_OUT(o
, MB_COMD
, continue)
3525 while (mp
->mb_active
& MB_COMD
)
3526 ok
= imap_answer(mp
, 1);
3532 ftruncate(fileno(fp
), 0);
3540 imap_strex(char const *cp
, char const **xp
)
3549 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
3552 else if (*cq
== '"')
3558 n
= salloc(cq
- cp
+ 2);
3559 memcpy(n
, cp
, cq
- cp
+1);
3560 n
[cq
- cp
+ 1] = '\0';
3569 check_expunged(void)
3574 if (expunged_messages
> 0) {
3575 n_err(_("Command not executed - messages have been expunged\n"));
3584 c_connect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
3587 int rv
, omsgCount
= msgCount
;
3591 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3592 n_err(_("Already connected\n"));
3597 if (!url_parse(&url
, CPROTO_IMAP
, mailname
)) {
3601 ok_bclear(disconnected
);
3602 vok_bclear(savecat("disconnected-", url
.url_u_h_p
.s
));
3604 if (mb
.mb_type
== MB_CACHE
) {
3605 enum fedit_mode fm
= FEDIT_NONE
;
3608 if (!(pstate
& PS_EDIT
))
3610 _imap_setfile1(&url
, fm
, 1);
3611 if (msgCount
> omsgCount
)
3612 newmailinfo(omsgCount
);
3621 c_disconnect(void *vp
) /* TODO v15-compat mailname<->URL (with password) */
3624 int rv
= 1, *msgvec
= vp
;
3627 if (mb
.mb_type
== MB_CACHE
) {
3628 n_err(_("Not connected\n"));
3631 if (mb
.mb_type
!= MB_IMAP
|| cached_uidvalidity(&mb
) == 0) {
3632 n_err(_("The current mailbox is not cached\n"));
3636 if (!url_parse(&url
, CPROTO_IMAP
, mailname
))
3641 ok_bset(disconnected
, TRU1
);
3642 if (mb
.mb_type
== MB_IMAP
) {
3643 enum fedit_mode fm
= FEDIT_NONE
;
3646 if (!(pstate
& PS_EDIT
))
3648 sclose(&mb
.mb_sock
);
3649 _imap_setfile1(&url
, fm
, 1);
3660 int rv
= 1, *msgvec
= vp
, *ip
;
3664 if (mb
.mb_type
!= MB_IMAP
) {
3665 n_err(_("Not connected to an IMAP server\n"));
3668 if (cached_uidvalidity(&mb
) == 0) {
3669 n_err(_("The current mailbox is not cached\n"));
3674 for (ip
= msgvec
; *ip
; ++ip
) {
3675 mp
= &message
[*ip
- 1];
3676 if (!(mp
->m_have
& HAVE_BODY
)) {
3689 disconnected(const char *file
)
3695 if (ok_blook(disconnected
)) {
3700 if (!url_parse(&url
, CPROTO_IMAP
, file
)) {
3704 rv
= vok_blook(savecat("disconnected-", url
.url_u_h_p
.s
));
3712 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
3714 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
3722 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
3723 PTRCMP(nmp
, <, message
+ msgCount
)) {
3724 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
3726 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
3728 if (omp
->m_uid
== nmp
->m_uid
) {
3729 hf
= nmp
->m_flag
& MHIDDEN
;
3730 if (transparent
&& mb
.mb_type
== MB_IMAP
)
3731 omp
->m_flag
&= ~MHIDDEN
;
3733 if (transparent
&& mb
.mb_type
== MB_CACHE
)
3734 nmp
[-1].m_flag
|= hf
;
3735 } else if (omp
->m_uid
< nmp
->m_uid
)
3742 prevdot
= newprevdot
;
3748 imap_read_date_time(const char *cp
)
3752 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
3755 /* "25-Jul-2004 15:33:44 +0200"
3757 * 0 5 10 15 20 25 */
3758 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3760 day
= strtol(&cp
[1], NULL
, 10);
3762 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3764 if (month_names
[++i
][0] == '\0')
3768 year
= strtol(&cp
[8], NULL
, 10);
3769 hour
= strtol(&cp
[13], NULL
, 10);
3770 minute
= strtol(&cp
[16], NULL
, 10);
3771 second
= strtol(&cp
[19], NULL
, 10);
3772 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
3786 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3789 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3799 imap_make_date_time(time_t t
)
3803 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3806 tzdiff
= t
- mktime(gmtime(&t
));
3807 tzdiff_hour
= (int)(tzdiff
/ 60);
3808 tzdiff_min
= tzdiff_hour
% 60;
3810 tmptr
= localtime(&t
);
3811 if (tmptr
->tm_isdst
> 0)
3813 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3814 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
], tmptr
->tm_year
+ 1900,
3815 tmptr
->tm_hour
, tmptr
->tm_min
, tmptr
->tm_sec
, tzdiff_hour
, tzdiff_min
);
3819 #endif /* HAVE_IMAP */
3821 #if defined HAVE_IMAP || defined HAVE_IMAP_SEARCH
3823 imap_quotestr(char const *s
)
3828 np
= n
= salloc(2 * strlen(s
) + 3);
3831 if (*s
== '"' || *s
== '\\')
3842 imap_unquotestr(char const *s
)
3852 np
= n
= salloc(strlen(s
) + 1);
3865 #endif /* defined HAVE_IMAP || defined HAVE_IMAP_SEARCH */