2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * Mail -- a mail program
45 * IMAP v4r1 client following RFC 2060.
59 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
64 #endif /* HAVE_ARPA_INET_H */
70 #define IMAP_ANSWER() { \
71 if (mp->mb_type != MB_CACHE) { \
72 enum okay ok = OKAY; \
73 while (mp->mb_active & MB_COMD) \
74 ok = imap_answer(mp, 1); \
79 #define IMAP_OUT(x, y, action) \
81 if (mp->mb_type != MB_CACHE) { \
82 if (imap_finish(mp) == STOP) \
85 fprintf(stderr, ">>> %s", x); \
86 mp->mb_active |= (y); \
87 if (swrite(&mp->mb_sock, x) == STOP) \
90 if (queuefp != NULL) \
95 static struct record
{
96 struct record
*rec_next
;
97 unsigned long rec_count
;
122 static char *responded_tag
;
123 static char *responded_text
;
124 static char *responded_other_text
;
125 static long responded_other_number
;
131 MAILBOX_DATA_MAILBOX
,
136 MESSAGE_DATA_EXPUNGE
,
139 RESPONSE_OTHER_UNKNOWN
142 static enum list_attributes
{
144 LIST_NOINFERIORS
= 001,
150 static int list_hierarchy_delimiter
;
151 static char *list_name
;
154 struct list_item
*l_next
;
157 enum list_attributes l_attr
;
163 static char *imapbuf
;
164 static size_t imapbufsize
;
165 static sigjmp_buf imapjmp
;
166 static sighandler_type savealrm
;
167 static int reset_tio
;
168 static struct termios otio
;
169 static int imapkeepalive
;
170 static long had_exists
= -1;
171 static long had_expunge
= -1;
172 static long expunged_messages
;
173 static volatile int imaplock
;
175 static int same_imap_account
;
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 maincatch(int s
);
186 static enum okay
imap_noop1(struct mailbox
*mp
);
187 static void rec_queue(enum rec_type type
, unsigned long count
);
188 static enum okay
rec_dequeue(void);
189 static void rec_rmqueue(void);
190 static void imapalarm(int s
);
191 static int imap_use_starttls(const char *uhp
);
192 static enum okay
imap_preauth(struct mailbox
*mp
, const char *xserver
,
194 static enum okay
imap_capability(struct mailbox
*mp
);
195 static enum okay
imap_auth(struct mailbox
*mp
, const char *uhp
,
196 char *xuser
, const char *pass
);
198 static enum okay
imap_cram_md5(struct mailbox
*mp
,
199 char *xuser
, const char *xpass
);
201 static enum okay
imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
);
203 static enum okay
imap_gss(struct mailbox
*mp
, char *user
);
204 #endif /* USE_GSSAPI */
205 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
206 static void imap_init(struct mailbox
*mp
, int n
);
207 static void imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
,
209 static char *imap_have_password(const char *server
);
210 static void imap_split(char **server
, const char **sp
, int *use_ssl
,
211 const char **cp
, char **uhp
, char **mbx
,
212 const char **pass
, char **user
);
213 static int imap_setfile1(const char *xserver
, int newmail
, int isedit
,
215 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
216 size_t expected
, int need
,
217 const char *head
, size_t headsize
, long headlines
);
218 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
220 const char *head
, size_t headsize
, long headlines
);
221 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
223 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
224 struct message from
, enum havespec have
);
225 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
227 static enum okay
imap_exit(struct mailbox
*mp
);
228 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int
230 static enum okay
imap_close(struct mailbox
*mp
);
231 static enum okay
imap_update(struct mailbox
*mp
);
232 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
,
233 int n
, int c
, const char *sp
, int needstat
);
234 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
235 static const char *tag(int new);
236 static char *imap_putflags(int f
);
237 static void imap_getflags(const char *cp
, char **xp
, enum mflag
*f
);
238 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
239 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
240 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
241 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
242 struct list_item
**list
, struct list_item
**lend
, int level
);
243 static enum okay
imap_list(struct mailbox
*mp
, const char *base
,
244 int strip
, FILE *fp
);
245 static void dopr(FILE *fp
);
246 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
248 static enum okay
imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
249 unsigned long *olduid
, unsigned long *newuid
);
250 static enum okay
imap_appenduid_parse(const char *cp
,
251 unsigned long *uidvalidity
, unsigned long *uid
);
252 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
254 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
255 long off1
, long xsize
, long size
, long lines
,
256 int flag
, const char *name
);
257 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
258 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
,
259 int count
, const char *spec
, int f
);
260 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
261 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
263 static char *imap_strex(const char *cp
, char **xp
);
264 static enum okay
check_expunged(void);
267 imap_other_get(char *pp
)
271 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
273 response_other
= MAILBOX_DATA_FLAGS
;
274 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
276 response_other
= MAILBOX_DATA_LIST
;
277 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
279 response_other
= MAILBOX_DATA_LSUB
;
280 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
282 response_other
= MAILBOX_DATA_MAILBOX
;
283 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
285 response_other
= MAILBOX_DATA_SEARCH
;
286 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
288 response_other
= MAILBOX_DATA_STATUS
;
289 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
291 response_other
= CAPABILITY_DATA
;
293 responded_other_number
= strtol(pp
, &xp
, 10);
296 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
297 response_other
= MAILBOX_DATA_EXISTS
;
298 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
299 response_other
= MAILBOX_DATA_RECENT
;
300 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
301 response_other
= MESSAGE_DATA_EXPUNGE
;
302 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
304 response_other
= MESSAGE_DATA_FETCH
;
306 response_other
= RESPONSE_OTHER_UNKNOWN
;
308 responded_other_text
= pp
;
312 imap_response_get(const char **cp
)
314 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
316 response_status
= RESPONSE_OK
;
317 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
319 response_status
= RESPONSE_NO
;
320 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
322 response_status
= RESPONSE_BAD
;
323 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
325 response_status
= RESPONSE_PREAUTH
;
326 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
328 response_status
= RESPONSE_BYE
;
330 response_status
= RESPONSE_OTHER
;
334 imap_response_parse(void)
336 static char *parsebuf
;
337 static size_t parsebufsize
;
338 const char *ip
= imapbuf
;
341 if (parsebufsize
< imapbufsize
) {
343 parsebuf
= smalloc(parsebufsize
= imapbufsize
);
345 strcpy(parsebuf
, imapbuf
);
349 response_type
= RESPONSE_CONT
;
364 imap_response_get(&ip
);
365 pp
= &parsebuf
[ip
- imapbuf
];
366 switch (response_status
) {
368 response_type
= RESPONSE_FATAL
;
371 response_type
= RESPONSE_DATA
;
375 responded_tag
= parsebuf
;
376 while (*pp
&& *pp
!= ' ')
379 response_type
= RESPONSE_ILLEGAL
;
383 while (*pp
&& *pp
== ' ')
386 response_type
= RESPONSE_ILLEGAL
;
389 ip
= &imapbuf
[pp
- parsebuf
];
390 response_type
= RESPONSE_TAGGED
;
391 imap_response_get(&ip
);
392 pp
= &parsebuf
[ip
- imapbuf
];
395 if (response_type
!= RESPONSE_CONT
&&
396 response_type
!= RESPONSE_ILLEGAL
&&
397 response_status
== RESPONSE_OTHER
)
402 imap_answer(struct mailbox
*mp
, int errprnt
)
407 if (mp
->mb_type
== MB_CACHE
)
409 again
: if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
411 fputs(imapbuf
, stderr
);
412 imap_response_parse();
413 if (response_type
== RESPONSE_ILLEGAL
)
415 if (response_type
== RESPONSE_CONT
)
417 if (response_status
== RESPONSE_OTHER
) {
418 if (response_other
== MAILBOX_DATA_EXISTS
) {
419 had_exists
= responded_other_number
;
420 rec_queue(REC_EXISTS
, responded_other_number
);
423 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
424 rec_queue(REC_EXPUNGE
, responded_other_number
);
432 if (response_type
== RESPONSE_TAGGED
) {
433 if (asccasecmp(responded_tag
, tag(0)) == 0)
438 switch (response_status
) {
439 case RESPONSE_PREAUTH
:
440 mp
->mb_active
&= ~MB_PREAUTH
;
451 fprintf(stderr
, catgets(catd
, CATSET
, 218,
452 "IMAP error: %s"), responded_text
);
454 case RESPONSE_UNKNOWN
: /* does not happen */
457 mp
->mb_active
= MB_NONE
;
465 if (response_status
!= RESPONSE_OTHER
&&
466 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
467 fprintf(stderr
, "IMAP alert: %s", &responded_text
[8]);
469 mp
->mb_active
&= ~MB_COMD
;
472 mp
->mb_active
= MB_NONE
;
478 imap_parse_list(void)
482 cp
= responded_other_text
;
483 list_attributes
= LIST_NONE
;
485 while (*cp
&& *cp
!= ')') {
487 if (ascncasecmp(&cp
[1], "Noinferiors ", 12)
489 list_attributes
|= LIST_NOINFERIORS
;
491 } else if (ascncasecmp(&cp
[1], "Noselect ", 9)
493 list_attributes
|= LIST_NOSELECT
;
495 } else if (ascncasecmp(&cp
[1], "Marked ", 7)
497 list_attributes
|= LIST_MARKED
;
499 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9)
501 list_attributes
|= LIST_UNMARKED
;
512 list_hierarchy_delimiter
= EOF
;
516 list_hierarchy_delimiter
= *cp
++ & 0377;
517 if (cp
[0] != '"' || cp
[1] != ' ')
520 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' &&
522 list_hierarchy_delimiter
= EOF
;
528 while (*cp
&& *cp
!= '\r')
535 imap_finish(struct mailbox
*mp
)
537 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
545 if (imapkeepalive
> 0) {
547 safe_signal(SIGALRM
, savealrm
);
555 tcsetattr(0, TCSADRAIN
, &otio
);
558 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
559 siglongjmp(imapjmp
, 1);
562 fprintf(stderr
, "Received SIGPIPE during IMAP operation\n");
571 if (interrupts
++ == 0) {
572 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
579 imap_noop1(struct mailbox
*mp
)
582 FILE *queuefp
= NULL
;
584 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
585 IMAP_OUT(o
, MB_COMD
, return STOP
)
593 sighandler_type saveint
, savepipe
;
599 if (mb
.mb_type
!= MB_IMAP
)
601 verbose
= value("verbose") != NULL
;
603 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
604 safe_signal(SIGINT
, maincatch
);
605 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
606 if (sigsetjmp(imapjmp
, 1) == 0) {
607 if (savepipe
!= SIG_IGN
)
608 safe_signal(SIGPIPE
, imapcatch
);
609 ok
= imap_noop1(&mb
);
611 safe_signal(SIGINT
, saveint
);
612 safe_signal(SIGPIPE
, savepipe
);
620 rec_queue(enum rec_type type
, unsigned long count
)
624 rp
= scalloc(1, sizeof *rp
);
626 rp
->rec_count
= count
;
627 if (record
&& recend
) {
628 recend
->rec_next
= rp
;
631 record
= recend
= rp
;
637 struct message
*omessage
;
639 struct record
*rp
= record
, *rq
= NULL
;
640 unsigned long exists
= 0, i
;
645 message
= smalloc((msgCount
+1) * sizeof *message
);
647 memcpy(message
, omessage
, msgCount
* sizeof *message
);
648 memset(&message
[msgCount
], 0, sizeof *message
);
650 switch (rp
->rec_type
) {
652 exists
= rp
->rec_count
;
655 if (rp
->rec_count
== 0) {
659 if (rp
->rec_count
> (unsigned long)msgCount
) {
660 if (exists
== 0 || rp
->rec_count
> exists
--)
666 delcache(&mb
, &message
[rp
->rec_count
-1]);
667 memmove(&message
[rp
->rec_count
-1],
668 &message
[rp
->rec_count
],
669 (msgCount
- rp
->rec_count
+ 1) *
673 * If the message was part of a collapsed thread,
674 * the m_collapsed field of one of its ancestors
675 * should be incremented. It seems hardly possible
676 * to do this with the current message structure,
677 * though. The result is that a '+' may be shown
678 * in the header summary even if no collapsed
688 record
= recend
= NULL
;
689 if (ok
== OKAY
&& exists
> (unsigned long)msgCount
) {
690 message
= srealloc(message
,
691 (exists
+ 1) * sizeof *message
);
692 memset(&message
[msgCount
], 0,
693 (exists
- msgCount
+ 1) * sizeof *message
);
694 for (i
= msgCount
; i
< exists
; i
++)
696 imap_flags(&mb
, msgCount
+1, exists
);
709 struct record
*rp
, *rq
= NULL
;
711 for (rp
= record
; rp
; rp
= rp
->rec_next
) {
716 record
= recend
= NULL
;
723 sighandler_type saveint
;
724 sighandler_type savepipe
;
727 if (imaplock
++ == 0) {
728 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
729 safe_signal(SIGINT
, maincatch
);
730 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
731 if (sigsetjmp(imapjmp
, 1)) {
732 safe_signal(SIGINT
, saveint
);
733 safe_signal(SIGPIPE
, savepipe
);
736 if (savepipe
!= SIG_IGN
)
737 safe_signal(SIGPIPE
, imapcatch
);
738 if (imap_noop1(&mb
) != OKAY
) {
739 safe_signal(SIGINT
, saveint
);
740 safe_signal(SIGPIPE
, savepipe
);
743 safe_signal(SIGINT
, saveint
);
744 safe_signal(SIGPIPE
, savepipe
);
746 brk
: alarm(imapkeepalive
);
751 imap_use_starttls(const char *uhp
)
755 if (value("imap-use-starttls"))
757 var
= savecat("imap-use-starttls-", uhp
);
758 return value(var
) != NULL
;
762 imap_preauth(struct mailbox
*mp
, const char *xserver
, const char *uhp
)
766 mp
->mb_active
|= MB_PREAUTH
;
768 if ((cp
= strchr(xserver
, ':')) != NULL
) {
769 server
= salloc(cp
- xserver
+ 1);
770 memcpy(server
, xserver
, cp
- xserver
);
771 server
[cp
- xserver
] = '\0';
773 server
= (char *)xserver
;
775 if (mp
->mb_sock
.s_use_ssl
== 0 && imap_use_starttls(uhp
)) {
776 FILE *queuefp
= NULL
;
779 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
780 IMAP_OUT(o
, MB_COMD
, return STOP
);
782 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
786 if (imap_use_starttls(uhp
)) {
787 fprintf(stderr
, "No SSL support compiled in.\n");
790 #endif /* !USE_SSL */
796 imap_capability(struct mailbox
*mp
)
799 FILE *queuefp
= NULL
;
803 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
804 IMAP_OUT(o
, MB_COMD
, return STOP
)
805 while (mp
->mb_active
& MB_COMD
) {
806 ok
= imap_answer(mp
, 0);
807 if (response_status
== RESPONSE_OTHER
&&
808 response_other
== CAPABILITY_DATA
) {
809 cp
= responded_other_text
;
811 while (spacechar(*cp
&0377))
813 if (strncmp(cp
, "UIDPLUS", 7) == 0 &&
814 spacechar(cp
[7]&0377))
816 mp
->mb_flags
|= MB_UIDPLUS
;
817 while (*cp
&& !spacechar(*cp
&0377))
826 imap_auth(struct mailbox
*mp
, const char *uhp
, char *xuser
, const char *pass
)
831 if (!(mp
->mb_active
& MB_PREAUTH
))
833 if ((auth
= value("imap-auth")) == NULL
) {
834 var
= ac_alloc(strlen(uhp
) + 11);
835 strcpy(var
, "imap-auth-");
836 strcpy(&var
[10], uhp
);
840 if (auth
== NULL
|| strcmp(auth
, "login") == 0)
841 return imap_login(mp
, xuser
, pass
);
842 if (strcmp(auth
, "cram-md5") == 0) {
844 return imap_cram_md5(mp
, xuser
, pass
);
846 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
850 if (strcmp(auth
, "gssapi") == 0) {
852 return imap_gss(mp
, xuser
);
853 #else /* !USE_GSSAPI */
854 fprintf(stderr
, tr(272, "No GSSAPI support compiled in.\n"));
856 #endif /* !USE_GSSAPI */
858 fprintf(stderr
, tr(273, "Unknown IMAP authentication method: %s\n"),
864 * Implementation of RFC 2194.
868 imap_cram_md5(struct mailbox
*mp
, char *xuser
, const char *xpass
)
871 const char *user
, *pass
;
873 FILE *queuefp
= NULL
;
876 retry
: if (xuser
== NULL
) {
877 if ((user
= getuser()) == NULL
)
882 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
886 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
887 IMAP_OUT(o
, 0, return STOP
)
889 if (response_type
!= RESPONSE_CONT
)
891 cp
= cram_md5_string(user
, pass
, responded_text
);
892 IMAP_OUT(cp
, MB_COMD
, return STOP
)
893 while (mp
->mb_active
& MB_COMD
)
894 ok
= imap_answer(mp
, 1);
904 imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
)
907 const char *user
, *pass
;
908 FILE *queuefp
= NULL
;
911 retry
: if (xuser
== NULL
) {
912 if ((user
= getuser()) == NULL
)
917 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
921 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
922 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
923 IMAP_OUT(o
, MB_COMD
, return STOP
)
924 while (mp
->mb_active
& MB_COMD
)
925 ok
= imap_answer(mp
, 1);
934 #include "imap_gssapi.c"
935 #endif /* USE_GSSAPI */
938 imap_select(struct mailbox
*mp
, off_t
*size
, int *count
, const char *mbx
)
943 FILE *queuefp
= NULL
;
946 mp
->mb_uidvalidity
= 0;
947 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
948 IMAP_OUT(o
, MB_COMD
, return STOP
)
949 while (mp
->mb_active
& MB_COMD
) {
950 ok
= imap_answer(mp
, 1);
951 if (response_status
!= RESPONSE_OTHER
&&
952 (cp
= asccasestr(responded_text
,
953 "[UIDVALIDITY ")) != NULL
)
954 mp
->mb_uidvalidity
= atol(&cp
[13]);
956 *count
= had_exists
> 0 ? had_exists
: 0;
957 if (response_status
!= RESPONSE_OTHER
&&
958 ascncasecmp(responded_text
, "[READ-ONLY] ", 12)
965 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
968 FILE *queuefp
= NULL
;
971 unsigned x
= X
, y
= Y
, n
;
973 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
974 IMAP_OUT(o
, MB_COMD
, return STOP
)
975 while (mp
->mb_active
& MB_COMD
) {
977 if (response_status
== RESPONSE_OTHER
&&
978 response_other
== MESSAGE_DATA_FETCH
) {
979 n
= responded_other_number
;
986 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
991 imap_getflags(cp
, &cp
, &m
->m_flag
);
993 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
994 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
995 getcache1(mp
, m
, NEED_UNSPEC
, 1);
996 m
->m_flag
&= ~MHIDDEN
;
998 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1000 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1003 snprintf(o
, sizeof o
,
1004 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1006 IMAP_OUT(o
, MB_COMD
, return STOP
)
1007 while (mp
->mb_active
& MB_COMD
) {
1009 if (response_status
== RESPONSE_OTHER
&&
1010 response_other
== MESSAGE_DATA_FETCH
) {
1011 n
= responded_other_number
;
1017 if ((cp
= asccasestr(responded_other_text
,
1018 "RFC822.SIZE ")) != NULL
)
1019 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1020 if ((cp
= asccasestr(responded_other_text
,
1021 "INTERNALDATE ")) != NULL
)
1022 m
->m_time
= imap_read_date_time(&cp
[13]);
1025 for (n
= X
; n
<= Y
; n
++)
1026 putcache(mp
, &message
[n
-1]);
1031 imap_init(struct mailbox
*mp
, int n
)
1033 struct message
*m
= &message
[n
];
1036 m
->m_flag
= MUSED
|MNOFROM
;
1042 imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
, int *prevcount
)
1044 struct message
*omessage
= 0;
1045 int i
, omsgCount
= 0;
1046 enum okay dequeued
= STOP
;
1048 if (newmail
|| transparent
) {
1050 omsgCount
= msgCount
;
1053 dequeued
= rec_dequeue();
1054 if (had_exists
>= 0) {
1055 if (dequeued
!= OKAY
)
1056 msgCount
= had_exists
;
1059 if (had_expunge
>= 0) {
1060 if (dequeued
!= OKAY
)
1061 msgCount
-= had_expunge
;
1064 if (newmail
&& expunged_messages
)
1065 printf("Expunged %ld message%s.\n",
1067 expunged_messages
!= 1 ? "s" : "");
1068 *prevcount
= omsgCount
- expunged_messages
;
1069 expunged_messages
= 0;
1071 fputs("IMAP error: Negative message count\n", stderr
);
1074 if (dequeued
!= OKAY
) {
1075 message
= scalloc(msgCount
+ 1, sizeof *message
);
1076 for (i
= 0; i
< msgCount
; i
++)
1078 if (!newmail
&& mp
->mb_type
== MB_IMAP
)
1081 imap_flags(mp
, 1, msgCount
);
1082 message
[msgCount
].m_size
= 0;
1083 message
[msgCount
].m_lines
= 0;
1086 if (newmail
|| transparent
)
1087 transflags(omessage
, omsgCount
, transparent
);
1093 imap_have_password(const char *server
)
1097 var
= ac_alloc(strlen(server
) + 10);
1098 strcpy(var
, "password-");
1099 strcpy(&var
[9], server
);
1100 if ((cp
= value(var
)) != NULL
)
1107 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1108 char **uhp
, char **mbx
, const char **pass
, char **user
)
1111 if (strncmp(*sp
, "imap://", 7) == 0) {
1115 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1118 #endif /* USE_SSL */
1120 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1121 *uhp
= savestr((char *)(*sp
));
1122 (*uhp
)[*cp
- *sp
] = '\0';
1123 *mbx
= (char *)&(*cp
)[1];
1126 (*server
)[*cp
- *server
] = '\0';
1127 *uhp
= (char *)(*sp
);
1130 *pass
= imap_have_password(*uhp
);
1131 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1132 *user
= salloc(*cp
- *uhp
+ 1);
1133 memcpy(*user
, *uhp
, *cp
- *uhp
);
1134 (*user
)[*cp
- *uhp
] = '\0';
1136 *user
= strdec(*user
);
1144 imap_setfile(const char *xserver
, int newmail
, int isedit
)
1146 return imap_setfile1(xserver
, newmail
, isedit
, 0);
1150 imap_setfile1(const char *xserver
, int newmail
, int isedit
, int transparent
)
1153 sighandler_type
volatile saveint
, savepipe
;
1154 char *server
, *user
, *account
;
1155 const char *cp
, *sp
, *pass
;
1158 enum mbflags same_flags
;
1165 server
= savestr((char *)xserver
);
1166 verbose
= value("verbose") != NULL
;
1168 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1169 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1170 if (saveint
!= SIG_IGN
)
1171 safe_signal(SIGINT
, imapcatch
);
1172 if (savepipe
!= SIG_IGN
)
1173 safe_signal(SIGPIPE
, imapcatch
);
1177 same_flags
= mb
.mb_flags
;
1178 same_imap_account
= 0;
1179 sp
= protbase(server
);
1180 if (mb
.mb_imap_account
) {
1181 if (mb
.mb_sock
.s_fd
> 0 &&
1182 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1183 disconnected(mb
.mb_imap_account
) == 0)
1184 same_imap_account
= 1;
1186 account
= sstrdup(sp
);
1187 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
1189 if (!same_imap_account
) {
1190 if (!disconnected(account
) &&
1191 sopen(sp
, &so
, use_ssl
, uhp
,
1192 use_ssl
? "imaps" : "imap", verbose
) != OKAY
)
1199 free(mb
.mb_imap_account
);
1200 mb
.mb_imap_account
= account
;
1201 if (!same_imap_account
) {
1202 if (mb
.mb_sock
.s_fd
>= 0)
1203 sclose(&mb
.mb_sock
);
1205 same_imap_account
= 0;
1215 free(mb
.mb_imap_mailbox
);
1216 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1219 mb
.mb_type
= MB_VOID
;
1220 mb
.mb_active
= MB_NONE
;;
1222 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1223 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1224 if (sigsetjmp(imapjmp
, 1)) {
1226 safe_signal(SIGINT
, saveint
);
1227 safe_signal(SIGPIPE
, savepipe
);
1231 if (saveint
!= SIG_IGN
)
1232 safe_signal(SIGINT
, imapcatch
);
1233 if (savepipe
!= SIG_IGN
)
1234 safe_signal(SIGPIPE
, imapcatch
);
1235 if (mb
.mb_sock
.s_fd
< 0) {
1236 if (disconnected(mb
.mb_imap_account
)) {
1237 if (cache_setptr(transparent
) == STOP
)
1239 "Mailbox \"%s\" is not cached.\n",
1243 if ((cp
= value("imap-keepalive")) != NULL
) {
1244 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1245 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1246 alarm(imapkeepalive
);
1250 mb
.mb_sock
.s_desc
= "IMAP";
1251 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1252 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1253 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1254 sclose(&mb
.mb_sock
);
1256 safe_signal(SIGINT
, saveint
);
1257 safe_signal(SIGPIPE
, savepipe
);
1261 } else /* same account */
1262 mb
.mb_flags
|= same_flags
;
1263 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
1264 mb
.mb_type
= MB_IMAP
;
1266 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1267 /*sclose(&mb.mb_sock);
1269 safe_signal(SIGINT
, saveint
);
1270 safe_signal(SIGPIPE
, savepipe
);
1272 mb
.mb_type
= MB_VOID
;
1276 imap_setptr(&mb
, newmail
, transparent
, &prevcount
);
1277 done
: setmsize(msgCount
);
1278 if (!newmail
&& !transparent
)
1280 safe_signal(SIGINT
, saveint
);
1281 safe_signal(SIGPIPE
, savepipe
);
1283 if (!newmail
&& mb
.mb_type
== MB_IMAP
)
1284 purgecache(&mb
, message
, msgCount
);
1285 if ((newmail
|| transparent
) && mb
.mb_sorted
) {
1289 if (!newmail
&& !edit
&& msgCount
== 0) {
1290 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1291 value("emptystart") == NULL
)
1292 fprintf(stderr
, catgets(catd
, CATSET
, 258,
1293 "No mail at %s\n"), server
);
1297 newmailinfo(prevcount
);
1302 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1304 const char *head
, size_t headsize
, long headlines
)
1306 char *line
= NULL
, *lp
;
1307 size_t linesize
= 0, linelen
, size
= 0;
1308 int emptyline
= 0, lines
= 0, excess
= 0;
1311 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1312 offset
= ftell(mp
->mb_otf
);
1314 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1315 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1317 if (linelen
> expected
) {
1318 excess
= linelen
- expected
;
1322 * Need to mask 'From ' lines. This cannot be done properly
1323 * since some servers pass them as 'From ' and others as
1324 * '>From '. Although one could identify the first kind of
1325 * server in principle, it is not possible to identify the
1326 * second as '>From ' may also come from a server of the
1327 * first type as actual data. So do what is absolutely
1328 * necessary only - mask 'From '.
1330 * If the line is the first line of the message header, it
1331 * is likely a real 'From ' line. In this case, it is just
1332 * ignored since it violates all standards.
1334 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
1335 lp
[3] == 'm' && lp
[4] == ' ') {
1336 if (lines
+ headlines
!= 0) {
1337 fputc('>', mp
->mb_otf
);
1342 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1343 lp
[linelen
-2] == '\r')) {
1344 emptyline
= linelen
<= 2;
1346 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1347 size
+= linelen
- 1;
1350 fputc('\n', mp
->mb_otf
);
1353 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1357 skip
: if ((expected
-= linelen
) <= 0)
1362 * This is very ugly; but some IMAP daemons don't end a
1363 * message with \r\n\r\n, and we need \n\n for mbox format.
1365 fputc('\n', mp
->mb_otf
);
1371 m
->m_size
= size
+ headsize
;
1372 m
->m_lines
= lines
+ headlines
;
1373 m
->m_block
= mailx_blockof(offset
);
1374 m
->m_offset
= mailx_offsetof(offset
);
1377 m
->m_have
|= HAVE_HEADER
;
1380 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1381 m
->m_xlines
= m
->m_lines
;
1382 m
->m_xsize
= m
->m_size
;
1391 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1392 const char *head
, size_t headsize
, long headlines
)
1398 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1399 offset
= ftell(mp
->mb_otf
);
1401 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1403 fwrite(str
, 1, len
, mp
->mb_otf
);
1404 fputc('\n', mp
->mb_otf
);
1409 m
->m_size
= headsize
+ len
;
1410 m
->m_lines
= headlines
+ 1;
1411 m
->m_block
= mailx_blockof(offset
);
1412 m
->m_offset
= mailx_offsetof(offset
);
1413 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1414 m
->m_xlines
= m
->m_lines
;
1415 m
->m_xsize
= m
->m_size
;
1420 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1423 sighandler_type saveint
= SIG_IGN
, savepipe
= SIG_IGN
;
1424 char o
[LINESIZE
], *cp
= NULL
, *loc
= NULL
,
1425 *volatile item
= NULL
, *volatile resp
= NULL
,
1426 *volatile head
= NULL
;
1428 size_t volatile headsize
= 0;
1429 int number
= m
- message
+ 1;
1430 enum okay ok
= STOP
;
1431 FILE *queuefp
= NULL
;
1432 long volatile headlines
= 0;
1434 unsigned long u
= 0;
1436 verbose
= value("verbose") != NULL
;
1437 if (getcache(mp
, m
, need
) == OKAY
)
1439 if (mp
->mb_type
== MB_CACHE
) {
1440 fprintf(stderr
, "Message %u not available.\n", number
);
1443 if (mp
->mb_sock
.s_fd
< 0) {
1444 fprintf(stderr
, "IMAP connection closed.\n");
1449 resp
= item
= "RFC822.HEADER";
1452 item
= "BODY.PEEK[]";
1454 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1455 char *hdr
= smalloc(m
->m_size
);
1457 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1458 m
->m_offset
), SEEK_SET
) < 0 ||
1459 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1465 headsize
= m
->m_size
;
1466 headlines
= m
->m_lines
;
1467 item
= "BODY.PEEK[TEXT]";
1468 resp
= "BODY[TEXT]";
1475 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1476 if (sigsetjmp(imapjmp
, 1)) {
1477 safe_signal(SIGINT
, saveint
);
1478 safe_signal(SIGPIPE
, savepipe
);
1482 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1483 safe_signal(SIGINT
, maincatch
);
1484 if (savepipe
!= SIG_IGN
)
1485 safe_signal(SIGPIPE
, imapcatch
);
1487 snprintf(o
, sizeof o
,
1488 "%s UID FETCH %lu (%s)\r\n",
1489 tag(1), m
->m_uid
, item
);
1491 if (check_expunged() == STOP
)
1493 snprintf(o
, sizeof o
,
1494 "%s FETCH %u (%s)\r\n",
1495 tag(1), number
, item
);
1497 IMAP_OUT(o
, MB_COMD
, goto out
)
1499 ok
= imap_answer(mp
, 1);
1502 if (response_status
!= RESPONSE_OTHER
||
1503 response_other
!= MESSAGE_DATA_FETCH
)
1505 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1508 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1516 n
= responded_other_number
;
1517 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1518 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1520 if ((cp
= strchr(loc
, '"')) != NULL
) {
1521 cp
= imap_unquotestr(cp
);
1522 imap_putstr(mp
, m
, cp
,
1523 head
, headsize
, headlines
);
1525 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1526 m
->m_xlines
= m
->m_lines
;
1527 m
->m_xsize
= m
->m_size
;
1531 expected
= atol(&cp
[1]);
1532 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1533 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1537 imap_fetchdata(mp
, &mt
, expected
, need
,
1538 head
, headsize
, headlines
);
1540 commitmsg(mp
, m
, mt
, mt
.m_have
);
1543 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1544 &mp
->mb_sock
) > 0) {
1546 fputs(imapbuf
, stderr
);
1547 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1549 if (u
== m
->m_uid
) {
1550 commitmsg(mp
, m
, mt
, mt
.m_have
);
1556 out
: while (mp
->mb_active
& MB_COMD
)
1557 ok
= imap_answer(mp
, 1);
1558 if (saveint
!= SIG_IGN
)
1559 safe_signal(SIGINT
, saveint
);
1560 if (savepipe
!= SIG_IGN
)
1561 safe_signal(SIGPIPE
, savepipe
);
1572 imap_header(struct message
*m
)
1574 return imap_get(&mb
, m
, NEED_HEADER
);
1579 imap_body(struct message
*m
)
1581 return imap_get(&mb
, m
, NEED_BODY
);
1585 commitmsg(struct mailbox
*mp
, struct message
*to
,
1586 struct message from
, enum havespec have
)
1588 to
->m_size
= from
.m_size
;
1589 to
->m_lines
= from
.m_lines
;
1590 to
->m_block
= from
.m_block
;
1591 to
->m_offset
= from
.m_offset
;
1593 if (have
& HAVE_BODY
) {
1594 to
->m_xlines
= from
.m_lines
;
1595 to
->m_xsize
= from
.m_size
;
1605 int top
/* bot > top */
1608 char o
[LINESIZE
], *cp
;
1613 FILE *queuefp
= NULL
;
1616 snprintf(o
, sizeof o
,
1617 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1618 tag(1), m
[bot
-1].m_uid
, m
[top
-1].m_uid
);
1620 if (check_expunged() == STOP
)
1622 snprintf(o
, sizeof o
,
1623 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1626 IMAP_OUT(o
, MB_COMD
, return STOP
)
1628 ok
= imap_answer(mp
, 1);
1629 if (response_status
!= RESPONSE_OTHER
)
1631 if (response_other
!= MESSAGE_DATA_FETCH
)
1633 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1635 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1637 expected
= atol(&cp
[1]);
1638 if (m
[bot
-1].m_uid
) {
1639 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1641 for (n
= bot
; n
<= top
; n
++)
1642 if ((unsigned long)u
== m
[n
-1].m_uid
)
1645 imap_fetchdata(mp
, NULL
, expected
,
1653 n
= responded_other_number
;
1654 if (n
<= 0 || n
> msgCount
) {
1655 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1660 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1661 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1662 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1663 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1664 &mp
->mb_sock
) > 0) {
1666 fputs(imapbuf
, stderr
);
1667 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1669 for (n
= bot
; n
<= top
; n
++)
1670 if ((unsigned long)u
== m
[n
-1].m_uid
)
1672 if (n
<= top
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1673 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1678 while (mp
->mb_active
& MB_COMD
)
1679 ok
= imap_answer(mp
, 1);
1684 imap_getheaders(int volatile bot
, int top
)
1686 sighandler_type saveint
, savepipe
;
1687 enum okay ok
= STOP
;
1690 verbose
= value("verbose") != NULL
;
1691 if (mb
.mb_type
== MB_CACHE
)
1697 for (i
= bot
; i
< top
; i
++) {
1698 if (message
[i
-1].m_have
& HAVE_HEADER
||
1699 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1705 for (i
= top
; i
> bot
; i
--) {
1706 if (message
[i
-1].m_have
& HAVE_HEADER
||
1707 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1716 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1717 safe_signal(SIGINT
, maincatch
);
1718 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1719 if (sigsetjmp(imapjmp
, 1) == 0) {
1720 if (savepipe
!= SIG_IGN
)
1721 safe_signal(SIGPIPE
, imapcatch
);
1722 for (i
= bot
; i
<= top
; i
+= chunk
) {
1723 ok
= imap_fetchheaders(&mb
, message
, i
,
1724 i
+chunk
-1 < top
? i
+chunk
-1 : top
);
1729 safe_signal(SIGINT
, saveint
);
1730 safe_signal(SIGPIPE
, savepipe
);
1735 imap_exit(struct mailbox
*mp
)
1738 FILE *queuefp
= NULL
;
1740 verbose
= value("verbose") != NULL
;
1741 mp
->mb_active
|= MB_BYE
;
1742 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1743 IMAP_OUT(o
, MB_COMD
, return STOP
)
1749 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1751 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1752 if (mp
->mb_type
== MB_IMAP
)
1758 imap_close(struct mailbox
*mp
)
1761 FILE *queuefp
= NULL
;
1763 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1764 IMAP_OUT(o
, MB_COMD
, return STOP
)
1770 imap_update(struct mailbox
*mp
)
1772 FILE *readstat
= NULL
;
1774 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1776 verbose
= value("verbose") != NULL
;
1777 if (Tflag
!= NULL
) {
1778 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
1781 if (!edit
&& mp
->mb_perm
!= 0) {
1783 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1784 if (m
->m_flag
& MBOX
)
1788 if (makembox() == STOP
)
1791 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1792 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
1795 if ((id
= hfield1("message-id", m
)) != NULL
||
1796 (id
= hfieldX("article-id", m
)) != NULL
)
1797 fprintf(readstat
, "%s\n", id
);
1799 if (mp
->mb_perm
== 0) {
1802 dodel
= m
->m_flag
& MDELETED
;
1804 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1805 (m
->m_flag
&MTOUCH
) == 0);
1808 * Fetch the result after around each 800 STORE commands
1809 * sent (approx. 32k data sent). Otherwise, servers will
1810 * try to flush the return queue at some point, leading
1811 * to a deadlock if we are still writing commands but not
1812 * reading their results.
1814 needstat
= stored
> 0 && stored
% 800 == 0;
1816 * Even if this message has been deleted, continue
1817 * to set further flags. This is necessary to support
1818 * Gmail semantics, where "delete" actually means
1819 * "archive", and the flags are applied to the copy
1822 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1823 imap_store(mp
, m
, m
-message
+1,
1824 '+', "\\Seen", needstat
);
1827 if (m
->m_flag
& MFLAG
) {
1828 imap_store(mp
, m
, m
-message
+1,
1829 '+', "\\Flagged", needstat
);
1832 if (m
->m_flag
& MUNFLAG
) {
1833 imap_store(mp
, m
, m
-message
+1,
1834 '-', "\\Flagged", needstat
);
1837 if (m
->m_flag
& MANSWER
) {
1838 imap_store(mp
, m
, m
-message
+1,
1839 '+', "\\Answered", needstat
);
1842 if (m
->m_flag
& MUNANSWER
) {
1843 imap_store(mp
, m
, m
-message
+1,
1844 '-', "\\Answered", needstat
);
1847 if (m
->m_flag
& MDRAFT
) {
1848 imap_store(mp
, m
, m
-message
+1,
1849 '+', "\\Draft", needstat
);
1852 if (m
->m_flag
& MUNDRAFT
) {
1853 imap_store(mp
, m
, m
-message
+1,
1854 '-', "\\Draft", needstat
);
1858 imap_delete(mp
, m
-message
+1, m
, needstat
);
1861 } else if (mp
->mb_type
!= MB_CACHE
||
1862 (! edit
&& ! (m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))) ||
1863 (m
->m_flag
& (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1864 (MPRESERVE
|MTOUCH
) ||
1865 (edit
&& ! (m
->m_flag
& MDELETED
)))
1867 if (m
->m_flag
& MNEW
) {
1869 m
->m_flag
|= MSTATUS
;
1872 bypass
: if (readstat
!= NULL
)
1876 for (m
= &message
[0]; m
< &message
[msgCount
]; m
++)
1877 if (!(m
->m_flag
&MUNLINKED
) &&
1878 m
->m_flag
&(MBOXED
|MDELETED
|MSAVED
|MSTATUS
|
1879 MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
1884 if ((gotcha
|| modflags
) && edit
) {
1885 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
1886 printf(value("bsdcompat") || value("bsdmsgs") ?
1887 catgets(catd
, CATSET
, 170, "complete\n") :
1888 catgets(catd
, CATSET
, 212, "updated.\n"));
1889 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1891 printf(catgets(catd
, CATSET
, 155,
1892 "Held 1 message in %s\n"), mailname
);
1894 printf(catgets(catd
, CATSET
, 156,
1895 "Held %d messages in %s\n"), held
, mailname
);
1904 sighandler_type saveint
;
1905 sighandler_type savepipe
;
1907 verbose
= value("verbose") != NULL
;
1908 if (mb
.mb_type
== MB_CACHE
) {
1912 if (mb
.mb_sock
.s_fd
< 0) {
1913 fprintf(stderr
, "IMAP connection closed.\n");
1917 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1918 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1919 if (sigsetjmp(imapjmp
, 1)) {
1920 safe_signal(SIGINT
, saveint
);
1921 safe_signal(SIGPIPE
, saveint
);
1925 if (saveint
!= SIG_IGN
)
1926 safe_signal(SIGINT
, imapcatch
);
1927 if (savepipe
!= SIG_IGN
)
1928 safe_signal(SIGPIPE
, imapcatch
);
1930 if (!same_imap_account
) {
1932 sclose(&mb
.mb_sock
);
1934 safe_signal(SIGINT
, saveint
);
1935 safe_signal(SIGPIPE
, savepipe
);
1940 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1941 int c
, const char *sp
, int needstat
)
1944 FILE *queuefp
= NULL
;
1946 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1949 snprintf(o
, sizeof o
,
1950 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1951 tag(1), m
->m_uid
, c
, sp
);
1953 if (check_expunged() == STOP
)
1955 snprintf(o
, sizeof o
,
1956 "%s STORE %u %cFLAGS (%s)\r\n",
1959 IMAP_OUT(o
, MB_COMD
, return STOP
)
1963 mb
.mb_active
&= ~MB_COMD
;
1964 if (queuefp
!= NULL
)
1970 imap_undelete(struct message
*m
, int n
)
1972 return imap_unstore(m
, n
, "\\Deleted");
1976 imap_unread(struct message
*m
, int n
)
1978 return imap_unstore(m
, n
, "\\Seen");
1982 imap_unstore(struct message
*m
, int n
, const char *flag
)
1984 sighandler_type saveint
, savepipe
;
1985 enum okay ok
= STOP
;
1990 verbose
= value("verbose") != NULL
;
1992 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1993 safe_signal(SIGINT
, maincatch
);
1994 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1995 if (sigsetjmp(imapjmp
, 1) == 0) {
1996 if (savepipe
!= SIG_IGN
)
1997 safe_signal(SIGPIPE
, imapcatch
);
1998 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2000 safe_signal(SIGINT
, saveint
);
2001 safe_signal(SIGPIPE
, savepipe
);
2016 snprintf(ts
, sizeof ts
, "T%lu", n
);
2023 sighandler_type saveint
, savepipe
;
2025 enum okay ok
= STOP
;
2026 struct mailbox
*mp
= &mb
;
2027 FILE *queuefp
= NULL
;
2032 verbose
= value("verbose") != NULL
;
2033 if (mp
->mb_type
!= MB_IMAP
) {
2034 printf("Not operating on an IMAP mailbox.\n");
2038 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2039 safe_signal(SIGINT
, maincatch
);
2040 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2041 if (sigsetjmp(imapjmp
, 1) == 0) {
2042 if (savepipe
!= SIG_IGN
)
2043 safe_signal(SIGPIPE
, imapcatch
);
2044 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2045 IMAP_OUT(o
, MB_COMD
, goto out
)
2046 while (mp
->mb_active
& MB_COMD
) {
2047 ok
= imap_answer(mp
, 0);
2048 fputs(responded_text
, stdout
);
2051 out
: safe_signal(SIGINT
, saveint
);
2052 safe_signal(SIGPIPE
, savepipe
);
2060 imap_newmail(int autoinc
)
2062 if (autoinc
&& had_exists
< 0 && had_expunge
< 0) {
2063 verbose
= value("verbose") != NULL
;
2068 if (had_exists
== msgCount
&& had_expunge
< 0)
2070 * Some servers always respond with EXISTS to NOOP. If
2071 * the mailbox has been changed but the number of messages
2072 * has not, an EXPUNGE must also had been sent; otherwise,
2073 * nothing has changed.
2076 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2080 imap_putflags(int f
)
2085 bp
= buf
= salloc(100);
2086 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2091 for (cp
= "\\Seen"; *cp
; cp
++)
2097 for (cp
= "\\Flagged"; *cp
; cp
++)
2100 if (f
& MANSWERED
) {
2103 for (cp
= "\\Answered"; *cp
; cp
++)
2109 for (cp
= "\\Draft"; *cp
; cp
++)
2120 imap_getflags(const char *cp
, char **xp
, enum mflag
*f
)
2122 while (*cp
!= ')') {
2124 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2126 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2128 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2130 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2132 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2134 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2144 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2145 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2149 size_t bufsize
, buflen
, count
;
2150 enum okay ok
= STOP
;
2151 long size
, lines
, ysize
;
2153 FILE *queuefp
= NULL
;
2155 if (mp
->mb_type
== MB_CACHE
) {
2156 queuefp
= cache_queue(mp
);
2157 if (queuefp
== NULL
)
2161 buf
= smalloc(bufsize
= LINESIZE
);
2163 again
: size
= xsize
;
2165 fseek(fp
, off1
, SEEK_SET
);
2166 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2167 tag(1), imap_quotestr(name
),
2168 imap_putflags(flag
),
2169 imap_make_date_time(t
),
2171 IMAP_OUT(o
, MB_COMD
, goto out
)
2172 while (mp
->mb_active
& MB_COMD
) {
2173 ok
= imap_answer(mp
, twice
);
2174 if (response_type
== RESPONSE_CONT
)
2177 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2185 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2188 buf
[buflen
-1] = '\r';
2190 if (mp
->mb_type
!= MB_CACHE
)
2191 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2193 fwrite(buf
, 1, buflen
+1, queuefp
);
2196 if (mp
->mb_type
!= MB_CACHE
)
2197 swrite(&mp
->mb_sock
, "\r\n");
2199 fputs("\r\n", queuefp
);
2200 while (mp
->mb_active
& MB_COMD
) {
2201 ok
= imap_answer(mp
, 0);
2202 if (response_status
== RESPONSE_NO
/*&&
2203 ascncasecmp(responded_text,
2204 "[TRYCREATE] ", 12) == 0*/) {
2205 trycreate
: if (twice
++) {
2209 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2211 imap_quotestr(name
));
2212 IMAP_OUT(o
, MB_COMD
, goto out
);
2213 while (mp
->mb_active
& MB_COMD
)
2214 ok
= imap_answer(mp
, 1);
2217 imap_created_mailbox
++;
2219 } else if (ok
!= OKAY
)
2220 fprintf(stderr
, "IMAP error: %s", responded_text
);
2221 else if (response_status
== RESPONSE_OK
&&
2222 mp
->mb_flags
& MB_UIDPLUS
)
2223 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2226 out
: if (queuefp
!= NULL
)
2233 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2235 char *buf
, *bp
, *lp
;
2236 size_t bufsize
, buflen
, count
;
2237 off_t off1
= -1, offs
;
2239 int flag
= MNEW
|MNEWEST
;
2244 buf
= smalloc(bufsize
= LINESIZE
);
2250 bp
= fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2251 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2252 if (off1
!= (off_t
)-1) {
2253 ok
=imap_append1(mp
, name
, fp
, off1
,
2257 fseek(fp
, offs
+buflen
, SEEK_SET
);
2259 off1
= offs
+ buflen
;
2264 tim
= unixtime(buf
);
2268 if (bp
&& buf
[0] == '\n')
2270 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2272 while (whitechar(*lp
&0377))
2275 while (*++lp
!= '\0')
2284 } else if (bp
&& inhead
&&
2285 ascncasecmp(buf
, "x-status", 8) == 0) {
2287 while (whitechar(*lp
&0377))
2290 while (*++lp
!= '\0')
2303 } while (bp
!= NULL
);
2309 imap_append(const char *xserver
, FILE *fp
)
2311 sighandler_type saveint
, savepipe
;
2312 char *server
, *uhp
, *mbx
, *user
;
2313 const char *sp
, *cp
, *pass
;
2315 enum okay ok
= STOP
;
2320 verbose
= value("verbose") != NULL
;
2321 server
= savestr((char *)xserver
);
2322 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
2324 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2325 safe_signal(SIGINT
, maincatch
);
2326 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2327 if (sigsetjmp(imapjmp
, 1))
2329 if (savepipe
!= SIG_IGN
)
2330 safe_signal(SIGPIPE
, imapcatch
);
2331 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2332 mb
.mb_imap_account
&&
2333 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2334 ok
= imap_append0(&mb
, mbx
, fp
);
2339 memset(&mx
, 0, sizeof mx
);
2340 if (disconnected(server
) == 0) {
2341 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2342 use_ssl
? "imaps" : "imap",
2345 mx
.mb_sock
.s_desc
= "IMAP";
2346 mx
.mb_type
= MB_IMAP
;
2347 mx
.mb_imap_account
= (char *)protbase(server
);
2348 mx
.mb_imap_mailbox
= mbx
;
2349 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2350 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2351 sclose(&mx
.mb_sock
);
2354 ok
= imap_append0(&mx
, mbx
, fp
);
2356 sclose(&mx
.mb_sock
);
2358 mx
.mb_imap_account
= (char *)protbase(server
);
2359 mx
.mb_imap_mailbox
= mbx
;
2360 mx
.mb_type
= MB_CACHE
;
2361 ok
= imap_append0(&mx
, mbx
, fp
);
2365 out
: safe_signal(SIGINT
, saveint
);
2366 safe_signal(SIGPIPE
, savepipe
);
2374 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2375 struct list_item
**lend
, int level
)
2378 enum okay ok
= STOP
;
2381 FILE *queuefp
= NULL
;
2382 struct list_item
*lp
;
2384 *list
= *lend
= NULL
;
2385 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2386 tag(1), imap_quotestr(base
));
2387 IMAP_OUT(o
, MB_COMD
, return STOP
);
2388 while (mp
->mb_active
& MB_COMD
) {
2389 ok
= imap_answer(mp
, 1);
2390 if (response_status
== RESPONSE_OTHER
&&
2391 response_other
== MAILBOX_DATA_LIST
&&
2392 imap_parse_list() == OKAY
) {
2393 cp
= imap_unquotestr(list_name
);
2394 lp
= csalloc(1, sizeof *lp
);
2396 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2398 lp
->l_base
= *cp
? cp
: savestr(base
);
2399 lp
->l_attr
= list_attributes
;
2400 lp
->l_level
= level
+1;
2401 lp
->l_delim
= list_hierarchy_delimiter
;
2402 if (*list
&& *lend
) {
2403 (*lend
)->l_next
= lp
;
2413 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2415 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2421 verbose
= value("verbose") != NULL
;
2422 depth
= (cp
= value("imap-list-depth")) != NULL
? atoi(cp
) : 2;
2423 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2425 if (list
== NULL
|| lend
== NULL
)
2427 for (lp
= list
; lp
; lp
= lp
->l_next
)
2428 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2429 lp
->l_level
< depth
&&
2430 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2431 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2432 strcpy(cp
, lp
->l_name
);
2433 cp
[n
] = lp
->l_delim
;
2435 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2437 lp
->l_has_children
= 1;
2438 if (strcmp(cp
, lx
->l_name
) == 0)
2446 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2449 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2453 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2454 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2455 else if (lp
->l_has_children
== 0)
2456 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2457 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2463 imap_folders(const char *name
, int strip
)
2465 sighandler_type saveint
, savepipe
;
2466 const char *fold
, *cp
, *sp
;
2469 int columnize
= is_a_tty
[1];
2471 cp
= protbase(name
);
2472 sp
= mb
.mb_imap_account
;
2473 if (strcmp(cp
, sp
)) {
2474 fprintf(stderr
, "Cannot list folders on other than the "
2475 "current IMAP account,\n\"%s\". "
2476 "Try \"folders @\".\n", sp
);
2479 fold
= protfile(name
);
2481 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2490 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2491 safe_signal(SIGINT
, maincatch
);
2492 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2493 if (sigsetjmp(imapjmp
, 1))
2495 if (savepipe
!= SIG_IGN
)
2496 safe_signal(SIGPIPE
, imapcatch
);
2497 if (mb
.mb_type
== MB_CACHE
)
2498 cache_list(&mb
, fold
, strip
, fp
);
2500 imap_list(&mb
, fold
, strip
, fp
);
2513 fprintf(stderr
, "Folder not found.\n");
2516 safe_signal(SIGINT
, saveint
);
2517 safe_signal(SIGPIPE
, savepipe
);
2525 char o
[LINESIZE
], *tempfn
;
2527 long n
= 0, mx
= 0, columns
, width
;
2530 if ((out
= Ftemp(&tempfn
, "Ro", "w+", 0600, 1)) == NULL
) {
2536 while ((c
= getc(fp
)) != EOF
) {
2546 if (mx
< width
/ 2) {
2547 columns
= width
/ (mx
+2);
2548 snprintf(o
, sizeof o
,
2549 "sort | pr -%lu -w%lu -t",
2552 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2553 run_command(SHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2559 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2563 enum okay ok
= STOP
;
2566 FILE *queuefp
= NULL
;
2568 if (mp
->mb_type
== MB_CACHE
) {
2569 if ((queuefp
= cache_queue(mp
)) == NULL
)
2573 qname
= imap_quotestr(name
= protfile(name
));
2575 * Since it is not possible to set flags on the copy, recently
2576 * set flags must be set on the original to include it in the copy.
2578 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
))
2579 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2580 if (m
->m_flag
&MFLAG
)
2581 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2582 if (m
->m_flag
&MUNFLAG
)
2583 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2584 if (m
->m_flag
&MANSWER
)
2585 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2586 if (m
->m_flag
&MUNANSWER
)
2587 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2588 if (m
->m_flag
&MDRAFT
)
2589 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2590 if (m
->m_flag
&MUNDRAFT
)
2591 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2592 again
: if (m
->m_uid
)
2593 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n",
2594 tag(1), m
->m_uid
, qname
);
2596 if (check_expunged() == STOP
)
2598 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n",
2601 IMAP_OUT(o
, MB_COMD
, goto out
)
2602 while (mp
->mb_active
& MB_COMD
)
2603 ok
= imap_answer(mp
, twice
);
2604 if (mp
->mb_type
== MB_IMAP
&&
2605 mp
->mb_flags
& MB_UIDPLUS
&&
2606 response_status
== RESPONSE_OK
)
2607 imap_copyuid(mp
, m
, name
);
2608 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2609 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2610 IMAP_OUT(o
, MB_COMD
, goto out
)
2611 while (mp
->mb_active
& MB_COMD
)
2612 ok
= imap_answer(mp
, 1);
2614 imap_created_mailbox
++;
2618 if (queuefp
!= NULL
)
2621 * ... and reset the flag to its initial value so that
2622 * the 'exit' command still leaves the message unread.
2624 out
: if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
2625 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2628 if (m
->m_flag
&MFLAG
) {
2629 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2632 if (m
->m_flag
&MUNFLAG
) {
2633 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2636 if (m
->m_flag
&MANSWER
) {
2637 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2640 if (m
->m_flag
&MUNANSWER
) {
2641 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2644 if (m
->m_flag
&MDRAFT
) {
2645 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2648 if (m
->m_flag
&MUNDRAFT
) {
2649 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2653 mp
->mb_active
|= MB_COMD
;
2660 imap_copy(struct message
*m
, int n
, const char *name
)
2662 sighandler_type saveint
, savepipe
;
2663 enum okay ok
= STOP
;
2668 verbose
= value("verbose") != NULL
;
2670 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2671 safe_signal(SIGINT
, maincatch
);
2672 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2673 if (sigsetjmp(imapjmp
, 1) == 0) {
2674 if (savepipe
!= SIG_IGN
)
2675 safe_signal(SIGPIPE
, imapcatch
);
2676 ok
= imap_copy1(&mb
, m
, n
, name
);
2678 safe_signal(SIGINT
, saveint
);
2679 safe_signal(SIGPIPE
, savepipe
);
2687 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2688 unsigned long *olduid
, unsigned long *newuid
)
2692 *uidvalidity
= strtoul(cp
, &xp
, 10);
2693 *olduid
= strtoul(xp
, &yp
, 10);
2694 *newuid
= strtoul(yp
, &zp
, 10);
2695 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2696 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2700 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2705 *uidvalidity
= strtoul(cp
, &xp
, 10);
2706 *uid
= strtoul(xp
, &yp
, 10);
2707 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2708 yp
> xp
&& *yp
== ']';
2712 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2715 unsigned long uidvalidity
, olduid
, newuid
;
2719 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2720 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2721 &olduid
, &newuid
) == STOP
)
2724 xmb
.mb_cache_directory
= NULL
;
2725 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2726 xmb
.mb_uidvalidity
= uidvalidity
;
2729 memset(&xm
, 0, sizeof xm
);
2731 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2733 getcache(mp
, &xm
, NEED_HEADER
);
2734 getcache(mp
, &xm
, NEED_BODY
);
2736 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2737 getcache(mp
, m
, NEED_HEADER
);
2738 if ((m
->m_flag
& HAVE_BODY
) == 0)
2739 getcache(mp
, m
, NEED_BODY
);
2743 xm
.m_flag
&= ~MFULLYCACHED
;
2744 putcache(&xmb
, &xm
);
2749 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2750 long xsize
, long size
, long lines
, int flag
, const char *name
)
2753 unsigned long uidvalidity
, uid
;
2757 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2758 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2762 xmb
.mb_cache_directory
= NULL
;
2763 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2764 xmb
.mb_uidvalidity
= uidvalidity
;
2765 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2767 memset(&xm
, 0, sizeof xm
);
2768 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2770 xm
.m_block
= mailx_blockof(off1
);
2771 xm
.m_offset
= mailx_offsetof(off1
);
2774 xm
.m_lines
= xm
.m_xlines
= lines
;
2776 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2777 putcache(&xmb
, &xm
);
2782 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2786 long size
, xsize
, ysize
, lines
;
2787 enum mflag flag
= MNEW
;
2788 char *name
, *buf
, *bp
, *cp
, *tempCopy
;
2789 size_t bufsize
, buflen
, count
;
2790 enum okay ok
= STOP
;
2792 buf
= smalloc(bufsize
= LINESIZE
);
2795 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2797 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
2800 if ((cp
= strrchr(bp
, '{')) == NULL
)
2802 xsize
= atol(&cp
[1]) + 2;
2803 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
2808 imap_getflags(cp
, &cp
, &flag
);
2809 while (*++cp
== ' ');
2811 t
= imap_read_date_time(cp
);
2812 if ((tp
= Ftemp(&tempCopy
, "Rc", "w+", 0600, 1)) == NULL
)
2819 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2822 buf
[--buflen
] = '\0';
2823 buf
[buflen
-1] = '\n';
2824 fwrite(buf
, 1, buflen
, tp
);
2830 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
2831 imap_unquotestr(name
));
2840 imap_search2(struct mailbox
*mp
, struct message
*m
, int count
,
2841 const char *spec
, int f
)
2845 FILE *queuefp
= NULL
;
2846 enum okay ok
= STOP
;
2853 for (cp
= spec
; *cp
; cp
++)
2858 if (asccasecmp(cp
, "utf-8")) {
2860 char *sp
, *nsp
, *nspec
;
2862 if ((it
= iconv_open_ft("utf-8", cp
)) != (iconv_t
)-1) {
2863 sz
= strlen(spec
) + 1;
2864 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2866 if (iconv_ft(it
, &sp
, &sz
, &nsp
, &nsz
, 0)
2867 != (size_t)-1 && sz
== 0) {
2874 #endif /* HAVE_ICONV */
2875 cp
= imap_quotestr(cp
);
2876 cs
= salloc(n
= strlen(cp
) + 10);
2877 snprintf(cs
, n
, "CHARSET %s ", cp
);
2880 o
= ac_alloc(osize
= strlen(spec
) + 60);
2881 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2882 IMAP_OUT(o
, MB_COMD
, goto out
)
2883 while (mp
->mb_active
& MB_COMD
) {
2884 ok
= imap_answer(mp
, 0);
2885 if (response_status
== RESPONSE_OTHER
&&
2886 response_other
== MAILBOX_DATA_SEARCH
) {
2887 xp
= responded_other_text
;
2888 while (*xp
&& *xp
!= '\r') {
2889 n
= strtoul(xp
, &xp
, 10);
2890 for (i
= 0; i
< count
; i
++)
2891 if (m
[i
].m_uid
== n
&&
2892 (m
[i
].m_flag
&MHIDDEN
)
2895 (m
[i
].m_flag
&MDELETED
)
2906 imap_search1(const char *spec
, int f
)
2908 sighandler_type saveint
, savepipe
;
2909 enum okay ok
= STOP
;
2914 if (mb
.mb_type
!= MB_IMAP
)
2916 verbose
= value("verbose") != NULL
;
2918 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2919 safe_signal(SIGINT
, maincatch
);
2920 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2921 if (sigsetjmp(imapjmp
, 1) == 0) {
2922 if (savepipe
!= SIG_IGN
)
2923 safe_signal(SIGPIPE
, imapcatch
);
2924 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2926 safe_signal(SIGINT
, saveint
);
2927 safe_signal(SIGPIPE
, savepipe
);
2935 imap_thisaccount(const char *cp
)
2937 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2939 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2940 mb
.mb_imap_account
== NULL
)
2942 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2946 imap_remove(const char *name
)
2948 sighandler_type saveint
, savepipe
;
2949 enum okay ok
= STOP
;
2954 verbose
= value("verbose") != NULL
;
2955 if (mb
.mb_type
!= MB_IMAP
) {
2956 fprintf(stderr
, "Refusing to remove \"%s\" "
2957 "in disconnected mode.\n", name
);
2960 if (!imap_thisaccount(name
)) {
2961 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2962 "server: \"%s\" not removed.\n", name
);
2966 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2967 safe_signal(SIGINT
, maincatch
);
2968 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2969 if (sigsetjmp(imapjmp
, 1) == 0) {
2970 if (savepipe
!= SIG_IGN
)
2971 safe_signal(SIGPIPE
, imapcatch
);
2972 ok
= imap_remove1(&mb
, protfile(name
));
2974 safe_signal(SIGINT
, saveint
);
2975 safe_signal(SIGPIPE
, savepipe
);
2978 ok
= cache_remove(name
);
2985 imap_remove1(struct mailbox
*mp
, const char *name
)
2987 FILE *queuefp
= NULL
;
2990 enum okay ok
= STOP
;
2992 o
= ac_alloc(os
= 2*strlen(name
) + 100);
2993 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
2994 IMAP_OUT(o
, MB_COMD
, goto out
)
2995 while (mp
->mb_active
& MB_COMD
)
2996 ok
= imap_answer(mp
, 1);
3002 imap_rename(const char *old
, const char *new)
3004 sighandler_type saveint
, savepipe
;
3005 enum okay ok
= STOP
;
3010 verbose
= value("verbose") != NULL
;
3011 if (mb
.mb_type
!= MB_IMAP
) {
3012 fprintf(stderr
, "Refusing to rename mailboxes "
3013 "in disconnected mode.\n");
3016 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3017 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3018 "server: \"%s\" not renamed to \"%s\".\n",
3023 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3024 safe_signal(SIGINT
, maincatch
);
3025 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3026 if (sigsetjmp(imapjmp
, 1) == 0) {
3027 if (savepipe
!= SIG_IGN
)
3028 safe_signal(SIGPIPE
, imapcatch
);
3029 ok
= imap_rename1(&mb
, protfile(old
), protfile(new));
3031 safe_signal(SIGINT
, saveint
);
3032 safe_signal(SIGPIPE
, savepipe
);
3035 ok
= cache_rename(old
, new);
3042 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3044 FILE *queuefp
= NULL
;
3047 enum okay ok
= STOP
;
3049 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3050 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3051 imap_quotestr(old
), imap_quotestr(new));
3052 IMAP_OUT(o
, MB_COMD
, goto out
)
3053 while (mp
->mb_active
& MB_COMD
)
3054 ok
= imap_answer(mp
, 1);
3060 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3062 FILE *queuefp
= NULL
;
3063 char o
[LINESIZE
], *newname
;
3064 char *buf
, *bp
, *cp
, iob
[4096];
3065 size_t bufsize
, buflen
, count
;
3066 enum okay ok
= OKAY
, rok
= OKAY
;
3067 long offs
, offs1
, offs2
, octets
;
3068 int twice
, gotcha
= 0;
3070 buf
= smalloc(bufsize
= LINESIZE
);
3073 while (offs1
= ftell(fp
),
3074 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0)
3076 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3081 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3082 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3084 while (digitchar(*cp
&0377))
3090 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3092 IMAP_OUT(o
, MB_COMD
, continue)
3093 while (mp
->mb_active
& MB_COMD
)
3094 ok
= imap_answer(mp
, twice
);
3095 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3097 if (response_status
== RESPONSE_OK
&&
3098 mp
->mb_flags
& MB_UIDPLUS
) {
3099 imap_copyuid(mp
, NULL
,
3100 imap_unquotestr(newname
));
3102 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3103 IMAP_OUT(o
, MB_COMD
, continue)
3104 while (mp
->mb_active
& MB_COMD
)
3105 ok
= imap_answer(mp
, 1);
3108 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3109 if ((cp
= strrchr(bp
, '{')) == NULL
)
3111 octets
= atol(&cp
[1]) + 2;
3112 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3114 IMAP_OUT(o
, MB_COMD
, continue)
3115 while (mp
->mb_active
& MB_COMD
) {
3116 ok
= imap_answer(mp
, twice
);
3117 if (response_type
== RESPONSE_CONT
)
3122 fseek(fp
, offs
, SEEK_SET
);
3127 while (octets
> 0) {
3128 size_t n
= (size_t)octets
> sizeof iob
3129 ? sizeof iob
: (size_t)octets
;
3131 if (n
!= fread(iob
, 1, n
, fp
))
3133 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3135 swrite(&mp
->mb_sock
, "");
3136 while (mp
->mb_active
& MB_COMD
) {
3137 ok
= imap_answer(mp
, 0);
3138 if (response_status
== RESPONSE_NO
&&
3140 fseek(fp
, offs
, SEEK_SET
);
3144 if (response_status
== RESPONSE_OK
&&
3145 mp
->mb_flags
& MB_UIDPLUS
) {
3147 fseek(fp
, offs1
, SEEK_SET
);
3148 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3149 fseek(fp
, offs2
, SEEK_SET
);
3154 fail
: fprintf(stderr
,
3155 "Invalid command in IMAP cache queue: \"%s\"\n",
3161 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3163 IMAP_OUT(o
, MB_COMD
, continue)
3164 while (mp
->mb_active
& MB_COMD
)
3165 ok
= imap_answer(mp
, 1);
3171 ftruncate(fileno(fp
), 0);
3178 imap_strex(const char *cp
, char **xp
)
3185 for (cq
= &cp
[1]; *cq
; cq
++) {
3188 else if (*cq
== '"')
3193 n
= salloc(cq
- cp
+ 2);
3194 memcpy(n
, cp
, cq
- cp
+ 1);
3195 n
[cq
- cp
+ 1] = '\0';
3197 *xp
= (char *)&cq
[1];
3202 check_expunged(void)
3204 if (expunged_messages
> 0) {
3206 "Command not executed - messages have been expunged\n");
3217 int omsgCount
= msgCount
;
3220 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3221 fprintf(stderr
, "Already connected.\n");
3224 unset_allow_undefined
= 1;
3225 unset_internal("disconnected");
3226 cp
= protbase(mailname
);
3227 if (strncmp(cp
, "imap://", 7) == 0)
3229 else if (strncmp(cp
, "imaps://", 8) == 0)
3231 if ((cq
= strchr(cp
, ':')) != NULL
)
3233 unset_internal(savecat("disconnected-", cp
));
3234 unset_allow_undefined
= 0;
3235 if (mb
.mb_type
== MB_CACHE
) {
3236 imap_setfile1(mailname
, 0, edit
, 1);
3237 if (msgCount
> omsgCount
)
3238 newmailinfo(omsgCount
);
3244 cdisconnect(void *vp
)
3248 if (mb
.mb_type
== MB_CACHE
) {
3249 fprintf(stderr
, "Not connected.\n");
3251 } else if (mb
.mb_type
== MB_IMAP
) {
3252 if (cached_uidvalidity(&mb
) == 0) {
3253 fprintf(stderr
, "The current mailbox is not cached.\n");
3259 assign("disconnected", "");
3260 if (mb
.mb_type
== MB_IMAP
) {
3261 sclose(&mb
.mb_sock
);
3262 imap_setfile1(mailname
, 0, edit
, 1);
3269 int *msgvec
= vp
, *ip
;
3272 if (mb
.mb_type
!= MB_IMAP
) {
3273 fprintf(stderr
, "Not connected to an IMAP server.\n");
3276 if (cached_uidvalidity(&mb
) == 0) {
3277 fprintf(stderr
, "The current mailbox is not cached.\n");
3280 for (ip
= msgvec
; *ip
; ip
++) {
3281 mp
= &message
[*ip
-1];
3282 if (!(mp
->m_have
& HAVE_BODY
))
3287 #else /* !USE_IMAP */
3294 fprintf(stderr
, catgets(catd
, CATSET
, 269,
3295 "No IMAP support compiled in.\n"));
3299 imap_setfile(const char *server
, int newmail
, int isedit
)
3309 imap_header(struct message
*mp
)
3317 imap_body(struct message
*mp
)
3325 imap_getheaders(int bot
, int top
)
3348 imap_newmail(int dummy
)
3356 imap_undelete(struct message
*m
, int n
)
3365 imap_unread(struct message
*m
, int n
)
3374 imap_append(const char *server
, FILE *fp
)
3384 imap_folders(const char *name
, int strip
)
3393 imap_remove(const char *name
)
3402 imap_rename(const char *old
, const char *new)
3411 imap_copy(struct message
*m
, int n
, const char *name
)
3422 imap_search1(const char *spec
, int f
)
3430 imap_thisaccount(const char *cp
)
3454 cdisconnect(void *vp
)
3469 #endif /* USE_IMAP */
3472 imap_read_date_time(const char *cp
)
3475 int i
, year
, month
, day
, hour
, minute
, second
;
3480 * "25-Jul-2004 15:33:44 +0200"
3484 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3486 day
= strtol(&cp
[1], NULL
, 10);
3487 for (i
= 0; month_names
[i
]; i
++)
3488 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3490 if (month_names
[i
] == NULL
)
3493 year
= strtol(&cp
[8], NULL
, 10);
3494 hour
= strtol(&cp
[13], NULL
, 10);
3495 minute
= strtol(&cp
[16], NULL
, 10);
3496 second
= strtol(&cp
[19], NULL
, 10);
3497 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3512 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3515 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3523 imap_read_date(const char *cp
)
3526 int year
, month
, day
, i
, tzdiff
;
3532 day
= strtol(cp
, &xp
, 10);
3533 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3535 for (i
= 0; month_names
[i
]; i
++)
3536 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3538 if (month_names
[i
] == NULL
)
3543 year
= strtol(&xp
[4], &yp
, 10);
3544 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3546 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3548 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3550 tzdiff
= t
- mktime(gmtime(&t
));
3551 tmptr
= localtime(&t
);
3552 if (tmptr
->tm_isdst
> 0)
3559 imap_make_date_time(time_t t
)
3563 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3565 tzdiff
= t
- mktime(gmtime(&t
));
3566 tzdiff_hour
= (int)(tzdiff
/ 60);
3567 tzdiff_min
= tzdiff_hour
% 60;
3569 tmptr
= localtime(&t
);
3570 if (tmptr
->tm_isdst
> 0)
3572 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3574 month_names
[tmptr
->tm_mon
],
3575 tmptr
->tm_year
+ 1900,
3585 imap_quotestr(const char *s
)
3589 np
= n
= salloc(2 * strlen(s
) + 3);
3592 if (*s
== '"' || *s
== '\\')
3602 imap_unquotestr(const char *s
)
3608 np
= n
= salloc(strlen(s
) + 1);