2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
8 * Gunnar Ritter. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gunnar Ritter
21 * and his contributors.
22 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 static char sccsid
[] = "@(#)imap.c 1.222 (gritter) 3/13/09";
48 * Mail -- a mail program
50 * IMAP v4r1 client following RFC 2060.
63 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #ifdef HAVE_ARPA_INET_H
67 #include <arpa/inet.h>
68 #endif /* HAVE_ARPA_INET_H */
74 #define IMAP_ANSWER() { \
75 if (mp->mb_type != MB_CACHE) { \
76 enum okay ok = OKAY; \
77 while (mp->mb_active & MB_COMD) \
78 ok = imap_answer(mp, 1); \
83 #define IMAP_OUT(x, y, action) \
85 if (mp->mb_type != MB_CACHE) { \
86 if (imap_finish(mp) == STOP) \
89 fprintf(stderr, ">>> %s", x); \
90 mp->mb_active |= (y); \
91 if (swrite(&mp->mb_sock, x) == STOP) \
94 if (queuefp != NULL) \
99 static struct record
{
100 struct record
*rec_next
;
101 unsigned long rec_count
;
126 static char *responded_tag
;
127 static char *responded_text
;
128 static char *responded_other_text
;
129 static long responded_other_number
;
135 MAILBOX_DATA_MAILBOX
,
140 MESSAGE_DATA_EXPUNGE
,
143 RESPONSE_OTHER_UNKNOWN
146 static enum list_attributes
{
148 LIST_NOINFERIORS
= 001,
154 static int list_hierarchy_delimiter
;
155 static char *list_name
;
158 struct list_item
*l_next
;
161 enum list_attributes l_attr
;
167 static char *imapbuf
;
168 static size_t imapbufsize
;
169 static sigjmp_buf imapjmp
;
170 static sighandler_type savealrm
;
171 static int reset_tio
;
172 static struct termios otio
;
173 static int imapkeepalive
;
174 static long had_exists
= -1;
175 static long had_expunge
= -1;
176 static long expunged_messages
;
177 static volatile int imaplock
;
179 static int same_imap_account
;
181 static void imap_other_get(char *pp
);
182 static void imap_response_get(const char **cp
);
183 static void imap_response_parse(void);
184 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
185 static enum okay
imap_parse_list(void);
186 static enum okay
imap_finish(struct mailbox
*mp
);
187 static void imap_timer_off(void);
188 static void imapcatch(int s
);
189 static void maincatch(int s
);
190 static enum okay
imap_noop1(struct mailbox
*mp
);
191 static void rec_queue(enum rec_type type
, unsigned long count
);
192 static enum okay
rec_dequeue(void);
193 static void rec_rmqueue(void);
194 static void imapalarm(int s
);
195 static int imap_use_starttls(const char *uhp
);
196 static enum okay
imap_preauth(struct mailbox
*mp
, const char *xserver
,
198 static enum okay
imap_capability(struct mailbox
*mp
);
199 static enum okay
imap_auth(struct mailbox
*mp
, const char *uhp
,
200 char *xuser
, const char *pass
);
201 static enum okay
imap_cram_md5(struct mailbox
*mp
,
202 char *xuser
, const char *xpass
);
203 static enum okay
imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
);
205 static enum okay
imap_gss(struct mailbox
*mp
, char *user
);
206 #endif /* USE_GSSAPI */
207 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
208 static void imap_init(struct mailbox
*mp
, int n
);
209 static void imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
,
211 static char *imap_have_password(const char *server
);
212 static void imap_split(char **server
, const char **sp
, int *use_ssl
,
213 const char **cp
, char **uhp
, char **mbx
,
214 const char **pass
, char **user
);
215 static int imap_setfile1(const char *xserver
, int newmail
, int isedit
,
217 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
218 size_t expected
, int need
,
219 const char *head
, size_t headsize
, long headlines
);
220 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
222 const char *head
, size_t headsize
, long headlines
);
223 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
225 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
226 struct message from
, enum havespec have
);
227 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
229 static enum okay
imap_exit(struct mailbox
*mp
);
230 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int
232 static enum okay
imap_close(struct mailbox
*mp
);
233 static enum okay
imap_update(struct mailbox
*mp
);
234 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
,
235 int n
, int c
, const char *sp
, int needstat
);
236 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
237 static const char *tag(int new);
238 static char *imap_putflags(int f
);
239 static void imap_getflags(const char *cp
, char **xp
, enum mflag
*f
);
240 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
241 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
242 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
243 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
244 struct list_item
**list
, struct list_item
**lend
, int level
);
245 static enum okay
imap_list(struct mailbox
*mp
, const char *base
,
246 int strip
, FILE *fp
);
247 static void dopr(FILE *fp
);
248 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
250 static enum okay
imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
251 unsigned long *olduid
, unsigned long *newuid
);
252 static enum okay
imap_appenduid_parse(const char *cp
,
253 unsigned long *uidvalidity
, unsigned long *uid
);
254 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
256 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
257 long off1
, long xsize
, long size
, long lines
,
258 int flag
, const char *name
);
259 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
260 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
,
261 int count
, const char *spec
, int f
);
262 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
263 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
265 static char *imap_strex(const char *cp
, char **xp
);
266 static enum okay
check_expunged(void);
269 imap_other_get(char *pp
)
273 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
275 response_other
= MAILBOX_DATA_FLAGS
;
276 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
278 response_other
= MAILBOX_DATA_LIST
;
279 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
281 response_other
= MAILBOX_DATA_LSUB
;
282 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
284 response_other
= MAILBOX_DATA_MAILBOX
;
285 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
287 response_other
= MAILBOX_DATA_SEARCH
;
288 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
290 response_other
= MAILBOX_DATA_STATUS
;
291 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
293 response_other
= CAPABILITY_DATA
;
295 responded_other_number
= strtol(pp
, &xp
, 10);
298 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
299 response_other
= MAILBOX_DATA_EXISTS
;
300 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
301 response_other
= MAILBOX_DATA_RECENT
;
302 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
303 response_other
= MESSAGE_DATA_EXPUNGE
;
304 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
306 response_other
= MESSAGE_DATA_FETCH
;
308 response_other
= RESPONSE_OTHER_UNKNOWN
;
310 responded_other_text
= pp
;
314 imap_response_get(const char **cp
)
316 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
318 response_status
= RESPONSE_OK
;
319 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
321 response_status
= RESPONSE_NO
;
322 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
324 response_status
= RESPONSE_BAD
;
325 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
327 response_status
= RESPONSE_PREAUTH
;
328 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
330 response_status
= RESPONSE_BYE
;
332 response_status
= RESPONSE_OTHER
;
336 imap_response_parse(void)
338 static char *parsebuf
;
339 static size_t parsebufsize
;
340 const char *ip
= imapbuf
;
343 if (parsebufsize
< imapbufsize
) {
345 parsebuf
= smalloc(parsebufsize
= imapbufsize
);
347 strcpy(parsebuf
, imapbuf
);
351 response_type
= RESPONSE_CONT
;
366 imap_response_get(&ip
);
367 pp
= &parsebuf
[ip
- imapbuf
];
368 switch (response_status
) {
370 response_type
= RESPONSE_FATAL
;
373 response_type
= RESPONSE_DATA
;
377 responded_tag
= parsebuf
;
378 while (*pp
&& *pp
!= ' ')
381 response_type
= RESPONSE_ILLEGAL
;
385 while (*pp
&& *pp
== ' ')
388 response_type
= RESPONSE_ILLEGAL
;
391 ip
= &imapbuf
[pp
- parsebuf
];
392 response_type
= RESPONSE_TAGGED
;
393 imap_response_get(&ip
);
394 pp
= &parsebuf
[ip
- imapbuf
];
397 if (response_type
!= RESPONSE_CONT
&&
398 response_type
!= RESPONSE_ILLEGAL
&&
399 response_status
== RESPONSE_OTHER
)
404 imap_answer(struct mailbox
*mp
, int errprnt
)
409 if (mp
->mb_type
== MB_CACHE
)
411 again
: if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
413 fputs(imapbuf
, stderr
);
414 imap_response_parse();
415 if (response_type
== RESPONSE_ILLEGAL
)
417 if (response_type
== RESPONSE_CONT
)
419 if (response_status
== RESPONSE_OTHER
) {
420 if (response_other
== MAILBOX_DATA_EXISTS
) {
421 had_exists
= responded_other_number
;
422 rec_queue(REC_EXISTS
, responded_other_number
);
425 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
426 rec_queue(REC_EXPUNGE
, responded_other_number
);
434 if (response_type
== RESPONSE_TAGGED
) {
435 if (asccasecmp(responded_tag
, tag(0)) == 0)
440 switch (response_status
) {
441 case RESPONSE_PREAUTH
:
442 mp
->mb_active
&= ~MB_PREAUTH
;
453 fprintf(stderr
, catgets(catd
, CATSET
, 218,
454 "IMAP error: %s"), responded_text
);
456 case RESPONSE_UNKNOWN
: /* does not happen */
459 mp
->mb_active
= MB_NONE
;
467 if (response_status
!= RESPONSE_OTHER
&&
468 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
469 fprintf(stderr
, "IMAP alert: %s", &responded_text
[8]);
471 mp
->mb_active
&= ~MB_COMD
;
474 mp
->mb_active
= MB_NONE
;
480 imap_parse_list(void)
484 cp
= responded_other_text
;
485 list_attributes
= LIST_NONE
;
487 while (*cp
&& *cp
!= ')') {
489 if (ascncasecmp(&cp
[1], "Noinferiors ", 12)
491 list_attributes
|= LIST_NOINFERIORS
;
493 } else if (ascncasecmp(&cp
[1], "Noselect ", 9)
495 list_attributes
|= LIST_NOSELECT
;
497 } else if (ascncasecmp(&cp
[1], "Marked ", 7)
499 list_attributes
|= LIST_MARKED
;
501 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9)
503 list_attributes
|= LIST_UNMARKED
;
514 list_hierarchy_delimiter
= EOF
;
518 list_hierarchy_delimiter
= *cp
++ & 0377;
519 if (cp
[0] != '"' || cp
[1] != ' ')
522 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' &&
524 list_hierarchy_delimiter
= EOF
;
530 while (*cp
&& *cp
!= '\r')
537 imap_finish(struct mailbox
*mp
)
539 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
547 if (imapkeepalive
> 0) {
549 safe_signal(SIGALRM
, savealrm
);
557 tcsetattr(0, TCSADRAIN
, &otio
);
560 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
561 siglongjmp(imapjmp
, 1);
564 fprintf(stderr
, "Received SIGPIPE during IMAP operation\n");
572 if (interrupts
++ == 0) {
573 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
580 imap_noop1(struct mailbox
*mp
)
583 FILE *queuefp
= NULL
;
585 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
586 IMAP_OUT(o
, MB_COMD
, return STOP
)
594 sighandler_type saveint
, savepipe
;
600 if (mb
.mb_type
!= MB_IMAP
)
602 verbose
= value("verbose") != NULL
;
604 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
605 safe_signal(SIGINT
, maincatch
);
606 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
607 if (sigsetjmp(imapjmp
, 1) == 0) {
608 if (savepipe
!= SIG_IGN
)
609 safe_signal(SIGPIPE
, imapcatch
);
610 ok
= imap_noop1(&mb
);
612 safe_signal(SIGINT
, saveint
);
613 safe_signal(SIGPIPE
, savepipe
);
621 rec_queue(enum rec_type type
, unsigned long count
)
625 rp
= scalloc(1, sizeof *rp
);
627 rp
->rec_count
= count
;
628 if (record
&& recend
) {
629 recend
->rec_next
= rp
;
632 record
= recend
= rp
;
638 struct message
*omessage
;
640 struct record
*rp
= record
, *rq
= NULL
;
641 unsigned long exists
= 0;
647 message
= smalloc((msgCount
+1) * sizeof *message
);
649 memcpy(message
, omessage
, msgCount
* sizeof *message
);
650 memset(&message
[msgCount
], 0, sizeof *message
);
652 switch (rp
->rec_type
) {
654 exists
= rp
->rec_count
;
657 if (rp
->rec_count
== 0) {
661 if (rp
->rec_count
> msgCount
) {
662 if (exists
== 0 || rp
->rec_count
> exists
--)
668 delcache(&mb
, &message
[rp
->rec_count
-1]);
669 memmove(&message
[rp
->rec_count
-1],
670 &message
[rp
->rec_count
],
671 (msgCount
- rp
->rec_count
+ 1) *
675 * If the message was part of a collapsed thread,
676 * the m_collapsed field of one of its ancestors
677 * should be incremented. It seems hardly possible
678 * to do this with the current message structure,
679 * though. The result is that a '+' may be shown
680 * in the header summary even if no collapsed
690 record
= recend
= NULL
;
691 if (ok
== OKAY
&& exists
> msgCount
) {
692 message
= srealloc(message
,
693 (exists
+ 1) * sizeof *message
);
694 memset(&message
[msgCount
], 0,
695 (exists
- msgCount
+ 1) * sizeof *message
);
696 for (i
= msgCount
; i
< exists
; i
++)
698 imap_flags(&mb
, msgCount
+1, exists
);
711 struct record
*rp
, *rq
= NULL
;
713 for (rp
= record
; rp
; rp
= rp
->rec_next
) {
718 record
= recend
= NULL
;
725 sighandler_type saveint
;
726 sighandler_type savepipe
;
728 if (imaplock
++ == 0) {
729 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
730 safe_signal(SIGINT
, maincatch
);
731 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
732 if (sigsetjmp(imapjmp
, 1)) {
733 safe_signal(SIGINT
, saveint
);
734 safe_signal(SIGPIPE
, savepipe
);
737 if (savepipe
!= SIG_IGN
)
738 safe_signal(SIGPIPE
, imapcatch
);
739 if (imap_noop1(&mb
) != OKAY
) {
740 safe_signal(SIGINT
, saveint
);
741 safe_signal(SIGPIPE
, savepipe
);
744 safe_signal(SIGINT
, saveint
);
745 safe_signal(SIGPIPE
, savepipe
);
747 brk
: alarm(imapkeepalive
);
752 imap_use_starttls(const char *uhp
)
756 if (value("imap-use-starttls"))
758 var
= savecat("imap-use-starttls-", uhp
);
759 return value(var
) != NULL
;
763 imap_preauth(struct mailbox
*mp
, const char *xserver
, const char *uhp
)
767 mp
->mb_active
|= MB_PREAUTH
;
769 if ((cp
= strchr(xserver
, ':')) != NULL
) {
770 server
= salloc(cp
- xserver
+ 1);
771 memcpy(server
, xserver
, cp
- xserver
);
772 server
[cp
- xserver
] = '\0';
774 server
= (char *)xserver
;
776 if (mp
->mb_sock
.s_use_ssl
== 0 && imap_use_starttls(uhp
)) {
777 FILE *queuefp
= NULL
;
780 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
781 IMAP_OUT(o
, MB_COMD
, return STOP
);
783 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
787 if (imap_use_starttls(uhp
)) {
788 fprintf(stderr
, "No SSL support compiled in.\n");
791 #endif /* !USE_SSL */
797 imap_capability(struct mailbox
*mp
)
800 FILE *queuefp
= NULL
;
804 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
805 IMAP_OUT(o
, MB_COMD
, return STOP
)
806 while (mp
->mb_active
& MB_COMD
) {
807 ok
= imap_answer(mp
, 0);
808 if (response_status
== RESPONSE_OTHER
&&
809 response_other
== CAPABILITY_DATA
) {
810 cp
= responded_other_text
;
812 while (spacechar(*cp
&0377))
814 if (strncmp(cp
, "UIDPLUS", 7) == 0 &&
815 spacechar(cp
[7]&0377))
817 mp
->mb_flags
|= MB_UIDPLUS
;
818 while (*cp
&& !spacechar(*cp
&0377))
827 imap_auth(struct mailbox
*mp
, const char *uhp
, char *xuser
, const char *pass
)
832 if (!(mp
->mb_active
& MB_PREAUTH
))
834 if ((auth
= value("imap-auth")) == NULL
) {
835 var
= ac_alloc(strlen(uhp
) + 11);
836 strcpy(var
, "imap-auth-");
837 strcpy(&var
[10], uhp
);
841 if (auth
== NULL
|| strcmp(auth
, "login") == 0)
842 return imap_login(mp
, xuser
, pass
);
843 if (strcmp(auth
, "cram-md5") == 0)
844 return imap_cram_md5(mp
, xuser
, pass
);
845 if (strcmp(auth
, "gssapi") == 0) {
847 return imap_gss(mp
, xuser
);
848 #else /* !USE_GSSAPI */
849 fprintf(stderr
, "No GSSAPI support compiled in.\n");
851 #endif /* !USE_GSSAPI */
853 fprintf(stderr
, "Unknown IMAP authentication method: \"%s\"\n", auth
);
858 * Implementation of RFC 2194.
861 imap_cram_md5(struct mailbox
*mp
, char *xuser
, const char *xpass
)
864 const char *user
, *pass
;
866 FILE *queuefp
= NULL
;
869 retry
: if (xuser
== NULL
) {
870 if ((user
= getuser()) == NULL
)
875 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
879 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
880 IMAP_OUT(o
, 0, return STOP
)
882 if (response_type
!= RESPONSE_CONT
)
884 cp
= cram_md5_string(user
, pass
, responded_text
);
885 IMAP_OUT(cp
, MB_COMD
, return STOP
)
886 while (mp
->mb_active
& MB_COMD
)
887 ok
= imap_answer(mp
, 1);
896 imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
)
899 const char *user
, *pass
;
900 FILE *queuefp
= NULL
;
903 retry
: if (xuser
== NULL
) {
904 if ((user
= getuser()) == NULL
)
909 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
913 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
914 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
915 IMAP_OUT(o
, MB_COMD
, return STOP
)
916 while (mp
->mb_active
& MB_COMD
)
917 ok
= imap_answer(mp
, 1);
926 #include "imap_gssapi.c"
927 #endif /* USE_GSSAPI */
930 imap_select(struct mailbox
*mp
, off_t
*size
, int *count
, const char *mbx
)
935 FILE *queuefp
= NULL
;
937 mp
->mb_uidvalidity
= 0;
938 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
939 IMAP_OUT(o
, MB_COMD
, return STOP
)
940 while (mp
->mb_active
& MB_COMD
) {
941 ok
= imap_answer(mp
, 1);
942 if (response_status
!= RESPONSE_OTHER
&&
943 (cp
= asccasestr(responded_text
,
944 "[UIDVALIDITY ")) != NULL
)
945 mp
->mb_uidvalidity
= atol(&cp
[13]);
947 *count
= had_exists
> 0 ? had_exists
: 0;
948 if (response_status
!= RESPONSE_OTHER
&&
949 ascncasecmp(responded_text
, "[READ-ONLY] ", 12)
956 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
959 FILE *queuefp
= NULL
;
962 unsigned x
= X
, y
= Y
;
965 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
966 IMAP_OUT(o
, MB_COMD
, return STOP
)
967 while (mp
->mb_active
& MB_COMD
) {
969 if (response_status
== RESPONSE_OTHER
&&
970 response_other
== MESSAGE_DATA_FETCH
) {
971 n
= responded_other_number
;
978 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
983 imap_getflags(cp
, &cp
, &m
->m_flag
);
985 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
986 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
987 getcache1(mp
, m
, NEED_UNSPEC
, 1);
988 m
->m_flag
&= ~MHIDDEN
;
990 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
992 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
995 snprintf(o
, sizeof o
,
996 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
998 IMAP_OUT(o
, MB_COMD
, return STOP
)
999 while (mp
->mb_active
& MB_COMD
) {
1001 if (response_status
== RESPONSE_OTHER
&&
1002 response_other
== MESSAGE_DATA_FETCH
) {
1003 n
= responded_other_number
;
1009 if ((cp
= asccasestr(responded_other_text
,
1010 "RFC822.SIZE ")) != NULL
)
1011 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1012 if ((cp
= asccasestr(responded_other_text
,
1013 "INTERNALDATE ")) != NULL
)
1014 m
->m_time
= imap_read_date_time(&cp
[13]);
1017 for (n
= X
; n
<= Y
; n
++)
1018 putcache(mp
, &message
[n
-1]);
1023 imap_init(struct mailbox
*mp
, int n
)
1025 struct message
*m
= &message
[n
];
1027 m
->m_flag
= MUSED
|MNOFROM
;
1033 imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
, int *prevcount
)
1035 struct message
*omessage
= 0;
1036 int i
, omsgCount
= 0;
1037 enum okay dequeued
= STOP
;
1039 if (newmail
|| transparent
) {
1041 omsgCount
= msgCount
;
1044 dequeued
= rec_dequeue();
1045 if (had_exists
>= 0) {
1046 if (dequeued
!= OKAY
)
1047 msgCount
= had_exists
;
1050 if (had_expunge
>= 0) {
1051 if (dequeued
!= OKAY
)
1052 msgCount
-= had_expunge
;
1055 if (newmail
&& expunged_messages
)
1056 printf("Expunged %ld message%s.\n",
1058 expunged_messages
!= 1 ? "s" : "");
1059 *prevcount
= omsgCount
- expunged_messages
;
1060 expunged_messages
= 0;
1062 fputs("IMAP error: Negative message count\n", stderr
);
1065 if (dequeued
!= OKAY
) {
1066 message
= scalloc(msgCount
+ 1, sizeof *message
);
1067 for (i
= 0; i
< msgCount
; i
++)
1069 if (!newmail
&& mp
->mb_type
== MB_IMAP
)
1072 imap_flags(mp
, 1, msgCount
);
1073 message
[msgCount
].m_size
= 0;
1074 message
[msgCount
].m_lines
= 0;
1077 if (newmail
|| transparent
)
1078 transflags(omessage
, omsgCount
, transparent
);
1084 imap_have_password(const char *server
)
1088 var
= ac_alloc(strlen(server
) + 10);
1089 strcpy(var
, "password-");
1090 strcpy(&var
[9], server
);
1091 if ((cp
= value(var
)) != NULL
)
1098 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1099 char **uhp
, char **mbx
, const char **pass
, char **user
)
1102 if (strncmp(*sp
, "imap://", 7) == 0) {
1106 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1109 #endif /* USE_SSL */
1111 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1112 *uhp
= savestr((char *)(*sp
));
1113 (*uhp
)[*cp
- *sp
] = '\0';
1114 *mbx
= (char *)&(*cp
)[1];
1117 (*server
)[*cp
- *server
] = '\0';
1118 *uhp
= (char *)(*sp
);
1121 *pass
= imap_have_password(*uhp
);
1122 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1123 *user
= salloc(*cp
- *uhp
+ 1);
1124 memcpy(*user
, *uhp
, *cp
- *uhp
);
1125 (*user
)[*cp
- *uhp
] = '\0';
1127 *user
= strdec(*user
);
1135 imap_setfile(const char *xserver
, int newmail
, int isedit
)
1137 return imap_setfile1(xserver
, newmail
, isedit
, 0);
1141 imap_setfile1(const char *xserver
, int newmail
, int isedit
, int transparent
)
1144 sighandler_type saveint
;
1145 sighandler_type savepipe
;
1146 char *server
, *user
, *account
;
1147 const char *cp
, *sp
, *pass
;
1150 enum mbflags same_flags
;
1157 server
= savestr((char *)xserver
);
1158 verbose
= value("verbose") != NULL
;
1160 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1161 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1162 if (saveint
!= SIG_IGN
)
1163 safe_signal(SIGINT
, imapcatch
);
1164 if (savepipe
!= SIG_IGN
)
1165 safe_signal(SIGPIPE
, imapcatch
);
1169 same_flags
= mb
.mb_flags
;
1170 same_imap_account
= 0;
1171 sp
= protbase(server
);
1172 if (mb
.mb_imap_account
) {
1173 if (mb
.mb_sock
.s_fd
> 0 &&
1174 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1175 disconnected(mb
.mb_imap_account
) == 0)
1176 same_imap_account
= 1;
1178 account
= sstrdup(sp
);
1179 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
1181 if (!same_imap_account
) {
1182 if (!disconnected(account
) &&
1183 sopen(sp
, &so
, use_ssl
, uhp
,
1184 use_ssl
? "imaps" : "imap", verbose
) != OKAY
)
1191 free(mb
.mb_imap_account
);
1192 mb
.mb_imap_account
= account
;
1193 if (!same_imap_account
) {
1194 if (mb
.mb_sock
.s_fd
>= 0)
1195 sclose(&mb
.mb_sock
);
1197 same_imap_account
= 0;
1207 free(mb
.mb_imap_mailbox
);
1208 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1211 mb
.mb_type
= MB_VOID
;
1212 mb
.mb_active
= MB_NONE
;;
1214 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1215 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1216 if (sigsetjmp(imapjmp
, 1)) {
1218 safe_signal(SIGINT
, saveint
);
1219 safe_signal(SIGPIPE
, savepipe
);
1223 if (saveint
!= SIG_IGN
)
1224 safe_signal(SIGINT
, imapcatch
);
1225 if (savepipe
!= SIG_IGN
)
1226 safe_signal(SIGPIPE
, imapcatch
);
1227 if (mb
.mb_sock
.s_fd
< 0) {
1228 if (disconnected(mb
.mb_imap_account
)) {
1229 if (cache_setptr(transparent
) == STOP
)
1231 "Mailbox \"%s\" is not cached.\n",
1235 if ((cp
= value("imap-keepalive")) != NULL
) {
1236 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1237 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1238 alarm(imapkeepalive
);
1242 mb
.mb_sock
.s_desc
= "IMAP";
1243 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1244 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1245 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1246 sclose(&mb
.mb_sock
);
1248 safe_signal(SIGINT
, saveint
);
1249 safe_signal(SIGPIPE
, savepipe
);
1253 } else /* same account */
1254 mb
.mb_flags
|= same_flags
;
1255 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
1256 mb
.mb_type
= MB_IMAP
;
1258 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1259 /*sclose(&mb.mb_sock);
1261 safe_signal(SIGINT
, saveint
);
1262 safe_signal(SIGPIPE
, savepipe
);
1264 mb
.mb_type
= MB_VOID
;
1268 imap_setptr(&mb
, newmail
, transparent
, &prevcount
);
1269 done
: setmsize(msgCount
);
1270 if (!newmail
&& !transparent
)
1272 safe_signal(SIGINT
, saveint
);
1273 safe_signal(SIGPIPE
, savepipe
);
1275 if (!newmail
&& mb
.mb_type
== MB_IMAP
)
1276 purgecache(&mb
, message
, msgCount
);
1277 if ((newmail
|| transparent
) && mb
.mb_sorted
) {
1281 if (!newmail
&& !edit
&& msgCount
== 0) {
1282 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1283 value("emptystart") == NULL
)
1284 fprintf(stderr
, catgets(catd
, CATSET
, 258,
1285 "No mail at %s\n"), server
);
1289 newmailinfo(prevcount
);
1294 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1296 const char *head
, size_t headsize
, long headlines
)
1298 char *line
= NULL
, *lp
;
1299 size_t linesize
= 0, linelen
, size
= 0;
1300 int emptyline
= 0, lines
= 0, excess
= 0;
1303 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1304 offset
= ftell(mp
->mb_otf
);
1306 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1307 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1309 if (linelen
> expected
) {
1310 excess
= linelen
- expected
;
1314 * Need to mask 'From ' lines. This cannot be done properly
1315 * since some servers pass them as 'From ' and others as
1316 * '>From '. Although one could identify the first kind of
1317 * server in principle, it is not possible to identify the
1318 * second as '>From ' may also come from a server of the
1319 * first type as actual data. So do what is absolutely
1320 * necessary only - mask 'From '.
1322 * If the line is the first line of the message header, it
1323 * is likely a real 'From ' line. In this case, it is just
1324 * ignored since it violates all standards.
1326 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
1327 lp
[3] == 'm' && lp
[4] == ' ') {
1328 if (lines
+ headlines
!= 0) {
1329 fputc('>', mp
->mb_otf
);
1334 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1335 lp
[linelen
-2] == '\r')) {
1336 emptyline
= linelen
<= 2;
1338 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1339 size
+= linelen
- 1;
1342 fputc('\n', mp
->mb_otf
);
1345 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1349 skip
: if ((expected
-= linelen
) <= 0)
1354 * This is very ugly; but some IMAP daemons don't end a
1355 * message with \r\n\r\n, and we need \n\n for mbox format.
1357 fputc('\n', mp
->mb_otf
);
1363 m
->m_size
= size
+ headsize
;
1364 m
->m_lines
= lines
+ headlines
;
1365 m
->m_block
= mailx_blockof(offset
);
1366 m
->m_offset
= mailx_offsetof(offset
);
1369 m
->m_have
|= HAVE_HEADER
;
1372 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1373 m
->m_xlines
= m
->m_lines
;
1374 m
->m_xsize
= m
->m_size
;
1383 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1384 const char *head
, size_t headsize
, long headlines
)
1390 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1391 offset
= ftell(mp
->mb_otf
);
1393 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1395 fwrite(str
, 1, len
, mp
->mb_otf
);
1396 fputc('\n', mp
->mb_otf
);
1401 m
->m_size
= headsize
+ len
;
1402 m
->m_lines
= headlines
+ 1;
1403 m
->m_block
= mailx_blockof(offset
);
1404 m
->m_offset
= mailx_offsetof(offset
);
1405 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1406 m
->m_xlines
= m
->m_lines
;
1407 m
->m_xsize
= m
->m_size
;
1412 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1414 sighandler_type saveint
= SIG_IGN
;
1415 sighandler_type savepipe
= SIG_IGN
;
1416 char o
[LINESIZE
], *cp
= NULL
, *item
= NULL
, *resp
= NULL
, *loc
= NULL
;
1417 size_t expected
, headsize
= 0;
1418 int number
= m
- message
+ 1;
1419 enum okay ok
= STOP
;
1420 FILE *queuefp
= NULL
;
1425 unsigned long u
= 0;
1440 verbose
= value("verbose") != NULL
;
1441 if (getcache(mp
, m
, need
) == OKAY
)
1443 if (mp
->mb_type
== MB_CACHE
) {
1444 fprintf(stderr
, "Message %u not available.\n", number
);
1447 if (mp
->mb_sock
.s_fd
< 0) {
1448 fprintf(stderr
, "IMAP connection closed.\n");
1453 resp
= item
= "RFC822.HEADER";
1456 item
= "BODY.PEEK[]";
1458 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1459 char *hdr
= smalloc(m
->m_size
);
1461 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1462 m
->m_offset
), SEEK_SET
) < 0 ||
1463 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1469 headsize
= m
->m_size
;
1470 headlines
= m
->m_lines
;
1471 item
= "BODY.PEEK[TEXT]";
1472 resp
= "BODY[TEXT]";
1479 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1480 if (sigsetjmp(imapjmp
, 1)) {
1481 safe_signal(SIGINT
, saveint
);
1482 safe_signal(SIGPIPE
, savepipe
);
1486 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1487 safe_signal(SIGINT
, maincatch
);
1488 if (savepipe
!= SIG_IGN
)
1489 safe_signal(SIGPIPE
, imapcatch
);
1491 snprintf(o
, sizeof o
,
1492 "%s UID FETCH %lu (%s)\r\n",
1493 tag(1), m
->m_uid
, item
);
1495 if (check_expunged() == STOP
)
1497 snprintf(o
, sizeof o
,
1498 "%s FETCH %u (%s)\r\n",
1499 tag(1), number
, item
);
1501 IMAP_OUT(o
, MB_COMD
, goto out
)
1503 ok
= imap_answer(mp
, 1);
1506 if (response_status
!= RESPONSE_OTHER
||
1507 response_other
!= MESSAGE_DATA_FETCH
)
1509 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1512 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1520 n
= responded_other_number
;
1521 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1522 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1524 if ((cp
= strchr(loc
, '"')) != NULL
) {
1525 cp
= imap_unquotestr(cp
);
1526 imap_putstr(mp
, m
, cp
,
1527 head
, headsize
, headlines
);
1529 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1530 m
->m_xlines
= m
->m_lines
;
1531 m
->m_xsize
= m
->m_size
;
1535 expected
= atol(&cp
[1]);
1536 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1537 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1541 imap_fetchdata(mp
, &mt
, expected
, need
,
1542 head
, headsize
, headlines
);
1544 commitmsg(mp
, m
, mt
, mt
.m_have
);
1547 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1548 &mp
->mb_sock
) > 0) {
1550 fputs(imapbuf
, stderr
);
1551 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1553 if (u
== m
->m_uid
) {
1554 commitmsg(mp
, m
, mt
, mt
.m_have
);
1560 out
: while (mp
->mb_active
& MB_COMD
)
1561 ok
= imap_answer(mp
, 1);
1562 if (saveint
!= SIG_IGN
)
1563 safe_signal(SIGINT
, saveint
);
1564 if (savepipe
!= SIG_IGN
)
1565 safe_signal(SIGPIPE
, savepipe
);
1576 imap_header(struct message
*m
)
1578 return imap_get(&mb
, m
, NEED_HEADER
);
1583 imap_body(struct message
*m
)
1585 return imap_get(&mb
, m
, NEED_BODY
);
1589 commitmsg(struct mailbox
*mp
, struct message
*to
,
1590 struct message from
, enum havespec have
)
1592 to
->m_size
= from
.m_size
;
1593 to
->m_lines
= from
.m_lines
;
1594 to
->m_block
= from
.m_block
;
1595 to
->m_offset
= from
.m_offset
;
1597 if (have
& HAVE_BODY
) {
1598 to
->m_xlines
= from
.m_lines
;
1599 to
->m_xsize
= from
.m_size
;
1609 int top
/* bot > top */
1612 char o
[LINESIZE
], *cp
;
1617 FILE *queuefp
= NULL
;
1620 snprintf(o
, sizeof o
,
1621 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1622 tag(1), m
[bot
-1].m_uid
, m
[top
-1].m_uid
);
1624 if (check_expunged() == STOP
)
1626 snprintf(o
, sizeof o
,
1627 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1630 IMAP_OUT(o
, MB_COMD
, return STOP
)
1632 ok
= imap_answer(mp
, 1);
1633 if (response_status
!= RESPONSE_OTHER
)
1635 if (response_other
!= MESSAGE_DATA_FETCH
)
1637 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1639 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1641 expected
= atol(&cp
[1]);
1642 if (m
[bot
-1].m_uid
) {
1643 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1645 for (n
= bot
; n
<= top
; n
++)
1646 if (m
[n
-1].m_uid
== u
)
1649 imap_fetchdata(mp
, NULL
, expected
,
1657 n
= responded_other_number
;
1658 if (n
<= 0 || n
> msgCount
) {
1659 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1664 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1665 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1666 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1667 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1668 &mp
->mb_sock
) > 0) {
1670 fputs(imapbuf
, stderr
);
1671 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1673 for (n
= bot
; n
<= top
; n
++)
1674 if (m
[n
-1].m_uid
== u
)
1676 if (n
<= top
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1677 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1682 while (mp
->mb_active
& MB_COMD
)
1683 ok
= imap_answer(mp
, 1);
1688 imap_getheaders(int bot
, int top
)
1690 sighandler_type saveint
, savepipe
;
1691 enum okay ok
= STOP
;
1699 verbose
= value("verbose") != NULL
;
1700 if (mb
.mb_type
== MB_CACHE
)
1706 for (i
= bot
; i
< top
; i
++) {
1707 if (message
[i
-1].m_have
& HAVE_HEADER
||
1708 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1714 for (i
= top
; i
> bot
; i
--) {
1715 if (message
[i
-1].m_have
& HAVE_HEADER
||
1716 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1725 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1726 safe_signal(SIGINT
, maincatch
);
1727 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1728 if (sigsetjmp(imapjmp
, 1) == 0) {
1729 if (savepipe
!= SIG_IGN
)
1730 safe_signal(SIGPIPE
, imapcatch
);
1731 for (i
= bot
; i
<= top
; i
+= chunk
) {
1732 ok
= imap_fetchheaders(&mb
, message
, i
,
1733 i
+chunk
-1 < top
? i
+chunk
-1 : top
);
1738 safe_signal(SIGINT
, saveint
);
1739 safe_signal(SIGPIPE
, savepipe
);
1744 imap_exit(struct mailbox
*mp
)
1747 FILE *queuefp
= NULL
;
1749 verbose
= value("verbose") != NULL
;
1750 mp
->mb_active
|= MB_BYE
;
1751 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1752 IMAP_OUT(o
, MB_COMD
, return STOP
)
1758 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1760 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1761 if (mp
->mb_type
== MB_IMAP
)
1767 imap_close(struct mailbox
*mp
)
1770 FILE *queuefp
= NULL
;
1772 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1773 IMAP_OUT(o
, MB_COMD
, return STOP
)
1779 imap_update(struct mailbox
*mp
)
1781 FILE *readstat
= NULL
;
1783 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1785 verbose
= value("verbose") != NULL
;
1786 if (Tflag
!= NULL
) {
1787 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
1790 if (!edit
&& mp
->mb_perm
!= 0) {
1792 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1793 if (m
->m_flag
& MBOX
)
1797 if (makembox() == STOP
)
1800 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1801 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
1804 if ((id
= hfield("message-id", m
)) != NULL
||
1805 (id
= hfield("article-id", m
)) != NULL
)
1806 fprintf(readstat
, "%s\n", id
);
1808 if (mp
->mb_perm
== 0) {
1811 dodel
= m
->m_flag
& MDELETED
;
1813 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1814 (m
->m_flag
&MTOUCH
) == 0);
1817 * Fetch the result after around each 800 STORE commands
1818 * sent (approx. 32k data sent). Otherwise, servers will
1819 * try to flush the return queue at some point, leading
1820 * to a deadlock if we are still writing commands but not
1821 * reading their results.
1823 needstat
= stored
> 0 && stored
% 800 == 0;
1825 * Even if this message has been deleted, continue
1826 * to set further flags. This is necessary to support
1827 * Gmail semantics, where "delete" actually means
1828 * "archive", and the flags are applied to the copy
1831 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1832 imap_store(mp
, m
, m
-message
+1,
1833 '+', "\\Seen", needstat
);
1836 if (m
->m_flag
& MFLAG
) {
1837 imap_store(mp
, m
, m
-message
+1,
1838 '+', "\\Flagged", needstat
);
1841 if (m
->m_flag
& MUNFLAG
) {
1842 imap_store(mp
, m
, m
-message
+1,
1843 '-', "\\Flagged", needstat
);
1846 if (m
->m_flag
& MANSWER
) {
1847 imap_store(mp
, m
, m
-message
+1,
1848 '+', "\\Answered", needstat
);
1851 if (m
->m_flag
& MUNANSWER
) {
1852 imap_store(mp
, m
, m
-message
+1,
1853 '-', "\\Answered", needstat
);
1856 if (m
->m_flag
& MDRAFT
) {
1857 imap_store(mp
, m
, m
-message
+1,
1858 '+', "\\Draft", needstat
);
1861 if (m
->m_flag
& MUNDRAFT
) {
1862 imap_store(mp
, m
, m
-message
+1,
1863 '-', "\\Draft", needstat
);
1867 imap_delete(mp
, m
-message
+1, m
, needstat
);
1870 } else if (mp
->mb_type
!= MB_CACHE
||
1871 !edit
&& (!(m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))
1873 (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1874 (MPRESERVE
|MTOUCH
)) ||
1875 edit
&& !(m
->m_flag
& MDELETED
))
1877 if (m
->m_flag
& MNEW
) {
1879 m
->m_flag
|= MSTATUS
;
1882 bypass
: if (readstat
!= NULL
)
1886 for (m
= &message
[0]; m
< &message
[msgCount
]; m
++)
1887 if (!(m
->m_flag
&MUNLINKED
) &&
1888 m
->m_flag
&(MBOXED
|MDELETED
|MSAVED
|MSTATUS
|
1889 MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
1894 if ((gotcha
|| modflags
) && edit
) {
1895 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
1896 printf(value("bsdcompat") || value("bsdmsgs") ?
1897 catgets(catd
, CATSET
, 170, "complete\n") :
1898 catgets(catd
, CATSET
, 212, "updated.\n"));
1899 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1901 printf(catgets(catd
, CATSET
, 155,
1902 "Held 1 message in %s\n"), mailname
);
1904 printf(catgets(catd
, CATSET
, 156,
1905 "Held %d messages in %s\n"), held
, mailname
);
1914 sighandler_type saveint
;
1915 sighandler_type savepipe
;
1917 verbose
= value("verbose") != NULL
;
1918 if (mb
.mb_type
== MB_CACHE
) {
1922 if (mb
.mb_sock
.s_fd
< 0) {
1923 fprintf(stderr
, "IMAP connection closed.\n");
1927 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1928 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1929 if (sigsetjmp(imapjmp
, 1)) {
1930 safe_signal(SIGINT
, saveint
);
1931 safe_signal(SIGPIPE
, saveint
);
1935 if (saveint
!= SIG_IGN
)
1936 safe_signal(SIGINT
, imapcatch
);
1937 if (savepipe
!= SIG_IGN
)
1938 safe_signal(SIGPIPE
, imapcatch
);
1940 if (!same_imap_account
) {
1942 sclose(&mb
.mb_sock
);
1944 safe_signal(SIGINT
, saveint
);
1945 safe_signal(SIGPIPE
, savepipe
);
1950 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1951 int c
, const char *sp
, int needstat
)
1954 FILE *queuefp
= NULL
;
1956 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1959 snprintf(o
, sizeof o
,
1960 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1961 tag(1), m
->m_uid
, c
, sp
);
1963 if (check_expunged() == STOP
)
1965 snprintf(o
, sizeof o
,
1966 "%s STORE %u %cFLAGS (%s)\r\n",
1969 IMAP_OUT(o
, MB_COMD
, return STOP
)
1973 mb
.mb_active
&= ~MB_COMD
;
1974 if (queuefp
!= NULL
)
1980 imap_undelete(struct message
*m
, int n
)
1982 return imap_unstore(m
, n
, "\\Deleted");
1986 imap_unread(struct message
*m
, int n
)
1988 return imap_unstore(m
, n
, "\\Seen");
1992 imap_unstore(struct message
*m
, int n
, const char *flag
)
1994 sighandler_type saveint
, savepipe
;
1995 enum okay ok
= STOP
;
2000 verbose
= value("verbose") != NULL
;
2002 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2003 safe_signal(SIGINT
, maincatch
);
2004 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2005 if (sigsetjmp(imapjmp
, 1) == 0) {
2006 if (savepipe
!= SIG_IGN
)
2007 safe_signal(SIGPIPE
, imapcatch
);
2008 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2010 safe_signal(SIGINT
, saveint
);
2011 safe_signal(SIGPIPE
, savepipe
);
2026 snprintf(ts
, sizeof ts
, "T%lu", n
);
2033 sighandler_type saveint
, savepipe
;
2035 enum okay ok
= STOP
;
2036 struct mailbox
*mp
= &mb
;
2037 FILE *queuefp
= NULL
;
2042 verbose
= value("verbose") != NULL
;
2043 if (mp
->mb_type
!= MB_IMAP
) {
2044 printf("Not operating on an IMAP mailbox.\n");
2048 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2049 safe_signal(SIGINT
, maincatch
);
2050 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2051 if (sigsetjmp(imapjmp
, 1) == 0) {
2052 if (savepipe
!= SIG_IGN
)
2053 safe_signal(SIGPIPE
, imapcatch
);
2054 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2055 IMAP_OUT(o
, MB_COMD
, goto out
)
2056 while (mp
->mb_active
& MB_COMD
) {
2057 ok
= imap_answer(mp
, 0);
2058 fputs(responded_text
, stdout
);
2061 out
: safe_signal(SIGINT
, saveint
);
2062 safe_signal(SIGPIPE
, savepipe
);
2070 imap_newmail(int autoinc
)
2072 if (autoinc
&& had_exists
< 0 && had_expunge
< 0) {
2073 verbose
= value("verbose") != NULL
;
2078 if (had_exists
== msgCount
&& had_expunge
< 0)
2080 * Some servers always respond with EXISTS to NOOP. If
2081 * the mailbox has been changed but the number of messages
2082 * has not, an EXPUNGE must also had been sent; otherwise,
2083 * nothing has changed.
2086 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2090 imap_putflags(int f
)
2095 bp
= buf
= salloc(100);
2096 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2101 for (cp
= "\\Seen"; *cp
; cp
++)
2107 for (cp
= "\\Flagged"; *cp
; cp
++)
2110 if (f
& MANSWERED
) {
2113 for (cp
= "\\Answered"; *cp
; cp
++)
2119 for (cp
= "\\Draft"; *cp
; cp
++)
2130 imap_getflags(const char *cp
, char **xp
, enum mflag
*f
)
2132 while (*cp
!= ')') {
2134 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2136 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2138 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2140 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2142 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2144 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2154 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2155 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2159 size_t bufsize
, buflen
, count
;
2160 enum okay ok
= STOP
;
2161 long size
, lines
, ysize
;
2163 FILE *queuefp
= NULL
;
2165 if (mp
->mb_type
== MB_CACHE
) {
2166 queuefp
= cache_queue(mp
);
2167 if (queuefp
== NULL
)
2171 buf
= smalloc(bufsize
= LINESIZE
);
2173 again
: size
= xsize
;
2175 fseek(fp
, off1
, SEEK_SET
);
2176 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2177 tag(1), imap_quotestr(name
),
2178 imap_putflags(flag
),
2179 imap_make_date_time(t
),
2181 IMAP_OUT(o
, MB_COMD
, goto out
)
2182 while (mp
->mb_active
& MB_COMD
) {
2183 ok
= imap_answer(mp
, twice
);
2184 if (response_type
== RESPONSE_CONT
)
2187 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2195 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2198 buf
[buflen
-1] = '\r';
2200 if (mp
->mb_type
!= MB_CACHE
)
2201 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2203 fwrite(buf
, 1, buflen
+1, queuefp
);
2206 if (mp
->mb_type
!= MB_CACHE
)
2207 swrite(&mp
->mb_sock
, "\r\n");
2209 fputs("\r\n", queuefp
);
2210 while (mp
->mb_active
& MB_COMD
) {
2211 ok
= imap_answer(mp
, 0);
2212 if (response_status
== RESPONSE_NO
/*&&
2213 ascncasecmp(responded_text,
2214 "[TRYCREATE] ", 12) == 0*/) {
2215 trycreate
: if (twice
++) {
2219 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2221 imap_quotestr(name
));
2222 IMAP_OUT(o
, MB_COMD
, goto out
);
2223 while (mp
->mb_active
& MB_COMD
)
2224 ok
= imap_answer(mp
, 1);
2227 imap_created_mailbox
++;
2229 } else if (ok
!= OKAY
)
2230 fprintf(stderr
, "IMAP error: %s", responded_text
);
2231 else if (response_status
== RESPONSE_OK
&&
2232 mp
->mb_flags
& MB_UIDPLUS
)
2233 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2236 out
: if (queuefp
!= NULL
)
2243 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2245 char *buf
, *bp
, *lp
;
2246 size_t bufsize
, buflen
, count
;
2247 off_t off1
= -1, offs
;
2249 int flag
= MNEW
|MNEWEST
;
2254 buf
= smalloc(bufsize
= LINESIZE
);
2260 bp
= fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2261 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2262 if (off1
!= (off_t
)-1) {
2263 ok
=imap_append1(mp
, name
, fp
, off1
,
2267 fseek(fp
, offs
+buflen
, SEEK_SET
);
2269 off1
= offs
+ buflen
;
2274 tim
= unixtime(buf
);
2278 if (bp
&& buf
[0] == '\n')
2280 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2282 while (whitechar(*lp
&0377))
2285 while (*++lp
!= '\0')
2294 } else if (bp
&& inhead
&&
2295 ascncasecmp(buf
, "x-status", 8) == 0) {
2297 while (whitechar(*lp
&0377))
2300 while (*++lp
!= '\0')
2313 } while (bp
!= NULL
);
2319 imap_append(const char *xserver
, FILE *fp
)
2321 sighandler_type saveint
, savepipe
;
2322 char *server
, *uhp
, *mbx
, *user
;
2323 const char *sp
, *cp
, *pass
;
2325 enum okay ok
= STOP
;
2330 verbose
= value("verbose") != NULL
;
2331 server
= savestr((char *)xserver
);
2332 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
2334 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2335 safe_signal(SIGINT
, maincatch
);
2336 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2337 if (sigsetjmp(imapjmp
, 1))
2339 if (savepipe
!= SIG_IGN
)
2340 safe_signal(SIGPIPE
, imapcatch
);
2341 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2342 mb
.mb_imap_account
&&
2343 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2344 ok
= imap_append0(&mb
, mbx
, fp
);
2349 memset(&mx
, 0, sizeof mx
);
2350 if (disconnected(server
) == 0) {
2351 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2352 use_ssl
? "imaps" : "imap",
2355 mx
.mb_sock
.s_desc
= "IMAP";
2356 mx
.mb_type
= MB_IMAP
;
2357 mx
.mb_imap_account
= (char *)protbase(server
);
2358 mx
.mb_imap_mailbox
= mbx
;
2359 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2360 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2361 sclose(&mx
.mb_sock
);
2364 ok
= imap_append0(&mx
, mbx
, fp
);
2366 sclose(&mx
.mb_sock
);
2368 mx
.mb_imap_account
= (char *)protbase(server
);
2369 mx
.mb_imap_mailbox
= mbx
;
2370 mx
.mb_type
= MB_CACHE
;
2371 ok
= imap_append0(&mx
, mbx
, fp
);
2375 out
: safe_signal(SIGINT
, saveint
);
2376 safe_signal(SIGPIPE
, savepipe
);
2384 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2385 struct list_item
**lend
, int level
)
2388 enum okay ok
= STOP
;
2391 FILE *queuefp
= NULL
;
2392 struct list_item
*lp
;
2394 *list
= *lend
= NULL
;
2395 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2396 tag(1), imap_quotestr(base
));
2397 IMAP_OUT(o
, MB_COMD
, return STOP
);
2398 while (mp
->mb_active
& MB_COMD
) {
2399 ok
= imap_answer(mp
, 1);
2400 if (response_status
== RESPONSE_OTHER
&&
2401 response_other
== MAILBOX_DATA_LIST
&&
2402 imap_parse_list() == OKAY
) {
2403 cp
= imap_unquotestr(list_name
);
2404 lp
= csalloc(1, sizeof *lp
);
2406 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2408 lp
->l_base
= *cp
? cp
: savestr(base
);
2409 lp
->l_attr
= list_attributes
;
2410 lp
->l_level
= level
+1;
2411 lp
->l_delim
= list_hierarchy_delimiter
;
2412 if (*list
&& *lend
) {
2413 (*lend
)->l_next
= lp
;
2423 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2425 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2431 verbose
= value("verbose") != NULL
;
2432 depth
= (cp
= value("imap-list-depth")) != NULL
? atoi(cp
) : 2;
2433 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2435 if (list
== NULL
|| lend
== NULL
)
2437 for (lp
= list
; lp
; lp
= lp
->l_next
)
2438 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2439 lp
->l_level
< depth
&&
2440 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2441 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2442 strcpy(cp
, lp
->l_name
);
2443 cp
[n
] = lp
->l_delim
;
2445 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2447 lp
->l_has_children
= 1;
2448 if (strcmp(cp
, lx
->l_name
) == 0)
2456 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2459 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2463 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2464 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2465 else if (lp
->l_has_children
== 0)
2466 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2467 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2473 imap_folders(const char *name
, int strip
)
2475 sighandler_type saveint
, savepipe
;
2476 const char *fold
, *cp
, *sp
;
2479 int columnize
= is_a_tty
[1];
2485 cp
= protbase(name
);
2486 sp
= mb
.mb_imap_account
;
2487 if (strcmp(cp
, sp
)) {
2488 fprintf(stderr
, "Cannot list folders on other than the "
2489 "current IMAP account,\n\"%s\". "
2490 "Try \"folders @\".\n", sp
);
2493 fold
= protfile(name
);
2495 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2504 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2505 safe_signal(SIGINT
, maincatch
);
2506 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2507 if (sigsetjmp(imapjmp
, 1))
2509 if (savepipe
!= SIG_IGN
)
2510 safe_signal(SIGPIPE
, imapcatch
);
2511 if (mb
.mb_type
== MB_CACHE
)
2512 cache_list(&mb
, fold
, strip
, fp
);
2514 imap_list(&mb
, fold
, strip
, fp
);
2527 fprintf(stderr
, "Folder not found.\n");
2530 safe_signal(SIGINT
, saveint
);
2531 safe_signal(SIGPIPE
, savepipe
);
2539 char o
[LINESIZE
], *tempfn
;
2541 long n
= 0, mx
= 0, columns
, width
;
2544 if ((out
= Ftemp(&tempfn
, "Ro", "w+", 0600, 1)) == NULL
) {
2550 while ((c
= getc(fp
)) != EOF
) {
2560 if (mx
< width
/ 2) {
2561 columns
= width
/ (mx
+2);
2562 snprintf(o
, sizeof o
,
2563 "sort | pr -%lu -w%lu -t",
2566 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2567 run_command(SHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2573 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2577 enum okay ok
= STOP
;
2580 FILE *queuefp
= NULL
;
2582 if (mp
->mb_type
== MB_CACHE
) {
2583 if ((queuefp
= cache_queue(mp
)) == NULL
)
2587 qname
= imap_quotestr(name
= protfile(name
));
2589 * Since it is not possible to set flags on the copy, recently
2590 * set flags must be set on the original to include it in the copy.
2592 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
))
2593 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2594 if (m
->m_flag
&MFLAG
)
2595 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2596 if (m
->m_flag
&MUNFLAG
)
2597 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2598 if (m
->m_flag
&MANSWER
)
2599 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2600 if (m
->m_flag
&MUNANSWER
)
2601 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2602 if (m
->m_flag
&MDRAFT
)
2603 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2604 if (m
->m_flag
&MUNDRAFT
)
2605 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2606 again
: if (m
->m_uid
)
2607 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n",
2608 tag(1), m
->m_uid
, qname
);
2610 if (check_expunged() == STOP
)
2612 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n",
2615 IMAP_OUT(o
, MB_COMD
, goto out
)
2616 while (mp
->mb_active
& MB_COMD
)
2617 ok
= imap_answer(mp
, twice
);
2618 if (mp
->mb_type
== MB_IMAP
&&
2619 mp
->mb_flags
& MB_UIDPLUS
&&
2620 response_status
== RESPONSE_OK
)
2621 imap_copyuid(mp
, m
, name
);
2622 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2623 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2624 IMAP_OUT(o
, MB_COMD
, goto out
)
2625 while (mp
->mb_active
& MB_COMD
)
2626 ok
= imap_answer(mp
, 1);
2628 imap_created_mailbox
++;
2632 if (queuefp
!= NULL
)
2635 * ... and reset the flag to its initial value so that
2636 * the 'exit' command still leaves the message unread.
2638 out
: if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
2639 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2642 if (m
->m_flag
&MFLAG
) {
2643 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2646 if (m
->m_flag
&MUNFLAG
) {
2647 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2650 if (m
->m_flag
&MANSWER
) {
2651 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2654 if (m
->m_flag
&MUNANSWER
) {
2655 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2658 if (m
->m_flag
&MDRAFT
) {
2659 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2662 if (m
->m_flag
&MUNDRAFT
) {
2663 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2667 mp
->mb_active
|= MB_COMD
;
2674 imap_copy(struct message
*m
, int n
, const char *name
)
2676 sighandler_type saveint
, savepipe
;
2677 enum okay ok
= STOP
;
2682 verbose
= value("verbose") != NULL
;
2684 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2685 safe_signal(SIGINT
, maincatch
);
2686 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2687 if (sigsetjmp(imapjmp
, 1) == 0) {
2688 if (savepipe
!= SIG_IGN
)
2689 safe_signal(SIGPIPE
, imapcatch
);
2690 ok
= imap_copy1(&mb
, m
, n
, name
);
2692 safe_signal(SIGINT
, saveint
);
2693 safe_signal(SIGPIPE
, savepipe
);
2701 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2702 unsigned long *olduid
, unsigned long *newuid
)
2706 *uidvalidity
= strtoul(cp
, &xp
, 10);
2707 *olduid
= strtoul(xp
, &yp
, 10);
2708 *newuid
= strtoul(yp
, &zp
, 10);
2709 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2710 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2714 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2719 *uidvalidity
= strtoul(cp
, &xp
, 10);
2720 *uid
= strtoul(xp
, &yp
, 10);
2721 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2722 yp
> xp
&& *yp
== ']';
2726 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2729 unsigned long uidvalidity
, olduid
, newuid
;
2733 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2734 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2735 &olduid
, &newuid
) == STOP
)
2738 xmb
.mb_cache_directory
= NULL
;
2739 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2740 xmb
.mb_uidvalidity
= uidvalidity
;
2743 memset(&xm
, 0, sizeof xm
);
2745 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2747 getcache(mp
, &xm
, NEED_HEADER
);
2748 getcache(mp
, &xm
, NEED_BODY
);
2750 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2751 getcache(mp
, m
, NEED_HEADER
);
2752 if ((m
->m_flag
& HAVE_BODY
) == 0)
2753 getcache(mp
, m
, NEED_BODY
);
2757 xm
.m_flag
&= ~MFULLYCACHED
;
2758 putcache(&xmb
, &xm
);
2763 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2764 long xsize
, long size
, long lines
, int flag
, const char *name
)
2767 unsigned long uidvalidity
, uid
;
2771 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2772 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2776 xmb
.mb_cache_directory
= NULL
;
2777 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2778 xmb
.mb_uidvalidity
= uidvalidity
;
2779 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2781 memset(&xm
, 0, sizeof xm
);
2782 xm
.m_flag
= flag
&MREAD
| MNEW
;
2784 xm
.m_block
= mailx_blockof(off1
);
2785 xm
.m_offset
= mailx_offsetof(off1
);
2788 xm
.m_lines
= xm
.m_xlines
= lines
;
2790 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2791 putcache(&xmb
, &xm
);
2796 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2800 long size
, xsize
, ysize
, lines
;
2801 enum mflag flag
= MNEW
;
2802 char *name
, *buf
, *bp
, *cp
, *tempCopy
;
2803 size_t bufsize
, buflen
, count
;
2804 enum okay ok
= STOP
;
2806 buf
= smalloc(bufsize
= LINESIZE
);
2809 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2811 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
2814 if ((cp
= strrchr(bp
, '{')) == NULL
)
2816 xsize
= atol(&cp
[1]) + 2;
2817 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
2822 imap_getflags(cp
, &cp
, &flag
);
2823 while (*++cp
== ' ');
2825 t
= imap_read_date_time(cp
);
2826 if ((tp
= Ftemp(&tempCopy
, "Rc", "w+", 0600, 1)) == NULL
)
2833 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2836 buf
[--buflen
] = '\0';
2837 buf
[buflen
-1] = '\n';
2838 fwrite(buf
, 1, buflen
, tp
);
2844 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
2845 imap_unquotestr(name
));
2854 imap_search2(struct mailbox
*mp
, struct message
*m
, int count
,
2855 const char *spec
, int f
)
2859 FILE *queuefp
= NULL
;
2860 enum okay ok
= STOP
;
2867 for (cp
= spec
; *cp
; cp
++)
2872 if (asccasecmp(cp
, "utf-8")) {
2874 char *sp
, *nsp
, *nspec
;
2876 if ((it
= iconv_open_ft("utf-8", cp
)) != (iconv_t
)-1) {
2877 sz
= strlen(spec
) + 1;
2878 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2880 if (iconv(it
, &sp
, &sz
, &nsp
, &nsz
)
2881 != (size_t)-1 && sz
== 0) {
2888 #endif /* HAVE_ICONV */
2889 cp
= imap_quotestr(cp
);
2890 cs
= salloc(n
= strlen(cp
) + 10);
2891 snprintf(cs
, n
, "CHARSET %s ", cp
);
2894 o
= ac_alloc(osize
= strlen(spec
) + 60);
2895 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2896 IMAP_OUT(o
, MB_COMD
, goto out
)
2897 while (mp
->mb_active
& MB_COMD
) {
2898 ok
= imap_answer(mp
, 0);
2899 if (response_status
== RESPONSE_OTHER
&&
2900 response_other
== MAILBOX_DATA_SEARCH
) {
2901 xp
= responded_other_text
;
2902 while (*xp
&& *xp
!= '\r') {
2903 n
= strtoul(xp
, &xp
, 10);
2904 for (i
= 0; i
< count
; i
++)
2905 if (m
[i
].m_uid
== n
&&
2906 (m
[i
].m_flag
&MHIDDEN
)
2909 (m
[i
].m_flag
&MDELETED
)
2920 imap_search1(const char *spec
, int f
)
2922 sighandler_type saveint
, savepipe
;
2923 enum okay ok
= STOP
;
2928 if (mb
.mb_type
!= MB_IMAP
)
2930 verbose
= value("verbose") != NULL
;
2932 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2933 safe_signal(SIGINT
, maincatch
);
2934 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2935 if (sigsetjmp(imapjmp
, 1) == 0) {
2936 if (savepipe
!= SIG_IGN
)
2937 safe_signal(SIGPIPE
, imapcatch
);
2938 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2940 safe_signal(SIGINT
, saveint
);
2941 safe_signal(SIGPIPE
, savepipe
);
2949 imap_thisaccount(const char *cp
)
2951 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2953 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2954 mb
.mb_imap_account
== NULL
)
2956 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2960 imap_remove(const char *name
)
2962 sighandler_type saveint
, savepipe
;
2963 enum okay ok
= STOP
;
2968 verbose
= value("verbose") != NULL
;
2969 if (mb
.mb_type
!= MB_IMAP
) {
2970 fprintf(stderr
, "Refusing to remove \"%s\" "
2971 "in disconnected mode.\n", name
);
2974 if (!imap_thisaccount(name
)) {
2975 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2976 "server: \"%s\" not removed.\n", name
);
2980 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2981 safe_signal(SIGINT
, maincatch
);
2982 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2983 if (sigsetjmp(imapjmp
, 1) == 0) {
2984 if (savepipe
!= SIG_IGN
)
2985 safe_signal(SIGPIPE
, imapcatch
);
2986 ok
= imap_remove1(&mb
, protfile(name
));
2988 safe_signal(SIGINT
, saveint
);
2989 safe_signal(SIGPIPE
, savepipe
);
2992 ok
= cache_remove(name
);
2999 imap_remove1(struct mailbox
*mp
, const char *name
)
3001 FILE *queuefp
= NULL
;
3004 enum okay ok
= STOP
;
3006 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3007 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3008 IMAP_OUT(o
, MB_COMD
, goto out
)
3009 while (mp
->mb_active
& MB_COMD
)
3010 ok
= imap_answer(mp
, 1);
3016 imap_rename(const char *old
, const char *new)
3018 sighandler_type saveint
, savepipe
;
3019 enum okay ok
= STOP
;
3024 verbose
= value("verbose") != NULL
;
3025 if (mb
.mb_type
!= MB_IMAP
) {
3026 fprintf(stderr
, "Refusing to rename mailboxes "
3027 "in disconnected mode.\n");
3030 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3031 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3032 "server: \"%s\" not renamed to \"%s\".\n",
3037 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3038 safe_signal(SIGINT
, maincatch
);
3039 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3040 if (sigsetjmp(imapjmp
, 1) == 0) {
3041 if (savepipe
!= SIG_IGN
)
3042 safe_signal(SIGPIPE
, imapcatch
);
3043 ok
= imap_rename1(&mb
, protfile(old
), protfile(new));
3045 safe_signal(SIGINT
, saveint
);
3046 safe_signal(SIGPIPE
, savepipe
);
3049 ok
= cache_rename(old
, new);
3056 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3058 FILE *queuefp
= NULL
;
3061 enum okay ok
= STOP
;
3063 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3064 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3065 imap_quotestr(old
), imap_quotestr(new));
3066 IMAP_OUT(o
, MB_COMD
, goto out
)
3067 while (mp
->mb_active
& MB_COMD
)
3068 ok
= imap_answer(mp
, 1);
3074 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3076 FILE *queuefp
= NULL
;
3077 char o
[LINESIZE
], *newname
;
3078 char *buf
, *bp
, *cp
, iob
[4096];
3079 size_t bufsize
, buflen
, count
;
3080 enum okay ok
= OKAY
, rok
= OKAY
;
3081 long offs
, offs1
, offs2
, octets
;
3082 int n
, twice
, gotcha
= 0;
3084 buf
= smalloc(bufsize
= LINESIZE
);
3087 while (offs1
= ftell(fp
),
3088 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0)
3090 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3095 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3096 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3098 while (digitchar(*cp
&0377))
3104 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3106 IMAP_OUT(o
, MB_COMD
, continue)
3107 while (mp
->mb_active
& MB_COMD
)
3108 ok
= imap_answer(mp
, twice
);
3109 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3111 if (response_status
== RESPONSE_OK
&&
3112 mp
->mb_flags
& MB_UIDPLUS
) {
3113 imap_copyuid(mp
, NULL
,
3114 imap_unquotestr(newname
));
3116 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3117 IMAP_OUT(o
, MB_COMD
, continue)
3118 while (mp
->mb_active
& MB_COMD
)
3119 ok
= imap_answer(mp
, 1);
3122 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3123 if ((cp
= strrchr(bp
, '{')) == NULL
)
3125 octets
= atol(&cp
[1]) + 2;
3126 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3128 IMAP_OUT(o
, MB_COMD
, continue)
3129 while (mp
->mb_active
& MB_COMD
) {
3130 ok
= imap_answer(mp
, twice
);
3131 if (response_type
== RESPONSE_CONT
)
3136 fseek(fp
, offs
, SEEK_SET
);
3141 while (octets
> 0) {
3142 n
= octets
> sizeof iob
? sizeof iob
: octets
;
3144 if (fread(iob
, 1, n
, fp
) != n
)
3146 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3148 swrite(&mp
->mb_sock
, "");
3149 while (mp
->mb_active
& MB_COMD
) {
3150 ok
= imap_answer(mp
, 0);
3151 if (response_status
== RESPONSE_NO
&&
3153 fseek(fp
, offs
, SEEK_SET
);
3157 if (response_status
== RESPONSE_OK
&&
3158 mp
->mb_flags
& MB_UIDPLUS
) {
3160 fseek(fp
, offs1
, SEEK_SET
);
3161 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3162 fseek(fp
, offs2
, SEEK_SET
);
3167 fail
: fprintf(stderr
,
3168 "Invalid command in IMAP cache queue: \"%s\"\n",
3174 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3176 IMAP_OUT(o
, MB_COMD
, continue)
3177 while (mp
->mb_active
& MB_COMD
)
3178 ok
= imap_answer(mp
, 1);
3184 ftruncate(fileno(fp
), 0);
3191 imap_strex(const char *cp
, char **xp
)
3198 for (cq
= &cp
[1]; *cq
; cq
++) {
3201 else if (*cq
== '"')
3206 n
= salloc(cq
- cp
+ 2);
3207 memcpy(n
, cp
, cq
- cp
+ 1);
3208 n
[cq
- cp
+ 1] = '\0';
3210 *xp
= (char *)&cq
[1];
3215 check_expunged(void)
3217 if (expunged_messages
> 0) {
3219 "Command not executed - messages have been expunged\n");
3230 int omsgCount
= msgCount
;
3232 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3233 fprintf(stderr
, "Already connected.\n");
3236 unset_allow_undefined
= 1;
3237 unset_internal("disconnected");
3238 cp
= protbase(mailname
);
3239 if (strncmp(cp
, "imap://", 7) == 0)
3241 else if (strncmp(cp
, "imaps://", 8) == 0)
3243 if ((cq
= strchr(cp
, ':')) != NULL
)
3245 unset_internal(savecat("disconnected-", cp
));
3246 unset_allow_undefined
= 0;
3247 if (mb
.mb_type
== MB_CACHE
) {
3248 imap_setfile1(mailname
, 0, edit
, 1);
3249 if (msgCount
> omsgCount
)
3250 newmailinfo(omsgCount
);
3256 cdisconnect(void *vp
)
3260 if (mb
.mb_type
== MB_CACHE
) {
3261 fprintf(stderr
, "Not connected.\n");
3263 } else if (mb
.mb_type
== MB_IMAP
) {
3264 if (cached_uidvalidity(&mb
) == 0) {
3265 fprintf(stderr
, "The current mailbox is not cached.\n");
3271 assign("disconnected", "");
3272 if (mb
.mb_type
== MB_IMAP
) {
3273 sclose(&mb
.mb_sock
);
3274 imap_setfile1(mailname
, 0, edit
, 1);
3281 int *msgvec
= vp
, *ip
;
3284 if (mb
.mb_type
!= MB_IMAP
) {
3285 fprintf(stderr
, "Not connected to an IMAP server.\n");
3288 if (cached_uidvalidity(&mb
) == 0) {
3289 fprintf(stderr
, "The current mailbox is not cached.\n");
3292 for (ip
= msgvec
; *ip
; ip
++) {
3293 mp
= &message
[*ip
-1];
3294 if (!(mp
->m_have
& HAVE_BODY
))
3299 #else /* !HAVE_SOCKETS */
3306 fprintf(stderr
, catgets(catd
, CATSET
, 216,
3307 "No IMAP support compiled in.\n"));
3311 imap_setfile(const char *server
, int newmail
, int isedit
)
3318 imap_header(struct message
*mp
)
3325 imap_body(struct message
*mp
)
3332 imap_getheaders(int bot
, int top
)
3352 imap_newmail(int dummy
)
3359 imap_undelete(struct message
*m
, int n
)
3366 imap_unread(struct message
*m
, int n
)
3373 imap_append(const char *server
, FILE *fp
)
3381 imap_folders(const char *name
, int strip
)
3388 imap_remove(const char *name
)
3396 imap_rename(const char *old
, const char *new)
3403 imap_copy(struct message
*m
, int n
, const char *name
)
3411 imap_search1(const char *spec
, int f
)
3417 imap_thisaccount(const char *cp
)
3439 cdisconnect(void *vp
)
3452 #endif /* HAVE_SOCKETS */
3455 imap_read_date_time(const char *cp
)
3458 int i
, year
, month
, day
, hour
, minute
, second
;
3463 * "25-Jul-2004 15:33:44 +0200"
3467 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3469 day
= strtol(&cp
[1], NULL
, 10);
3470 for (i
= 0; month_names
[i
]; i
++)
3471 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3473 if (month_names
[i
] == NULL
)
3476 year
= strtol(&cp
[8], NULL
, 10);
3477 hour
= strtol(&cp
[13], NULL
, 10);
3478 minute
= strtol(&cp
[16], NULL
, 10);
3479 second
= strtol(&cp
[19], NULL
, 10);
3480 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3495 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3498 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3506 imap_read_date(const char *cp
)
3509 int year
, month
, day
, i
, tzdiff
;
3515 day
= strtol(cp
, &xp
, 10);
3516 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3518 for (i
= 0; month_names
[i
]; i
++)
3519 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3521 if (month_names
[i
] == NULL
)
3526 year
= strtol(&xp
[4], &yp
, 10);
3527 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3529 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3531 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3533 tzdiff
= t
- mktime(gmtime(&t
));
3534 tmptr
= localtime(&t
);
3535 if (tmptr
->tm_isdst
> 0)
3542 imap_make_date_time(time_t t
)
3546 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3548 tzdiff
= t
- mktime(gmtime(&t
));
3549 tzdiff_hour
= (int)(tzdiff
/ 60);
3550 tzdiff_min
= tzdiff_hour
% 60;
3552 tmptr
= localtime(&t
);
3553 if (tmptr
->tm_isdst
> 0)
3555 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3557 month_names
[tmptr
->tm_mon
],
3558 tmptr
->tm_year
+ 1900,
3568 imap_quotestr(const char *s
)
3572 np
= n
= salloc(2 * strlen(s
) + 3);
3575 if (*s
== '"' || *s
== '\\')
3585 imap_unquotestr(const char *s
)
3591 np
= n
= salloc(strlen(s
) + 1);