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 saveint
;
1154 sighandler_type savepipe
;
1155 char *server
, *user
, *account
;
1156 const char *cp
, *sp
, *pass
;
1159 enum mbflags same_flags
;
1166 server
= savestr((char *)xserver
);
1167 verbose
= value("verbose") != NULL
;
1169 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1170 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1171 if (saveint
!= SIG_IGN
)
1172 safe_signal(SIGINT
, imapcatch
);
1173 if (savepipe
!= SIG_IGN
)
1174 safe_signal(SIGPIPE
, imapcatch
);
1178 same_flags
= mb
.mb_flags
;
1179 same_imap_account
= 0;
1180 sp
= protbase(server
);
1181 if (mb
.mb_imap_account
) {
1182 if (mb
.mb_sock
.s_fd
> 0 &&
1183 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1184 disconnected(mb
.mb_imap_account
) == 0)
1185 same_imap_account
= 1;
1187 account
= sstrdup(sp
);
1188 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
1190 if (!same_imap_account
) {
1191 if (!disconnected(account
) &&
1192 sopen(sp
, &so
, use_ssl
, uhp
,
1193 use_ssl
? "imaps" : "imap", verbose
) != OKAY
)
1200 free(mb
.mb_imap_account
);
1201 mb
.mb_imap_account
= account
;
1202 if (!same_imap_account
) {
1203 if (mb
.mb_sock
.s_fd
>= 0)
1204 sclose(&mb
.mb_sock
);
1206 same_imap_account
= 0;
1216 free(mb
.mb_imap_mailbox
);
1217 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1220 mb
.mb_type
= MB_VOID
;
1221 mb
.mb_active
= MB_NONE
;;
1223 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1224 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1225 if (sigsetjmp(imapjmp
, 1)) {
1227 safe_signal(SIGINT
, saveint
);
1228 safe_signal(SIGPIPE
, savepipe
);
1232 if (saveint
!= SIG_IGN
)
1233 safe_signal(SIGINT
, imapcatch
);
1234 if (savepipe
!= SIG_IGN
)
1235 safe_signal(SIGPIPE
, imapcatch
);
1236 if (mb
.mb_sock
.s_fd
< 0) {
1237 if (disconnected(mb
.mb_imap_account
)) {
1238 if (cache_setptr(transparent
) == STOP
)
1240 "Mailbox \"%s\" is not cached.\n",
1244 if ((cp
= value("imap-keepalive")) != NULL
) {
1245 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1246 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1247 alarm(imapkeepalive
);
1251 mb
.mb_sock
.s_desc
= "IMAP";
1252 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1253 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1254 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1255 sclose(&mb
.mb_sock
);
1257 safe_signal(SIGINT
, saveint
);
1258 safe_signal(SIGPIPE
, savepipe
);
1262 } else /* same account */
1263 mb
.mb_flags
|= same_flags
;
1264 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
1265 mb
.mb_type
= MB_IMAP
;
1267 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1268 /*sclose(&mb.mb_sock);
1270 safe_signal(SIGINT
, saveint
);
1271 safe_signal(SIGPIPE
, savepipe
);
1273 mb
.mb_type
= MB_VOID
;
1277 imap_setptr(&mb
, newmail
, transparent
, &prevcount
);
1278 done
: setmsize(msgCount
);
1279 if (!newmail
&& !transparent
)
1281 safe_signal(SIGINT
, saveint
);
1282 safe_signal(SIGPIPE
, savepipe
);
1284 if (!newmail
&& mb
.mb_type
== MB_IMAP
)
1285 purgecache(&mb
, message
, msgCount
);
1286 if ((newmail
|| transparent
) && mb
.mb_sorted
) {
1290 if (!newmail
&& !edit
&& msgCount
== 0) {
1291 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1292 value("emptystart") == NULL
)
1293 fprintf(stderr
, catgets(catd
, CATSET
, 258,
1294 "No mail at %s\n"), server
);
1298 newmailinfo(prevcount
);
1303 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1305 const char *head
, size_t headsize
, long headlines
)
1307 char *line
= NULL
, *lp
;
1308 size_t linesize
= 0, linelen
, size
= 0;
1309 int emptyline
= 0, lines
= 0, excess
= 0;
1312 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1313 offset
= ftell(mp
->mb_otf
);
1315 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1316 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1318 if (linelen
> expected
) {
1319 excess
= linelen
- expected
;
1323 * Need to mask 'From ' lines. This cannot be done properly
1324 * since some servers pass them as 'From ' and others as
1325 * '>From '. Although one could identify the first kind of
1326 * server in principle, it is not possible to identify the
1327 * second as '>From ' may also come from a server of the
1328 * first type as actual data. So do what is absolutely
1329 * necessary only - mask 'From '.
1331 * If the line is the first line of the message header, it
1332 * is likely a real 'From ' line. In this case, it is just
1333 * ignored since it violates all standards.
1335 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
1336 lp
[3] == 'm' && lp
[4] == ' ') {
1337 if (lines
+ headlines
!= 0) {
1338 fputc('>', mp
->mb_otf
);
1343 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1344 lp
[linelen
-2] == '\r')) {
1345 emptyline
= linelen
<= 2;
1347 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1348 size
+= linelen
- 1;
1351 fputc('\n', mp
->mb_otf
);
1354 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1358 skip
: if ((expected
-= linelen
) <= 0)
1363 * This is very ugly; but some IMAP daemons don't end a
1364 * message with \r\n\r\n, and we need \n\n for mbox format.
1366 fputc('\n', mp
->mb_otf
);
1372 m
->m_size
= size
+ headsize
;
1373 m
->m_lines
= lines
+ headlines
;
1374 m
->m_block
= mailx_blockof(offset
);
1375 m
->m_offset
= mailx_offsetof(offset
);
1378 m
->m_have
|= HAVE_HEADER
;
1381 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1382 m
->m_xlines
= m
->m_lines
;
1383 m
->m_xsize
= m
->m_size
;
1392 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1393 const char *head
, size_t headsize
, long headlines
)
1399 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1400 offset
= ftell(mp
->mb_otf
);
1402 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1404 fwrite(str
, 1, len
, mp
->mb_otf
);
1405 fputc('\n', mp
->mb_otf
);
1410 m
->m_size
= headsize
+ len
;
1411 m
->m_lines
= headlines
+ 1;
1412 m
->m_block
= mailx_blockof(offset
);
1413 m
->m_offset
= mailx_offsetof(offset
);
1414 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1415 m
->m_xlines
= m
->m_lines
;
1416 m
->m_xsize
= m
->m_size
;
1421 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1423 sighandler_type saveint
= SIG_IGN
;
1424 sighandler_type savepipe
= SIG_IGN
;
1425 char o
[LINESIZE
], *cp
= NULL
, *item
= NULL
, *resp
= NULL
, *loc
= NULL
;
1426 size_t expected
, headsize
= 0;
1427 int number
= m
- message
+ 1;
1428 enum okay ok
= STOP
;
1429 FILE *queuefp
= NULL
;
1434 unsigned long u
= 0;
1449 verbose
= value("verbose") != NULL
;
1450 if (getcache(mp
, m
, need
) == OKAY
)
1452 if (mp
->mb_type
== MB_CACHE
) {
1453 fprintf(stderr
, "Message %u not available.\n", number
);
1456 if (mp
->mb_sock
.s_fd
< 0) {
1457 fprintf(stderr
, "IMAP connection closed.\n");
1462 resp
= item
= "RFC822.HEADER";
1465 item
= "BODY.PEEK[]";
1467 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1468 char *hdr
= smalloc(m
->m_size
);
1470 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1471 m
->m_offset
), SEEK_SET
) < 0 ||
1472 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1478 headsize
= m
->m_size
;
1479 headlines
= m
->m_lines
;
1480 item
= "BODY.PEEK[TEXT]";
1481 resp
= "BODY[TEXT]";
1488 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1489 if (sigsetjmp(imapjmp
, 1)) {
1490 safe_signal(SIGINT
, saveint
);
1491 safe_signal(SIGPIPE
, savepipe
);
1495 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1496 safe_signal(SIGINT
, maincatch
);
1497 if (savepipe
!= SIG_IGN
)
1498 safe_signal(SIGPIPE
, imapcatch
);
1500 snprintf(o
, sizeof o
,
1501 "%s UID FETCH %lu (%s)\r\n",
1502 tag(1), m
->m_uid
, item
);
1504 if (check_expunged() == STOP
)
1506 snprintf(o
, sizeof o
,
1507 "%s FETCH %u (%s)\r\n",
1508 tag(1), number
, item
);
1510 IMAP_OUT(o
, MB_COMD
, goto out
)
1512 ok
= imap_answer(mp
, 1);
1515 if (response_status
!= RESPONSE_OTHER
||
1516 response_other
!= MESSAGE_DATA_FETCH
)
1518 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1521 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1529 n
= responded_other_number
;
1530 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1531 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1533 if ((cp
= strchr(loc
, '"')) != NULL
) {
1534 cp
= imap_unquotestr(cp
);
1535 imap_putstr(mp
, m
, cp
,
1536 head
, headsize
, headlines
);
1538 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1539 m
->m_xlines
= m
->m_lines
;
1540 m
->m_xsize
= m
->m_size
;
1544 expected
= atol(&cp
[1]);
1545 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1546 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1550 imap_fetchdata(mp
, &mt
, expected
, need
,
1551 head
, headsize
, headlines
);
1553 commitmsg(mp
, m
, mt
, mt
.m_have
);
1556 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1557 &mp
->mb_sock
) > 0) {
1559 fputs(imapbuf
, stderr
);
1560 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1562 if (u
== m
->m_uid
) {
1563 commitmsg(mp
, m
, mt
, mt
.m_have
);
1569 out
: while (mp
->mb_active
& MB_COMD
)
1570 ok
= imap_answer(mp
, 1);
1571 if (saveint
!= SIG_IGN
)
1572 safe_signal(SIGINT
, saveint
);
1573 if (savepipe
!= SIG_IGN
)
1574 safe_signal(SIGPIPE
, savepipe
);
1585 imap_header(struct message
*m
)
1587 return imap_get(&mb
, m
, NEED_HEADER
);
1592 imap_body(struct message
*m
)
1594 return imap_get(&mb
, m
, NEED_BODY
);
1598 commitmsg(struct mailbox
*mp
, struct message
*to
,
1599 struct message from
, enum havespec have
)
1601 to
->m_size
= from
.m_size
;
1602 to
->m_lines
= from
.m_lines
;
1603 to
->m_block
= from
.m_block
;
1604 to
->m_offset
= from
.m_offset
;
1606 if (have
& HAVE_BODY
) {
1607 to
->m_xlines
= from
.m_lines
;
1608 to
->m_xsize
= from
.m_size
;
1618 int top
/* bot > top */
1621 char o
[LINESIZE
], *cp
;
1626 FILE *queuefp
= NULL
;
1629 snprintf(o
, sizeof o
,
1630 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1631 tag(1), m
[bot
-1].m_uid
, m
[top
-1].m_uid
);
1633 if (check_expunged() == STOP
)
1635 snprintf(o
, sizeof o
,
1636 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1639 IMAP_OUT(o
, MB_COMD
, return STOP
)
1641 ok
= imap_answer(mp
, 1);
1642 if (response_status
!= RESPONSE_OTHER
)
1644 if (response_other
!= MESSAGE_DATA_FETCH
)
1646 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1648 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1650 expected
= atol(&cp
[1]);
1651 if (m
[bot
-1].m_uid
) {
1652 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1654 for (n
= bot
; n
<= top
; n
++)
1655 if ((unsigned long)u
== m
[n
-1].m_uid
)
1658 imap_fetchdata(mp
, NULL
, expected
,
1666 n
= responded_other_number
;
1667 if (n
<= 0 || n
> msgCount
) {
1668 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1673 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1674 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1675 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1676 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1677 &mp
->mb_sock
) > 0) {
1679 fputs(imapbuf
, stderr
);
1680 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1682 for (n
= bot
; n
<= top
; n
++)
1683 if ((unsigned long)u
== m
[n
-1].m_uid
)
1685 if (n
<= top
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1686 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1691 while (mp
->mb_active
& MB_COMD
)
1692 ok
= imap_answer(mp
, 1);
1697 imap_getheaders(int bot
, int top
)
1699 sighandler_type saveint
, savepipe
;
1700 enum okay ok
= STOP
;
1708 verbose
= value("verbose") != NULL
;
1709 if (mb
.mb_type
== MB_CACHE
)
1715 for (i
= bot
; i
< top
; i
++) {
1716 if (message
[i
-1].m_have
& HAVE_HEADER
||
1717 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1723 for (i
= top
; i
> bot
; i
--) {
1724 if (message
[i
-1].m_have
& HAVE_HEADER
||
1725 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1734 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1735 safe_signal(SIGINT
, maincatch
);
1736 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1737 if (sigsetjmp(imapjmp
, 1) == 0) {
1738 if (savepipe
!= SIG_IGN
)
1739 safe_signal(SIGPIPE
, imapcatch
);
1740 for (i
= bot
; i
<= top
; i
+= chunk
) {
1741 ok
= imap_fetchheaders(&mb
, message
, i
,
1742 i
+chunk
-1 < top
? i
+chunk
-1 : top
);
1747 safe_signal(SIGINT
, saveint
);
1748 safe_signal(SIGPIPE
, savepipe
);
1753 imap_exit(struct mailbox
*mp
)
1756 FILE *queuefp
= NULL
;
1758 verbose
= value("verbose") != NULL
;
1759 mp
->mb_active
|= MB_BYE
;
1760 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1761 IMAP_OUT(o
, MB_COMD
, return STOP
)
1767 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1769 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1770 if (mp
->mb_type
== MB_IMAP
)
1776 imap_close(struct mailbox
*mp
)
1779 FILE *queuefp
= NULL
;
1781 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1782 IMAP_OUT(o
, MB_COMD
, return STOP
)
1788 imap_update(struct mailbox
*mp
)
1790 FILE *readstat
= NULL
;
1792 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1794 verbose
= value("verbose") != NULL
;
1795 if (Tflag
!= NULL
) {
1796 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
1799 if (!edit
&& mp
->mb_perm
!= 0) {
1801 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1802 if (m
->m_flag
& MBOX
)
1806 if (makembox() == STOP
)
1809 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1810 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
1813 if ((id
= hfield("message-id", m
)) != NULL
||
1814 (id
= hfield("article-id", m
)) != NULL
)
1815 fprintf(readstat
, "%s\n", id
);
1817 if (mp
->mb_perm
== 0) {
1820 dodel
= m
->m_flag
& MDELETED
;
1822 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1823 (m
->m_flag
&MTOUCH
) == 0);
1826 * Fetch the result after around each 800 STORE commands
1827 * sent (approx. 32k data sent). Otherwise, servers will
1828 * try to flush the return queue at some point, leading
1829 * to a deadlock if we are still writing commands but not
1830 * reading their results.
1832 needstat
= stored
> 0 && stored
% 800 == 0;
1834 * Even if this message has been deleted, continue
1835 * to set further flags. This is necessary to support
1836 * Gmail semantics, where "delete" actually means
1837 * "archive", and the flags are applied to the copy
1840 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1841 imap_store(mp
, m
, m
-message
+1,
1842 '+', "\\Seen", needstat
);
1845 if (m
->m_flag
& MFLAG
) {
1846 imap_store(mp
, m
, m
-message
+1,
1847 '+', "\\Flagged", needstat
);
1850 if (m
->m_flag
& MUNFLAG
) {
1851 imap_store(mp
, m
, m
-message
+1,
1852 '-', "\\Flagged", needstat
);
1855 if (m
->m_flag
& MANSWER
) {
1856 imap_store(mp
, m
, m
-message
+1,
1857 '+', "\\Answered", needstat
);
1860 if (m
->m_flag
& MUNANSWER
) {
1861 imap_store(mp
, m
, m
-message
+1,
1862 '-', "\\Answered", needstat
);
1865 if (m
->m_flag
& MDRAFT
) {
1866 imap_store(mp
, m
, m
-message
+1,
1867 '+', "\\Draft", needstat
);
1870 if (m
->m_flag
& MUNDRAFT
) {
1871 imap_store(mp
, m
, m
-message
+1,
1872 '-', "\\Draft", needstat
);
1876 imap_delete(mp
, m
-message
+1, m
, needstat
);
1879 } else if (mp
->mb_type
!= MB_CACHE
||
1880 (! edit
&& ! (m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))) ||
1881 (m
->m_flag
& (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1882 (MPRESERVE
|MTOUCH
) ||
1883 (edit
&& ! (m
->m_flag
& MDELETED
)))
1885 if (m
->m_flag
& MNEW
) {
1887 m
->m_flag
|= MSTATUS
;
1890 bypass
: if (readstat
!= NULL
)
1894 for (m
= &message
[0]; m
< &message
[msgCount
]; m
++)
1895 if (!(m
->m_flag
&MUNLINKED
) &&
1896 m
->m_flag
&(MBOXED
|MDELETED
|MSAVED
|MSTATUS
|
1897 MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
1902 if ((gotcha
|| modflags
) && edit
) {
1903 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
1904 printf(value("bsdcompat") || value("bsdmsgs") ?
1905 catgets(catd
, CATSET
, 170, "complete\n") :
1906 catgets(catd
, CATSET
, 212, "updated.\n"));
1907 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1909 printf(catgets(catd
, CATSET
, 155,
1910 "Held 1 message in %s\n"), mailname
);
1912 printf(catgets(catd
, CATSET
, 156,
1913 "Held %d messages in %s\n"), held
, mailname
);
1922 sighandler_type saveint
;
1923 sighandler_type savepipe
;
1925 verbose
= value("verbose") != NULL
;
1926 if (mb
.mb_type
== MB_CACHE
) {
1930 if (mb
.mb_sock
.s_fd
< 0) {
1931 fprintf(stderr
, "IMAP connection closed.\n");
1935 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1936 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1937 if (sigsetjmp(imapjmp
, 1)) {
1938 safe_signal(SIGINT
, saveint
);
1939 safe_signal(SIGPIPE
, saveint
);
1943 if (saveint
!= SIG_IGN
)
1944 safe_signal(SIGINT
, imapcatch
);
1945 if (savepipe
!= SIG_IGN
)
1946 safe_signal(SIGPIPE
, imapcatch
);
1948 if (!same_imap_account
) {
1950 sclose(&mb
.mb_sock
);
1952 safe_signal(SIGINT
, saveint
);
1953 safe_signal(SIGPIPE
, savepipe
);
1958 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1959 int c
, const char *sp
, int needstat
)
1962 FILE *queuefp
= NULL
;
1964 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1967 snprintf(o
, sizeof o
,
1968 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1969 tag(1), m
->m_uid
, c
, sp
);
1971 if (check_expunged() == STOP
)
1973 snprintf(o
, sizeof o
,
1974 "%s STORE %u %cFLAGS (%s)\r\n",
1977 IMAP_OUT(o
, MB_COMD
, return STOP
)
1981 mb
.mb_active
&= ~MB_COMD
;
1982 if (queuefp
!= NULL
)
1988 imap_undelete(struct message
*m
, int n
)
1990 return imap_unstore(m
, n
, "\\Deleted");
1994 imap_unread(struct message
*m
, int n
)
1996 return imap_unstore(m
, n
, "\\Seen");
2000 imap_unstore(struct message
*m
, int n
, const char *flag
)
2002 sighandler_type saveint
, savepipe
;
2003 enum okay ok
= STOP
;
2008 verbose
= value("verbose") != NULL
;
2010 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2011 safe_signal(SIGINT
, maincatch
);
2012 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2013 if (sigsetjmp(imapjmp
, 1) == 0) {
2014 if (savepipe
!= SIG_IGN
)
2015 safe_signal(SIGPIPE
, imapcatch
);
2016 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2018 safe_signal(SIGINT
, saveint
);
2019 safe_signal(SIGPIPE
, savepipe
);
2034 snprintf(ts
, sizeof ts
, "T%lu", n
);
2041 sighandler_type saveint
, savepipe
;
2043 enum okay ok
= STOP
;
2044 struct mailbox
*mp
= &mb
;
2045 FILE *queuefp
= NULL
;
2050 verbose
= value("verbose") != NULL
;
2051 if (mp
->mb_type
!= MB_IMAP
) {
2052 printf("Not operating on an IMAP mailbox.\n");
2056 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2057 safe_signal(SIGINT
, maincatch
);
2058 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2059 if (sigsetjmp(imapjmp
, 1) == 0) {
2060 if (savepipe
!= SIG_IGN
)
2061 safe_signal(SIGPIPE
, imapcatch
);
2062 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2063 IMAP_OUT(o
, MB_COMD
, goto out
)
2064 while (mp
->mb_active
& MB_COMD
) {
2065 ok
= imap_answer(mp
, 0);
2066 fputs(responded_text
, stdout
);
2069 out
: safe_signal(SIGINT
, saveint
);
2070 safe_signal(SIGPIPE
, savepipe
);
2078 imap_newmail(int autoinc
)
2080 if (autoinc
&& had_exists
< 0 && had_expunge
< 0) {
2081 verbose
= value("verbose") != NULL
;
2086 if (had_exists
== msgCount
&& had_expunge
< 0)
2088 * Some servers always respond with EXISTS to NOOP. If
2089 * the mailbox has been changed but the number of messages
2090 * has not, an EXPUNGE must also had been sent; otherwise,
2091 * nothing has changed.
2094 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2098 imap_putflags(int f
)
2103 bp
= buf
= salloc(100);
2104 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2109 for (cp
= "\\Seen"; *cp
; cp
++)
2115 for (cp
= "\\Flagged"; *cp
; cp
++)
2118 if (f
& MANSWERED
) {
2121 for (cp
= "\\Answered"; *cp
; cp
++)
2127 for (cp
= "\\Draft"; *cp
; cp
++)
2138 imap_getflags(const char *cp
, char **xp
, enum mflag
*f
)
2140 while (*cp
!= ')') {
2142 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2144 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2146 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2148 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2150 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2152 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2162 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2163 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2167 size_t bufsize
, buflen
, count
;
2168 enum okay ok
= STOP
;
2169 long size
, lines
, ysize
;
2171 FILE *queuefp
= NULL
;
2173 if (mp
->mb_type
== MB_CACHE
) {
2174 queuefp
= cache_queue(mp
);
2175 if (queuefp
== NULL
)
2179 buf
= smalloc(bufsize
= LINESIZE
);
2181 again
: size
= xsize
;
2183 fseek(fp
, off1
, SEEK_SET
);
2184 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2185 tag(1), imap_quotestr(name
),
2186 imap_putflags(flag
),
2187 imap_make_date_time(t
),
2189 IMAP_OUT(o
, MB_COMD
, goto out
)
2190 while (mp
->mb_active
& MB_COMD
) {
2191 ok
= imap_answer(mp
, twice
);
2192 if (response_type
== RESPONSE_CONT
)
2195 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2203 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2206 buf
[buflen
-1] = '\r';
2208 if (mp
->mb_type
!= MB_CACHE
)
2209 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2211 fwrite(buf
, 1, buflen
+1, queuefp
);
2214 if (mp
->mb_type
!= MB_CACHE
)
2215 swrite(&mp
->mb_sock
, "\r\n");
2217 fputs("\r\n", queuefp
);
2218 while (mp
->mb_active
& MB_COMD
) {
2219 ok
= imap_answer(mp
, 0);
2220 if (response_status
== RESPONSE_NO
/*&&
2221 ascncasecmp(responded_text,
2222 "[TRYCREATE] ", 12) == 0*/) {
2223 trycreate
: if (twice
++) {
2227 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2229 imap_quotestr(name
));
2230 IMAP_OUT(o
, MB_COMD
, goto out
);
2231 while (mp
->mb_active
& MB_COMD
)
2232 ok
= imap_answer(mp
, 1);
2235 imap_created_mailbox
++;
2237 } else if (ok
!= OKAY
)
2238 fprintf(stderr
, "IMAP error: %s", responded_text
);
2239 else if (response_status
== RESPONSE_OK
&&
2240 mp
->mb_flags
& MB_UIDPLUS
)
2241 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2244 out
: if (queuefp
!= NULL
)
2251 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2253 char *buf
, *bp
, *lp
;
2254 size_t bufsize
, buflen
, count
;
2255 off_t off1
= -1, offs
;
2257 int flag
= MNEW
|MNEWEST
;
2262 buf
= smalloc(bufsize
= LINESIZE
);
2268 bp
= fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2269 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2270 if (off1
!= (off_t
)-1) {
2271 ok
=imap_append1(mp
, name
, fp
, off1
,
2275 fseek(fp
, offs
+buflen
, SEEK_SET
);
2277 off1
= offs
+ buflen
;
2282 tim
= unixtime(buf
);
2286 if (bp
&& buf
[0] == '\n')
2288 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2290 while (whitechar(*lp
&0377))
2293 while (*++lp
!= '\0')
2302 } else if (bp
&& inhead
&&
2303 ascncasecmp(buf
, "x-status", 8) == 0) {
2305 while (whitechar(*lp
&0377))
2308 while (*++lp
!= '\0')
2321 } while (bp
!= NULL
);
2327 imap_append(const char *xserver
, FILE *fp
)
2329 sighandler_type saveint
, savepipe
;
2330 char *server
, *uhp
, *mbx
, *user
;
2331 const char *sp
, *cp
, *pass
;
2333 enum okay ok
= STOP
;
2338 verbose
= value("verbose") != NULL
;
2339 server
= savestr((char *)xserver
);
2340 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
2342 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2343 safe_signal(SIGINT
, maincatch
);
2344 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2345 if (sigsetjmp(imapjmp
, 1))
2347 if (savepipe
!= SIG_IGN
)
2348 safe_signal(SIGPIPE
, imapcatch
);
2349 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2350 mb
.mb_imap_account
&&
2351 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2352 ok
= imap_append0(&mb
, mbx
, fp
);
2357 memset(&mx
, 0, sizeof mx
);
2358 if (disconnected(server
) == 0) {
2359 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2360 use_ssl
? "imaps" : "imap",
2363 mx
.mb_sock
.s_desc
= "IMAP";
2364 mx
.mb_type
= MB_IMAP
;
2365 mx
.mb_imap_account
= (char *)protbase(server
);
2366 mx
.mb_imap_mailbox
= mbx
;
2367 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2368 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2369 sclose(&mx
.mb_sock
);
2372 ok
= imap_append0(&mx
, mbx
, fp
);
2374 sclose(&mx
.mb_sock
);
2376 mx
.mb_imap_account
= (char *)protbase(server
);
2377 mx
.mb_imap_mailbox
= mbx
;
2378 mx
.mb_type
= MB_CACHE
;
2379 ok
= imap_append0(&mx
, mbx
, fp
);
2383 out
: safe_signal(SIGINT
, saveint
);
2384 safe_signal(SIGPIPE
, savepipe
);
2392 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2393 struct list_item
**lend
, int level
)
2396 enum okay ok
= STOP
;
2399 FILE *queuefp
= NULL
;
2400 struct list_item
*lp
;
2402 *list
= *lend
= NULL
;
2403 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2404 tag(1), imap_quotestr(base
));
2405 IMAP_OUT(o
, MB_COMD
, return STOP
);
2406 while (mp
->mb_active
& MB_COMD
) {
2407 ok
= imap_answer(mp
, 1);
2408 if (response_status
== RESPONSE_OTHER
&&
2409 response_other
== MAILBOX_DATA_LIST
&&
2410 imap_parse_list() == OKAY
) {
2411 cp
= imap_unquotestr(list_name
);
2412 lp
= csalloc(1, sizeof *lp
);
2414 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2416 lp
->l_base
= *cp
? cp
: savestr(base
);
2417 lp
->l_attr
= list_attributes
;
2418 lp
->l_level
= level
+1;
2419 lp
->l_delim
= list_hierarchy_delimiter
;
2420 if (*list
&& *lend
) {
2421 (*lend
)->l_next
= lp
;
2431 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2433 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2439 verbose
= value("verbose") != NULL
;
2440 depth
= (cp
= value("imap-list-depth")) != NULL
? atoi(cp
) : 2;
2441 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2443 if (list
== NULL
|| lend
== NULL
)
2445 for (lp
= list
; lp
; lp
= lp
->l_next
)
2446 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2447 lp
->l_level
< depth
&&
2448 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2449 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2450 strcpy(cp
, lp
->l_name
);
2451 cp
[n
] = lp
->l_delim
;
2453 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2455 lp
->l_has_children
= 1;
2456 if (strcmp(cp
, lx
->l_name
) == 0)
2464 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2467 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2471 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2472 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2473 else if (lp
->l_has_children
== 0)
2474 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2475 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2481 imap_folders(const char *name
, int strip
)
2483 sighandler_type saveint
, savepipe
;
2484 const char *fold
, *cp
, *sp
;
2487 int columnize
= is_a_tty
[1];
2493 cp
= protbase(name
);
2494 sp
= mb
.mb_imap_account
;
2495 if (strcmp(cp
, sp
)) {
2496 fprintf(stderr
, "Cannot list folders on other than the "
2497 "current IMAP account,\n\"%s\". "
2498 "Try \"folders @\".\n", sp
);
2501 fold
= protfile(name
);
2503 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2512 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2513 safe_signal(SIGINT
, maincatch
);
2514 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2515 if (sigsetjmp(imapjmp
, 1))
2517 if (savepipe
!= SIG_IGN
)
2518 safe_signal(SIGPIPE
, imapcatch
);
2519 if (mb
.mb_type
== MB_CACHE
)
2520 cache_list(&mb
, fold
, strip
, fp
);
2522 imap_list(&mb
, fold
, strip
, fp
);
2535 fprintf(stderr
, "Folder not found.\n");
2538 safe_signal(SIGINT
, saveint
);
2539 safe_signal(SIGPIPE
, savepipe
);
2547 char o
[LINESIZE
], *tempfn
;
2549 long n
= 0, mx
= 0, columns
, width
;
2552 if ((out
= Ftemp(&tempfn
, "Ro", "w+", 0600, 1)) == NULL
) {
2558 while ((c
= getc(fp
)) != EOF
) {
2568 if (mx
< width
/ 2) {
2569 columns
= width
/ (mx
+2);
2570 snprintf(o
, sizeof o
,
2571 "sort | pr -%lu -w%lu -t",
2574 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2575 run_command(SHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2581 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2585 enum okay ok
= STOP
;
2588 FILE *queuefp
= NULL
;
2590 if (mp
->mb_type
== MB_CACHE
) {
2591 if ((queuefp
= cache_queue(mp
)) == NULL
)
2595 qname
= imap_quotestr(name
= protfile(name
));
2597 * Since it is not possible to set flags on the copy, recently
2598 * set flags must be set on the original to include it in the copy.
2600 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
))
2601 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2602 if (m
->m_flag
&MFLAG
)
2603 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2604 if (m
->m_flag
&MUNFLAG
)
2605 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2606 if (m
->m_flag
&MANSWER
)
2607 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2608 if (m
->m_flag
&MUNANSWER
)
2609 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2610 if (m
->m_flag
&MDRAFT
)
2611 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2612 if (m
->m_flag
&MUNDRAFT
)
2613 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2614 again
: if (m
->m_uid
)
2615 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n",
2616 tag(1), m
->m_uid
, qname
);
2618 if (check_expunged() == STOP
)
2620 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n",
2623 IMAP_OUT(o
, MB_COMD
, goto out
)
2624 while (mp
->mb_active
& MB_COMD
)
2625 ok
= imap_answer(mp
, twice
);
2626 if (mp
->mb_type
== MB_IMAP
&&
2627 mp
->mb_flags
& MB_UIDPLUS
&&
2628 response_status
== RESPONSE_OK
)
2629 imap_copyuid(mp
, m
, name
);
2630 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2631 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2632 IMAP_OUT(o
, MB_COMD
, goto out
)
2633 while (mp
->mb_active
& MB_COMD
)
2634 ok
= imap_answer(mp
, 1);
2636 imap_created_mailbox
++;
2640 if (queuefp
!= NULL
)
2643 * ... and reset the flag to its initial value so that
2644 * the 'exit' command still leaves the message unread.
2646 out
: if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
2647 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2650 if (m
->m_flag
&MFLAG
) {
2651 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2654 if (m
->m_flag
&MUNFLAG
) {
2655 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2658 if (m
->m_flag
&MANSWER
) {
2659 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2662 if (m
->m_flag
&MUNANSWER
) {
2663 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2666 if (m
->m_flag
&MDRAFT
) {
2667 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2670 if (m
->m_flag
&MUNDRAFT
) {
2671 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2675 mp
->mb_active
|= MB_COMD
;
2682 imap_copy(struct message
*m
, int n
, const char *name
)
2684 sighandler_type saveint
, savepipe
;
2685 enum okay ok
= STOP
;
2690 verbose
= value("verbose") != NULL
;
2692 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2693 safe_signal(SIGINT
, maincatch
);
2694 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2695 if (sigsetjmp(imapjmp
, 1) == 0) {
2696 if (savepipe
!= SIG_IGN
)
2697 safe_signal(SIGPIPE
, imapcatch
);
2698 ok
= imap_copy1(&mb
, m
, n
, name
);
2700 safe_signal(SIGINT
, saveint
);
2701 safe_signal(SIGPIPE
, savepipe
);
2709 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2710 unsigned long *olduid
, unsigned long *newuid
)
2714 *uidvalidity
= strtoul(cp
, &xp
, 10);
2715 *olduid
= strtoul(xp
, &yp
, 10);
2716 *newuid
= strtoul(yp
, &zp
, 10);
2717 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2718 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2722 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2727 *uidvalidity
= strtoul(cp
, &xp
, 10);
2728 *uid
= strtoul(xp
, &yp
, 10);
2729 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2730 yp
> xp
&& *yp
== ']';
2734 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2737 unsigned long uidvalidity
, olduid
, newuid
;
2741 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2742 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2743 &olduid
, &newuid
) == STOP
)
2746 xmb
.mb_cache_directory
= NULL
;
2747 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2748 xmb
.mb_uidvalidity
= uidvalidity
;
2751 memset(&xm
, 0, sizeof xm
);
2753 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2755 getcache(mp
, &xm
, NEED_HEADER
);
2756 getcache(mp
, &xm
, NEED_BODY
);
2758 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2759 getcache(mp
, m
, NEED_HEADER
);
2760 if ((m
->m_flag
& HAVE_BODY
) == 0)
2761 getcache(mp
, m
, NEED_BODY
);
2765 xm
.m_flag
&= ~MFULLYCACHED
;
2766 putcache(&xmb
, &xm
);
2771 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2772 long xsize
, long size
, long lines
, int flag
, const char *name
)
2775 unsigned long uidvalidity
, uid
;
2779 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2780 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2784 xmb
.mb_cache_directory
= NULL
;
2785 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2786 xmb
.mb_uidvalidity
= uidvalidity
;
2787 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2789 memset(&xm
, 0, sizeof xm
);
2790 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2792 xm
.m_block
= mailx_blockof(off1
);
2793 xm
.m_offset
= mailx_offsetof(off1
);
2796 xm
.m_lines
= xm
.m_xlines
= lines
;
2798 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2799 putcache(&xmb
, &xm
);
2804 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2808 long size
, xsize
, ysize
, lines
;
2809 enum mflag flag
= MNEW
;
2810 char *name
, *buf
, *bp
, *cp
, *tempCopy
;
2811 size_t bufsize
, buflen
, count
;
2812 enum okay ok
= STOP
;
2814 buf
= smalloc(bufsize
= LINESIZE
);
2817 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2819 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
2822 if ((cp
= strrchr(bp
, '{')) == NULL
)
2824 xsize
= atol(&cp
[1]) + 2;
2825 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
2830 imap_getflags(cp
, &cp
, &flag
);
2831 while (*++cp
== ' ');
2833 t
= imap_read_date_time(cp
);
2834 if ((tp
= Ftemp(&tempCopy
, "Rc", "w+", 0600, 1)) == NULL
)
2841 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2844 buf
[--buflen
] = '\0';
2845 buf
[buflen
-1] = '\n';
2846 fwrite(buf
, 1, buflen
, tp
);
2852 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
2853 imap_unquotestr(name
));
2862 imap_search2(struct mailbox
*mp
, struct message
*m
, int count
,
2863 const char *spec
, int f
)
2867 FILE *queuefp
= NULL
;
2868 enum okay ok
= STOP
;
2875 for (cp
= spec
; *cp
; cp
++)
2880 if (asccasecmp(cp
, "utf-8")) {
2882 char *sp
, *nsp
, *nspec
;
2884 if ((it
= iconv_open_ft("utf-8", cp
)) != (iconv_t
)-1) {
2885 sz
= strlen(spec
) + 1;
2886 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2888 if (iconv_ft(it
, &sp
, &sz
, &nsp
, &nsz
, 0)
2889 != (size_t)-1 && sz
== 0) {
2896 #endif /* HAVE_ICONV */
2897 cp
= imap_quotestr(cp
);
2898 cs
= salloc(n
= strlen(cp
) + 10);
2899 snprintf(cs
, n
, "CHARSET %s ", cp
);
2902 o
= ac_alloc(osize
= strlen(spec
) + 60);
2903 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2904 IMAP_OUT(o
, MB_COMD
, goto out
)
2905 while (mp
->mb_active
& MB_COMD
) {
2906 ok
= imap_answer(mp
, 0);
2907 if (response_status
== RESPONSE_OTHER
&&
2908 response_other
== MAILBOX_DATA_SEARCH
) {
2909 xp
= responded_other_text
;
2910 while (*xp
&& *xp
!= '\r') {
2911 n
= strtoul(xp
, &xp
, 10);
2912 for (i
= 0; i
< count
; i
++)
2913 if (m
[i
].m_uid
== n
&&
2914 (m
[i
].m_flag
&MHIDDEN
)
2917 (m
[i
].m_flag
&MDELETED
)
2928 imap_search1(const char *spec
, int f
)
2930 sighandler_type saveint
, savepipe
;
2931 enum okay ok
= STOP
;
2936 if (mb
.mb_type
!= MB_IMAP
)
2938 verbose
= value("verbose") != NULL
;
2940 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2941 safe_signal(SIGINT
, maincatch
);
2942 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2943 if (sigsetjmp(imapjmp
, 1) == 0) {
2944 if (savepipe
!= SIG_IGN
)
2945 safe_signal(SIGPIPE
, imapcatch
);
2946 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2948 safe_signal(SIGINT
, saveint
);
2949 safe_signal(SIGPIPE
, savepipe
);
2957 imap_thisaccount(const char *cp
)
2959 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2961 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2962 mb
.mb_imap_account
== NULL
)
2964 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2968 imap_remove(const char *name
)
2970 sighandler_type saveint
, savepipe
;
2971 enum okay ok
= STOP
;
2976 verbose
= value("verbose") != NULL
;
2977 if (mb
.mb_type
!= MB_IMAP
) {
2978 fprintf(stderr
, "Refusing to remove \"%s\" "
2979 "in disconnected mode.\n", name
);
2982 if (!imap_thisaccount(name
)) {
2983 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2984 "server: \"%s\" not removed.\n", name
);
2988 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2989 safe_signal(SIGINT
, maincatch
);
2990 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2991 if (sigsetjmp(imapjmp
, 1) == 0) {
2992 if (savepipe
!= SIG_IGN
)
2993 safe_signal(SIGPIPE
, imapcatch
);
2994 ok
= imap_remove1(&mb
, protfile(name
));
2996 safe_signal(SIGINT
, saveint
);
2997 safe_signal(SIGPIPE
, savepipe
);
3000 ok
= cache_remove(name
);
3007 imap_remove1(struct mailbox
*mp
, const char *name
)
3009 FILE *queuefp
= NULL
;
3012 enum okay ok
= STOP
;
3014 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3015 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3016 IMAP_OUT(o
, MB_COMD
, goto out
)
3017 while (mp
->mb_active
& MB_COMD
)
3018 ok
= imap_answer(mp
, 1);
3024 imap_rename(const char *old
, const char *new)
3026 sighandler_type saveint
, savepipe
;
3027 enum okay ok
= STOP
;
3032 verbose
= value("verbose") != NULL
;
3033 if (mb
.mb_type
!= MB_IMAP
) {
3034 fprintf(stderr
, "Refusing to rename mailboxes "
3035 "in disconnected mode.\n");
3038 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3039 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3040 "server: \"%s\" not renamed to \"%s\".\n",
3045 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3046 safe_signal(SIGINT
, maincatch
);
3047 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3048 if (sigsetjmp(imapjmp
, 1) == 0) {
3049 if (savepipe
!= SIG_IGN
)
3050 safe_signal(SIGPIPE
, imapcatch
);
3051 ok
= imap_rename1(&mb
, protfile(old
), protfile(new));
3053 safe_signal(SIGINT
, saveint
);
3054 safe_signal(SIGPIPE
, savepipe
);
3057 ok
= cache_rename(old
, new);
3064 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3066 FILE *queuefp
= NULL
;
3069 enum okay ok
= STOP
;
3071 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3072 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3073 imap_quotestr(old
), imap_quotestr(new));
3074 IMAP_OUT(o
, MB_COMD
, goto out
)
3075 while (mp
->mb_active
& MB_COMD
)
3076 ok
= imap_answer(mp
, 1);
3082 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3084 FILE *queuefp
= NULL
;
3085 char o
[LINESIZE
], *newname
;
3086 char *buf
, *bp
, *cp
, iob
[4096];
3087 size_t bufsize
, buflen
, count
;
3088 enum okay ok
= OKAY
, rok
= OKAY
;
3089 long offs
, offs1
, offs2
, octets
;
3090 int twice
, gotcha
= 0;
3092 buf
= smalloc(bufsize
= LINESIZE
);
3095 while (offs1
= ftell(fp
),
3096 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0)
3098 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3103 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3104 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3106 while (digitchar(*cp
&0377))
3112 if ((newname
= imap_strex(cp
, 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_status
== RESPONSE_NO
&& twice
++ == 0)
3119 if (response_status
== RESPONSE_OK
&&
3120 mp
->mb_flags
& MB_UIDPLUS
) {
3121 imap_copyuid(mp
, NULL
,
3122 imap_unquotestr(newname
));
3124 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3125 IMAP_OUT(o
, MB_COMD
, continue)
3126 while (mp
->mb_active
& MB_COMD
)
3127 ok
= imap_answer(mp
, 1);
3130 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3131 if ((cp
= strrchr(bp
, '{')) == NULL
)
3133 octets
= atol(&cp
[1]) + 2;
3134 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3136 IMAP_OUT(o
, MB_COMD
, continue)
3137 while (mp
->mb_active
& MB_COMD
) {
3138 ok
= imap_answer(mp
, twice
);
3139 if (response_type
== RESPONSE_CONT
)
3144 fseek(fp
, offs
, SEEK_SET
);
3149 while (octets
> 0) {
3150 size_t n
= (size_t)octets
> sizeof iob
3151 ? sizeof iob
: (size_t)octets
;
3153 if (n
!= fread(iob
, 1, n
, fp
))
3155 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3157 swrite(&mp
->mb_sock
, "");
3158 while (mp
->mb_active
& MB_COMD
) {
3159 ok
= imap_answer(mp
, 0);
3160 if (response_status
== RESPONSE_NO
&&
3162 fseek(fp
, offs
, SEEK_SET
);
3166 if (response_status
== RESPONSE_OK
&&
3167 mp
->mb_flags
& MB_UIDPLUS
) {
3169 fseek(fp
, offs1
, SEEK_SET
);
3170 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3171 fseek(fp
, offs2
, SEEK_SET
);
3176 fail
: fprintf(stderr
,
3177 "Invalid command in IMAP cache queue: \"%s\"\n",
3183 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3185 IMAP_OUT(o
, MB_COMD
, continue)
3186 while (mp
->mb_active
& MB_COMD
)
3187 ok
= imap_answer(mp
, 1);
3193 ftruncate(fileno(fp
), 0);
3200 imap_strex(const char *cp
, char **xp
)
3207 for (cq
= &cp
[1]; *cq
; cq
++) {
3210 else if (*cq
== '"')
3215 n
= salloc(cq
- cp
+ 2);
3216 memcpy(n
, cp
, cq
- cp
+ 1);
3217 n
[cq
- cp
+ 1] = '\0';
3219 *xp
= (char *)&cq
[1];
3224 check_expunged(void)
3226 if (expunged_messages
> 0) {
3228 "Command not executed - messages have been expunged\n");
3239 int omsgCount
= msgCount
;
3242 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3243 fprintf(stderr
, "Already connected.\n");
3246 unset_allow_undefined
= 1;
3247 unset_internal("disconnected");
3248 cp
= protbase(mailname
);
3249 if (strncmp(cp
, "imap://", 7) == 0)
3251 else if (strncmp(cp
, "imaps://", 8) == 0)
3253 if ((cq
= strchr(cp
, ':')) != NULL
)
3255 unset_internal(savecat("disconnected-", cp
));
3256 unset_allow_undefined
= 0;
3257 if (mb
.mb_type
== MB_CACHE
) {
3258 imap_setfile1(mailname
, 0, edit
, 1);
3259 if (msgCount
> omsgCount
)
3260 newmailinfo(omsgCount
);
3266 cdisconnect(void *vp
)
3270 if (mb
.mb_type
== MB_CACHE
) {
3271 fprintf(stderr
, "Not connected.\n");
3273 } else if (mb
.mb_type
== MB_IMAP
) {
3274 if (cached_uidvalidity(&mb
) == 0) {
3275 fprintf(stderr
, "The current mailbox is not cached.\n");
3281 assign("disconnected", "");
3282 if (mb
.mb_type
== MB_IMAP
) {
3283 sclose(&mb
.mb_sock
);
3284 imap_setfile1(mailname
, 0, edit
, 1);
3291 int *msgvec
= vp
, *ip
;
3294 if (mb
.mb_type
!= MB_IMAP
) {
3295 fprintf(stderr
, "Not connected to an IMAP server.\n");
3298 if (cached_uidvalidity(&mb
) == 0) {
3299 fprintf(stderr
, "The current mailbox is not cached.\n");
3302 for (ip
= msgvec
; *ip
; ip
++) {
3303 mp
= &message
[*ip
-1];
3304 if (!(mp
->m_have
& HAVE_BODY
))
3309 #else /* !USE_IMAP */
3316 fprintf(stderr
, catgets(catd
, CATSET
, 269,
3317 "No IMAP support compiled in.\n"));
3321 imap_setfile(const char *server
, int newmail
, int isedit
)
3331 imap_header(struct message
*mp
)
3339 imap_body(struct message
*mp
)
3347 imap_getheaders(int bot
, int top
)
3370 imap_newmail(int dummy
)
3378 imap_undelete(struct message
*m
, int n
)
3387 imap_unread(struct message
*m
, int n
)
3396 imap_append(const char *server
, FILE *fp
)
3406 imap_folders(const char *name
, int strip
)
3415 imap_remove(const char *name
)
3424 imap_rename(const char *old
, const char *new)
3433 imap_copy(struct message
*m
, int n
, const char *name
)
3444 imap_search1(const char *spec
, int f
)
3452 imap_thisaccount(const char *cp
)
3476 cdisconnect(void *vp
)
3491 #endif /* USE_IMAP */
3494 imap_read_date_time(const char *cp
)
3497 int i
, year
, month
, day
, hour
, minute
, second
;
3502 * "25-Jul-2004 15:33:44 +0200"
3506 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3508 day
= strtol(&cp
[1], NULL
, 10);
3509 for (i
= 0; month_names
[i
]; i
++)
3510 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3512 if (month_names
[i
] == NULL
)
3515 year
= strtol(&cp
[8], NULL
, 10);
3516 hour
= strtol(&cp
[13], NULL
, 10);
3517 minute
= strtol(&cp
[16], NULL
, 10);
3518 second
= strtol(&cp
[19], NULL
, 10);
3519 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3534 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3537 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3545 imap_read_date(const char *cp
)
3548 int year
, month
, day
, i
, tzdiff
;
3554 day
= strtol(cp
, &xp
, 10);
3555 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3557 for (i
= 0; month_names
[i
]; i
++)
3558 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3560 if (month_names
[i
] == NULL
)
3565 year
= strtol(&xp
[4], &yp
, 10);
3566 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3568 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3570 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3572 tzdiff
= t
- mktime(gmtime(&t
));
3573 tmptr
= localtime(&t
);
3574 if (tmptr
->tm_isdst
> 0)
3581 imap_make_date_time(time_t t
)
3585 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3587 tzdiff
= t
- mktime(gmtime(&t
));
3588 tzdiff_hour
= (int)(tzdiff
/ 60);
3589 tzdiff_min
= tzdiff_hour
% 60;
3591 tmptr
= localtime(&t
);
3592 if (tmptr
->tm_isdst
> 0)
3594 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3596 month_names
[tmptr
->tm_mon
],
3597 tmptr
->tm_year
+ 1900,
3607 imap_quotestr(const char *s
)
3611 np
= n
= salloc(2 * strlen(s
) + 3);
3614 if (*s
== '"' || *s
== '\\')
3624 imap_unquotestr(const char *s
)
3630 np
= n
= salloc(strlen(s
) + 1);