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
, tr(272, "No GSSAPI support compiled in.\n"));
853 #endif /* !USE_GSSAPI */
855 fprintf(stderr
, tr(273, "Unknown IMAP authentication method: %s\n"),
861 * Implementation of RFC 2194.
864 imap_cram_md5(struct mailbox
*mp
, char *xuser
, const char *xpass
)
867 const char *user
, *pass
;
869 FILE *queuefp
= NULL
;
872 retry
: if (xuser
== NULL
) {
873 if ((user
= getuser()) == NULL
)
878 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
882 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
883 IMAP_OUT(o
, 0, return STOP
)
885 if (response_type
!= RESPONSE_CONT
)
887 cp
= cram_md5_string(user
, pass
, responded_text
);
888 IMAP_OUT(cp
, MB_COMD
, return STOP
)
889 while (mp
->mb_active
& MB_COMD
)
890 ok
= imap_answer(mp
, 1);
899 imap_login(struct mailbox
*mp
, char *xuser
, const char *xpass
)
902 const char *user
, *pass
;
903 FILE *queuefp
= NULL
;
906 retry
: if (xuser
== NULL
) {
907 if ((user
= getuser()) == NULL
)
912 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
916 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
917 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
918 IMAP_OUT(o
, MB_COMD
, return STOP
)
919 while (mp
->mb_active
& MB_COMD
)
920 ok
= imap_answer(mp
, 1);
929 #include "imap_gssapi.c"
930 #endif /* USE_GSSAPI */
933 imap_select(struct mailbox
*mp
, off_t
*size
, int *count
, const char *mbx
)
938 FILE *queuefp
= NULL
;
941 mp
->mb_uidvalidity
= 0;
942 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
943 IMAP_OUT(o
, MB_COMD
, return STOP
)
944 while (mp
->mb_active
& MB_COMD
) {
945 ok
= imap_answer(mp
, 1);
946 if (response_status
!= RESPONSE_OTHER
&&
947 (cp
= asccasestr(responded_text
,
948 "[UIDVALIDITY ")) != NULL
)
949 mp
->mb_uidvalidity
= atol(&cp
[13]);
951 *count
= had_exists
> 0 ? had_exists
: 0;
952 if (response_status
!= RESPONSE_OTHER
&&
953 ascncasecmp(responded_text
, "[READ-ONLY] ", 12)
960 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
963 FILE *queuefp
= NULL
;
966 unsigned x
= X
, y
= Y
, n
;
968 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
969 IMAP_OUT(o
, MB_COMD
, return STOP
)
970 while (mp
->mb_active
& MB_COMD
) {
972 if (response_status
== RESPONSE_OTHER
&&
973 response_other
== MESSAGE_DATA_FETCH
) {
974 n
= responded_other_number
;
981 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
986 imap_getflags(cp
, &cp
, &m
->m_flag
);
988 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
989 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
990 getcache1(mp
, m
, NEED_UNSPEC
, 1);
991 m
->m_flag
&= ~MHIDDEN
;
993 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
995 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
998 snprintf(o
, sizeof o
,
999 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1001 IMAP_OUT(o
, MB_COMD
, return STOP
)
1002 while (mp
->mb_active
& MB_COMD
) {
1004 if (response_status
== RESPONSE_OTHER
&&
1005 response_other
== MESSAGE_DATA_FETCH
) {
1006 n
= responded_other_number
;
1012 if ((cp
= asccasestr(responded_other_text
,
1013 "RFC822.SIZE ")) != NULL
)
1014 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1015 if ((cp
= asccasestr(responded_other_text
,
1016 "INTERNALDATE ")) != NULL
)
1017 m
->m_time
= imap_read_date_time(&cp
[13]);
1020 for (n
= X
; n
<= Y
; n
++)
1021 putcache(mp
, &message
[n
-1]);
1026 imap_init(struct mailbox
*mp
, int n
)
1028 struct message
*m
= &message
[n
];
1031 m
->m_flag
= MUSED
|MNOFROM
;
1037 imap_setptr(struct mailbox
*mp
, int newmail
, int transparent
, int *prevcount
)
1039 struct message
*omessage
= 0;
1040 int i
, omsgCount
= 0;
1041 enum okay dequeued
= STOP
;
1043 if (newmail
|| transparent
) {
1045 omsgCount
= msgCount
;
1048 dequeued
= rec_dequeue();
1049 if (had_exists
>= 0) {
1050 if (dequeued
!= OKAY
)
1051 msgCount
= had_exists
;
1054 if (had_expunge
>= 0) {
1055 if (dequeued
!= OKAY
)
1056 msgCount
-= had_expunge
;
1059 if (newmail
&& expunged_messages
)
1060 printf("Expunged %ld message%s.\n",
1062 expunged_messages
!= 1 ? "s" : "");
1063 *prevcount
= omsgCount
- expunged_messages
;
1064 expunged_messages
= 0;
1066 fputs("IMAP error: Negative message count\n", stderr
);
1069 if (dequeued
!= OKAY
) {
1070 message
= scalloc(msgCount
+ 1, sizeof *message
);
1071 for (i
= 0; i
< msgCount
; i
++)
1073 if (!newmail
&& mp
->mb_type
== MB_IMAP
)
1076 imap_flags(mp
, 1, msgCount
);
1077 message
[msgCount
].m_size
= 0;
1078 message
[msgCount
].m_lines
= 0;
1081 if (newmail
|| transparent
)
1082 transflags(omessage
, omsgCount
, transparent
);
1088 imap_have_password(const char *server
)
1092 var
= ac_alloc(strlen(server
) + 10);
1093 strcpy(var
, "password-");
1094 strcpy(&var
[9], server
);
1095 if ((cp
= value(var
)) != NULL
)
1102 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1103 char **uhp
, char **mbx
, const char **pass
, char **user
)
1106 if (strncmp(*sp
, "imap://", 7) == 0) {
1110 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1113 #endif /* USE_SSL */
1115 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1116 *uhp
= savestr((char *)(*sp
));
1117 (*uhp
)[*cp
- *sp
] = '\0';
1118 *mbx
= (char *)&(*cp
)[1];
1121 (*server
)[*cp
- *server
] = '\0';
1122 *uhp
= (char *)(*sp
);
1125 *pass
= imap_have_password(*uhp
);
1126 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1127 *user
= salloc(*cp
- *uhp
+ 1);
1128 memcpy(*user
, *uhp
, *cp
- *uhp
);
1129 (*user
)[*cp
- *uhp
] = '\0';
1131 *user
= strdec(*user
);
1139 imap_setfile(const char *xserver
, int newmail
, int isedit
)
1141 return imap_setfile1(xserver
, newmail
, isedit
, 0);
1145 imap_setfile1(const char *xserver
, int newmail
, int isedit
, int transparent
)
1148 sighandler_type saveint
;
1149 sighandler_type savepipe
;
1150 char *server
, *user
, *account
;
1151 const char *cp
, *sp
, *pass
;
1154 enum mbflags same_flags
;
1161 server
= savestr((char *)xserver
);
1162 verbose
= value("verbose") != NULL
;
1164 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1165 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1166 if (saveint
!= SIG_IGN
)
1167 safe_signal(SIGINT
, imapcatch
);
1168 if (savepipe
!= SIG_IGN
)
1169 safe_signal(SIGPIPE
, imapcatch
);
1173 same_flags
= mb
.mb_flags
;
1174 same_imap_account
= 0;
1175 sp
= protbase(server
);
1176 if (mb
.mb_imap_account
) {
1177 if (mb
.mb_sock
.s_fd
> 0 &&
1178 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1179 disconnected(mb
.mb_imap_account
) == 0)
1180 same_imap_account
= 1;
1182 account
= sstrdup(sp
);
1183 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
1185 if (!same_imap_account
) {
1186 if (!disconnected(account
) &&
1187 sopen(sp
, &so
, use_ssl
, uhp
,
1188 use_ssl
? "imaps" : "imap", verbose
) != OKAY
)
1195 free(mb
.mb_imap_account
);
1196 mb
.mb_imap_account
= account
;
1197 if (!same_imap_account
) {
1198 if (mb
.mb_sock
.s_fd
>= 0)
1199 sclose(&mb
.mb_sock
);
1201 same_imap_account
= 0;
1211 free(mb
.mb_imap_mailbox
);
1212 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1215 mb
.mb_type
= MB_VOID
;
1216 mb
.mb_active
= MB_NONE
;;
1218 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1219 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1220 if (sigsetjmp(imapjmp
, 1)) {
1222 safe_signal(SIGINT
, saveint
);
1223 safe_signal(SIGPIPE
, savepipe
);
1227 if (saveint
!= SIG_IGN
)
1228 safe_signal(SIGINT
, imapcatch
);
1229 if (savepipe
!= SIG_IGN
)
1230 safe_signal(SIGPIPE
, imapcatch
);
1231 if (mb
.mb_sock
.s_fd
< 0) {
1232 if (disconnected(mb
.mb_imap_account
)) {
1233 if (cache_setptr(transparent
) == STOP
)
1235 "Mailbox \"%s\" is not cached.\n",
1239 if ((cp
= value("imap-keepalive")) != NULL
) {
1240 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1241 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1242 alarm(imapkeepalive
);
1246 mb
.mb_sock
.s_desc
= "IMAP";
1247 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1248 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1249 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1250 sclose(&mb
.mb_sock
);
1252 safe_signal(SIGINT
, saveint
);
1253 safe_signal(SIGPIPE
, savepipe
);
1257 } else /* same account */
1258 mb
.mb_flags
|= same_flags
;
1259 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
1260 mb
.mb_type
= MB_IMAP
;
1262 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1263 /*sclose(&mb.mb_sock);
1265 safe_signal(SIGINT
, saveint
);
1266 safe_signal(SIGPIPE
, savepipe
);
1268 mb
.mb_type
= MB_VOID
;
1272 imap_setptr(&mb
, newmail
, transparent
, &prevcount
);
1273 done
: setmsize(msgCount
);
1274 if (!newmail
&& !transparent
)
1276 safe_signal(SIGINT
, saveint
);
1277 safe_signal(SIGPIPE
, savepipe
);
1279 if (!newmail
&& mb
.mb_type
== MB_IMAP
)
1280 purgecache(&mb
, message
, msgCount
);
1281 if ((newmail
|| transparent
) && mb
.mb_sorted
) {
1285 if (!newmail
&& !edit
&& msgCount
== 0) {
1286 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1287 value("emptystart") == NULL
)
1288 fprintf(stderr
, catgets(catd
, CATSET
, 258,
1289 "No mail at %s\n"), server
);
1293 newmailinfo(prevcount
);
1298 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1300 const char *head
, size_t headsize
, long headlines
)
1302 char *line
= NULL
, *lp
;
1303 size_t linesize
= 0, linelen
, size
= 0;
1304 int emptyline
= 0, lines
= 0, excess
= 0;
1307 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1308 offset
= ftell(mp
->mb_otf
);
1310 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1311 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1313 if (linelen
> expected
) {
1314 excess
= linelen
- expected
;
1318 * Need to mask 'From ' lines. This cannot be done properly
1319 * since some servers pass them as 'From ' and others as
1320 * '>From '. Although one could identify the first kind of
1321 * server in principle, it is not possible to identify the
1322 * second as '>From ' may also come from a server of the
1323 * first type as actual data. So do what is absolutely
1324 * necessary only - mask 'From '.
1326 * If the line is the first line of the message header, it
1327 * is likely a real 'From ' line. In this case, it is just
1328 * ignored since it violates all standards.
1330 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
1331 lp
[3] == 'm' && lp
[4] == ' ') {
1332 if (lines
+ headlines
!= 0) {
1333 fputc('>', mp
->mb_otf
);
1338 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1339 lp
[linelen
-2] == '\r')) {
1340 emptyline
= linelen
<= 2;
1342 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1343 size
+= linelen
- 1;
1346 fputc('\n', mp
->mb_otf
);
1349 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1353 skip
: if ((expected
-= linelen
) <= 0)
1358 * This is very ugly; but some IMAP daemons don't end a
1359 * message with \r\n\r\n, and we need \n\n for mbox format.
1361 fputc('\n', mp
->mb_otf
);
1367 m
->m_size
= size
+ headsize
;
1368 m
->m_lines
= lines
+ headlines
;
1369 m
->m_block
= mailx_blockof(offset
);
1370 m
->m_offset
= mailx_offsetof(offset
);
1373 m
->m_have
|= HAVE_HEADER
;
1376 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1377 m
->m_xlines
= m
->m_lines
;
1378 m
->m_xsize
= m
->m_size
;
1387 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1388 const char *head
, size_t headsize
, long headlines
)
1394 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1395 offset
= ftell(mp
->mb_otf
);
1397 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1399 fwrite(str
, 1, len
, mp
->mb_otf
);
1400 fputc('\n', mp
->mb_otf
);
1405 m
->m_size
= headsize
+ len
;
1406 m
->m_lines
= headlines
+ 1;
1407 m
->m_block
= mailx_blockof(offset
);
1408 m
->m_offset
= mailx_offsetof(offset
);
1409 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1410 m
->m_xlines
= m
->m_lines
;
1411 m
->m_xsize
= m
->m_size
;
1416 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1418 sighandler_type saveint
= SIG_IGN
;
1419 sighandler_type savepipe
= SIG_IGN
;
1420 char o
[LINESIZE
], *cp
= NULL
, *item
= NULL
, *resp
= NULL
, *loc
= NULL
;
1421 size_t expected
, headsize
= 0;
1422 int number
= m
- message
+ 1;
1423 enum okay ok
= STOP
;
1424 FILE *queuefp
= NULL
;
1429 unsigned long u
= 0;
1444 verbose
= value("verbose") != NULL
;
1445 if (getcache(mp
, m
, need
) == OKAY
)
1447 if (mp
->mb_type
== MB_CACHE
) {
1448 fprintf(stderr
, "Message %u not available.\n", number
);
1451 if (mp
->mb_sock
.s_fd
< 0) {
1452 fprintf(stderr
, "IMAP connection closed.\n");
1457 resp
= item
= "RFC822.HEADER";
1460 item
= "BODY.PEEK[]";
1462 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1463 char *hdr
= smalloc(m
->m_size
);
1465 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1466 m
->m_offset
), SEEK_SET
) < 0 ||
1467 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1473 headsize
= m
->m_size
;
1474 headlines
= m
->m_lines
;
1475 item
= "BODY.PEEK[TEXT]";
1476 resp
= "BODY[TEXT]";
1483 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1484 if (sigsetjmp(imapjmp
, 1)) {
1485 safe_signal(SIGINT
, saveint
);
1486 safe_signal(SIGPIPE
, savepipe
);
1490 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1491 safe_signal(SIGINT
, maincatch
);
1492 if (savepipe
!= SIG_IGN
)
1493 safe_signal(SIGPIPE
, imapcatch
);
1495 snprintf(o
, sizeof o
,
1496 "%s UID FETCH %lu (%s)\r\n",
1497 tag(1), m
->m_uid
, item
);
1499 if (check_expunged() == STOP
)
1501 snprintf(o
, sizeof o
,
1502 "%s FETCH %u (%s)\r\n",
1503 tag(1), number
, item
);
1505 IMAP_OUT(o
, MB_COMD
, goto out
)
1507 ok
= imap_answer(mp
, 1);
1510 if (response_status
!= RESPONSE_OTHER
||
1511 response_other
!= MESSAGE_DATA_FETCH
)
1513 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1516 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1524 n
= responded_other_number
;
1525 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1526 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1528 if ((cp
= strchr(loc
, '"')) != NULL
) {
1529 cp
= imap_unquotestr(cp
);
1530 imap_putstr(mp
, m
, cp
,
1531 head
, headsize
, headlines
);
1533 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1534 m
->m_xlines
= m
->m_lines
;
1535 m
->m_xsize
= m
->m_size
;
1539 expected
= atol(&cp
[1]);
1540 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1541 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1545 imap_fetchdata(mp
, &mt
, expected
, need
,
1546 head
, headsize
, headlines
);
1548 commitmsg(mp
, m
, mt
, mt
.m_have
);
1551 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1552 &mp
->mb_sock
) > 0) {
1554 fputs(imapbuf
, stderr
);
1555 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1557 if (u
== m
->m_uid
) {
1558 commitmsg(mp
, m
, mt
, mt
.m_have
);
1564 out
: while (mp
->mb_active
& MB_COMD
)
1565 ok
= imap_answer(mp
, 1);
1566 if (saveint
!= SIG_IGN
)
1567 safe_signal(SIGINT
, saveint
);
1568 if (savepipe
!= SIG_IGN
)
1569 safe_signal(SIGPIPE
, savepipe
);
1580 imap_header(struct message
*m
)
1582 return imap_get(&mb
, m
, NEED_HEADER
);
1587 imap_body(struct message
*m
)
1589 return imap_get(&mb
, m
, NEED_BODY
);
1593 commitmsg(struct mailbox
*mp
, struct message
*to
,
1594 struct message from
, enum havespec have
)
1596 to
->m_size
= from
.m_size
;
1597 to
->m_lines
= from
.m_lines
;
1598 to
->m_block
= from
.m_block
;
1599 to
->m_offset
= from
.m_offset
;
1601 if (have
& HAVE_BODY
) {
1602 to
->m_xlines
= from
.m_lines
;
1603 to
->m_xsize
= from
.m_size
;
1613 int top
/* bot > top */
1616 char o
[LINESIZE
], *cp
;
1621 FILE *queuefp
= NULL
;
1624 snprintf(o
, sizeof o
,
1625 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1626 tag(1), m
[bot
-1].m_uid
, m
[top
-1].m_uid
);
1628 if (check_expunged() == STOP
)
1630 snprintf(o
, sizeof o
,
1631 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1634 IMAP_OUT(o
, MB_COMD
, return STOP
)
1636 ok
= imap_answer(mp
, 1);
1637 if (response_status
!= RESPONSE_OTHER
)
1639 if (response_other
!= MESSAGE_DATA_FETCH
)
1641 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1643 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1645 expected
= atol(&cp
[1]);
1646 if (m
[bot
-1].m_uid
) {
1647 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1649 for (n
= bot
; n
<= top
; n
++)
1650 if ((unsigned long)u
== m
[n
-1].m_uid
)
1653 imap_fetchdata(mp
, NULL
, expected
,
1661 n
= responded_other_number
;
1662 if (n
<= 0 || n
> msgCount
) {
1663 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1668 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1669 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1670 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1671 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1672 &mp
->mb_sock
) > 0) {
1674 fputs(imapbuf
, stderr
);
1675 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1677 for (n
= bot
; n
<= top
; n
++)
1678 if ((unsigned long)u
== m
[n
-1].m_uid
)
1680 if (n
<= top
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1681 commitmsg(mp
, &m
[n
-1], mt
, HAVE_HEADER
);
1686 while (mp
->mb_active
& MB_COMD
)
1687 ok
= imap_answer(mp
, 1);
1692 imap_getheaders(int bot
, int top
)
1694 sighandler_type saveint
, savepipe
;
1695 enum okay ok
= STOP
;
1703 verbose
= value("verbose") != NULL
;
1704 if (mb
.mb_type
== MB_CACHE
)
1710 for (i
= bot
; i
< top
; i
++) {
1711 if (message
[i
-1].m_have
& HAVE_HEADER
||
1712 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1718 for (i
= top
; i
> bot
; i
--) {
1719 if (message
[i
-1].m_have
& HAVE_HEADER
||
1720 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1729 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1730 safe_signal(SIGINT
, maincatch
);
1731 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1732 if (sigsetjmp(imapjmp
, 1) == 0) {
1733 if (savepipe
!= SIG_IGN
)
1734 safe_signal(SIGPIPE
, imapcatch
);
1735 for (i
= bot
; i
<= top
; i
+= chunk
) {
1736 ok
= imap_fetchheaders(&mb
, message
, i
,
1737 i
+chunk
-1 < top
? i
+chunk
-1 : top
);
1742 safe_signal(SIGINT
, saveint
);
1743 safe_signal(SIGPIPE
, savepipe
);
1748 imap_exit(struct mailbox
*mp
)
1751 FILE *queuefp
= NULL
;
1753 verbose
= value("verbose") != NULL
;
1754 mp
->mb_active
|= MB_BYE
;
1755 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1756 IMAP_OUT(o
, MB_COMD
, return STOP
)
1762 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1764 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1765 if (mp
->mb_type
== MB_IMAP
)
1771 imap_close(struct mailbox
*mp
)
1774 FILE *queuefp
= NULL
;
1776 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1777 IMAP_OUT(o
, MB_COMD
, return STOP
)
1783 imap_update(struct mailbox
*mp
)
1785 FILE *readstat
= NULL
;
1787 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1789 verbose
= value("verbose") != NULL
;
1790 if (Tflag
!= NULL
) {
1791 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
1794 if (!edit
&& mp
->mb_perm
!= 0) {
1796 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1797 if (m
->m_flag
& MBOX
)
1801 if (makembox() == STOP
)
1804 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1805 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
1808 if ((id
= hfield("message-id", m
)) != NULL
||
1809 (id
= hfield("article-id", m
)) != NULL
)
1810 fprintf(readstat
, "%s\n", id
);
1812 if (mp
->mb_perm
== 0) {
1815 dodel
= m
->m_flag
& MDELETED
;
1817 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1818 (m
->m_flag
&MTOUCH
) == 0);
1821 * Fetch the result after around each 800 STORE commands
1822 * sent (approx. 32k data sent). Otherwise, servers will
1823 * try to flush the return queue at some point, leading
1824 * to a deadlock if we are still writing commands but not
1825 * reading their results.
1827 needstat
= stored
> 0 && stored
% 800 == 0;
1829 * Even if this message has been deleted, continue
1830 * to set further flags. This is necessary to support
1831 * Gmail semantics, where "delete" actually means
1832 * "archive", and the flags are applied to the copy
1835 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1836 imap_store(mp
, m
, m
-message
+1,
1837 '+', "\\Seen", needstat
);
1840 if (m
->m_flag
& MFLAG
) {
1841 imap_store(mp
, m
, m
-message
+1,
1842 '+', "\\Flagged", needstat
);
1845 if (m
->m_flag
& MUNFLAG
) {
1846 imap_store(mp
, m
, m
-message
+1,
1847 '-', "\\Flagged", needstat
);
1850 if (m
->m_flag
& MANSWER
) {
1851 imap_store(mp
, m
, m
-message
+1,
1852 '+', "\\Answered", needstat
);
1855 if (m
->m_flag
& MUNANSWER
) {
1856 imap_store(mp
, m
, m
-message
+1,
1857 '-', "\\Answered", needstat
);
1860 if (m
->m_flag
& MDRAFT
) {
1861 imap_store(mp
, m
, m
-message
+1,
1862 '+', "\\Draft", needstat
);
1865 if (m
->m_flag
& MUNDRAFT
) {
1866 imap_store(mp
, m
, m
-message
+1,
1867 '-', "\\Draft", needstat
);
1871 imap_delete(mp
, m
-message
+1, m
, needstat
);
1874 } else if (mp
->mb_type
!= MB_CACHE
||
1875 (! edit
&& ! (m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))) ||
1876 (m
->m_flag
& (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1877 (MPRESERVE
|MTOUCH
) ||
1878 (edit
&& ! (m
->m_flag
& MDELETED
)))
1880 if (m
->m_flag
& MNEW
) {
1882 m
->m_flag
|= MSTATUS
;
1885 bypass
: if (readstat
!= NULL
)
1889 for (m
= &message
[0]; m
< &message
[msgCount
]; m
++)
1890 if (!(m
->m_flag
&MUNLINKED
) &&
1891 m
->m_flag
&(MBOXED
|MDELETED
|MSAVED
|MSTATUS
|
1892 MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
1897 if ((gotcha
|| modflags
) && edit
) {
1898 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
1899 printf(value("bsdcompat") || value("bsdmsgs") ?
1900 catgets(catd
, CATSET
, 170, "complete\n") :
1901 catgets(catd
, CATSET
, 212, "updated.\n"));
1902 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1904 printf(catgets(catd
, CATSET
, 155,
1905 "Held 1 message in %s\n"), mailname
);
1907 printf(catgets(catd
, CATSET
, 156,
1908 "Held %d messages in %s\n"), held
, mailname
);
1917 sighandler_type saveint
;
1918 sighandler_type savepipe
;
1920 verbose
= value("verbose") != NULL
;
1921 if (mb
.mb_type
== MB_CACHE
) {
1925 if (mb
.mb_sock
.s_fd
< 0) {
1926 fprintf(stderr
, "IMAP connection closed.\n");
1930 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1931 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1932 if (sigsetjmp(imapjmp
, 1)) {
1933 safe_signal(SIGINT
, saveint
);
1934 safe_signal(SIGPIPE
, saveint
);
1938 if (saveint
!= SIG_IGN
)
1939 safe_signal(SIGINT
, imapcatch
);
1940 if (savepipe
!= SIG_IGN
)
1941 safe_signal(SIGPIPE
, imapcatch
);
1943 if (!same_imap_account
) {
1945 sclose(&mb
.mb_sock
);
1947 safe_signal(SIGINT
, saveint
);
1948 safe_signal(SIGPIPE
, savepipe
);
1953 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1954 int c
, const char *sp
, int needstat
)
1957 FILE *queuefp
= NULL
;
1959 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1962 snprintf(o
, sizeof o
,
1963 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1964 tag(1), m
->m_uid
, c
, sp
);
1966 if (check_expunged() == STOP
)
1968 snprintf(o
, sizeof o
,
1969 "%s STORE %u %cFLAGS (%s)\r\n",
1972 IMAP_OUT(o
, MB_COMD
, return STOP
)
1976 mb
.mb_active
&= ~MB_COMD
;
1977 if (queuefp
!= NULL
)
1983 imap_undelete(struct message
*m
, int n
)
1985 return imap_unstore(m
, n
, "\\Deleted");
1989 imap_unread(struct message
*m
, int n
)
1991 return imap_unstore(m
, n
, "\\Seen");
1995 imap_unstore(struct message
*m
, int n
, const char *flag
)
1997 sighandler_type saveint
, savepipe
;
1998 enum okay ok
= STOP
;
2003 verbose
= value("verbose") != NULL
;
2005 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2006 safe_signal(SIGINT
, maincatch
);
2007 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2008 if (sigsetjmp(imapjmp
, 1) == 0) {
2009 if (savepipe
!= SIG_IGN
)
2010 safe_signal(SIGPIPE
, imapcatch
);
2011 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2013 safe_signal(SIGINT
, saveint
);
2014 safe_signal(SIGPIPE
, savepipe
);
2029 snprintf(ts
, sizeof ts
, "T%lu", n
);
2036 sighandler_type saveint
, savepipe
;
2038 enum okay ok
= STOP
;
2039 struct mailbox
*mp
= &mb
;
2040 FILE *queuefp
= NULL
;
2045 verbose
= value("verbose") != NULL
;
2046 if (mp
->mb_type
!= MB_IMAP
) {
2047 printf("Not operating on an IMAP mailbox.\n");
2051 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2052 safe_signal(SIGINT
, maincatch
);
2053 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2054 if (sigsetjmp(imapjmp
, 1) == 0) {
2055 if (savepipe
!= SIG_IGN
)
2056 safe_signal(SIGPIPE
, imapcatch
);
2057 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2058 IMAP_OUT(o
, MB_COMD
, goto out
)
2059 while (mp
->mb_active
& MB_COMD
) {
2060 ok
= imap_answer(mp
, 0);
2061 fputs(responded_text
, stdout
);
2064 out
: safe_signal(SIGINT
, saveint
);
2065 safe_signal(SIGPIPE
, savepipe
);
2073 imap_newmail(int autoinc
)
2075 if (autoinc
&& had_exists
< 0 && had_expunge
< 0) {
2076 verbose
= value("verbose") != NULL
;
2081 if (had_exists
== msgCount
&& had_expunge
< 0)
2083 * Some servers always respond with EXISTS to NOOP. If
2084 * the mailbox has been changed but the number of messages
2085 * has not, an EXPUNGE must also had been sent; otherwise,
2086 * nothing has changed.
2089 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2093 imap_putflags(int f
)
2098 bp
= buf
= salloc(100);
2099 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2104 for (cp
= "\\Seen"; *cp
; cp
++)
2110 for (cp
= "\\Flagged"; *cp
; cp
++)
2113 if (f
& MANSWERED
) {
2116 for (cp
= "\\Answered"; *cp
; cp
++)
2122 for (cp
= "\\Draft"; *cp
; cp
++)
2133 imap_getflags(const char *cp
, char **xp
, enum mflag
*f
)
2135 while (*cp
!= ')') {
2137 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2139 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2141 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2143 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2145 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2147 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2157 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2158 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2162 size_t bufsize
, buflen
, count
;
2163 enum okay ok
= STOP
;
2164 long size
, lines
, ysize
;
2166 FILE *queuefp
= NULL
;
2168 if (mp
->mb_type
== MB_CACHE
) {
2169 queuefp
= cache_queue(mp
);
2170 if (queuefp
== NULL
)
2174 buf
= smalloc(bufsize
= LINESIZE
);
2176 again
: size
= xsize
;
2178 fseek(fp
, off1
, SEEK_SET
);
2179 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2180 tag(1), imap_quotestr(name
),
2181 imap_putflags(flag
),
2182 imap_make_date_time(t
),
2184 IMAP_OUT(o
, MB_COMD
, goto out
)
2185 while (mp
->mb_active
& MB_COMD
) {
2186 ok
= imap_answer(mp
, twice
);
2187 if (response_type
== RESPONSE_CONT
)
2190 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2198 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2201 buf
[buflen
-1] = '\r';
2203 if (mp
->mb_type
!= MB_CACHE
)
2204 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2206 fwrite(buf
, 1, buflen
+1, queuefp
);
2209 if (mp
->mb_type
!= MB_CACHE
)
2210 swrite(&mp
->mb_sock
, "\r\n");
2212 fputs("\r\n", queuefp
);
2213 while (mp
->mb_active
& MB_COMD
) {
2214 ok
= imap_answer(mp
, 0);
2215 if (response_status
== RESPONSE_NO
/*&&
2216 ascncasecmp(responded_text,
2217 "[TRYCREATE] ", 12) == 0*/) {
2218 trycreate
: if (twice
++) {
2222 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2224 imap_quotestr(name
));
2225 IMAP_OUT(o
, MB_COMD
, goto out
);
2226 while (mp
->mb_active
& MB_COMD
)
2227 ok
= imap_answer(mp
, 1);
2230 imap_created_mailbox
++;
2232 } else if (ok
!= OKAY
)
2233 fprintf(stderr
, "IMAP error: %s", responded_text
);
2234 else if (response_status
== RESPONSE_OK
&&
2235 mp
->mb_flags
& MB_UIDPLUS
)
2236 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2239 out
: if (queuefp
!= NULL
)
2246 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2248 char *buf
, *bp
, *lp
;
2249 size_t bufsize
, buflen
, count
;
2250 off_t off1
= -1, offs
;
2252 int flag
= MNEW
|MNEWEST
;
2257 buf
= smalloc(bufsize
= LINESIZE
);
2263 bp
= fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 1);
2264 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2265 if (off1
!= (off_t
)-1) {
2266 ok
=imap_append1(mp
, name
, fp
, off1
,
2270 fseek(fp
, offs
+buflen
, SEEK_SET
);
2272 off1
= offs
+ buflen
;
2277 tim
= unixtime(buf
);
2281 if (bp
&& buf
[0] == '\n')
2283 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2285 while (whitechar(*lp
&0377))
2288 while (*++lp
!= '\0')
2297 } else if (bp
&& inhead
&&
2298 ascncasecmp(buf
, "x-status", 8) == 0) {
2300 while (whitechar(*lp
&0377))
2303 while (*++lp
!= '\0')
2316 } while (bp
!= NULL
);
2322 imap_append(const char *xserver
, FILE *fp
)
2324 sighandler_type saveint
, savepipe
;
2325 char *server
, *uhp
, *mbx
, *user
;
2326 const char *sp
, *cp
, *pass
;
2328 enum okay ok
= STOP
;
2333 verbose
= value("verbose") != NULL
;
2334 server
= savestr((char *)xserver
);
2335 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &mbx
, &pass
, &user
);
2337 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2338 safe_signal(SIGINT
, maincatch
);
2339 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2340 if (sigsetjmp(imapjmp
, 1))
2342 if (savepipe
!= SIG_IGN
)
2343 safe_signal(SIGPIPE
, imapcatch
);
2344 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2345 mb
.mb_imap_account
&&
2346 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2347 ok
= imap_append0(&mb
, mbx
, fp
);
2352 memset(&mx
, 0, sizeof mx
);
2353 if (disconnected(server
) == 0) {
2354 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2355 use_ssl
? "imaps" : "imap",
2358 mx
.mb_sock
.s_desc
= "IMAP";
2359 mx
.mb_type
= MB_IMAP
;
2360 mx
.mb_imap_account
= (char *)protbase(server
);
2361 mx
.mb_imap_mailbox
= mbx
;
2362 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2363 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2364 sclose(&mx
.mb_sock
);
2367 ok
= imap_append0(&mx
, mbx
, fp
);
2369 sclose(&mx
.mb_sock
);
2371 mx
.mb_imap_account
= (char *)protbase(server
);
2372 mx
.mb_imap_mailbox
= mbx
;
2373 mx
.mb_type
= MB_CACHE
;
2374 ok
= imap_append0(&mx
, mbx
, fp
);
2378 out
: safe_signal(SIGINT
, saveint
);
2379 safe_signal(SIGPIPE
, savepipe
);
2387 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2388 struct list_item
**lend
, int level
)
2391 enum okay ok
= STOP
;
2394 FILE *queuefp
= NULL
;
2395 struct list_item
*lp
;
2397 *list
= *lend
= NULL
;
2398 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2399 tag(1), imap_quotestr(base
));
2400 IMAP_OUT(o
, MB_COMD
, return STOP
);
2401 while (mp
->mb_active
& MB_COMD
) {
2402 ok
= imap_answer(mp
, 1);
2403 if (response_status
== RESPONSE_OTHER
&&
2404 response_other
== MAILBOX_DATA_LIST
&&
2405 imap_parse_list() == OKAY
) {
2406 cp
= imap_unquotestr(list_name
);
2407 lp
= csalloc(1, sizeof *lp
);
2409 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2411 lp
->l_base
= *cp
? cp
: savestr(base
);
2412 lp
->l_attr
= list_attributes
;
2413 lp
->l_level
= level
+1;
2414 lp
->l_delim
= list_hierarchy_delimiter
;
2415 if (*list
&& *lend
) {
2416 (*lend
)->l_next
= lp
;
2426 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2428 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2434 verbose
= value("verbose") != NULL
;
2435 depth
= (cp
= value("imap-list-depth")) != NULL
? atoi(cp
) : 2;
2436 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2438 if (list
== NULL
|| lend
== NULL
)
2440 for (lp
= list
; lp
; lp
= lp
->l_next
)
2441 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2442 lp
->l_level
< depth
&&
2443 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2444 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2445 strcpy(cp
, lp
->l_name
);
2446 cp
[n
] = lp
->l_delim
;
2448 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2450 lp
->l_has_children
= 1;
2451 if (strcmp(cp
, lx
->l_name
) == 0)
2459 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2462 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2466 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2467 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2468 else if (lp
->l_has_children
== 0)
2469 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2470 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2476 imap_folders(const char *name
, int strip
)
2478 sighandler_type saveint
, savepipe
;
2479 const char *fold
, *cp
, *sp
;
2482 int columnize
= is_a_tty
[1];
2488 cp
= protbase(name
);
2489 sp
= mb
.mb_imap_account
;
2490 if (strcmp(cp
, sp
)) {
2491 fprintf(stderr
, "Cannot list folders on other than the "
2492 "current IMAP account,\n\"%s\". "
2493 "Try \"folders @\".\n", sp
);
2496 fold
= protfile(name
);
2498 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2507 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2508 safe_signal(SIGINT
, maincatch
);
2509 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2510 if (sigsetjmp(imapjmp
, 1))
2512 if (savepipe
!= SIG_IGN
)
2513 safe_signal(SIGPIPE
, imapcatch
);
2514 if (mb
.mb_type
== MB_CACHE
)
2515 cache_list(&mb
, fold
, strip
, fp
);
2517 imap_list(&mb
, fold
, strip
, fp
);
2530 fprintf(stderr
, "Folder not found.\n");
2533 safe_signal(SIGINT
, saveint
);
2534 safe_signal(SIGPIPE
, savepipe
);
2542 char o
[LINESIZE
], *tempfn
;
2544 long n
= 0, mx
= 0, columns
, width
;
2547 if ((out
= Ftemp(&tempfn
, "Ro", "w+", 0600, 1)) == NULL
) {
2553 while ((c
= getc(fp
)) != EOF
) {
2563 if (mx
< width
/ 2) {
2564 columns
= width
/ (mx
+2);
2565 snprintf(o
, sizeof o
,
2566 "sort | pr -%lu -w%lu -t",
2569 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2570 run_command(SHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2576 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2580 enum okay ok
= STOP
;
2583 FILE *queuefp
= NULL
;
2585 if (mp
->mb_type
== MB_CACHE
) {
2586 if ((queuefp
= cache_queue(mp
)) == NULL
)
2590 qname
= imap_quotestr(name
= protfile(name
));
2592 * Since it is not possible to set flags on the copy, recently
2593 * set flags must be set on the original to include it in the copy.
2595 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
))
2596 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2597 if (m
->m_flag
&MFLAG
)
2598 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2599 if (m
->m_flag
&MUNFLAG
)
2600 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2601 if (m
->m_flag
&MANSWER
)
2602 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2603 if (m
->m_flag
&MUNANSWER
)
2604 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2605 if (m
->m_flag
&MDRAFT
)
2606 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2607 if (m
->m_flag
&MUNDRAFT
)
2608 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2609 again
: if (m
->m_uid
)
2610 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n",
2611 tag(1), m
->m_uid
, qname
);
2613 if (check_expunged() == STOP
)
2615 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n",
2618 IMAP_OUT(o
, MB_COMD
, goto out
)
2619 while (mp
->mb_active
& MB_COMD
)
2620 ok
= imap_answer(mp
, twice
);
2621 if (mp
->mb_type
== MB_IMAP
&&
2622 mp
->mb_flags
& MB_UIDPLUS
&&
2623 response_status
== RESPONSE_OK
)
2624 imap_copyuid(mp
, m
, name
);
2625 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2626 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2627 IMAP_OUT(o
, MB_COMD
, goto out
)
2628 while (mp
->mb_active
& MB_COMD
)
2629 ok
= imap_answer(mp
, 1);
2631 imap_created_mailbox
++;
2635 if (queuefp
!= NULL
)
2638 * ... and reset the flag to its initial value so that
2639 * the 'exit' command still leaves the message unread.
2641 out
: if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
2642 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2645 if (m
->m_flag
&MFLAG
) {
2646 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2649 if (m
->m_flag
&MUNFLAG
) {
2650 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2653 if (m
->m_flag
&MANSWER
) {
2654 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2657 if (m
->m_flag
&MUNANSWER
) {
2658 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2661 if (m
->m_flag
&MDRAFT
) {
2662 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2665 if (m
->m_flag
&MUNDRAFT
) {
2666 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2670 mp
->mb_active
|= MB_COMD
;
2677 imap_copy(struct message
*m
, int n
, const char *name
)
2679 sighandler_type saveint
, savepipe
;
2680 enum okay ok
= STOP
;
2685 verbose
= value("verbose") != NULL
;
2687 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2688 safe_signal(SIGINT
, maincatch
);
2689 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2690 if (sigsetjmp(imapjmp
, 1) == 0) {
2691 if (savepipe
!= SIG_IGN
)
2692 safe_signal(SIGPIPE
, imapcatch
);
2693 ok
= imap_copy1(&mb
, m
, n
, name
);
2695 safe_signal(SIGINT
, saveint
);
2696 safe_signal(SIGPIPE
, savepipe
);
2704 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2705 unsigned long *olduid
, unsigned long *newuid
)
2709 *uidvalidity
= strtoul(cp
, &xp
, 10);
2710 *olduid
= strtoul(xp
, &yp
, 10);
2711 *newuid
= strtoul(yp
, &zp
, 10);
2712 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2713 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2717 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2722 *uidvalidity
= strtoul(cp
, &xp
, 10);
2723 *uid
= strtoul(xp
, &yp
, 10);
2724 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2725 yp
> xp
&& *yp
== ']';
2729 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2732 unsigned long uidvalidity
, olduid
, newuid
;
2736 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2737 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2738 &olduid
, &newuid
) == STOP
)
2741 xmb
.mb_cache_directory
= NULL
;
2742 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2743 xmb
.mb_uidvalidity
= uidvalidity
;
2746 memset(&xm
, 0, sizeof xm
);
2748 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2750 getcache(mp
, &xm
, NEED_HEADER
);
2751 getcache(mp
, &xm
, NEED_BODY
);
2753 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2754 getcache(mp
, m
, NEED_HEADER
);
2755 if ((m
->m_flag
& HAVE_BODY
) == 0)
2756 getcache(mp
, m
, NEED_BODY
);
2760 xm
.m_flag
&= ~MFULLYCACHED
;
2761 putcache(&xmb
, &xm
);
2766 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2767 long xsize
, long size
, long lines
, int flag
, const char *name
)
2770 unsigned long uidvalidity
, uid
;
2774 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2775 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2779 xmb
.mb_cache_directory
= NULL
;
2780 xmb
.mb_imap_mailbox
= savestr((char *)name
);
2781 xmb
.mb_uidvalidity
= uidvalidity
;
2782 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2784 memset(&xm
, 0, sizeof xm
);
2785 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2787 xm
.m_block
= mailx_blockof(off1
);
2788 xm
.m_offset
= mailx_offsetof(off1
);
2791 xm
.m_lines
= xm
.m_xlines
= lines
;
2793 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2794 putcache(&xmb
, &xm
);
2799 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2803 long size
, xsize
, ysize
, lines
;
2804 enum mflag flag
= MNEW
;
2805 char *name
, *buf
, *bp
, *cp
, *tempCopy
;
2806 size_t bufsize
, buflen
, count
;
2807 enum okay ok
= STOP
;
2809 buf
= smalloc(bufsize
= LINESIZE
);
2812 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2814 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
2817 if ((cp
= strrchr(bp
, '{')) == NULL
)
2819 xsize
= atol(&cp
[1]) + 2;
2820 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
2825 imap_getflags(cp
, &cp
, &flag
);
2826 while (*++cp
== ' ');
2828 t
= imap_read_date_time(cp
);
2829 if ((tp
= Ftemp(&tempCopy
, "Rc", "w+", 0600, 1)) == NULL
)
2836 if (fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0) == NULL
)
2839 buf
[--buflen
] = '\0';
2840 buf
[buflen
-1] = '\n';
2841 fwrite(buf
, 1, buflen
, tp
);
2847 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
2848 imap_unquotestr(name
));
2857 imap_search2(struct mailbox
*mp
, struct message
*m
, int count
,
2858 const char *spec
, int f
)
2862 FILE *queuefp
= NULL
;
2863 enum okay ok
= STOP
;
2870 for (cp
= spec
; *cp
; cp
++)
2875 if (asccasecmp(cp
, "utf-8")) {
2877 char *sp
, *nsp
, *nspec
;
2879 if ((it
= iconv_open_ft("utf-8", cp
)) != (iconv_t
)-1) {
2880 sz
= strlen(spec
) + 1;
2881 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2883 if (iconv_ft(it
, &sp
, &sz
, &nsp
, &nsz
, 0)
2884 != (size_t)-1 && sz
== 0) {
2891 #endif /* HAVE_ICONV */
2892 cp
= imap_quotestr(cp
);
2893 cs
= salloc(n
= strlen(cp
) + 10);
2894 snprintf(cs
, n
, "CHARSET %s ", cp
);
2897 o
= ac_alloc(osize
= strlen(spec
) + 60);
2898 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2899 IMAP_OUT(o
, MB_COMD
, goto out
)
2900 while (mp
->mb_active
& MB_COMD
) {
2901 ok
= imap_answer(mp
, 0);
2902 if (response_status
== RESPONSE_OTHER
&&
2903 response_other
== MAILBOX_DATA_SEARCH
) {
2904 xp
= responded_other_text
;
2905 while (*xp
&& *xp
!= '\r') {
2906 n
= strtoul(xp
, &xp
, 10);
2907 for (i
= 0; i
< count
; i
++)
2908 if (m
[i
].m_uid
== n
&&
2909 (m
[i
].m_flag
&MHIDDEN
)
2912 (m
[i
].m_flag
&MDELETED
)
2923 imap_search1(const char *spec
, int f
)
2925 sighandler_type saveint
, savepipe
;
2926 enum okay ok
= STOP
;
2931 if (mb
.mb_type
!= MB_IMAP
)
2933 verbose
= value("verbose") != NULL
;
2935 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2936 safe_signal(SIGINT
, maincatch
);
2937 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2938 if (sigsetjmp(imapjmp
, 1) == 0) {
2939 if (savepipe
!= SIG_IGN
)
2940 safe_signal(SIGPIPE
, imapcatch
);
2941 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2943 safe_signal(SIGINT
, saveint
);
2944 safe_signal(SIGPIPE
, savepipe
);
2952 imap_thisaccount(const char *cp
)
2954 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2956 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2957 mb
.mb_imap_account
== NULL
)
2959 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2963 imap_remove(const char *name
)
2965 sighandler_type saveint
, savepipe
;
2966 enum okay ok
= STOP
;
2971 verbose
= value("verbose") != NULL
;
2972 if (mb
.mb_type
!= MB_IMAP
) {
2973 fprintf(stderr
, "Refusing to remove \"%s\" "
2974 "in disconnected mode.\n", name
);
2977 if (!imap_thisaccount(name
)) {
2978 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2979 "server: \"%s\" not removed.\n", name
);
2983 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2984 safe_signal(SIGINT
, maincatch
);
2985 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2986 if (sigsetjmp(imapjmp
, 1) == 0) {
2987 if (savepipe
!= SIG_IGN
)
2988 safe_signal(SIGPIPE
, imapcatch
);
2989 ok
= imap_remove1(&mb
, protfile(name
));
2991 safe_signal(SIGINT
, saveint
);
2992 safe_signal(SIGPIPE
, savepipe
);
2995 ok
= cache_remove(name
);
3002 imap_remove1(struct mailbox
*mp
, const char *name
)
3004 FILE *queuefp
= NULL
;
3007 enum okay ok
= STOP
;
3009 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3010 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3011 IMAP_OUT(o
, MB_COMD
, goto out
)
3012 while (mp
->mb_active
& MB_COMD
)
3013 ok
= imap_answer(mp
, 1);
3019 imap_rename(const char *old
, const char *new)
3021 sighandler_type saveint
, savepipe
;
3022 enum okay ok
= STOP
;
3027 verbose
= value("verbose") != NULL
;
3028 if (mb
.mb_type
!= MB_IMAP
) {
3029 fprintf(stderr
, "Refusing to rename mailboxes "
3030 "in disconnected mode.\n");
3033 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3034 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3035 "server: \"%s\" not renamed to \"%s\".\n",
3040 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3041 safe_signal(SIGINT
, maincatch
);
3042 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3043 if (sigsetjmp(imapjmp
, 1) == 0) {
3044 if (savepipe
!= SIG_IGN
)
3045 safe_signal(SIGPIPE
, imapcatch
);
3046 ok
= imap_rename1(&mb
, protfile(old
), protfile(new));
3048 safe_signal(SIGINT
, saveint
);
3049 safe_signal(SIGPIPE
, savepipe
);
3052 ok
= cache_rename(old
, new);
3059 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3061 FILE *queuefp
= NULL
;
3064 enum okay ok
= STOP
;
3066 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3067 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3068 imap_quotestr(old
), imap_quotestr(new));
3069 IMAP_OUT(o
, MB_COMD
, goto out
)
3070 while (mp
->mb_active
& MB_COMD
)
3071 ok
= imap_answer(mp
, 1);
3077 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3079 FILE *queuefp
= NULL
;
3080 char o
[LINESIZE
], *newname
;
3081 char *buf
, *bp
, *cp
, iob
[4096];
3082 size_t bufsize
, buflen
, count
;
3083 enum okay ok
= OKAY
, rok
= OKAY
;
3084 long offs
, offs1
, offs2
, octets
;
3085 int twice
, gotcha
= 0;
3087 buf
= smalloc(bufsize
= LINESIZE
);
3090 while (offs1
= ftell(fp
),
3091 fgetline(&buf
, &bufsize
, &count
, &buflen
, fp
, 0)
3093 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3098 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3099 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3101 while (digitchar(*cp
&0377))
3107 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3109 IMAP_OUT(o
, MB_COMD
, continue)
3110 while (mp
->mb_active
& MB_COMD
)
3111 ok
= imap_answer(mp
, twice
);
3112 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3114 if (response_status
== RESPONSE_OK
&&
3115 mp
->mb_flags
& MB_UIDPLUS
) {
3116 imap_copyuid(mp
, NULL
,
3117 imap_unquotestr(newname
));
3119 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3120 IMAP_OUT(o
, MB_COMD
, continue)
3121 while (mp
->mb_active
& MB_COMD
)
3122 ok
= imap_answer(mp
, 1);
3125 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3126 if ((cp
= strrchr(bp
, '{')) == NULL
)
3128 octets
= atol(&cp
[1]) + 2;
3129 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3131 IMAP_OUT(o
, MB_COMD
, continue)
3132 while (mp
->mb_active
& MB_COMD
) {
3133 ok
= imap_answer(mp
, twice
);
3134 if (response_type
== RESPONSE_CONT
)
3139 fseek(fp
, offs
, SEEK_SET
);
3144 while (octets
> 0) {
3145 size_t n
= (size_t)octets
> sizeof iob
3146 ? sizeof iob
: (size_t)octets
;
3148 if (n
!= fread(iob
, 1, n
, fp
))
3150 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3152 swrite(&mp
->mb_sock
, "");
3153 while (mp
->mb_active
& MB_COMD
) {
3154 ok
= imap_answer(mp
, 0);
3155 if (response_status
== RESPONSE_NO
&&
3157 fseek(fp
, offs
, SEEK_SET
);
3161 if (response_status
== RESPONSE_OK
&&
3162 mp
->mb_flags
& MB_UIDPLUS
) {
3164 fseek(fp
, offs1
, SEEK_SET
);
3165 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3166 fseek(fp
, offs2
, SEEK_SET
);
3171 fail
: fprintf(stderr
,
3172 "Invalid command in IMAP cache queue: \"%s\"\n",
3178 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3180 IMAP_OUT(o
, MB_COMD
, continue)
3181 while (mp
->mb_active
& MB_COMD
)
3182 ok
= imap_answer(mp
, 1);
3188 ftruncate(fileno(fp
), 0);
3195 imap_strex(const char *cp
, char **xp
)
3202 for (cq
= &cp
[1]; *cq
; cq
++) {
3205 else if (*cq
== '"')
3210 n
= salloc(cq
- cp
+ 2);
3211 memcpy(n
, cp
, cq
- cp
+ 1);
3212 n
[cq
- cp
+ 1] = '\0';
3214 *xp
= (char *)&cq
[1];
3219 check_expunged(void)
3221 if (expunged_messages
> 0) {
3223 "Command not executed - messages have been expunged\n");
3234 int omsgCount
= msgCount
;
3237 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3238 fprintf(stderr
, "Already connected.\n");
3241 unset_allow_undefined
= 1;
3242 unset_internal("disconnected");
3243 cp
= protbase(mailname
);
3244 if (strncmp(cp
, "imap://", 7) == 0)
3246 else if (strncmp(cp
, "imaps://", 8) == 0)
3248 if ((cq
= strchr(cp
, ':')) != NULL
)
3250 unset_internal(savecat("disconnected-", cp
));
3251 unset_allow_undefined
= 0;
3252 if (mb
.mb_type
== MB_CACHE
) {
3253 imap_setfile1(mailname
, 0, edit
, 1);
3254 if (msgCount
> omsgCount
)
3255 newmailinfo(omsgCount
);
3261 cdisconnect(void *vp
)
3265 if (mb
.mb_type
== MB_CACHE
) {
3266 fprintf(stderr
, "Not connected.\n");
3268 } else if (mb
.mb_type
== MB_IMAP
) {
3269 if (cached_uidvalidity(&mb
) == 0) {
3270 fprintf(stderr
, "The current mailbox is not cached.\n");
3276 assign("disconnected", "");
3277 if (mb
.mb_type
== MB_IMAP
) {
3278 sclose(&mb
.mb_sock
);
3279 imap_setfile1(mailname
, 0, edit
, 1);
3286 int *msgvec
= vp
, *ip
;
3289 if (mb
.mb_type
!= MB_IMAP
) {
3290 fprintf(stderr
, "Not connected to an IMAP server.\n");
3293 if (cached_uidvalidity(&mb
) == 0) {
3294 fprintf(stderr
, "The current mailbox is not cached.\n");
3297 for (ip
= msgvec
; *ip
; ip
++) {
3298 mp
= &message
[*ip
-1];
3299 if (!(mp
->m_have
& HAVE_BODY
))
3304 #else /* !USE_IMAP */
3311 fprintf(stderr
, catgets(catd
, CATSET
, 269,
3312 "No IMAP support compiled in.\n"));
3316 imap_setfile(const char *server
, int newmail
, int isedit
)
3326 imap_header(struct message
*mp
)
3334 imap_body(struct message
*mp
)
3342 imap_getheaders(int bot
, int top
)
3365 imap_newmail(int dummy
)
3373 imap_undelete(struct message
*m
, int n
)
3382 imap_unread(struct message
*m
, int n
)
3391 imap_append(const char *server
, FILE *fp
)
3401 imap_folders(const char *name
, int strip
)
3410 imap_remove(const char *name
)
3419 imap_rename(const char *old
, const char *new)
3428 imap_copy(struct message
*m
, int n
, const char *name
)
3439 imap_search1(const char *spec
, int f
)
3447 imap_thisaccount(const char *cp
)
3471 cdisconnect(void *vp
)
3486 #endif /* USE_IMAP */
3489 imap_read_date_time(const char *cp
)
3492 int i
, year
, month
, day
, hour
, minute
, second
;
3497 * "25-Jul-2004 15:33:44 +0200"
3501 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3503 day
= strtol(&cp
[1], NULL
, 10);
3504 for (i
= 0; month_names
[i
]; i
++)
3505 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3507 if (month_names
[i
] == NULL
)
3510 year
= strtol(&cp
[8], NULL
, 10);
3511 hour
= strtol(&cp
[13], NULL
, 10);
3512 minute
= strtol(&cp
[16], NULL
, 10);
3513 second
= strtol(&cp
[19], NULL
, 10);
3514 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3529 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3532 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3540 imap_read_date(const char *cp
)
3543 int year
, month
, day
, i
, tzdiff
;
3549 day
= strtol(cp
, &xp
, 10);
3550 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3552 for (i
= 0; month_names
[i
]; i
++)
3553 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3555 if (month_names
[i
] == NULL
)
3560 year
= strtol(&xp
[4], &yp
, 10);
3561 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3563 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3565 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3567 tzdiff
= t
- mktime(gmtime(&t
));
3568 tmptr
= localtime(&t
);
3569 if (tmptr
->tm_isdst
> 0)
3576 imap_make_date_time(time_t t
)
3580 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3582 tzdiff
= t
- mktime(gmtime(&t
));
3583 tzdiff_hour
= (int)(tzdiff
/ 60);
3584 tzdiff_min
= tzdiff_hour
% 60;
3586 tmptr
= localtime(&t
);
3587 if (tmptr
->tm_isdst
> 0)
3589 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3591 month_names
[tmptr
->tm_mon
],
3592 tmptr
->tm_year
+ 1900,
3602 imap_quotestr(const char *s
)
3606 np
= n
= salloc(2 * strlen(s
) + 3);
3609 if (*s
== '"' || *s
== '\\')
3619 imap_unquotestr(const char *s
)
3625 np
= n
= salloc(strlen(s
) + 1);