2 * Heirloom mailx - 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
42 static char sccsid
[] = "@(#)imap.c 1.222 (gritter) 3/13/09";
49 * Mail -- a mail program
51 * IMAP v4r1 client following RFC 2060.
64 #include <sys/socket.h>
66 #include <netinet/in.h>
67 #ifdef HAVE_ARPA_INET_H
68 #include <arpa/inet.h>
69 #endif /* HAVE_ARPA_INET_H */
75 #define IMAP_ANSWER() { \
76 if (mp->mb_type != MB_CACHE) { \
77 enum okay ok = OKAY; \
78 while (mp->mb_active & MB_COMD) \
79 ok = imap_answer(mp, 1); \
84 #define IMAP_OUT(x, y, action) \
86 if (mp->mb_type != MB_CACHE) { \
87 if (imap_finish(mp) == STOP) \
90 fprintf(stderr, ">>> %s", x); \
91 mp->mb_active |= (y); \
92 if (swrite(&mp->mb_sock, x) == STOP) \
95 if (queuefp != NULL) \
100 static struct record
{
101 struct record
*rec_next
;
102 unsigned long rec_count
;
127 static char *responded_tag
;
128 static char *responded_text
;
129 static char *responded_other_text
;
130 static long responded_other_number
;
136 MAILBOX_DATA_MAILBOX
,
141 MESSAGE_DATA_EXPUNGE
,
144 RESPONSE_OTHER_UNKNOWN
147 static enum list_attributes
{
149 LIST_NOINFERIORS
= 001,
155 static int list_hierarchy_delimiter
;
156 static char *list_name
;
159 struct list_item
*l_next
;
162 enum list_attributes l_attr
;
168 static char *imapbuf
;
169 static size_t imapbufsize
;
170 static sigjmp_buf imapjmp
;
171 static sighandler_type savealrm
;
172 static int reset_tio
;
173 static struct termios otio
;
174 static int imapkeepalive
;
175 static long had_exists
= -1;
176 static long had_expunge
= -1;
177 static long expunged_messages
;
178 static volatile int imaplock
;
180 static int same_imap_account
;
182 static void imap_other_get(char *pp
);
183 static void imap_response_get(const char **cp
);
184 static void imap_response_parse(void);
185 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
186 static enum okay
imap_parse_list(void);
187 static enum okay
imap_finish(struct mailbox
*mp
);
188 static void imap_timer_off(void);
189 static void imapcatch(int s
);
190 static void maincatch(int s
);
191 static enum okay
imap_noop1(struct mailbox
*mp
);
192 static void rec_queue(enum rec_type type
, unsigned long count
);
193 static enum okay
rec_dequeue(void);
194 static void rec_rmqueue(void);
195 static void imapalarm(int s
);
196 static int imap_use_starttls(const char *uhp
);
197 static enum okay
imap_preauth(struct mailbox
*mp
, const char *xserver
,
199 static enum okay
imap_capability(struct mailbox
*mp
);
200 static enum okay
imap_auth(struct mailbox
*mp
, const char *uhp
,
201 char *xuser
, const char *pass
);
202 static enum okay
imap_cram_md5(struct mailbox
*mp
,
203 char *xuser
, const char *xpass
);
204 static enum okay
imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
);
206 static enum okay
imap_gss(struct mailbox
*mp
, char *user
);
207 #endif /* USE_GSSAPI */
208 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
209 static void imap_init(struct mailbox
*mp
, int n
);
210 static void imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
,
212 static char *imap_have_password(const char *server
);
213 static void imap_split(char **server
, const char **sp
, int *use_ssl
,
214 const char **cp
, char **uhp
, char **mbx
,
215 const char **pass
, char **user
);
216 static int imap_setfile1(const char *xserver
, int newmail
, int isedit
,
218 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
219 size_t expected
, int need
,
220 const char *head
, size_t headsize
, long headlines
);
221 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
223 const char *head
, size_t headsize
, long headlines
);
224 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
226 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
227 struct message from
, enum havespec have
);
228 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
230 static enum okay
imap_exit(struct mailbox
*mp
);
231 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int
233 static enum okay
imap_close(struct mailbox
*mp
);
234 static enum okay
imap_update(struct mailbox
*mp
);
235 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
,
236 int n
, int c
, const char *sp
, int needstat
);
237 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
238 static const char *tag(int new);
239 static char *imap_putflags(int f
);
240 static void imap_getflags(const char *cp
, char **xp
, enum mflag
*f
);
241 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
242 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
243 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
244 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
245 struct list_item
**list
, struct list_item
**lend
, int level
);
246 static enum okay
imap_list(struct mailbox
*mp
, const char *base
,
247 int strip
, FILE *fp
);
248 static void dopr(FILE *fp
);
249 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
251 static enum okay
imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
252 unsigned long *olduid
, unsigned long *newuid
);
253 static enum okay
imap_appenduid_parse(const char *cp
,
254 unsigned long *uidvalidity
, unsigned long *uid
);
255 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
257 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
258 long off1
, long xsize
, long size
, long lines
,
259 int flag
, const char *name
);
260 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
261 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
,
262 int count
, const char *spec
, int f
);
263 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
264 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
266 static char *imap_strex(const char *cp
, char **xp
);
267 static enum okay
check_expunged(void);
270 imap_other_get(char *pp
)
274 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
276 response_other
= MAILBOX_DATA_FLAGS
;
277 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
279 response_other
= MAILBOX_DATA_LIST
;
280 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
282 response_other
= MAILBOX_DATA_LSUB
;
283 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
285 response_other
= MAILBOX_DATA_MAILBOX
;
286 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
288 response_other
= MAILBOX_DATA_SEARCH
;
289 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
291 response_other
= MAILBOX_DATA_STATUS
;
292 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
294 response_other
= CAPABILITY_DATA
;
296 responded_other_number
= strtol(pp
, &xp
, 10);
299 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
300 response_other
= MAILBOX_DATA_EXISTS
;
301 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
302 response_other
= MAILBOX_DATA_RECENT
;
303 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
304 response_other
= MESSAGE_DATA_EXPUNGE
;
305 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
307 response_other
= MESSAGE_DATA_FETCH
;
309 response_other
= RESPONSE_OTHER_UNKNOWN
;
311 responded_other_text
= pp
;
315 imap_response_get(const char **cp
)
317 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
319 response_status
= RESPONSE_OK
;
320 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
322 response_status
= RESPONSE_NO
;
323 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
325 response_status
= RESPONSE_BAD
;
326 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
328 response_status
= RESPONSE_PREAUTH
;
329 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
331 response_status
= RESPONSE_BYE
;
333 response_status
= RESPONSE_OTHER
;
337 imap_response_parse(void)
339 static char *parsebuf
;
340 static size_t parsebufsize
;
341 const char *ip
= imapbuf
;
344 if (parsebufsize
< imapbufsize
) {
346 parsebuf
= smalloc(parsebufsize
= imapbufsize
);
348 strcpy(parsebuf
, imapbuf
);
352 response_type
= RESPONSE_CONT
;
367 imap_response_get(&ip
);
368 pp
= &parsebuf
[ip
- imapbuf
];
369 switch (response_status
) {
371 response_type
= RESPONSE_FATAL
;
374 response_type
= RESPONSE_DATA
;
378 responded_tag
= parsebuf
;
379 while (*pp
&& *pp
!= ' ')
382 response_type
= RESPONSE_ILLEGAL
;
386 while (*pp
&& *pp
== ' ')
389 response_type
= RESPONSE_ILLEGAL
;
392 ip
= &imapbuf
[pp
- parsebuf
];
393 response_type
= RESPONSE_TAGGED
;
394 imap_response_get(&ip
);
395 pp
= &parsebuf
[ip
- imapbuf
];
398 if (response_type
!= RESPONSE_CONT
&&
399 response_type
!= RESPONSE_ILLEGAL
&&
400 response_status
== RESPONSE_OTHER
)
405 imap_answer(struct mailbox
*mp
, int errprnt
)
410 if (mp
->mb_type
== MB_CACHE
)
412 again
: if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
414 fputs(imapbuf
, stderr
);
415 imap_response_parse();
416 if (response_type
== RESPONSE_ILLEGAL
)
418 if (response_type
== RESPONSE_CONT
)
420 if (response_status
== RESPONSE_OTHER
) {
421 if (response_other
== MAILBOX_DATA_EXISTS
) {
422 had_exists
= responded_other_number
;
423 rec_queue(REC_EXISTS
, responded_other_number
);
426 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
427 rec_queue(REC_EXPUNGE
, responded_other_number
);
435 if (response_type
== RESPONSE_TAGGED
) {
436 if (asccasecmp(responded_tag
, tag(0)) == 0)
441 switch (response_status
) {
442 case RESPONSE_PREAUTH
:
443 mp
->mb_active
&= ~MB_PREAUTH
;
454 fprintf(stderr
, catgets(catd
, CATSET
, 218,
455 "IMAP error: %s"), responded_text
);
457 case RESPONSE_UNKNOWN
: /* does not happen */
460 mp
->mb_active
= MB_NONE
;
468 if (response_status
!= RESPONSE_OTHER
&&
469 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
470 fprintf(stderr
, "IMAP alert: %s", &responded_text
[8]);
472 mp
->mb_active
&= ~MB_COMD
;
475 mp
->mb_active
= MB_NONE
;
481 imap_parse_list(void)
485 cp
= responded_other_text
;
486 list_attributes
= LIST_NONE
;
488 while (*cp
&& *cp
!= ')') {
490 if (ascncasecmp(&cp
[1], "Noinferiors ", 12)
492 list_attributes
|= LIST_NOINFERIORS
;
494 } else if (ascncasecmp(&cp
[1], "Noselect ", 9)
496 list_attributes
|= LIST_NOSELECT
;
498 } else if (ascncasecmp(&cp
[1], "Marked ", 7)
500 list_attributes
|= LIST_MARKED
;
502 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9)
504 list_attributes
|= LIST_UNMARKED
;
515 list_hierarchy_delimiter
= EOF
;
519 list_hierarchy_delimiter
= *cp
++ & 0377;
520 if (cp
[0] != '"' || cp
[1] != ' ')
523 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' &&
525 list_hierarchy_delimiter
= EOF
;
531 while (*cp
&& *cp
!= '\r')
538 imap_finish(struct mailbox
*mp
)
540 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
548 if (imapkeepalive
> 0) {
550 safe_signal(SIGALRM
, savealrm
);
558 tcsetattr(0, TCSADRAIN
, &otio
);
561 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
562 siglongjmp(imapjmp
, 1);
565 fprintf(stderr
, "Received SIGPIPE during IMAP operation\n");
574 if (interrupts
++ == 0) {
575 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
582 imap_noop1(struct mailbox
*mp
)
585 FILE *queuefp
= NULL
;
587 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
588 IMAP_OUT(o
, MB_COMD
, return STOP
)
596 sighandler_type saveint
, savepipe
;
602 if (mb
.mb_type
!= MB_IMAP
)
604 verbose
= value("verbose") != NULL
;
606 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
607 safe_signal(SIGINT
, maincatch
);
608 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
609 if (sigsetjmp(imapjmp
, 1) == 0) {
610 if (savepipe
!= SIG_IGN
)
611 safe_signal(SIGPIPE
, imapcatch
);
612 ok
= imap_noop1(&mb
);
614 safe_signal(SIGINT
, saveint
);
615 safe_signal(SIGPIPE
, savepipe
);
623 rec_queue(enum rec_type type
, unsigned long count
)
627 rp
= scalloc(1, sizeof *rp
);
629 rp
->rec_count
= count
;
630 if (record
&& recend
) {
631 recend
->rec_next
= rp
;
634 record
= recend
= rp
;
640 struct message
*omessage
;
642 struct record
*rp
= record
, *rq
= NULL
;
643 unsigned long exists
= 0, i
;
648 message
= smalloc((msgCount
+1) * sizeof *message
);
650 memcpy(message
, omessage
, msgCount
* sizeof *message
);
651 memset(&message
[msgCount
], 0, sizeof *message
);
653 switch (rp
->rec_type
) {
655 exists
= rp
->rec_count
;
658 if (rp
->rec_count
== 0) {
662 if (rp
->rec_count
> (unsigned long)msgCount
) {
663 if (exists
== 0 || rp
->rec_count
> exists
--)
669 delcache(&mb
, &message
[rp
->rec_count
-1]);
670 memmove(&message
[rp
->rec_count
-1],
671 &message
[rp
->rec_count
],
672 (msgCount
- rp
->rec_count
+ 1) *
676 * If the message was part of a collapsed thread,
677 * the m_collapsed field of one of its ancestors
678 * should be incremented. It seems hardly possible
679 * to do this with the current message structure,
680 * though. The result is that a '+' may be shown
681 * in the header summary even if no collapsed
691 record
= recend
= NULL
;
692 if (ok
== OKAY
&& exists
> (unsigned long)msgCount
) {
693 message
= srealloc(message
,
694 (exists
+ 1) * sizeof *message
);
695 memset(&message
[msgCount
], 0,
696 (exists
- msgCount
+ 1) * sizeof *message
);
697 for (i
= msgCount
; i
< exists
; i
++)
699 imap_flags(&mb
, msgCount
+1, exists
);
712 struct record
*rp
, *rq
= NULL
;
714 for (rp
= record
; rp
; rp
= rp
->rec_next
) {
719 record
= recend
= NULL
;
726 sighandler_type saveint
;
727 sighandler_type savepipe
;
730 if (imaplock
++ == 0) {
731 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
732 safe_signal(SIGINT
, maincatch
);
733 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
734 if (sigsetjmp(imapjmp
, 1)) {
735 safe_signal(SIGINT
, saveint
);
736 safe_signal(SIGPIPE
, savepipe
);
739 if (savepipe
!= SIG_IGN
)
740 safe_signal(SIGPIPE
, imapcatch
);
741 if (imap_noop1(&mb
) != OKAY
) {
742 safe_signal(SIGINT
, saveint
);
743 safe_signal(SIGPIPE
, savepipe
);
746 safe_signal(SIGINT
, saveint
);
747 safe_signal(SIGPIPE
, savepipe
);
749 brk
: alarm(imapkeepalive
);
754 imap_use_starttls(const char *uhp
)
758 if (value("imap-use-starttls"))
760 var
= savecat("imap-use-starttls-", uhp
);
761 return value(var
) != NULL
;
765 imap_preauth(struct mailbox
*mp
, const char *xserver
, const char *uhp
)
769 mp
->mb_active
|= MB_PREAUTH
;
771 if ((cp
= strchr(xserver
, ':')) != NULL
) {
772 server
= salloc(cp
- xserver
+ 1);
773 memcpy(server
, xserver
, cp
- xserver
);
774 server
[cp
- xserver
] = '\0';
776 server
= (char *)xserver
;
778 if (mp
->mb_sock
.s_use_ssl
== 0 && imap_use_starttls(uhp
)) {
779 FILE *queuefp
= NULL
;
782 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
783 IMAP_OUT(o
, MB_COMD
, return STOP
);
785 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
789 if (imap_use_starttls(uhp
)) {
790 fprintf(stderr
, "No SSL support compiled in.\n");
793 #endif /* !USE_SSL */
799 imap_capability(struct mailbox
*mp
)
802 FILE *queuefp
= NULL
;
806 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
807 IMAP_OUT(o
, MB_COMD
, return STOP
)
808 while (mp
->mb_active
& MB_COMD
) {
809 ok
= imap_answer(mp
, 0);
810 if (response_status
== RESPONSE_OTHER
&&
811 response_other
== CAPABILITY_DATA
) {
812 cp
= responded_other_text
;
814 while (spacechar(*cp
&0377))
816 if (strncmp(cp
, "UIDPLUS", 7) == 0 &&
817 spacechar(cp
[7]&0377))
819 mp
->mb_flags
|= MB_UIDPLUS
;
820 while (*cp
&& !spacechar(*cp
&0377))
829 imap_auth(struct mailbox
*mp
, const char *uhp
, char *xuser
, const char *pass
)
834 if (!(mp
->mb_active
& MB_PREAUTH
))
836 if ((auth
= value("imap-auth")) == NULL
) {
837 var
= ac_alloc(strlen(uhp
) + 11);
838 strcpy(var
, "imap-auth-");
839 strcpy(&var
[10], uhp
);
843 if (auth
== NULL
|| strcmp(auth
, "login") == 0)
844 return imap_login(mp
, xuser
, pass
);
845 if (strcmp(auth
, "cram-md5") == 0)
846 return imap_cram_md5(mp
, xuser
, pass
);
847 if (strcmp(auth
, "gssapi") == 0) {
849 return imap_gss(mp
, xuser
);
850 #else /* !USE_GSSAPI */
851 fprintf(stderr
, "No GSSAPI support compiled in.\n");
853 #endif /* !USE_GSSAPI */
855 fprintf(stderr
, "Unknown IMAP authentication method: \"%s\"\n", auth
);
860 * Implementation of RFC 2194.
863 imap_cram_md5(struct mailbox
*mp
, char *xuser
, const char *xpass
)
866 const char *user
, *pass
;
868 FILE *queuefp
= NULL
;
871 retry
: if (xuser
== NULL
) {
872 if ((user
= getuser()) == NULL
)
877 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
881 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
882 IMAP_OUT(o
, 0, return STOP
)
884 if (response_type
!= RESPONSE_CONT
)
886 cp
= cram_md5_string(user
, pass
, responded_text
);
887 IMAP_OUT(cp
, MB_COMD
, return STOP
)
888 while (mp
->mb_active
& MB_COMD
)
889 ok
= imap_answer(mp
, 1);
898 imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
)
901 const char *user
, *pass
;
902 FILE *queuefp
= NULL
;
905 retry
: if (xuser
== NULL
) {
906 if ((user
= getuser()) == NULL
)
911 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
915 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
916 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
917 IMAP_OUT(o
, MB_COMD
, return STOP
)
918 while (mp
->mb_active
& MB_COMD
)
919 ok
= imap_answer(mp
, 1);
928 #include "imap_gssapi.c"
929 #endif /* USE_GSSAPI */
932 imap_select(struct mailbox
*mp
, off_t
*size
, int *count
, const char *mbx
)
937 FILE *queuefp
= NULL
;
940 mp
->mb_uidvalidity
= 0;
941 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
942 IMAP_OUT(o
, MB_COMD
, return STOP
)
943 while (mp
->mb_active
& MB_COMD
) {
944 ok
= imap_answer(mp
, 1);
945 if (response_status
!= RESPONSE_OTHER
&&
946 (cp
= asccasestr(responded_text
,
947 "[UIDVALIDITY ")) != NULL
)
948 mp
->mb_uidvalidity
= atol(&cp
[13]);
950 *count
= had_exists
> 0 ? had_exists
: 0;
951 if (response_status
!= RESPONSE_OTHER
&&
952 ascncasecmp(responded_text
, "[READ-ONLY] ", 12)
959 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
962 FILE *queuefp
= NULL
;
965 unsigned x
= X
, y
= Y
, n
;
967 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
968 IMAP_OUT(o
, MB_COMD
, return STOP
)
969 while (mp
->mb_active
& MB_COMD
) {
971 if (response_status
== RESPONSE_OTHER
&&
972 response_other
== MESSAGE_DATA_FETCH
) {
973 n
= responded_other_number
;
980 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
985 imap_getflags(cp
, &cp
, &m
->m_flag
);
987 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
988 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
989 getcache1(mp
, m
, NEED_UNSPEC
, 1);
990 m
->m_flag
&= ~MHIDDEN
;
992 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
994 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
997 snprintf(o
, sizeof o
,
998 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1000 IMAP_OUT(o
, MB_COMD
, return STOP
)
1001 while (mp
->mb_active
& MB_COMD
) {
1003 if (response_status
== RESPONSE_OTHER
&&
1004 response_other
== MESSAGE_DATA_FETCH
) {
1005 n
= responded_other_number
;
1011 if ((cp
= asccasestr(responded_other_text
,
1012 "RFC822.SIZE ")) != NULL
)
1013 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1014 if ((cp
= asccasestr(responded_other_text
,
1015 "INTERNALDATE ")) != NULL
)
1016 m
->m_time
= imap_read_date_time(&cp
[13]);
1019 for (n
= X
; n
<= Y
; n
++)
1020 putcache(mp
, &message
[n
-1]);
1025 imap_init(struct mailbox
*mp
, int n
)
1027 struct message
*m
= &message
[n
];
1030 m
->m_flag
= MUSED
|MNOFROM
;
1036 imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
, int *prevcount
)
1038 struct message
*omessage
= 0;
1039 int i
, omsgCount
= 0;
1040 enum okay dequeued
= STOP
;
1042 if (newmail
|| transparent
) {
1044 omsgCount
= msgCount
;
1047 dequeued
= rec_dequeue();
1048 if (had_exists
>= 0) {
1049 if (dequeued
!= OKAY
)
1050 msgCount
= had_exists
;
1053 if (had_expunge
>= 0) {
1054 if (dequeued
!= OKAY
)
1055 msgCount
-= had_expunge
;
1058 if (newmail
&& expunged_messages
)
1059 printf("Expunged %ld message%s.\n",
1061 expunged_messages
!= 1 ? "s" : "");
1062 *prevcount
= omsgCount
- expunged_messages
;
1063 expunged_messages
= 0;
1065 fputs("IMAP error: Negative message count\n", stderr
);
1068 if (dequeued
!= OKAY
) {
1069 message
= scalloc(msgCount
+ 1, sizeof *message
);
1070 for (i
= 0; i
< msgCount
; i
++)
1072 if (!newmail
&& mp
->mb_type
== MB_IMAP
)
1075 imap_flags(mp
, 1, msgCount
);
1076 message
[msgCount
].m_size
= 0;
1077 message
[msgCount
].m_lines
= 0;
1080 if (newmail
|| transparent
)
1081 transflags(omessage
, omsgCount
, transparent
);
1087 imap_have_password(const char *server
)
1091 var
= ac_alloc(strlen(server
) + 10);
1092 strcpy(var
, "password-");
1093 strcpy(&var
[9], server
);
1094 if ((cp
= value(var
)) != NULL
)
1101 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1102 char **uhp
, char **mbx
, const char **pass
, char **user
)
1105 if (strncmp(*sp
, "imap://", 7) == 0) {
1109 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1112 #endif /* USE_SSL */
1114 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1115 *uhp
= savestr((char *)(*sp
));
1116 (*uhp
)[*cp
- *sp
] = '\0';
1117 *mbx
= (char *)&(*cp
)[1];
1120 (*server
)[*cp
- *server
] = '\0';
1121 *uhp
= (char *)(*sp
);
1124 *pass
= imap_have_password(*uhp
);
1125 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1126 *user
= salloc(*cp
- *uhp
+ 1);
1127 memcpy(*user
, *uhp
, *cp
- *uhp
);
1128 (*user
)[*cp
- *uhp
] = '\0';
1130 *user
= strdec(*user
);
1138 imap_setfile(const char *xserver
, int newmail
, int isedit
)
1140 return imap_setfile1(xserver
, newmail
, isedit
, 0);
1144 imap_setfile1(const char *xserver
, int newmail
, int isedit
, int transparent
)
1147 sighandler_type saveint
;
1148 sighandler_type savepipe
;
1149 char *server
, *user
, *account
;
1150 const char *cp
, *sp
, *pass
;
1153 enum mbflags same_flags
;
1160 server
= savestr((char *)xserver
);
1161 verbose
= value("verbose") != NULL
;
1163 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1164 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1165 if (saveint
!= SIG_IGN
)
1166 safe_signal(SIGINT
, imapcatch
);
1167 if (savepipe
!= SIG_IGN
)
1168 safe_signal(SIGPIPE
, imapcatch
);
1172 same_flags
= mb
.mb_flags
;
1173 same_imap_account
= 0;
1174 sp
= protbase(server
);
1175 if (mb
.mb_imap_account
) {
1176 if (mb
.mb_sock
.s_fd
> 0 &&
1177 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1178 disconnected(mb
.mb_imap_account
) == 0)
1179 same_imap_account
= 1;
1181 account
= sstrdup(sp
);
1182 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
1184 if (!same_imap_account
) {
1185 if (!disconnected(account
) &&
1186 sopen(sp
, &so
, use_ssl
, uhp
,
1187 use_ssl
? "imaps" : "imap", verbose
) != OKAY
)
1194 free(mb
.mb_imap_account
);
1195 mb
.mb_imap_account
= account
;
1196 if (!same_imap_account
) {
1197 if (mb
.mb_sock
.s_fd
>= 0)
1198 sclose(&mb
.mb_sock
);
1200 same_imap_account
= 0;
1210 free(mb
.mb_imap_mailbox
);
1211 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1214 mb
.mb_type
= MB_VOID
;
1215 mb
.mb_active
= MB_NONE
;;
1217 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1218 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1219 if (sigsetjmp(imapjmp
, 1)) {
1221 safe_signal(SIGINT
, saveint
);
1222 safe_signal(SIGPIPE
, savepipe
);
1226 if (saveint
!= SIG_IGN
)
1227 safe_signal(SIGINT
, imapcatch
);
1228 if (savepipe
!= SIG_IGN
)
1229 safe_signal(SIGPIPE
, imapcatch
);
1230 if (mb
.mb_sock
.s_fd
< 0) {
1231 if (disconnected(mb
.mb_imap_account
)) {
1232 if (cache_setptr(transparent
) == STOP
)
1234 "Mailbox \"%s\" is not cached.\n",
1238 if ((cp
= value("imap-keepalive")) != NULL
) {
1239 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1240 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1241 alarm(imapkeepalive
);
1245 mb
.mb_sock
.s_desc
= "IMAP";
1246 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1247 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1248 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1249 sclose(&mb
.mb_sock
);
1251 safe_signal(SIGINT
, saveint
);
1252 safe_signal(SIGPIPE
, savepipe
);
1256 } else /* same account */
1257 mb
.mb_flags
|= same_flags
;
1258 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
1259 mb
.mb_type
= MB_IMAP
;
1261 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1262 /*sclose(&mb.mb_sock);
1264 safe_signal(SIGINT
, saveint
);
1265 safe_signal(SIGPIPE
, savepipe
);
1267 mb
.mb_type
= MB_VOID
;
1271 imap_setptr(&mb
, newmail
, transparent
, &prevcount
);
1272 done
: setmsize(msgCount
);
1273 if (!newmail
&& !transparent
)
1275 safe_signal(SIGINT
, saveint
);
1276 safe_signal(SIGPIPE
, savepipe
);
1278 if (!newmail
&& mb
.mb_type
== MB_IMAP
)
1279 purgecache(&mb
, message
, msgCount
);
1280 if ((newmail
|| transparent
) && mb
.mb_sorted
) {
1284 if (!newmail
&& !edit
&& msgCount
== 0) {
1285 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1286 value("emptystart") == NULL
)
1287 fprintf(stderr
, catgets(catd
, CATSET
, 258,
1288 "No mail at %s\n"), server
);
1292 newmailinfo(prevcount
);
1297 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1299 const char *head
, size_t headsize
, long headlines
)
1301 char *line
= NULL
, *lp
;
1302 size_t linesize
= 0, linelen
, size
= 0;
1303 int emptyline
= 0, lines
= 0, excess
= 0;
1306 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1307 offset
= ftell(mp
->mb_otf
);
1309 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1310 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1312 if (linelen
> expected
) {
1313 excess
= linelen
- expected
;
1317 * Need to mask 'From ' lines. This cannot be done properly
1318 * since some servers pass them as 'From ' and others as
1319 * '>From '. Although one could identify the first kind of
1320 * server in principle, it is not possible to identify the
1321 * second as '>From ' may also come from a server of the
1322 * first type as actual data. So do what is absolutely
1323 * necessary only - mask 'From '.
1325 * If the line is the first line of the message header, it
1326 * is likely a real 'From ' line. In this case, it is just
1327 * ignored since it violates all standards.
1329 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
1330 lp
[3] == 'm' && lp
[4] == ' ') {
1331 if (lines
+ headlines
!= 0) {
1332 fputc('>', mp
->mb_otf
);
1337 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1338 lp
[linelen
-2] == '\r')) {
1339 emptyline
= linelen
<= 2;
1341 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1342 size
+= linelen
- 1;
1345 fputc('\n', mp
->mb_otf
);
1348 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1352 skip
: if ((expected
-= linelen
) <= 0)
1357 * This is very ugly; but some IMAP daemons don't end a
1358 * message with \r\n\r\n, and we need \n\n for mbox format.
1360 fputc('\n', mp
->mb_otf
);
1366 m
->m_size
= size
+ headsize
;
1367 m
->m_lines
= lines
+ headlines
;
1368 m
->m_block
= mailx_blockof(offset
);
1369 m
->m_offset
= mailx_offsetof(offset
);
1372 m
->m_have
|= HAVE_HEADER
;
1375 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1376 m
->m_xlines
= m
->m_lines
;
1377 m
->m_xsize
= m
->m_size
;
1386 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1387 const char *head
, size_t headsize
, long headlines
)
1393 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1394 offset
= ftell(mp
->mb_otf
);
1396 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1398 fwrite(str
, 1, len
, mp
->mb_otf
);
1399 fputc('\n', mp
->mb_otf
);
1404 m
->m_size
= headsize
+ len
;
1405 m
->m_lines
= headlines
+ 1;
1406 m
->m_block
= mailx_blockof(offset
);
1407 m
->m_offset
= mailx_offsetof(offset
);
1408 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1409 m
->m_xlines
= m
->m_lines
;
1410 m
->m_xsize
= m
->m_size
;
1415 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1417 sighandler_type saveint
= SIG_IGN
;
1418 sighandler_type savepipe
= SIG_IGN
;
1419 char o
[LINESIZE
], *cp
= NULL
, *item
= NULL
, *resp
= NULL
, *loc
= NULL
;
1420 size_t expected
, headsize
= 0;
1421 int number
= m
- message
+ 1;
1422 enum okay ok
= STOP
;
1423 FILE *queuefp
= NULL
;
1428 unsigned long u
= 0;
1443 verbose
= value("verbose") != NULL
;
1444 if (getcache(mp
, m
, need
) == OKAY
)
1446 if (mp
->mb_type
== MB_CACHE
) {
1447 fprintf(stderr
, "Message %u not available.\n", number
);
1450 if (mp
->mb_sock
.s_fd
< 0) {
1451 fprintf(stderr
, "IMAP connection closed.\n");
1456 resp
= item
= "RFC822.HEADER";
1459 item
= "BODY.PEEK[]";
1461 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1462 char *hdr
= smalloc(m
->m_size
);
1464 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1465 m
->m_offset
), SEEK_SET
) < 0 ||
1466 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1472 headsize
= m
->m_size
;
1473 headlines
= m
->m_lines
;
1474 item
= "BODY.PEEK[TEXT]";
1475 resp
= "BODY[TEXT]";
1482 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1483 if (sigsetjmp(imapjmp
, 1)) {
1484 safe_signal(SIGINT
, saveint
);
1485 safe_signal(SIGPIPE
, savepipe
);
1489 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1490 safe_signal(SIGINT
, maincatch
);
1491 if (savepipe
!= SIG_IGN
)
1492 safe_signal(SIGPIPE
, imapcatch
);
1494 snprintf(o
, sizeof o
,
1495 "%s UID FETCH %lu (%s)\r\n",
1496 tag(1), m
->m_uid
, item
);
1498 if (check_expunged() == STOP
)
1500 snprintf(o
, sizeof o
,
1501 "%s FETCH %u (%s)\r\n",
1502 tag(1), number
, item
);
1504 IMAP_OUT(o
, MB_COMD
, goto out
)
1506 ok
= imap_answer(mp
, 1);
1509 if (response_status
!= RESPONSE_OTHER
||
1510 response_other
!= MESSAGE_DATA_FETCH
)
1512 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1515 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1523 n
= responded_other_number
;
1524 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1525 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1527 if ((cp
= strchr(loc
, '"')) != NULL
) {
1528 cp
= imap_unquotestr(cp
);
1529 imap_putstr(mp
, m
, cp
,
1530 head
, headsize
, headlines
);
1532 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1533 m
->m_xlines
= m
->m_lines
;
1534 m
->m_xsize
= m
->m_size
;
1538 expected
= atol(&cp
[1]);
1539 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1540 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1544 imap_fetchdata(mp
, &mt
, expected
, need
,
1545 head
, headsize
, headlines
);
1547 commitmsg(mp
, m
, mt
, mt
.m_have
);
1550 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1551 &mp
->mb_sock
) > 0) {
1553 fputs(imapbuf
, stderr
);
1554 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1556 if (u
== m
->m_uid
) {
1557 commitmsg(mp
, m
, mt
, mt
.m_have
);
1563 out
: while (mp
->mb_active
& MB_COMD
)
1564 ok
= imap_answer(mp
, 1);
1565 if (saveint
!= SIG_IGN
)
1566 safe_signal(SIGINT
, saveint
);
1567 if (savepipe
!= SIG_IGN
)
1568 safe_signal(SIGPIPE
, savepipe
);
1579 imap_header(struct message
*m
)
1581 return imap_get(&mb
, m
, NEED_HEADER
);
1586 imap_body(struct message
*m
)
1588 return imap_get(&mb
, m
, NEED_BODY
);
1592 commitmsg(struct mailbox
*mp
, struct message
*to
,
1593 struct message from
, enum havespec have
)
1595 to
->m_size
= from
.m_size
;
1596 to
->m_lines
= from
.m_lines
;
1597 to
->m_block
= from
.m_block
;
1598 to
->m_offset
= from
.m_offset
;
1600 if (have
& HAVE_BODY
) {
1601 to
->m_xlines
= from
.m_lines
;
1602 to
->m_xsize
= from
.m_size
;
1612 int top
/* bot > top */
1615 char o
[LINESIZE
], *cp
;
1620 FILE *queuefp
= NULL
;
1623 snprintf(o
, sizeof o
,
1624 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1625 tag(1), m
[bot
-1].m_uid
, m
[top
-1].m_uid
);
1627 if (check_expunged() == STOP
)
1629 snprintf(o
, sizeof o
,
1630 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1633 IMAP_OUT(o
, MB_COMD
, return STOP
)
1635 ok
= imap_answer(mp
, 1);
1636 if (response_status
!= RESPONSE_OTHER
)
1638 if (response_other
!= MESSAGE_DATA_FETCH
)
1640 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1642 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1644 expected
= atol(&cp
[1]);
1645 if (m
[bot
-1].m_uid
) {
1646 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1648 for (n
= bot
; n
<= top
; n
++)
1649 if ((unsigned long)u
== m
[n
-1].m_uid
)
1652 imap_fetchdata(mp
, NULL
, expected
,
1660 n
= responded_other_number
;
1661 if (n
<= 0 || n
> msgCount
) {
1662 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1667 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1668 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1669 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1670 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1671 &mp
->mb_sock
) > 0) {
1673 fputs(imapbuf
, stderr
);
1674 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1676 for (n
= bot
; n
<= top
; n
++)
1677 if ((unsigned long)u
== m
[n
-1].m_uid
)
1679 if (n
<= top
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1680 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1685 while (mp
->mb_active
& MB_COMD
)
1686 ok
= imap_answer(mp
, 1);
1691 imap_getheaders(int bot
, int top
)
1693 sighandler_type saveint
, savepipe
;
1694 enum okay ok
= STOP
;
1702 verbose
= value("verbose") != NULL
;
1703 if (mb
.mb_type
== MB_CACHE
)
1709 for (i
= bot
; i
< top
; i
++) {
1710 if (message
[i
-1].m_have
& HAVE_HEADER
||
1711 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1717 for (i
= top
; i
> bot
; i
--) {
1718 if (message
[i
-1].m_have
& HAVE_HEADER
||
1719 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1728 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1729 safe_signal(SIGINT
, maincatch
);
1730 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1731 if (sigsetjmp(imapjmp
, 1) == 0) {
1732 if (savepipe
!= SIG_IGN
)
1733 safe_signal(SIGPIPE
, imapcatch
);
1734 for (i
= bot
; i
<= top
; i
+= chunk
) {
1735 ok
= imap_fetchheaders(&mb
, message
, i
,
1736 i
+chunk
-1 < top
? i
+chunk
-1 : top
);
1741 safe_signal(SIGINT
, saveint
);
1742 safe_signal(SIGPIPE
, savepipe
);
1747 imap_exit(struct mailbox
*mp
)
1750 FILE *queuefp
= NULL
;
1752 verbose
= value("verbose") != NULL
;
1753 mp
->mb_active
|= MB_BYE
;
1754 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1755 IMAP_OUT(o
, MB_COMD
, return STOP
)
1761 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1763 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1764 if (mp
->mb_type
== MB_IMAP
)
1770 imap_close(struct mailbox
*mp
)
1773 FILE *queuefp
= NULL
;
1775 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1776 IMAP_OUT(o
, MB_COMD
, return STOP
)
1782 imap_update(struct mailbox
*mp
)
1784 FILE *readstat
= NULL
;
1786 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1788 verbose
= value("verbose") != NULL
;
1789 if (Tflag
!= NULL
) {
1790 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
1793 if (!edit
&& mp
->mb_perm
!= 0) {
1795 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1796 if (m
->m_flag
& MBOX
)
1800 if (makembox() == STOP
)
1803 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1804 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
1807 if ((id
= hfield("message-id", m
)) != NULL
||
1808 (id
= hfield("article-id", m
)) != NULL
)
1809 fprintf(readstat
, "%s\n", id
);
1811 if (mp
->mb_perm
== 0) {
1814 dodel
= m
->m_flag
& MDELETED
;
1816 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1817 (m
->m_flag
&MTOUCH
) == 0);
1820 * Fetch the result after around each 800 STORE commands
1821 * sent (approx. 32k data sent). Otherwise, servers will
1822 * try to flush the return queue at some point, leading
1823 * to a deadlock if we are still writing commands but not
1824 * reading their results.
1826 needstat
= stored
> 0 && stored
% 800 == 0;
1828 * Even if this message has been deleted, continue
1829 * to set further flags. This is necessary to support
1830 * Gmail semantics, where "delete" actually means
1831 * "archive", and the flags are applied to the copy
1834 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1835 imap_store(mp
, m
, m
-message
+1,
1836 '+', "\\Seen", needstat
);
1839 if (m
->m_flag
& MFLAG
) {
1840 imap_store(mp
, m
, m
-message
+1,
1841 '+', "\\Flagged", needstat
);
1844 if (m
->m_flag
& MUNFLAG
) {
1845 imap_store(mp
, m
, m
-message
+1,
1846 '-', "\\Flagged", needstat
);
1849 if (m
->m_flag
& MANSWER
) {
1850 imap_store(mp
, m
, m
-message
+1,
1851 '+', "\\Answered", needstat
);
1854 if (m
->m_flag
& MUNANSWER
) {
1855 imap_store(mp
, m
, m
-message
+1,
1856 '-', "\\Answered", needstat
);
1859 if (m
->m_flag
& MDRAFT
) {
1860 imap_store(mp
, m
, m
-message
+1,
1861 '+', "\\Draft", needstat
);
1864 if (m
->m_flag
& MUNDRAFT
) {
1865 imap_store(mp
, m
, m
-message
+1,
1866 '-', "\\Draft", needstat
);
1870 imap_delete(mp
, m
-message
+1, m
, needstat
);
1873 } else if (mp
->mb_type
!= MB_CACHE
||
1874 (! edit
&& ! (m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))) ||
1875 (m
->m_flag
& (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1876 (MPRESERVE
|MTOUCH
) ||
1877 (edit
&& ! (m
->m_flag
& MDELETED
)))
1879 if (m
->m_flag
& MNEW
) {
1881 m
->m_flag
|= MSTATUS
;
1884 bypass
: if (readstat
!= NULL
)
1888 for (m
= &message
[0]; m
< &message
[msgCount
]; m
++)
1889 if (!(m
->m_flag
&MUNLINKED
) &&
1890 m
->m_flag
&(MBOXED
|MDELETED
|MSAVED
|MSTATUS
|
1891 MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
1896 if ((gotcha
|| modflags
) && edit
) {
1897 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
1898 printf(value("bsdcompat") || value("bsdmsgs") ?
1899 catgets(catd
, CATSET
, 170, "complete\n") :
1900 catgets(catd
, CATSET
, 212, "updated.\n"));
1901 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1903 printf(catgets(catd
, CATSET
, 155,
1904 "Held 1 message in %s\n"), mailname
);
1906 printf(catgets(catd
, CATSET
, 156,
1907 "Held %d messages in %s\n"), held
, mailname
);
1916 sighandler_type saveint
;
1917 sighandler_type savepipe
;
1919 verbose
= value("verbose") != NULL
;
1920 if (mb
.mb_type
== MB_CACHE
) {
1924 if (mb
.mb_sock
.s_fd
< 0) {
1925 fprintf(stderr
, "IMAP connection closed.\n");
1929 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1930 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1931 if (sigsetjmp(imapjmp
, 1)) {
1932 safe_signal(SIGINT
, saveint
);
1933 safe_signal(SIGPIPE
, saveint
);
1937 if (saveint
!= SIG_IGN
)
1938 safe_signal(SIGINT
, imapcatch
);
1939 if (savepipe
!= SIG_IGN
)
1940 safe_signal(SIGPIPE
, imapcatch
);
1942 if (!same_imap_account
) {
1944 sclose(&mb
.mb_sock
);
1946 safe_signal(SIGINT
, saveint
);
1947 safe_signal(SIGPIPE
, savepipe
);
1952 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1953 int c
, const char *sp
, int needstat
)
1956 FILE *queuefp
= NULL
;
1958 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1961 snprintf(o
, sizeof o
,
1962 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1963 tag(1), m
->m_uid
, c
, sp
);
1965 if (check_expunged() == STOP
)
1967 snprintf(o
, sizeof o
,
1968 "%s STORE %u %cFLAGS (%s)\r\n",
1971 IMAP_OUT(o
, MB_COMD
, return STOP
)
1975 mb
.mb_active
&= ~MB_COMD
;
1976 if (queuefp
!= NULL
)
1982 imap_undelete(struct message
*m
, int n
)
1984 return imap_unstore(m
, n
, "\\Deleted");
1988 imap_unread(struct message
*m
, int n
)
1990 return imap_unstore(m
, n
, "\\Seen");
1994 imap_unstore(struct message
*m
, int n
, const char *flag
)
1996 sighandler_type saveint
, savepipe
;
1997 enum okay ok
= STOP
;
2002 verbose
= value("verbose") != NULL
;
2004 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2005 safe_signal(SIGINT
, maincatch
);
2006 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2007 if (sigsetjmp(imapjmp
, 1) == 0) {
2008 if (savepipe
!= SIG_IGN
)
2009 safe_signal(SIGPIPE
, imapcatch
);
2010 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2012 safe_signal(SIGINT
, saveint
);
2013 safe_signal(SIGPIPE
, savepipe
);
2028 snprintf(ts
, sizeof ts
, "T%lu", n
);
2035 sighandler_type saveint
, savepipe
;
2037 enum okay ok
= STOP
;
2038 struct mailbox
*mp
= &mb
;
2039 FILE *queuefp
= NULL
;
2044 verbose
= value("verbose") != NULL
;
2045 if (mp
->mb_type
!= MB_IMAP
) {
2046 printf("Not operating on an IMAP mailbox.\n");
2050 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2051 safe_signal(SIGINT
, maincatch
);
2052 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2053 if (sigsetjmp(imapjmp
, 1) == 0) {
2054 if (savepipe
!= SIG_IGN
)
2055 safe_signal(SIGPIPE
, imapcatch
);
2056 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2057 IMAP_OUT(o
, MB_COMD
, goto out
)
2058 while (mp
->mb_active
& MB_COMD
) {
2059 ok
= imap_answer(mp
, 0);
2060 fputs(responded_text
, stdout
);
2063 out
: safe_signal(SIGINT
, saveint
);
2064 safe_signal(SIGPIPE
, savepipe
);
2072 imap_newmail(int autoinc
)
2074 if (autoinc
&& had_exists
< 0 && had_expunge
< 0) {
2075 verbose
= value("verbose") != NULL
;
2080 if (had_exists
== msgCount
&& had_expunge
< 0)
2082 * Some servers always respond with EXISTS to NOOP. If
2083 * the mailbox has been changed but the number of messages
2084 * has not, an EXPUNGE must also had been sent; otherwise,
2085 * nothing has changed.
2088 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2092 imap_putflags(int f
)
2097 bp
= buf
= salloc(100);
2098 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2103 for (cp
= "\\Seen"; *cp
; cp
++)
2109 for (cp
= "\\Flagged"; *cp
; cp
++)
2112 if (f
& MANSWERED
) {
2115 for (cp
= "\\Answered"; *cp
; cp
++)
2121 for (cp
= "\\Draft"; *cp
; cp
++)
2132 imap_getflags(const char *cp
, char **xp
, enum mflag
*f
)
2134 while (*cp
!= ')') {
2136 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2138 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2140 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2142 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2144 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2146 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2156 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2157 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2161 size_t bufsize
, buflen
, count
;
2162 enum okay ok
= STOP
;
2163 long size
, lines
, ysize
;
2165 FILE *queuefp
= NULL
;
2167 if (mp
->mb_type
== MB_CACHE
) {
2168 queuefp
= cache_queue(mp
);
2169 if (queuefp
== NULL
)
2173 buf
= smalloc(bufsize
= LINESIZE
);
2175 again
: size
= xsize
;
2177 fseek(fp
, off1
, SEEK_SET
);
2178 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2179 tag(1), imap_quotestr(name
),
2180 imap_putflags(flag
),
2181 imap_make_date_time(t
),
2183 IMAP_OUT(o
, MB_COMD
, goto out
)
2184 while (mp
->mb_active
& MB_COMD
) {
2185 ok
= imap_answer(mp
, twice
);
2186 if (response_type
== RESPONSE_CONT
)
2189 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2197 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2200 buf
[buflen
-1] = '\r';
2202 if (mp
->mb_type
!= MB_CACHE
)
2203 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2205 fwrite(buf
, 1, buflen
+1, queuefp
);
2208 if (mp
->mb_type
!= MB_CACHE
)
2209 swrite(&mp
->mb_sock
, "\r\n");
2211 fputs("\r\n", queuefp
);
2212 while (mp
->mb_active
& MB_COMD
) {
2213 ok
= imap_answer(mp
, 0);
2214 if (response_status
== RESPONSE_NO
/*&&
2215 ascncasecmp(responded_text,
2216 "[TRYCREATE] ", 12) == 0*/) {
2217 trycreate
: if (twice
++) {
2221 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2223 imap_quotestr(name
));
2224 IMAP_OUT(o
, MB_COMD
, goto out
);
2225 while (mp
->mb_active
& MB_COMD
)
2226 ok
= imap_answer(mp
, 1);
2229 imap_created_mailbox
++;
2231 } else if (ok
!= OKAY
)
2232 fprintf(stderr
, "IMAP error: %s", responded_text
);
2233 else if (response_status
== RESPONSE_OK
&&
2234 mp
->mb_flags
& MB_UIDPLUS
)
2235 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2238 out
: if (queuefp
!= NULL
)
2245 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2247 char *buf
, *bp
, *lp
;
2248 size_t bufsize
, buflen
, count
;
2249 off_t off1
= -1, offs
;
2251 int flag
= MNEW
|MNEWEST
;
2256 buf
= smalloc(bufsize
= LINESIZE
);
2262 bp
= fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2263 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2264 if (off1
!= (off_t
)-1) {
2265 ok
=imap_append1(mp
, name
, fp
, off1
,
2269 fseek(fp
, offs
+buflen
, SEEK_SET
);
2271 off1
= offs
+ buflen
;
2276 tim
= unixtime(buf
);
2280 if (bp
&& buf
[0] == '\n')
2282 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2284 while (whitechar(*lp
&0377))
2287 while (*++lp
!= '\0')
2296 } else if (bp
&& inhead
&&
2297 ascncasecmp(buf
, "x-status", 8) == 0) {
2299 while (whitechar(*lp
&0377))
2302 while (*++lp
!= '\0')
2315 } while (bp
!= NULL
);
2321 imap_append(const char *xserver
, FILE *fp
)
2323 sighandler_type saveint
, savepipe
;
2324 char *server
, *uhp
, *mbx
, *user
;
2325 const char *sp
, *cp
, *pass
;
2327 enum okay ok
= STOP
;
2332 verbose
= value("verbose") != NULL
;
2333 server
= savestr((char *)xserver
);
2334 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
2336 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2337 safe_signal(SIGINT
, maincatch
);
2338 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2339 if (sigsetjmp(imapjmp
, 1))
2341 if (savepipe
!= SIG_IGN
)
2342 safe_signal(SIGPIPE
, imapcatch
);
2343 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2344 mb
.mb_imap_account
&&
2345 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2346 ok
= imap_append0(&mb
, mbx
, fp
);
2351 memset(&mx
, 0, sizeof mx
);
2352 if (disconnected(server
) == 0) {
2353 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2354 use_ssl
? "imaps" : "imap",
2357 mx
.mb_sock
.s_desc
= "IMAP";
2358 mx
.mb_type
= MB_IMAP
;
2359 mx
.mb_imap_account
= (char *)protbase(server
);
2360 mx
.mb_imap_mailbox
= mbx
;
2361 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2362 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2363 sclose(&mx
.mb_sock
);
2366 ok
= imap_append0(&mx
, mbx
, fp
);
2368 sclose(&mx
.mb_sock
);
2370 mx
.mb_imap_account
= (char *)protbase(server
);
2371 mx
.mb_imap_mailbox
= mbx
;
2372 mx
.mb_type
= MB_CACHE
;
2373 ok
= imap_append0(&mx
, mbx
, fp
);
2377 out
: safe_signal(SIGINT
, saveint
);
2378 safe_signal(SIGPIPE
, savepipe
);
2386 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2387 struct list_item
**lend
, int level
)
2390 enum okay ok
= STOP
;
2393 FILE *queuefp
= NULL
;
2394 struct list_item
*lp
;
2396 *list
= *lend
= NULL
;
2397 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2398 tag(1), imap_quotestr(base
));
2399 IMAP_OUT(o
, MB_COMD
, return STOP
);
2400 while (mp
->mb_active
& MB_COMD
) {
2401 ok
= imap_answer(mp
, 1);
2402 if (response_status
== RESPONSE_OTHER
&&
2403 response_other
== MAILBOX_DATA_LIST
&&
2404 imap_parse_list() == OKAY
) {
2405 cp
= imap_unquotestr(list_name
);
2406 lp
= csalloc(1, sizeof *lp
);
2408 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2410 lp
->l_base
= *cp
? cp
: savestr(base
);
2411 lp
->l_attr
= list_attributes
;
2412 lp
->l_level
= level
+1;
2413 lp
->l_delim
= list_hierarchy_delimiter
;
2414 if (*list
&& *lend
) {
2415 (*lend
)->l_next
= lp
;
2425 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2427 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2433 verbose
= value("verbose") != NULL
;
2434 depth
= (cp
= value("imap-list-depth")) != NULL
? atoi(cp
) : 2;
2435 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2437 if (list
== NULL
|| lend
== NULL
)
2439 for (lp
= list
; lp
; lp
= lp
->l_next
)
2440 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2441 lp
->l_level
< depth
&&
2442 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2443 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2444 strcpy(cp
, lp
->l_name
);
2445 cp
[n
] = lp
->l_delim
;
2447 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2449 lp
->l_has_children
= 1;
2450 if (strcmp(cp
, lx
->l_name
) == 0)
2458 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2461 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2465 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2466 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2467 else if (lp
->l_has_children
== 0)
2468 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2469 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2475 imap_folders(const char *name
, int strip
)
2477 sighandler_type saveint
, savepipe
;
2478 const char *fold
, *cp
, *sp
;
2481 int columnize
= is_a_tty
[1];
2487 cp
= protbase(name
);
2488 sp
= mb
.mb_imap_account
;
2489 if (strcmp(cp
, sp
)) {
2490 fprintf(stderr
, "Cannot list folders on other than the "
2491 "current IMAP account,\n\"%s\". "
2492 "Try \"folders @\".\n", sp
);
2495 fold
= protfile(name
);
2497 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2506 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2507 safe_signal(SIGINT
, maincatch
);
2508 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2509 if (sigsetjmp(imapjmp
, 1))
2511 if (savepipe
!= SIG_IGN
)
2512 safe_signal(SIGPIPE
, imapcatch
);
2513 if (mb
.mb_type
== MB_CACHE
)
2514 cache_list(&mb
, fold
, strip
, fp
);
2516 imap_list(&mb
, fold
, strip
, fp
);
2529 fprintf(stderr
, "Folder not found.\n");
2532 safe_signal(SIGINT
, saveint
);
2533 safe_signal(SIGPIPE
, savepipe
);
2541 char o
[LINESIZE
], *tempfn
;
2543 long n
= 0, mx
= 0, columns
, width
;
2546 if ((out
= Ftemp(&tempfn
, "Ro", "w+", 0600, 1)) == NULL
) {
2552 while ((c
= getc(fp
)) != EOF
) {
2562 if (mx
< width
/ 2) {
2563 columns
= width
/ (mx
+2);
2564 snprintf(o
, sizeof o
,
2565 "sort | pr -%lu -w%lu -t",
2568 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2569 run_command(SHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2575 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2579 enum okay ok
= STOP
;
2582 FILE *queuefp
= NULL
;
2584 if (mp
->mb_type
== MB_CACHE
) {
2585 if ((queuefp
= cache_queue(mp
)) == NULL
)
2589 qname
= imap_quotestr(name
= protfile(name
));
2591 * Since it is not possible to set flags on the copy, recently
2592 * set flags must be set on the original to include it in the copy.
2594 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
))
2595 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2596 if (m
->m_flag
&MFLAG
)
2597 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2598 if (m
->m_flag
&MUNFLAG
)
2599 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2600 if (m
->m_flag
&MANSWER
)
2601 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2602 if (m
->m_flag
&MUNANSWER
)
2603 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2604 if (m
->m_flag
&MDRAFT
)
2605 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2606 if (m
->m_flag
&MUNDRAFT
)
2607 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2608 again
: if (m
->m_uid
)
2609 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n",
2610 tag(1), m
->m_uid
, qname
);
2612 if (check_expunged() == STOP
)
2614 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n",
2617 IMAP_OUT(o
, MB_COMD
, goto out
)
2618 while (mp
->mb_active
& MB_COMD
)
2619 ok
= imap_answer(mp
, twice
);
2620 if (mp
->mb_type
== MB_IMAP
&&
2621 mp
->mb_flags
& MB_UIDPLUS
&&
2622 response_status
== RESPONSE_OK
)
2623 imap_copyuid(mp
, m
, name
);
2624 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2625 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2626 IMAP_OUT(o
, MB_COMD
, goto out
)
2627 while (mp
->mb_active
& MB_COMD
)
2628 ok
= imap_answer(mp
, 1);
2630 imap_created_mailbox
++;
2634 if (queuefp
!= NULL
)
2637 * ... and reset the flag to its initial value so that
2638 * the 'exit' command still leaves the message unread.
2640 out
: if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
2641 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2644 if (m
->m_flag
&MFLAG
) {
2645 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2648 if (m
->m_flag
&MUNFLAG
) {
2649 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2652 if (m
->m_flag
&MANSWER
) {
2653 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2656 if (m
->m_flag
&MUNANSWER
) {
2657 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2660 if (m
->m_flag
&MDRAFT
) {
2661 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2664 if (m
->m_flag
&MUNDRAFT
) {
2665 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2669 mp
->mb_active
|= MB_COMD
;
2676 imap_copy(struct message
*m
, int n
, const char *name
)
2678 sighandler_type saveint
, savepipe
;
2679 enum okay ok
= STOP
;
2684 verbose
= value("verbose") != NULL
;
2686 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2687 safe_signal(SIGINT
, maincatch
);
2688 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2689 if (sigsetjmp(imapjmp
, 1) == 0) {
2690 if (savepipe
!= SIG_IGN
)
2691 safe_signal(SIGPIPE
, imapcatch
);
2692 ok
= imap_copy1(&mb
, m
, n
, name
);
2694 safe_signal(SIGINT
, saveint
);
2695 safe_signal(SIGPIPE
, savepipe
);
2703 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2704 unsigned long *olduid
, unsigned long *newuid
)
2708 *uidvalidity
= strtoul(cp
, &xp
, 10);
2709 *olduid
= strtoul(xp
, &yp
, 10);
2710 *newuid
= strtoul(yp
, &zp
, 10);
2711 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2712 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2716 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2721 *uidvalidity
= strtoul(cp
, &xp
, 10);
2722 *uid
= strtoul(xp
, &yp
, 10);
2723 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2724 yp
> xp
&& *yp
== ']';
2728 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2731 unsigned long uidvalidity
, olduid
, newuid
;
2735 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2736 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2737 &olduid
, &newuid
) == STOP
)
2740 xmb
.mb_cache_directory
= NULL
;
2741 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2742 xmb
.mb_uidvalidity
= uidvalidity
;
2745 memset(&xm
, 0, sizeof xm
);
2747 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2749 getcache(mp
, &xm
, NEED_HEADER
);
2750 getcache(mp
, &xm
, NEED_BODY
);
2752 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2753 getcache(mp
, m
, NEED_HEADER
);
2754 if ((m
->m_flag
& HAVE_BODY
) == 0)
2755 getcache(mp
, m
, NEED_BODY
);
2759 xm
.m_flag
&= ~MFULLYCACHED
;
2760 putcache(&xmb
, &xm
);
2765 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2766 long xsize
, long size
, long lines
, int flag
, const char *name
)
2769 unsigned long uidvalidity
, uid
;
2773 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2774 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2778 xmb
.mb_cache_directory
= NULL
;
2779 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2780 xmb
.mb_uidvalidity
= uidvalidity
;
2781 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2783 memset(&xm
, 0, sizeof xm
);
2784 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2786 xm
.m_block
= mailx_blockof(off1
);
2787 xm
.m_offset
= mailx_offsetof(off1
);
2790 xm
.m_lines
= xm
.m_xlines
= lines
;
2792 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2793 putcache(&xmb
, &xm
);
2798 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2802 long size
, xsize
, ysize
, lines
;
2803 enum mflag flag
= MNEW
;
2804 char *name
, *buf
, *bp
, *cp
, *tempCopy
;
2805 size_t bufsize
, buflen
, count
;
2806 enum okay ok
= STOP
;
2808 buf
= smalloc(bufsize
= LINESIZE
);
2811 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2813 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
2816 if ((cp
= strrchr(bp
, '{')) == NULL
)
2818 xsize
= atol(&cp
[1]) + 2;
2819 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
2824 imap_getflags(cp
, &cp
, &flag
);
2825 while (*++cp
== ' ');
2827 t
= imap_read_date_time(cp
);
2828 if ((tp
= Ftemp(&tempCopy
, "Rc", "w+", 0600, 1)) == NULL
)
2835 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2838 buf
[--buflen
] = '\0';
2839 buf
[buflen
-1] = '\n';
2840 fwrite(buf
, 1, buflen
, tp
);
2846 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
2847 imap_unquotestr(name
));
2856 imap_search2(struct mailbox
*mp
, struct message
*m
, int count
,
2857 const char *spec
, int f
)
2861 FILE *queuefp
= NULL
;
2862 enum okay ok
= STOP
;
2869 for (cp
= spec
; *cp
; cp
++)
2874 if (asccasecmp(cp
, "utf-8")) {
2876 char *sp
, *nsp
, *nspec
;
2878 if ((it
= iconv_open_ft("utf-8", cp
)) != (iconv_t
)-1) {
2879 sz
= strlen(spec
) + 1;
2880 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2882 if (iconv_ft(it
, &sp
, &sz
, &nsp
, &nsz
, 0)
2883 != (size_t)-1 && sz
== 0) {
2890 #endif /* HAVE_ICONV */
2891 cp
= imap_quotestr(cp
);
2892 cs
= salloc(n
= strlen(cp
) + 10);
2893 snprintf(cs
, n
, "CHARSET %s ", cp
);
2896 o
= ac_alloc(osize
= strlen(spec
) + 60);
2897 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2898 IMAP_OUT(o
, MB_COMD
, goto out
)
2899 while (mp
->mb_active
& MB_COMD
) {
2900 ok
= imap_answer(mp
, 0);
2901 if (response_status
== RESPONSE_OTHER
&&
2902 response_other
== MAILBOX_DATA_SEARCH
) {
2903 xp
= responded_other_text
;
2904 while (*xp
&& *xp
!= '\r') {
2905 n
= strtoul(xp
, &xp
, 10);
2906 for (i
= 0; i
< count
; i
++)
2907 if (m
[i
].m_uid
== n
&&
2908 (m
[i
].m_flag
&MHIDDEN
)
2911 (m
[i
].m_flag
&MDELETED
)
2922 imap_search1(const char *spec
, int f
)
2924 sighandler_type saveint
, savepipe
;
2925 enum okay ok
= STOP
;
2930 if (mb
.mb_type
!= MB_IMAP
)
2932 verbose
= value("verbose") != NULL
;
2934 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2935 safe_signal(SIGINT
, maincatch
);
2936 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2937 if (sigsetjmp(imapjmp
, 1) == 0) {
2938 if (savepipe
!= SIG_IGN
)
2939 safe_signal(SIGPIPE
, imapcatch
);
2940 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2942 safe_signal(SIGINT
, saveint
);
2943 safe_signal(SIGPIPE
, savepipe
);
2951 imap_thisaccount(const char *cp
)
2953 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2955 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2956 mb
.mb_imap_account
== NULL
)
2958 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2962 imap_remove(const char *name
)
2964 sighandler_type saveint
, savepipe
;
2965 enum okay ok
= STOP
;
2970 verbose
= value("verbose") != NULL
;
2971 if (mb
.mb_type
!= MB_IMAP
) {
2972 fprintf(stderr
, "Refusing to remove \"%s\" "
2973 "in disconnected mode.\n", name
);
2976 if (!imap_thisaccount(name
)) {
2977 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2978 "server: \"%s\" not removed.\n", name
);
2982 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2983 safe_signal(SIGINT
, maincatch
);
2984 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2985 if (sigsetjmp(imapjmp
, 1) == 0) {
2986 if (savepipe
!= SIG_IGN
)
2987 safe_signal(SIGPIPE
, imapcatch
);
2988 ok
= imap_remove1(&mb
, protfile(name
));
2990 safe_signal(SIGINT
, saveint
);
2991 safe_signal(SIGPIPE
, savepipe
);
2994 ok
= cache_remove(name
);
3001 imap_remove1(struct mailbox
*mp
, const char *name
)
3003 FILE *queuefp
= NULL
;
3006 enum okay ok
= STOP
;
3008 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3009 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3010 IMAP_OUT(o
, MB_COMD
, goto out
)
3011 while (mp
->mb_active
& MB_COMD
)
3012 ok
= imap_answer(mp
, 1);
3018 imap_rename(const char *old
, const char *new)
3020 sighandler_type saveint
, savepipe
;
3021 enum okay ok
= STOP
;
3026 verbose
= value("verbose") != NULL
;
3027 if (mb
.mb_type
!= MB_IMAP
) {
3028 fprintf(stderr
, "Refusing to rename mailboxes "
3029 "in disconnected mode.\n");
3032 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3033 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3034 "server: \"%s\" not renamed to \"%s\".\n",
3039 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3040 safe_signal(SIGINT
, maincatch
);
3041 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3042 if (sigsetjmp(imapjmp
, 1) == 0) {
3043 if (savepipe
!= SIG_IGN
)
3044 safe_signal(SIGPIPE
, imapcatch
);
3045 ok
= imap_rename1(&mb
, protfile(old
), protfile(new));
3047 safe_signal(SIGINT
, saveint
);
3048 safe_signal(SIGPIPE
, savepipe
);
3051 ok
= cache_rename(old
, new);
3058 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3060 FILE *queuefp
= NULL
;
3063 enum okay ok
= STOP
;
3065 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3066 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3067 imap_quotestr(old
), imap_quotestr(new));
3068 IMAP_OUT(o
, MB_COMD
, goto out
)
3069 while (mp
->mb_active
& MB_COMD
)
3070 ok
= imap_answer(mp
, 1);
3076 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3078 FILE *queuefp
= NULL
;
3079 char o
[LINESIZE
], *newname
;
3080 char *buf
, *bp
, *cp
, iob
[4096];
3081 size_t bufsize
, buflen
, count
;
3082 enum okay ok
= OKAY
, rok
= OKAY
;
3083 long offs
, offs1
, offs2
, octets
;
3084 int twice
, gotcha
= 0;
3086 buf
= smalloc(bufsize
= LINESIZE
);
3089 while (offs1
= ftell(fp
),
3090 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0)
3092 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3097 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3098 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3100 while (digitchar(*cp
&0377))
3106 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3108 IMAP_OUT(o
, MB_COMD
, continue)
3109 while (mp
->mb_active
& MB_COMD
)
3110 ok
= imap_answer(mp
, twice
);
3111 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3113 if (response_status
== RESPONSE_OK
&&
3114 mp
->mb_flags
& MB_UIDPLUS
) {
3115 imap_copyuid(mp
, NULL
,
3116 imap_unquotestr(newname
));
3118 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3119 IMAP_OUT(o
, MB_COMD
, continue)
3120 while (mp
->mb_active
& MB_COMD
)
3121 ok
= imap_answer(mp
, 1);
3124 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3125 if ((cp
= strrchr(bp
, '{')) == NULL
)
3127 octets
= atol(&cp
[1]) + 2;
3128 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3130 IMAP_OUT(o
, MB_COMD
, continue)
3131 while (mp
->mb_active
& MB_COMD
) {
3132 ok
= imap_answer(mp
, twice
);
3133 if (response_type
== RESPONSE_CONT
)
3138 fseek(fp
, offs
, SEEK_SET
);
3143 while (octets
> 0) {
3144 size_t n
= (size_t)octets
> sizeof iob
3145 ? sizeof iob
: (size_t)octets
;
3147 if (n
!= fread(iob
, 1, n
, fp
))
3149 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3151 swrite(&mp
->mb_sock
, "");
3152 while (mp
->mb_active
& MB_COMD
) {
3153 ok
= imap_answer(mp
, 0);
3154 if (response_status
== RESPONSE_NO
&&
3156 fseek(fp
, offs
, SEEK_SET
);
3160 if (response_status
== RESPONSE_OK
&&
3161 mp
->mb_flags
& MB_UIDPLUS
) {
3163 fseek(fp
, offs1
, SEEK_SET
);
3164 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3165 fseek(fp
, offs2
, SEEK_SET
);
3170 fail
: fprintf(stderr
,
3171 "Invalid command in IMAP cache queue: \"%s\"\n",
3177 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3179 IMAP_OUT(o
, MB_COMD
, continue)
3180 while (mp
->mb_active
& MB_COMD
)
3181 ok
= imap_answer(mp
, 1);
3187 ftruncate(fileno(fp
), 0);
3194 imap_strex(const char *cp
, char **xp
)
3201 for (cq
= &cp
[1]; *cq
; cq
++) {
3204 else if (*cq
== '"')
3209 n
= salloc(cq
- cp
+ 2);
3210 memcpy(n
, cp
, cq
- cp
+ 1);
3211 n
[cq
- cp
+ 1] = '\0';
3213 *xp
= (char *)&cq
[1];
3218 check_expunged(void)
3220 if (expunged_messages
> 0) {
3222 "Command not executed - messages have been expunged\n");
3233 int omsgCount
= msgCount
;
3236 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3237 fprintf(stderr
, "Already connected.\n");
3240 unset_allow_undefined
= 1;
3241 unset_internal("disconnected");
3242 cp
= protbase(mailname
);
3243 if (strncmp(cp
, "imap://", 7) == 0)
3245 else if (strncmp(cp
, "imaps://", 8) == 0)
3247 if ((cq
= strchr(cp
, ':')) != NULL
)
3249 unset_internal(savecat("disconnected-", cp
));
3250 unset_allow_undefined
= 0;
3251 if (mb
.mb_type
== MB_CACHE
) {
3252 imap_setfile1(mailname
, 0, edit
, 1);
3253 if (msgCount
> omsgCount
)
3254 newmailinfo(omsgCount
);
3260 cdisconnect(void *vp
)
3264 if (mb
.mb_type
== MB_CACHE
) {
3265 fprintf(stderr
, "Not connected.\n");
3267 } else if (mb
.mb_type
== MB_IMAP
) {
3268 if (cached_uidvalidity(&mb
) == 0) {
3269 fprintf(stderr
, "The current mailbox is not cached.\n");
3275 assign("disconnected", "");
3276 if (mb
.mb_type
== MB_IMAP
) {
3277 sclose(&mb
.mb_sock
);
3278 imap_setfile1(mailname
, 0, edit
, 1);
3285 int *msgvec
= vp
, *ip
;
3288 if (mb
.mb_type
!= MB_IMAP
) {
3289 fprintf(stderr
, "Not connected to an IMAP server.\n");
3292 if (cached_uidvalidity(&mb
) == 0) {
3293 fprintf(stderr
, "The current mailbox is not cached.\n");
3296 for (ip
= msgvec
; *ip
; ip
++) {
3297 mp
= &message
[*ip
-1];
3298 if (!(mp
->m_have
& HAVE_BODY
))
3303 #else /* !USE_IMAP */
3310 fprintf(stderr
, catgets(catd
, CATSET
, 269,
3311 "No IMAP support compiled in.\n"));
3315 imap_setfile(const char *server
, int newmail
, int isedit
)
3325 imap_header(struct message
*mp
)
3333 imap_body(struct message
*mp
)
3341 imap_getheaders(int bot
, int top
)
3364 imap_newmail(int dummy
)
3372 imap_undelete(struct message
*m
, int n
)
3381 imap_unread(struct message
*m
, int n
)
3390 imap_append(const char *server
, FILE *fp
)
3400 imap_folders(const char *name
, int strip
)
3409 imap_remove(const char *name
)
3418 imap_rename(const char *old
, const char *new)
3427 imap_copy(struct message
*m
, int n
, const char *name
)
3438 imap_search1(const char *spec
, int f
)
3446 imap_thisaccount(const char *cp
)
3470 cdisconnect(void *vp
)
3485 #endif /* USE_IMAP */
3488 imap_read_date_time(const char *cp
)
3491 int i
, year
, month
, day
, hour
, minute
, second
;
3496 * "25-Jul-2004 15:33:44 +0200"
3500 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3502 day
= strtol(&cp
[1], NULL
, 10);
3503 for (i
= 0; month_names
[i
]; i
++)
3504 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3506 if (month_names
[i
] == NULL
)
3509 year
= strtol(&cp
[8], NULL
, 10);
3510 hour
= strtol(&cp
[13], NULL
, 10);
3511 minute
= strtol(&cp
[16], NULL
, 10);
3512 second
= strtol(&cp
[19], NULL
, 10);
3513 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3528 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3531 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3539 imap_read_date(const char *cp
)
3542 int year
, month
, day
, i
, tzdiff
;
3548 day
= strtol(cp
, &xp
, 10);
3549 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3551 for (i
= 0; month_names
[i
]; i
++)
3552 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3554 if (month_names
[i
] == NULL
)
3559 year
= strtol(&xp
[4], &yp
, 10);
3560 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3562 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3564 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3566 tzdiff
= t
- mktime(gmtime(&t
));
3567 tmptr
= localtime(&t
);
3568 if (tmptr
->tm_isdst
> 0)
3575 imap_make_date_time(time_t t
)
3579 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3581 tzdiff
= t
- mktime(gmtime(&t
));
3582 tzdiff_hour
= (int)(tzdiff
/ 60);
3583 tzdiff_min
= tzdiff_hour
% 60;
3585 tmptr
= localtime(&t
);
3586 if (tmptr
->tm_isdst
> 0)
3588 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3590 month_names
[tmptr
->tm_mon
],
3591 tmptr
->tm_year
+ 1900,
3601 imap_quotestr(const char *s
)
3605 np
= n
= salloc(2 * strlen(s
) + 3);
3608 if (*s
== '"' || *s
== '\\')
3618 imap_unquotestr(const char *s
)
3624 np
= n
= salloc(strlen(s
) + 1);