1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ IMAP v4r1 client following RFC 2060.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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
40 #ifndef HAVE_AMALGAMATION
45 # include <sys/socket.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
57 #define IMAP_ANSWER() \
59 if (mp->mb_type != MB_CACHE) {\
61 while (mp->mb_active & MB_COMD)\
62 ok = imap_answer(mp, 1);\
68 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
69 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
70 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
71 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
72 * TODO I.e., that should be a function, not a macro ... or so.
73 * TODO This entire module needs MASSIVE work! */
74 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
75 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL) \
77 if (mp->mb_type != MB_CACHE) {\
78 if (imap_finish(mp) == STOP) {\
81 if (options & OPT_VERBOSE)\
82 fprintf(stderr, ">>> %s", X);\
84 if (swrite(&mp->mb_sock, X) == STOP) {\
93 static struct record
{
94 struct record
*rec_next
;
95 unsigned long rec_count
;
120 static char *responded_tag
;
121 static char *responded_text
;
122 static char *responded_other_text
;
123 static long responded_other_number
;
129 MAILBOX_DATA_MAILBOX
,
134 MESSAGE_DATA_EXPUNGE
,
137 RESPONSE_OTHER_UNKNOWN
140 static enum list_attributes
{
142 LIST_NOINFERIORS
= 001,
148 static int list_hierarchy_delimiter
;
149 static char *list_name
;
152 struct list_item
*l_next
;
155 enum list_attributes l_attr
;
161 static char *imapbuf
; /* TODO not static, use pool */
162 static size_t imapbufsize
;
163 static sigjmp_buf imapjmp
;
164 static sighandler_type savealrm
;
165 static int imapkeepalive
;
166 static long had_exists
= -1;
167 static long had_expunge
= -1;
168 static long expunged_messages
;
169 static int volatile imaplock
;
170 static int same_imap_account
;
172 static void imap_other_get(char *pp
);
173 static void imap_response_get(const char **cp
);
174 static void imap_response_parse(void);
175 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
176 static enum okay
imap_parse_list(void);
177 static enum okay
imap_finish(struct mailbox
*mp
);
178 static void imap_timer_off(void);
179 static void imapcatch(int s
);
180 static void _imap_maincatch(int s
);
181 static enum okay
imap_noop1(struct mailbox
*mp
);
182 static void rec_queue(enum rec_type type
, unsigned long cnt
);
183 static enum okay
rec_dequeue(void);
184 static void rec_rmqueue(void);
185 static void imapalarm(int s
);
186 static int imap_use_starttls(const char *uhp
);
187 static enum okay
imap_preauth(struct mailbox
*mp
, const char *xserver
,
189 static enum okay
imap_capability(struct mailbox
*mp
);
190 static enum okay
imap_auth(struct mailbox
*mp
, const char *uhp
,
191 char *xuser
, char *pass
);
193 static enum okay
imap_cram_md5(struct mailbox
*mp
, char *xuser
, char *xpass
);
195 static enum okay
imap_login(struct mailbox
*mp
, char *xuser
, char *xpass
);
197 static enum okay
imap_gss(struct mailbox
*mp
, char *user
);
199 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
200 static void imap_init(struct mailbox
*mp
, int n
);
201 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
203 static void imap_split(char **server
, const char **sp
, int *use_ssl
,
204 const char **cp
, char const **uhp
, char const **mbx
,
205 char **pass
, char **user
);
206 static int imap_setfile1(const char *xserver
, int nmail
, int isedit
,
208 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
209 size_t expected
, int need
, const char *head
,
210 size_t headsize
, long headlines
);
211 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
212 const char *str
, const char *head
, size_t headsize
,
214 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
216 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
217 struct message
*from
, enum havespec have
);
218 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
220 static enum okay
imap_exit(struct mailbox
*mp
);
221 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
,
223 static enum okay
imap_close(struct mailbox
*mp
);
224 static enum okay
imap_update(struct mailbox
*mp
);
225 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
226 int c
, const char *sp
, int needstat
);
227 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
228 static const char *tag(int new);
229 static char * imap_putflags(int f
);
230 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
231 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
232 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
233 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
234 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
235 struct list_item
**list
, struct list_item
**lend
,
237 static enum okay
imap_list(struct mailbox
*mp
, const char *base
, int strip
,
239 static void dopr(FILE *fp
);
240 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
242 static enum okay
imap_copyuid_parse(const char *cp
,
243 unsigned long *uidvalidity
, unsigned long *olduid
,
244 unsigned long *newuid
);
245 static enum okay
imap_appenduid_parse(const char *cp
,
246 unsigned long *uidvalidity
, unsigned long *uid
);
247 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
249 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
250 long off1
, long xsize
, long size
, long lines
, int flag
,
252 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
253 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
,
254 const char *spec
, int f
);
255 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
256 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
258 static char * imap_strex(char const *cp
, char const **xp
);
259 static enum okay
check_expunged(void);
262 imap_other_get(char *pp
)
267 if (ascncasecmp(pp
, "FLAGS ", 6) == 0) {
269 response_other
= MAILBOX_DATA_FLAGS
;
270 } else if (ascncasecmp(pp
, "LIST ", 5) == 0) {
272 response_other
= MAILBOX_DATA_LIST
;
273 } else if (ascncasecmp(pp
, "LSUB ", 5) == 0) {
275 response_other
= MAILBOX_DATA_LSUB
;
276 } else if (ascncasecmp(pp
, "MAILBOX ", 8) == 0) {
278 response_other
= MAILBOX_DATA_MAILBOX
;
279 } else if (ascncasecmp(pp
, "SEARCH ", 7) == 0) {
281 response_other
= MAILBOX_DATA_SEARCH
;
282 } else if (ascncasecmp(pp
, "STATUS ", 7) == 0) {
284 response_other
= MAILBOX_DATA_STATUS
;
285 } else if (ascncasecmp(pp
, "CAPABILITY ", 11) == 0) {
287 response_other
= CAPABILITY_DATA
;
289 responded_other_number
= strtol(pp
, &xp
, 10);
292 if (ascncasecmp(xp
, "EXISTS\r\n", 8) == 0) {
293 response_other
= MAILBOX_DATA_EXISTS
;
294 } else if (ascncasecmp(xp
, "RECENT\r\n", 8) == 0) {
295 response_other
= MAILBOX_DATA_RECENT
;
296 } else if (ascncasecmp(xp
, "EXPUNGE\r\n", 9) == 0) {
297 response_other
= MESSAGE_DATA_EXPUNGE
;
298 } else if (ascncasecmp(xp
, "FETCH ", 6) == 0) {
300 response_other
= MESSAGE_DATA_FETCH
;
302 response_other
= RESPONSE_OTHER_UNKNOWN
;
304 responded_other_text
= pp
;
309 imap_response_get(const char **cp
)
312 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
314 response_status
= RESPONSE_OK
;
315 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
317 response_status
= RESPONSE_NO
;
318 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
320 response_status
= RESPONSE_BAD
;
321 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
323 response_status
= RESPONSE_PREAUTH
;
324 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
326 response_status
= RESPONSE_BYE
;
328 response_status
= RESPONSE_OTHER
;
333 imap_response_parse(void)
335 static char *parsebuf
; /* TODO Use pool */
336 static size_t parsebufsize
;
338 const char *ip
= imapbuf
;
342 if (parsebufsize
< imapbufsize
)
343 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
344 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
348 response_type
= RESPONSE_CONT
;
363 imap_response_get(&ip
);
364 pp
= &parsebuf
[ip
- imapbuf
];
365 switch (response_status
) {
367 response_type
= RESPONSE_FATAL
;
370 response_type
= RESPONSE_DATA
;
374 responded_tag
= parsebuf
;
375 while (*pp
&& *pp
!= ' ')
378 response_type
= RESPONSE_ILLEGAL
;
382 while (*pp
&& *pp
== ' ')
385 response_type
= RESPONSE_ILLEGAL
;
388 ip
= &imapbuf
[pp
- parsebuf
];
389 response_type
= RESPONSE_TAGGED
;
390 imap_response_get(&ip
);
391 pp
= &parsebuf
[ip
- imapbuf
];
394 if (response_type
!= RESPONSE_CONT
&& response_type
!= RESPONSE_ILLEGAL
&&
395 response_status
== RESPONSE_OTHER
)
401 imap_answer(struct mailbox
*mp
, int errprnt
)
408 if (mp
->mb_type
== MB_CACHE
)
412 if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
413 if (options
& OPT_VERBOSE
)
414 fputs(imapbuf
, stderr
);
415 imap_response_parse();
416 if (response_type
== RESPONSE_ILLEGAL
)
418 if (response_type
== RESPONSE_CONT
) {
422 if (response_status
== RESPONSE_OTHER
) {
423 if (response_other
== MAILBOX_DATA_EXISTS
) {
424 had_exists
= responded_other_number
;
425 rec_queue(REC_EXISTS
, responded_other_number
);
428 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
429 rec_queue(REC_EXPUNGE
, responded_other_number
);
437 if (response_type
== RESPONSE_TAGGED
) {
438 if (asccasecmp(responded_tag
, tag(0)) == 0)
443 switch (response_status
) {
444 case RESPONSE_PREAUTH
:
445 mp
->mb_active
&= ~MB_PREAUTH
;
458 fprintf(stderr
, tr(270, "IMAP error: %s"), responded_text
);
460 case RESPONSE_UNKNOWN
: /* does not happen */
463 mp
->mb_active
= MB_NONE
;
471 if (response_status
!= RESPONSE_OTHER
&&
472 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
473 fprintf(stderr
, "IMAP alert: %s", &responded_text
[8]);
475 mp
->mb_active
&= ~MB_COMD
;
478 mp
->mb_active
= MB_NONE
;
486 imap_parse_list(void)
494 cp
= responded_other_text
;
495 list_attributes
= LIST_NONE
;
497 while (*cp
&& *cp
!= ')') {
499 if (ascncasecmp(&cp
[1], "Noinferiors ", 12) == 0) {
500 list_attributes
|= LIST_NOINFERIORS
;
502 } else if (ascncasecmp(&cp
[1], "Noselect ", 9) == 0) {
503 list_attributes
|= LIST_NOSELECT
;
505 } else if (ascncasecmp(&cp
[1], "Marked ", 7) == 0) {
506 list_attributes
|= LIST_MARKED
;
508 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9) == 0) {
509 list_attributes
|= LIST_UNMARKED
;
521 list_hierarchy_delimiter
= EOF
;
525 list_hierarchy_delimiter
= *cp
++ & 0377;
526 if (cp
[0] != '"' || cp
[1] != ' ')
529 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' && cp
[3] == ' ') {
530 list_hierarchy_delimiter
= EOF
;
537 while (*cp
&& *cp
!= '\r')
547 imap_finish(struct mailbox
*mp
)
550 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
560 if (imapkeepalive
> 0) {
562 safe_signal(SIGALRM
, savealrm
);
570 NYD_X
; /* Signal handler */
571 termios_state_reset();
574 fprintf(stderr
, tr(102, "Interrupt\n"));
575 siglongjmp(imapjmp
, 1);
578 fprintf(stderr
, tr(98, "Received SIGPIPE during IMAP operation\n"));
584 _imap_maincatch(int s
)
586 NYD_X
; /* Signal handler */
588 if (interrupts
++ == 0) {
589 fprintf(stderr
, tr(102, "Interrupt\n"));
596 imap_noop1(struct mailbox
*mp
)
599 FILE *queuefp
= NULL
;
602 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
603 IMAP_OUT(o
, MB_COMD
, return STOP
)
609 imap_fileof(char const *xcp
)
611 char const *cp
= xcp
;
616 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
620 if (cp
[0] == '/' && state
== 1) {
638 sighandler_type
volatile oldint
, oldpipe
;
642 if (mb
.mb_type
!= MB_IMAP
)
646 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
647 safe_signal(SIGINT
, &_imap_maincatch
);
648 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
649 if (sigsetjmp(imapjmp
, 1) == 0) {
650 if (oldpipe
!= SIG_IGN
)
651 safe_signal(SIGPIPE
, imapcatch
);
653 rv
= imap_noop1(&mb
);
655 safe_signal(SIGINT
, oldint
);
656 safe_signal(SIGPIPE
, oldpipe
);
666 rec_queue(enum rec_type rt
, unsigned long cnt
)
671 rp
= scalloc(1, sizeof *rp
);
674 if (record
&& recend
) {
675 recend
->rec_next
= rp
;
678 record
= recend
= rp
;
685 struct message
*omessage
;
686 struct record
*rp
, *rq
;
695 message
= smalloc((msgCount
+1) * sizeof *message
);
697 memcpy(message
, omessage
, msgCount
* sizeof *message
);
698 memset(&message
[msgCount
], 0, sizeof *message
);
700 rp
= record
, rq
= NULL
;
703 switch (rp
->rec_type
) {
705 exists
= rp
->rec_count
;
708 if (rp
->rec_count
== 0) {
712 if (rp
->rec_count
> (unsigned long)msgCount
) {
713 if (exists
== 0 || rp
->rec_count
> exists
--)
719 delcache(&mb
, &message
[rp
->rec_count
-1]);
720 memmove(&message
[rp
->rec_count
-1], &message
[rp
->rec_count
],
721 ((msgCount
- rp
->rec_count
+ 1) * sizeof *message
));
723 /* If the message was part of a collapsed thread,
724 * the m_collapsed field of one of its ancestors
725 * should be incremented. It seems hardly possible
726 * to do this with the current message structure,
727 * though. The result is that a '+' may be shown
728 * in the header summary even if no collapsed
740 record
= recend
= NULL
;
741 if (rv
== OKAY
&& UICMP(z
, exists
, >, msgCount
)) {
742 message
= srealloc(message
, (exists
+ 1) * sizeof *message
);
743 memset(&message
[msgCount
], 0, (exists
- msgCount
+ 1) * sizeof *message
);
744 for (i
= msgCount
; i
< exists
; ++i
)
746 imap_flags(&mb
, msgCount
+1, exists
);
765 for (rp
= record
; rp
!= NULL
;) {
766 struct record
*tmp
= rp
;
770 record
= recend
= NULL
;
778 sighandler_type
volatile saveint
, savepipe
;
779 NYD_X
; /* Signal handler */
782 if (imaplock
++ == 0) {
783 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
784 safe_signal(SIGINT
, &_imap_maincatch
);
785 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
786 if (sigsetjmp(imapjmp
, 1)) {
787 safe_signal(SIGINT
, saveint
);
788 safe_signal(SIGPIPE
, savepipe
);
791 if (savepipe
!= SIG_IGN
)
792 safe_signal(SIGPIPE
, imapcatch
);
793 if (imap_noop1(&mb
) != OKAY
) {
794 safe_signal(SIGINT
, saveint
);
795 safe_signal(SIGPIPE
, savepipe
);
798 safe_signal(SIGINT
, saveint
);
799 safe_signal(SIGPIPE
, savepipe
);
802 alarm(imapkeepalive
);
808 imap_use_starttls(const char *uhp
)
813 if (ok_blook(imap_use_starttls
))
816 char *var
= savecat("imap-use-starttls-", uhp
);
824 imap_preauth(struct mailbox
*mp
, const char *xserver
, const char *uhp
)
829 mp
->mb_active
|= MB_PREAUTH
;
831 if ((cp
= strchr(xserver
, ':')) != NULL
) {
832 char *x
= salloc(cp
- xserver
+ 1);
833 memcpy(x
, xserver
, cp
- xserver
);
834 x
[cp
- xserver
] = '\0';
838 if (mp
->mb_sock
.s_use_ssl
== 0 && imap_use_starttls(uhp
)) {
839 FILE *queuefp
= NULL
;
842 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
843 IMAP_OUT(o
, MB_COMD
, return STOP
)
845 if (ssl_open(xserver
, &mp
->mb_sock
, uhp
) != OKAY
)
849 if (imap_use_starttls(uhp
)) {
850 fprintf(stderr
, "No SSL support compiled in.\n");
859 imap_capability(struct mailbox
*mp
)
862 FILE *queuefp
= NULL
;
867 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
868 IMAP_OUT(o
, MB_COMD
, return STOP
)
869 while (mp
->mb_active
& MB_COMD
) {
870 ok
= imap_answer(mp
, 0);
871 if (response_status
== RESPONSE_OTHER
&&
872 response_other
== CAPABILITY_DATA
) {
873 cp
= responded_other_text
;
875 while (spacechar(*cp
))
877 if (strncmp(cp
, "UIDPLUS", 7) == 0 && spacechar(cp
[7]))
879 mp
->mb_flags
|= MB_UIDPLUS
;
880 while (*cp
&& !spacechar(*cp
))
889 imap_auth(struct mailbox
*mp
, const char *uhp
, char *xuser
, char *pass
)
895 if (!(mp
->mb_active
& MB_PREAUTH
)) {
900 if ((auth
= ok_vlook(imap_auth
)) == NULL
) {
901 size_t i
= strlen(uhp
) + 1;
902 var
= ac_alloc(i
+ 10);
903 memcpy(var
, "imap-auth-", 10);
904 memcpy(var
+ 10, uhp
, i
);
905 auth
= vok_vlook(var
);
909 if (auth
== NULL
|| !strcmp(auth
, "login"))
910 rv
= imap_login(mp
, xuser
, pass
);
911 else if (!strcmp(auth
, "cram-md5")) {
913 rv
= imap_cram_md5(mp
, xuser
, pass
);
915 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
918 } else if (!strcmp(auth
, "gssapi")) {
920 rv
= imap_gss(mp
, xuser
);
922 fprintf(stderr
, tr(272, "No GSSAPI support compiled in.\n"));
926 fprintf(stderr
, tr(273,
927 "Unknown IMAP authentication method: %s\n"), auth
);
935 /* Implementation of RFC 2194 */
938 imap_cram_md5(struct mailbox
*mp
, char *xuser
, char *xpass
)
940 char o
[LINESIZE
], *user
, *pass
, *cp
;
941 FILE *queuefp
= NULL
;
948 if (!getcredentials(&user
, &pass
))
951 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
952 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
954 if (response_type
!= RESPONSE_CONT
)
957 cp
= cram_md5_string(user
, pass
, responded_text
);
958 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
959 while (mp
->mb_active
& MB_COMD
)
960 rv
= imap_answer(mp
, 1);
969 #endif /* HAVE_MD5 */
972 imap_login(struct mailbox
*mp
, char *xuser
, char *xpass
)
976 FILE *queuefp
= NULL
;
983 if (!getcredentials(&user
, &pass
))
986 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
987 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
988 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
989 while (mp
->mb_active
& MB_COMD
)
990 rv
= imap_answer(mp
, 1);
1001 # include "imap_gssapi.h"
1005 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
)
1007 enum okay ok
= OKAY
;
1010 FILE *queuefp
= NULL
;
1014 mp
->mb_uidvalidity
= 0;
1015 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
1016 IMAP_OUT(o
, MB_COMD
, return STOP
)
1017 while (mp
->mb_active
& MB_COMD
) {
1018 ok
= imap_answer(mp
, 1);
1019 if (response_status
!= RESPONSE_OTHER
&&
1020 (cp
= asccasestr(responded_text
, "[UIDVALIDITY ")) != NULL
)
1021 mp
->mb_uidvalidity
= atol(&cp
[13]);
1023 *cnt
= (had_exists
> 0) ? had_exists
: 0;
1024 if (response_status
!= RESPONSE_OTHER
&&
1025 ascncasecmp(responded_text
, "[READ-ONLY] ", 12) == 0)
1031 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
1034 FILE *queuefp
= NULL
;
1037 unsigned x
= X
, y
= Y
, n
;
1040 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
1041 IMAP_OUT(o
, MB_COMD
, return STOP
)
1042 while (mp
->mb_active
& MB_COMD
) {
1044 if (response_status
== RESPONSE_OTHER
&&
1045 response_other
== MESSAGE_DATA_FETCH
) {
1046 n
= responded_other_number
;
1054 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
1059 imap_getflags(cp
, &cp
, &m
->m_flag
);
1062 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
1063 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1064 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1065 m
->m_flag
&= ~MHIDDEN
;
1068 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1070 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1073 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1075 IMAP_OUT(o
, MB_COMD
, return STOP
)
1076 while (mp
->mb_active
& MB_COMD
) {
1078 if (response_status
== RESPONSE_OTHER
&&
1079 response_other
== MESSAGE_DATA_FETCH
) {
1080 n
= responded_other_number
;
1086 if ((cp
= asccasestr(responded_other_text
, "RFC822.SIZE ")) != NULL
)
1087 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1088 if ((cp
= asccasestr(responded_other_text
, "INTERNALDATE ")) != NULL
)
1089 m
->m_time
= imap_read_date_time(&cp
[13]);
1093 for (n
= X
; n
<= Y
; n
++)
1094 putcache(mp
, &message
[n
-1]);
1099 imap_init(struct mailbox
*mp
, int n
)
1106 m
->m_flag
= MUSED
| MNOFROM
;
1113 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1115 struct message
*omessage
= 0;
1116 int i
, omsgCount
= 0;
1117 enum okay dequeued
= STOP
;
1120 if (nmail
|| transparent
) {
1122 omsgCount
= msgCount
;
1125 dequeued
= rec_dequeue();
1127 if (had_exists
>= 0) {
1128 if (dequeued
!= OKAY
)
1129 msgCount
= had_exists
;
1132 if (had_expunge
>= 0) {
1133 if (dequeued
!= OKAY
)
1134 msgCount
-= had_expunge
;
1138 if (nmail
&& expunged_messages
)
1139 printf("Expunged %ld message%s.\n", expunged_messages
,
1140 (expunged_messages
!= 1 ? "s" : ""));
1141 *prevcount
= omsgCount
- expunged_messages
;
1142 expunged_messages
= 0;
1144 fputs("IMAP error: Negative message count\n", stderr
);
1148 if (dequeued
!= OKAY
) {
1149 message
= scalloc(msgCount
+ 1, sizeof *message
);
1150 for (i
= 0; i
< msgCount
; i
++)
1152 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1155 imap_flags(mp
, 1, msgCount
);
1156 message
[msgCount
].m_size
= 0;
1157 message
[msgCount
].m_lines
= 0;
1160 if (nmail
|| transparent
)
1161 transflags(omessage
, omsgCount
, transparent
);
1168 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1169 char const **uhp
, char const **mbx
, char **pass
, char **user
)
1173 if (strncmp(*sp
, "imap://", 7) == 0) {
1177 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1182 *use_ssl
= 0; /* (silence CC) */
1184 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1185 char *x
= savestr(*sp
);
1186 x
[*cp
- *sp
] = '\0';
1191 (*server
)[*cp
- *server
] = '\0';
1196 *pass
= lookup_password_for_token(*uhp
);
1197 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1198 *user
= salloc(*cp
- *uhp
+ 1);
1199 memcpy(*user
, *uhp
, *cp
- *uhp
);
1200 (*user
)[*cp
- *uhp
] = '\0';
1202 *user
= urlxdec(*user
);
1211 imap_setfile(const char *xserver
, int nmail
, int isedit
)
1216 rv
= imap_setfile1(xserver
, nmail
, isedit
, 0);
1222 imap_setfile1(const char *xserver
, int nmail
, int isedit
,
1223 int volatile transparent
)
1226 sighandler_type
volatile saveint
, savepipe
;
1227 char *server
, *user
, *pass
, *acc
;
1228 char const *cp
, *sp
, * volatile mbx
, *uhp
;
1229 int rv
, use_ssl
= 0, prevcount
= 0;
1230 enum mbflags same_flags
;
1233 server
= savestr(xserver
);
1235 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1236 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1237 if (saveint
!= SIG_IGN
)
1238 safe_signal(SIGINT
, imapcatch
);
1239 if (savepipe
!= SIG_IGN
)
1240 safe_signal(SIGPIPE
, imapcatch
);
1245 same_flags
= mb
.mb_flags
;
1246 same_imap_account
= 0;
1247 sp
= protbase(server
);
1248 if (mb
.mb_imap_account
&& mb
.mb_type
== MB_IMAP
) {
1249 if (mb
.mb_sock
.s_fd
> 0 && strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1250 disconnected(mb
.mb_imap_account
) == 0)
1251 same_imap_account
= 1;
1257 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &xmbx
, &pass
, &user
);
1261 if (!same_imap_account
) {
1262 if (!disconnected(acc
) &&
1263 sopen(sp
, &so
, use_ssl
, uhp
, (use_ssl
? "imaps" : "imap")
1274 edit
= (isedit
!= 0);
1275 if (mb
.mb_imap_account
!= NULL
)
1276 free(mb
.mb_imap_account
);
1277 mb
.mb_imap_account
= acc
;
1278 if (!same_imap_account
) {
1279 if (mb
.mb_sock
.s_fd
>= 0)
1280 sclose(&mb
.mb_sock
);
1282 same_imap_account
= 0;
1293 if (mb
.mb_imap_mailbox
!= NULL
)
1294 free(mb
.mb_imap_mailbox
);
1295 mb
.mb_imap_mailbox
= sstrdup(mbx
);
1298 mb
.mb_type
= MB_VOID
;
1299 mb
.mb_active
= MB_NONE
;
1302 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1303 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1304 if (sigsetjmp(imapjmp
, 1)) {
1305 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1306 sclose(&mb
.mb_sock
);
1307 safe_signal(SIGINT
, saveint
);
1308 safe_signal(SIGPIPE
, savepipe
);
1311 mb
.mb_type
= MB_VOID
;
1312 mb
.mb_active
= MB_NONE
;
1316 if (saveint
!= SIG_IGN
)
1317 safe_signal(SIGINT
, imapcatch
);
1318 if (savepipe
!= SIG_IGN
)
1319 safe_signal(SIGPIPE
, imapcatch
);
1321 if (mb
.mb_sock
.s_fd
< 0) {
1322 if (disconnected(mb
.mb_imap_account
)) {
1323 if (cache_setptr(transparent
) == STOP
)
1325 "Mailbox \"%s\" is not cached.\n",
1329 if ((cp
= ok_vlook(imap_keepalive
)) != NULL
) {
1330 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1331 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1332 alarm(imapkeepalive
);
1337 mb
.mb_sock
.s_desc
= "IMAP";
1338 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1339 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1340 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1341 sclose(&mb
.mb_sock
);
1343 safe_signal(SIGINT
, saveint
);
1344 safe_signal(SIGPIPE
, savepipe
);
1349 } else /* same account */
1350 mb
.mb_flags
|= same_flags
;
1352 mb
.mb_perm
= (options
& OPT_R_FLAG
) ? 0 : MB_DELE
;
1353 mb
.mb_type
= MB_IMAP
;
1355 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1356 /*sclose(&mb.mb_sock);
1358 safe_signal(SIGINT
, saveint
);
1359 safe_signal(SIGPIPE
, savepipe
);
1361 mb
.mb_type
= MB_VOID
;
1367 imap_setptr(&mb
, nmail
, transparent
, &prevcount
);
1370 if (!nmail
&& !transparent
)
1372 safe_signal(SIGINT
, saveint
);
1373 safe_signal(SIGPIPE
, savepipe
);
1376 if (!nmail
&& mb
.mb_type
== MB_IMAP
)
1377 purgecache(&mb
, message
, msgCount
);
1378 if ((nmail
|| transparent
) && mb
.mb_sorted
) {
1383 if (!nmail
&& !edit
&& msgCount
== 0) {
1384 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1385 !ok_blook(emptystart
))
1386 fprintf(stderr
, tr(258, "No mail at %s\n"), server
);
1391 newmailinfo(prevcount
);
1399 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1400 int need
, const char *head
, size_t headsize
, long headlines
)
1402 char *line
= NULL
, *lp
;
1403 size_t linesize
= 0, linelen
, size
= 0;
1404 int emptyline
= 0, lines
= 0, excess
= 0;
1408 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1409 offset
= ftell(mp
->mb_otf
);
1412 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1414 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1416 if (linelen
> expected
) {
1417 excess
= linelen
- expected
;
1421 * Need to mask 'From ' lines. This cannot be done properly
1422 * since some servers pass them as 'From ' and others as
1423 * '>From '. Although one could identify the first kind of
1424 * server in principle, it is not possible to identify the
1425 * second as '>From ' may also come from a server of the
1426 * first type as actual data. So do what is absolutely
1427 * necessary only - mask 'From '.
1429 * If the line is the first line of the message header, it
1430 * is likely a real 'From ' line. In this case, it is just
1431 * ignored since it violates all standards.
1432 * TODO can the latter *really* happen??
1435 /* Since we simply copy over data without doing any transfer
1436 * encoding reclassification/adjustment we *have* to perform
1437 * RFC 4155 compliant From_ quoting here */
1438 if (is_head(lp
, linelen
)) {
1439 if (lines
+ headlines
== 0)
1441 fputc('>', mp
->mb_otf
);
1444 if (lp
[linelen
-1] == '\n' && (linelen
== 1 || lp
[linelen
-2] == '\r')) {
1445 emptyline
= linelen
<= 2;
1447 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1448 size
+= linelen
- 1;
1451 fputc('\n', mp
->mb_otf
);
1454 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1459 if ((expected
-= linelen
) <= 0)
1463 /* This is very ugly; but some IMAP daemons don't end a
1464 * message with \r\n\r\n, and we need \n\n for mbox format */
1465 fputc('\n', mp
->mb_otf
);
1472 m
->m_size
= size
+ headsize
;
1473 m
->m_lines
= lines
+ headlines
;
1474 m
->m_block
= mailx_blockof(offset
);
1475 m
->m_offset
= mailx_offsetof(offset
);
1478 m
->m_have
|= HAVE_HEADER
;
1481 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1482 m
->m_xlines
= m
->m_lines
;
1483 m
->m_xsize
= m
->m_size
;
1493 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1494 const char *head
, size_t headsize
, long headlines
)
1501 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1502 offset
= ftell(mp
->mb_otf
);
1504 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1506 fwrite(str
, 1, len
, mp
->mb_otf
);
1507 fputc('\n', mp
->mb_otf
);
1513 m
->m_size
= headsize
+ len
;
1514 m
->m_lines
= headlines
+ 1;
1515 m
->m_block
= mailx_blockof(offset
);
1516 m
->m_offset
= mailx_offsetof(offset
);
1517 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
1518 m
->m_xlines
= m
->m_lines
;
1519 m
->m_xsize
= m
->m_size
;
1525 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1529 sighandler_type
volatile saveint
= SIG_IGN
, savepipe
= SIG_IGN
;
1530 char *volatile head
= NULL
;
1531 char const *cp
= NULL
, *loc
= NULL
, * volatile item
= NULL
,
1532 * volatile resp
= NULL
;
1534 size_t volatile headsize
= 0;
1535 int number
= m
- message
+ 1;
1536 FILE *queuefp
= NULL
;
1537 long volatile headlines
= 0;
1539 unsigned long u
= 0;
1540 enum okay ok
= STOP
;
1543 if (getcache(mp
, m
, need
) == OKAY
)
1545 if (mp
->mb_type
== MB_CACHE
) {
1546 fprintf(stderr
, "Message %u not available.\n", number
);
1550 if (mp
->mb_sock
.s_fd
< 0) {
1551 fprintf(stderr
, "IMAP connection closed.\n");
1557 resp
= item
= "RFC822.HEADER";
1560 item
= "BODY.PEEK[]";
1562 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1563 char *hdr
= smalloc(m
->m_size
);
1565 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
1567 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
) != m
->m_size
) {
1572 headsize
= m
->m_size
;
1573 headlines
= m
->m_lines
;
1574 item
= "BODY.PEEK[TEXT]";
1575 resp
= "BODY[TEXT]";
1583 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1584 if (sigsetjmp(imapjmp
, 1)) {
1585 safe_signal(SIGINT
, saveint
);
1586 safe_signal(SIGPIPE
, savepipe
);
1590 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1591 safe_signal(SIGINT
, &_imap_maincatch
);
1592 if (savepipe
!= SIG_IGN
)
1593 safe_signal(SIGPIPE
, imapcatch
);
1596 snprintf(o
, sizeof o
, "%s UID FETCH %lu (%s)\r\n",
1597 tag(1), m
->m_uid
, item
);
1599 if (check_expunged() == STOP
)
1601 snprintf(o
, sizeof o
, "%s FETCH %u (%s)\r\n", tag(1), number
, item
);
1603 IMAP_OUT(o
, MB_COMD
, goto out
)
1605 ok
= imap_answer(mp
, 1);
1608 if (response_status
!= RESPONSE_OTHER
||
1609 response_other
!= MESSAGE_DATA_FETCH
)
1611 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1614 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1622 n
= responded_other_number
;
1623 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1624 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1626 if ((cp
= strchr(loc
, '"')) != NULL
) {
1627 cp
= imap_unquotestr(cp
);
1628 imap_putstr(mp
, m
, cp
, head
, headsize
, headlines
);
1630 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1631 m
->m_xlines
= m
->m_lines
;
1632 m
->m_xsize
= m
->m_size
;
1636 expected
= atol(&cp
[1]);
1637 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1638 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1642 imap_fetchdata(mp
, &mt
, expected
, need
, head
, headsize
, headlines
);
1644 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1647 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1648 if (options
& OPT_VERBOSE
)
1649 fputs(imapbuf
, stderr
);
1650 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1652 if (u
== m
->m_uid
) {
1653 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1660 while (mp
->mb_active
& MB_COMD
)
1661 ok
= imap_answer(mp
, 1);
1663 if (saveint
!= SIG_IGN
)
1664 safe_signal(SIGINT
, saveint
);
1665 if (savepipe
!= SIG_IGN
)
1666 safe_signal(SIGPIPE
, savepipe
);
1679 imap_header(struct message
*m
)
1684 rv
= imap_get(&mb
, m
, NEED_HEADER
);
1691 imap_body(struct message
*m
)
1696 rv
= imap_get(&mb
, m
, NEED_BODY
);
1702 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
1706 tomp
->m_size
= frommp
->m_size
;
1707 tomp
->m_lines
= frommp
->m_lines
;
1708 tomp
->m_block
= frommp
->m_block
;
1709 tomp
->m_offset
= frommp
->m_offset
;
1710 tomp
->m_have
= have
;
1711 if (have
& HAVE_BODY
) {
1712 tomp
->m_xlines
= frommp
->m_lines
;
1713 tomp
->m_xsize
= frommp
->m_size
;
1720 imap_fetchheaders(struct mailbox
*mp
, struct message
*m
, int bot
, int topp
)
1728 FILE *queuefp
= NULL
;
1733 snprintf(o
, sizeof o
, "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1734 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
1736 if (check_expunged() == STOP
)
1738 snprintf(o
, sizeof o
, "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1741 IMAP_OUT(o
, MB_COMD
, return STOP
)
1743 ok
= imap_answer(mp
, 1);
1744 if (response_status
!= RESPONSE_OTHER
)
1746 if (response_other
!= MESSAGE_DATA_FETCH
)
1748 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1750 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1752 expected
= atol(&cp
[1]);
1753 if (m
[bot
-1].m_uid
) {
1754 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
) {
1756 for (n
= bot
; n
<= topp
; n
++)
1757 if ((unsigned long)u
== m
[n
-1].m_uid
)
1760 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1766 n
= responded_other_number
;
1767 if (n
<= 0 || n
> msgCount
) {
1768 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
, NULL
, 0, 0);
1772 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1773 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1774 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
1775 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
1776 if (options
& OPT_VERBOSE
)
1777 fputs(imapbuf
, stderr
);
1778 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1780 for (n
= bot
; n
<= topp
; n
++)
1781 if ((unsigned long)u
== m
[n
-1].m_uid
)
1783 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1784 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
1789 while (mp
->mb_active
& MB_COMD
)
1790 ok
= imap_answer(mp
, 1);
1795 imap_getheaders(int volatile bot
, int topp
)
1797 sighandler_type saveint
, savepipe
;
1798 /* enum okay ok = STOP;*/
1802 if (mb
.mb_type
== MB_CACHE
)
1806 if (topp
> msgCount
)
1808 for (i
= bot
; i
< topp
; i
++) {
1809 if (message
[i
-1].m_have
& HAVE_HEADER
||
1810 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1815 for (i
= topp
; i
> bot
; i
--) {
1816 if (message
[i
-1].m_have
& HAVE_HEADER
||
1817 getcache(&mb
, &message
[i
-1], NEED_HEADER
) == OKAY
)
1826 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1827 safe_signal(SIGINT
, &_imap_maincatch
);
1828 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1829 if (sigsetjmp(imapjmp
, 1) == 0) {
1830 if (savepipe
!= SIG_IGN
)
1831 safe_signal(SIGPIPE
, imapcatch
);
1833 for (i
= bot
; i
<= topp
; i
+= chunk
) {
1834 int j
= i
+ chunk
- 1;
1835 /*ok = */imap_fetchheaders(&mb
, message
, i
, (j
< topp
? j
: topp
));
1837 onintr(0); /* XXX imaplock? */
1840 safe_signal(SIGINT
, saveint
);
1841 safe_signal(SIGPIPE
, savepipe
);
1846 __imap_exit(struct mailbox
*mp
)
1849 FILE *queuefp
= NULL
;
1852 mp
->mb_active
|= MB_BYE
;
1853 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1854 IMAP_OUT(o
, MB_COMD
, return STOP
)
1860 imap_exit(struct mailbox
*mp
)
1865 rv
= __imap_exit(mp
);
1866 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1867 free(mp
->mb_imap_account
);
1868 free(mp
->mb_imap_mailbox
);
1869 if (mp
->mb_cache_directory
!= NULL
)
1870 free(mp
->mb_cache_directory
);
1871 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1872 mp
->mb_imap_account
=
1873 mp
->mb_imap_mailbox
=
1874 mp
->mb_cache_directory
= "";
1876 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
1877 mp
->mb_imap_mailbox
= NULL
;
1878 mp
->mb_cache_directory
= NULL
;
1881 sclose(&mp
->mb_sock
);
1887 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1890 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1891 if (mp
->mb_type
== MB_IMAP
)
1898 imap_close(struct mailbox
*mp
)
1901 FILE *queuefp
= NULL
;
1904 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1905 IMAP_OUT(o
, MB_COMD
, return STOP
)
1911 imap_update(struct mailbox
*mp
)
1914 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1917 if (!edit
&& mp
->mb_perm
!= 0) {
1920 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
)
1921 if (m
->m_flag
& MBOX
)
1924 if (makembox() == STOP
)
1929 for (m
= message
; PTRCMP(m
, <, message
+ msgCount
); ++m
) {
1930 if (mp
->mb_perm
== 0)
1933 dodel
= ((m
->m_flag
& MDELETED
) != 0);
1935 dodel
= !((m
->m_flag
& MPRESERVE
) || !(m
->m_flag
& MTOUCH
));
1937 /* Fetch the result after around each 800 STORE commands
1938 * sent (approx. 32k data sent). Otherwise, servers will
1939 * try to flush the return queue at some point, leading
1940 * to a deadlock if we are still writing commands but not
1941 * reading their results */
1942 needstat
= stored
> 0 && stored
% 800 == 0;
1943 /* Even if this message has been deleted, continue
1944 * to set further flags. This is necessary to support
1945 * Gmail semantics, where "delete" actually means
1946 * "archive", and the flags are applied to the copy
1948 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
1949 imap_store(mp
, m
, m
-message
+1, '+', "\\Seen", needstat
);
1952 if (m
->m_flag
& MFLAG
) {
1953 imap_store(mp
, m
, m
-message
+1, '+', "\\Flagged", needstat
);
1956 if (m
->m_flag
& MUNFLAG
) {
1957 imap_store(mp
, m
, m
-message
+1, '-', "\\Flagged", needstat
);
1960 if (m
->m_flag
& MANSWER
) {
1961 imap_store(mp
, m
, m
-message
+1, '+', "\\Answered", needstat
);
1964 if (m
->m_flag
& MUNANSWER
) {
1965 imap_store(mp
, m
, m
-message
+1, '-', "\\Answered", needstat
);
1968 if (m
->m_flag
& MDRAFT
) {
1969 imap_store(mp
, m
, m
-message
+1, '+', "\\Draft", needstat
);
1972 if (m
->m_flag
& MUNDRAFT
) {
1973 imap_store(mp
, m
, m
-message
+1, '-', "\\Draft", needstat
);
1978 imap_delete(mp
, m
-message
+1, m
, needstat
);
1981 } else if (mp
->mb_type
!= MB_CACHE
||
1982 (!edit
&& !(m
->m_flag
& (MBOXED
| MSAVED
| MDELETED
))) ||
1983 (m
->m_flag
& (MBOXED
| MPRESERVE
| MTOUCH
)) ==
1984 (MPRESERVE
| MTOUCH
) || (edit
&& !(m
->m_flag
& MDELETED
)))
1986 if (m
->m_flag
& MNEW
) {
1988 m
->m_flag
|= MSTATUS
;
1995 for (m
= &message
[0]; PTRCMP(m
, <, message
+ msgCount
); ++m
)
1996 if (!(m
->m_flag
& MUNLINKED
) &&
1997 m
->m_flag
& (MBOXED
| MDELETED
| MSAVED
| MSTATUS
| MFLAG
|
1998 MUNFLAG
| MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
)) {
2002 if ((gotcha
|| modflags
) && edit
) {
2003 printf(tr(168, "\"%s\" "), displayname
);
2004 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
2005 ? tr(170, "complete\n") : tr(212, "updated.\n"));
2006 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
2008 printf(tr(155, "Held 1 message in %s\n"), displayname
);
2010 printf(tr(156, "Held %d messages in %s\n"), held
, displayname
);
2020 sighandler_type
volatile saveint
, savepipe
;
2023 if (mb
.mb_type
== MB_CACHE
) {
2028 if (mb
.mb_sock
.s_fd
< 0) {
2029 fprintf(stderr
, "IMAP connection closed.\n");
2034 saveint
= safe_signal(SIGINT
, SIG_IGN
);
2035 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2036 if (sigsetjmp(imapjmp
, 1)) {
2037 safe_signal(SIGINT
, saveint
);
2038 safe_signal(SIGPIPE
, saveint
);
2042 if (saveint
!= SIG_IGN
)
2043 safe_signal(SIGINT
, imapcatch
);
2044 if (savepipe
!= SIG_IGN
)
2045 safe_signal(SIGPIPE
, imapcatch
);
2048 if (!same_imap_account
)
2051 safe_signal(SIGINT
, saveint
);
2052 safe_signal(SIGPIPE
, savepipe
);
2059 imap_store(struct mailbox
*mp
, struct message
*m
, int n
, int c
, const char *sp
,
2063 FILE *queuefp
= NULL
;
2066 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
2069 snprintf(o
, sizeof o
, "%s UID STORE %lu %cFLAGS (%s)\r\n",
2070 tag(1), m
->m_uid
, c
, sp
);
2072 if (check_expunged() == STOP
)
2074 snprintf(o
, sizeof o
, "%s STORE %u %cFLAGS (%s)\r\n", tag(1), n
, c
, sp
);
2076 IMAP_OUT(o
, MB_COMD
, return STOP
)
2080 mb
.mb_active
&= ~MB_COMD
;
2081 if (queuefp
!= NULL
)
2087 imap_undelete(struct message
*m
, int n
)
2092 rv
= imap_unstore(m
, n
, "\\Deleted");
2098 imap_unread(struct message
*m
, int n
)
2103 rv
= imap_unstore(m
, n
, "\\Seen");
2109 imap_unstore(struct message
*m
, int n
, const char *flag
)
2111 sighandler_type saveint
, savepipe
;
2112 enum okay rv
= STOP
;
2116 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2117 safe_signal(SIGINT
, &_imap_maincatch
);
2118 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2119 if (sigsetjmp(imapjmp
, 1) == 0) {
2120 if (savepipe
!= SIG_IGN
)
2121 safe_signal(SIGPIPE
, imapcatch
);
2123 rv
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2125 safe_signal(SIGINT
, saveint
);
2126 safe_signal(SIGPIPE
, savepipe
);
2144 snprintf(ts
, sizeof ts
, "T%lu", n
);
2153 sighandler_type saveint
, savepipe
;
2154 struct mailbox
*mp
= &mb
;
2155 FILE *queuefp
= NULL
;
2156 enum okay ok
= STOP
;
2159 if (mp
->mb_type
!= MB_IMAP
) {
2160 printf("Not operating on an IMAP mailbox.\n");
2164 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2165 safe_signal(SIGINT
, &_imap_maincatch
);
2166 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2167 if (sigsetjmp(imapjmp
, 1) == 0) {
2168 if (savepipe
!= SIG_IGN
)
2169 safe_signal(SIGPIPE
, imapcatch
);
2171 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2172 IMAP_OUT(o
, MB_COMD
, goto out
)
2173 while (mp
->mb_active
& MB_COMD
) {
2174 ok
= imap_answer(mp
, 0);
2175 fputs(responded_text
, stdout
);
2179 safe_signal(SIGINT
, saveint
);
2180 safe_signal(SIGPIPE
, savepipe
);
2189 imap_newmail(int nmail
)
2193 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2199 if (had_exists
== msgCount
&& had_expunge
< 0)
2200 /* Some servers always respond with EXISTS to NOOP. If
2201 * the mailbox has been changed but the number of messages
2202 * has not, an EXPUNGE must also had been sent; otherwise,
2203 * nothing has changed */
2206 return (had_expunge
>= 0 ? 2 : (had_exists
>= 0 ? 1 : 0));
2210 imap_putflags(int f
)
2216 bp
= buf
= salloc(100);
2217 if (f
& (MREAD
| MFLAGGED
| MANSWERED
| MDRAFTED
)) {
2222 for (cp
= "\\Seen"; *cp
; cp
++)
2228 for (cp
= "\\Flagged"; *cp
; cp
++)
2231 if (f
& MANSWERED
) {
2234 for (cp
= "\\Answered"; *cp
; cp
++)
2240 for (cp
= "\\Draft"; *cp
; cp
++)
2252 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2255 while (*cp
!= ')') {
2257 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2259 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2261 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2263 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2265 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2267 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2279 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
, off_t off1
,
2280 long xsize
, enum mflag flag
, time_t t
)
2282 char o
[LINESIZE
], *buf
;
2283 size_t bufsize
, buflen
, cnt
;
2284 long size
, lines
, ysize
;
2286 FILE *queuefp
= NULL
;
2290 if (mp
->mb_type
== MB_CACHE
) {
2291 queuefp
= cache_queue(mp
);
2292 if (queuefp
== NULL
) {
2301 buf
= smalloc(bufsize
= LINESIZE
);
2306 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2311 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2312 tag(1), imap_quotestr(name
), imap_putflags(flag
),
2313 imap_make_date_time(t
), size
);
2314 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2315 while (mp
->mb_active
& MB_COMD
) {
2316 rv
= imap_answer(mp
, twice
);
2317 if (response_type
== RESPONSE_CONT
)
2321 if (mp
->mb_type
!= MB_CACHE
&& rv
== STOP
) {
2330 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2333 buf
[buflen
- 1] = '\r';
2335 if (mp
->mb_type
!= MB_CACHE
)
2336 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2338 fwrite(buf
, 1, buflen
+1, queuefp
);
2341 if (mp
->mb_type
!= MB_CACHE
)
2342 swrite(&mp
->mb_sock
, "\r\n");
2344 fputs("\r\n", queuefp
);
2345 while (mp
->mb_active
& MB_COMD
) {
2346 rv
= imap_answer(mp
, 0);
2347 if (response_status
== RESPONSE_NO
/*&&
2348 ascncasecmp(responded_text,
2349 "[TRYCREATE] ", 12) == 0*/) {
2355 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), imap_quotestr(name
));
2356 IMAP_XOUT(o
, MB_COMD
, goto jleave
, rv
=STOP
;goto jleave
)
2357 while (mp
->mb_active
& MB_COMD
)
2358 rv
= imap_answer(mp
, 1);
2361 imap_created_mailbox
++;
2363 } else if (rv
!= OKAY
)
2364 fprintf(stderr
, tr(270, "IMAP error: %s"), responded_text
);
2365 else if (response_status
== RESPONSE_OK
&& (mp
->mb_flags
& MB_UIDPLUS
))
2366 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
, flag
, name
);
2369 if (queuefp
!= NULL
)
2378 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2380 char *buf
, *bp
, *lp
;
2381 size_t bufsize
, buflen
, cnt
;
2382 off_t off1
= -1, offs
;
2383 int inhead
= 1, flag
= MNEW
| MNEWEST
;
2389 buf
= smalloc(bufsize
= LINESIZE
);
2396 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2397 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2398 if (off1
!= (off_t
)-1) {
2399 rv
= imap_append1(mp
, name
, fp
, off1
, size
, flag
, tim
);
2402 fseek(fp
, offs
+buflen
, SEEK_SET
);
2404 off1
= offs
+ buflen
;
2409 tim
= unixtime(buf
);
2414 if (bp
&& buf
[0] == '\n')
2416 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2418 while (whitechar(*lp
))
2421 while (*++lp
!= '\0')
2430 } else if (bp
&& inhead
&& ascncasecmp(buf
, "x-status", 8) == 0) {
2432 while (whitechar(*lp
))
2435 while (*++lp
!= '\0')
2448 } while (bp
!= NULL
);
2457 imap_append(const char *xserver
, FILE *fp
)
2459 sighandler_type
volatile saveint
, savepipe
;
2460 char *server
, *user
, *pass
;
2461 char const *sp
, *cp
, * volatile mbx
, *uhp
;
2462 int volatile use_ssl
;
2463 enum okay rv
= STOP
;
2466 server
= savestr(xserver
);
2470 imap_split(&server
, &sp
, &xus
, &cp
, &uhp
, &xmbx
, &pass
, &user
);
2476 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2477 safe_signal(SIGINT
, &_imap_maincatch
);
2478 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2479 if (sigsetjmp(imapjmp
, 1))
2481 if (savepipe
!= SIG_IGN
)
2482 safe_signal(SIGPIPE
, imapcatch
);
2484 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) && mb
.mb_imap_account
&&
2485 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2486 rv
= imap_append0(&mb
, mbx
, fp
);
2490 memset(&mx
, 0, sizeof mx
);
2491 if (disconnected(server
) == 0) {
2492 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2493 (use_ssl
? "imaps" : "imap")) != OKAY
)
2495 mx
.mb_sock
.s_desc
= "IMAP";
2496 mx
.mb_type
= MB_IMAP
;
2497 mx
.mb_imap_account
= (char *)protbase(server
);
2498 /* TODO the code now did
2499 * TODO mx.mb_imap_mailbox = mbx;
2500 * TODO though imap_mailbox is sfree()d and mbx
2501 * TODO is possibly even a constant
2502 * TODO i changed this to sstrdup() sofar, as is used
2503 * TODO somewhere else in this file for this! */
2504 mx
.mb_imap_mailbox
= sstrdup(mbx
);
2505 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2506 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2507 sclose(&mx
.mb_sock
);
2510 rv
= imap_append0(&mx
, mbx
, fp
);
2513 mx
.mb_imap_account
= (char*)protbase(server
);
2514 mx
.mb_imap_mailbox
= sstrdup(mbx
); /* TODO as above */
2515 mx
.mb_type
= MB_CACHE
;
2516 rv
= imap_append0(&mx
, mbx
, fp
);
2522 safe_signal(SIGINT
, saveint
);
2523 safe_signal(SIGPIPE
, savepipe
);
2533 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2534 struct list_item
**lend
, int level
)
2536 char o
[LINESIZE
], *cp
;
2538 FILE *queuefp
= NULL
;
2539 struct list_item
*lp
;
2540 enum okay ok
= STOP
;
2543 *list
= *lend
= NULL
;
2544 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n", tag(1), imap_quotestr(base
));
2545 IMAP_OUT(o
, MB_COMD
, return STOP
)
2546 while (mp
->mb_active
& MB_COMD
) {
2547 ok
= imap_answer(mp
, 1);
2548 if (response_status
== RESPONSE_OTHER
&&
2549 response_other
== MAILBOX_DATA_LIST
&& imap_parse_list() == OKAY
) {
2550 cp
= imap_unquotestr(list_name
);
2551 lp
= csalloc(1, sizeof *lp
);
2553 for (bp
= base
; *bp
!= '\0' && *bp
== *cp
; ++bp
)
2555 lp
->l_base
= *cp
? cp
: savestr(base
);
2556 lp
->l_attr
= list_attributes
;
2557 lp
->l_level
= level
+1;
2558 lp
->l_delim
= list_hierarchy_delimiter
;
2559 if (*list
&& *lend
) {
2560 (*lend
)->l_next
= lp
;
2570 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2572 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2579 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
2580 if ((rv
= imap_list1(mp
, base
, &list
, &lend
, 0)) == STOP
)
2583 if (list
== NULL
|| lend
== NULL
)
2586 for (lp
= list
; lp
; lp
= lp
->l_next
)
2587 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&& lp
->l_level
< depth
&&
2588 !(lp
->l_attr
& LIST_NOINFERIORS
)) {
2589 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2590 memcpy(cp
, lp
->l_name
, n
);
2591 cp
[n
] = lp
->l_delim
;
2593 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&& lx
&& ly
) {
2594 lp
->l_has_children
= 1;
2595 if (strcmp(cp
, lx
->l_name
) == 0)
2604 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2607 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2611 if (!(lp
->l_attr
& LIST_NOSELECT
))
2612 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2613 else if (lp
->l_has_children
== 0)
2614 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2615 (lp
->l_delim
!= EOF
? lp
->l_delim
: '\n'));
2623 imap_folders(const char * volatile name
, int strip
)
2625 sighandler_type saveint
, savepipe
;
2626 const char *fold
, *cp
, *sp
;
2630 cp
= protbase(name
);
2631 sp
= mb
.mb_imap_account
;
2632 if (sp
== NULL
|| strcmp(cp
, sp
)) {
2633 fprintf(stderr
, tr(502,
2634 "Cannot perform `folders' but when on the very IMAP "
2635 "account; the current one is\n `%s' -- "
2636 "try `folders @'.\n"),
2637 (sp
!= NULL
? sp
: tr(503, "[NONE]")));
2641 fold
= imap_fileof(name
);
2642 if (options
& OPT_TTYOUT
) {
2643 if ((fp
= Ftmp(NULL
, "imapfold", OF_RDWR
| OF_UNLINK
| OF_REGISTER
,
2652 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2653 safe_signal(SIGINT
, &_imap_maincatch
);
2654 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2655 if (sigsetjmp(imapjmp
, 1)) /* TODO imaplock? */
2657 if (savepipe
!= SIG_IGN
)
2658 safe_signal(SIGPIPE
, imapcatch
);
2660 if (mb
.mb_type
== MB_CACHE
)
2661 cache_list(&mb
, fold
, strip
, fp
);
2663 imap_list(&mb
, fold
, strip
, fp
);
2667 if (options
& OPT_TTYOUT
)
2673 if (options
& OPT_TTYOUT
) {
2678 fprintf(stderr
, "Folder not found.\n");
2681 safe_signal(SIGINT
, saveint
);
2682 safe_signal(SIGPIPE
, savepipe
);
2683 if (options
& OPT_TTYOUT
)
2696 long n
= 0, mx
= 0, columns
, width
;
2700 if ((out
= Ftmp(NULL
, "imapdopr", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600))
2706 while ((c
= getc(fp
)) != EOF
) {
2717 if (mx
< width
/ 2) {
2718 columns
= width
/ (mx
+2);
2719 snprintf(o
, sizeof o
, "sort | pr -%lu -w%lu -t", columns
, width
);
2721 strncpy(o
, "sort", sizeof o
)[sizeof o
- 1] = '\0';
2722 run_command(XSHELL
, 0, fileno(fp
), fileno(out
), "-c", o
, NULL
);
2730 imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
, const char *name
)
2734 int twice
= 0, stored
= 0;
2735 FILE *queuefp
= NULL
;
2736 enum okay ok
= STOP
;
2739 if (mp
->mb_type
== MB_CACHE
) {
2740 if ((queuefp
= cache_queue(mp
)) == NULL
)
2744 qname
= imap_quotestr(name
= imap_fileof(name
));
2745 /* Since it is not possible to set flags on the copy, recently
2746 * set flags must be set on the original to include it in the copy */
2747 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
))
2748 imap_store(mp
, m
, n
, '+', "\\Seen", 0);
2749 if (m
->m_flag
&MFLAG
)
2750 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2751 if (m
->m_flag
&MUNFLAG
)
2752 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2753 if (m
->m_flag
&MANSWER
)
2754 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2755 if (m
->m_flag
&MUNANSWER
)
2756 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2757 if (m
->m_flag
&MDRAFT
)
2758 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2759 if (m
->m_flag
&MUNDRAFT
)
2760 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2763 snprintf(o
, sizeof o
, "%s UID COPY %lu %s\r\n", tag(1), m
->m_uid
, qname
);
2765 if (check_expunged() == STOP
)
2767 snprintf(o
, sizeof o
, "%s COPY %u %s\r\n", tag(1), n
, qname
);
2769 IMAP_OUT(o
, MB_COMD
, goto out
)
2770 while (mp
->mb_active
& MB_COMD
)
2771 ok
= imap_answer(mp
, twice
);
2773 if (mp
->mb_type
== MB_IMAP
&& mp
->mb_flags
& MB_UIDPLUS
&&
2774 response_status
== RESPONSE_OK
)
2775 imap_copyuid(mp
, m
, name
);
2777 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
2778 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), qname
);
2779 IMAP_OUT(o
, MB_COMD
, goto out
)
2780 while (mp
->mb_active
& MB_COMD
)
2781 ok
= imap_answer(mp
, 1);
2783 imap_created_mailbox
++;
2788 if (queuefp
!= NULL
)
2791 /* ... and reset the flag to its initial value so that the 'exit'
2792 * command still leaves the message unread */
2794 if ((m
->m_flag
& (MREAD
| MSTATUS
)) == (MREAD
| MSTATUS
)) {
2795 imap_store(mp
, m
, n
, '-', "\\Seen", 0);
2798 if (m
->m_flag
& MFLAG
) {
2799 imap_store(mp
, m
, n
, '-', "\\Flagged", 0);
2802 if (m
->m_flag
& MUNFLAG
) {
2803 imap_store(mp
, m
, n
, '+', "\\Flagged", 0);
2806 if (m
->m_flag
& MANSWER
) {
2807 imap_store(mp
, m
, n
, '-', "\\Answered", 0);
2810 if (m
->m_flag
& MUNANSWER
) {
2811 imap_store(mp
, m
, n
, '+', "\\Answered", 0);
2814 if (m
->m_flag
& MDRAFT
) {
2815 imap_store(mp
, m
, n
, '-', "\\Draft", 0);
2818 if (m
->m_flag
& MUNDRAFT
) {
2819 imap_store(mp
, m
, n
, '+', "\\Draft", 0);
2823 mp
->mb_active
|= MB_COMD
;
2824 (void)imap_finish(mp
);
2830 imap_copy(struct message
*m
, int n
, const char *name
)
2832 sighandler_type saveint
, savepipe
;
2833 enum okay rv
= STOP
;
2837 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2838 safe_signal(SIGINT
, &_imap_maincatch
);
2839 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2840 if (sigsetjmp(imapjmp
, 1) == 0) {
2841 if (savepipe
!= SIG_IGN
)
2842 safe_signal(SIGPIPE
, imapcatch
);
2844 rv
= imap_copy1(&mb
, m
, n
, name
);
2846 safe_signal(SIGINT
, saveint
);
2847 safe_signal(SIGPIPE
, savepipe
);
2857 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2858 unsigned long *olduid
, unsigned long *newuid
)
2864 *uidvalidity
= strtoul(cp
, &xp
, 10);
2865 *olduid
= strtoul(xp
, &yp
, 10);
2866 *newuid
= strtoul(yp
, &zp
, 10);
2867 rv
= (*uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2868 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']');
2874 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2881 *uidvalidity
= strtoul(cp
, &xp
, 10);
2882 *uid
= strtoul(xp
, &yp
, 10);
2883 rv
= (*uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' && yp
> xp
&&
2890 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2895 unsigned long uidvalidity
, olduid
, newuid
;
2900 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2901 imap_copyuid_parse(&cp
[9], &uidvalidity
, &olduid
, &newuid
) == STOP
)
2906 xmb
.mb_cache_directory
= NULL
;
2907 xmb
.mb_imap_mailbox
= savestr(name
);
2908 xmb
.mb_uidvalidity
= uidvalidity
;
2911 memset(&xm
, 0, sizeof xm
);
2913 if ((rv
= getcache1(mp
, &xm
, NEED_UNSPEC
, 3)) != OKAY
)
2915 getcache(mp
, &xm
, NEED_HEADER
);
2916 getcache(mp
, &xm
, NEED_BODY
);
2918 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2919 getcache(mp
, m
, NEED_HEADER
);
2920 if ((m
->m_flag
& HAVE_BODY
) == 0)
2921 getcache(mp
, m
, NEED_BODY
);
2925 xm
.m_flag
&= ~MFULLYCACHED
;
2926 putcache(&xmb
, &xm
);
2933 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
, long xsize
,
2934 long size
, long lines
, int flag
, const char *name
)
2939 unsigned long uidvalidity
, uid
;
2944 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2945 imap_appenduid_parse(&cp
[11], &uidvalidity
, &uid
) == STOP
)
2950 xmb
.mb_cache_directory
= NULL
;
2951 xmb
.mb_imap_mailbox
= savestr(name
);
2952 xmb
.mb_uidvalidity
= uidvalidity
;
2953 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2955 memset(&xm
, 0, sizeof xm
);
2956 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2958 xm
.m_block
= mailx_blockof(off1
);
2959 xm
.m_offset
= mailx_offsetof(off1
);
2962 xm
.m_lines
= xm
.m_xlines
= lines
;
2964 xm
.m_have
= HAVE_HEADER
| HAVE_BODY
;
2965 putcache(&xmb
, &xm
);
2972 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2976 long size
, xsize
, ysize
, lines
;
2977 enum mflag flag
= MNEW
;
2978 char *name
, *buf
, *bp
;
2980 size_t bufsize
, buflen
, cnt
;
2981 enum okay rv
= STOP
;
2984 buf
= smalloc(bufsize
= LINESIZE
);
2987 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
2990 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
2995 if ((cp
= strrchr(bp
, '{')) == NULL
)
2998 xsize
= atol(&cp
[1]) + 2;
2999 if ((name
= imap_strex(&bp
[7], &cp
)) == NULL
)
3005 imap_getflags(cp
, &cp
, &flag
);
3006 while (*++cp
== ' ')
3009 t
= imap_read_date_time(cp
);
3011 if ((tp
= Ftmp(NULL
, "imapapui", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
3018 if (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) == NULL
)
3021 buf
[--buflen
] = '\0';
3022 buf
[buflen
-1] = '\n';
3023 fwrite(buf
, 1, buflen
, tp
);
3030 imap_appenduid(mp
, tp
, t
, 0, xsize
-2, ysize
-1, lines
-1, flag
,
3031 imap_unquotestr(name
));
3042 imap_search2(struct mailbox
*mp
, struct message
*m
, int cnt
, const char *spec
,
3045 char *o
, *xp
, *cs
, c
;
3047 FILE *queuefp
= NULL
;
3051 enum okay ok
= STOP
;
3055 for (cp
= spec
; *cp
; cp
++)
3058 cp
= charset_get_lc();
3060 if (asccasecmp(cp
, "utf-8") && asccasecmp(cp
, "utf8")) {
3065 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
3066 sz
= strlen(spec
) + 1;
3067 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
3068 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
, FAL0
) == 0 &&
3077 cp
= imap_quotestr(cp
);
3078 cs
= salloc(n
= strlen(cp
) + 10);
3079 snprintf(cs
, n
, "CHARSET %s ", cp
);
3083 o
= ac_alloc(osize
= strlen(spec
) + 60);
3084 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
3085 IMAP_OUT(o
, MB_COMD
, goto out
)
3086 while (mp
->mb_active
& MB_COMD
) {
3087 ok
= imap_answer(mp
, 0);
3088 if (response_status
== RESPONSE_OTHER
&&
3089 response_other
== MAILBOX_DATA_SEARCH
) {
3090 xp
= responded_other_text
;
3091 while (*xp
&& *xp
!= '\r') {
3092 n
= strtoul(xp
, &xp
, 10);
3093 for (i
= 0; i
< cnt
; i
++)
3094 if (m
[i
].m_uid
== n
&& !(m
[i
].m_flag
& MHIDDEN
) &&
3095 (f
== MDELETED
|| !(m
[i
].m_flag
& MDELETED
)))
3106 imap_search1(const char *volatile spec
, int f
)
3108 sighandler_type saveint
, savepipe
;
3109 enum okay rv
= STOP
;
3112 if (mb
.mb_type
!= MB_IMAP
)
3116 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3117 safe_signal(SIGINT
, &_imap_maincatch
);
3118 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3119 if (sigsetjmp(imapjmp
, 1) == 0) {
3120 if (savepipe
!= SIG_IGN
)
3121 safe_signal(SIGPIPE
, imapcatch
);
3123 rv
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
3125 safe_signal(SIGINT
, saveint
);
3126 safe_signal(SIGPIPE
, savepipe
);
3136 imap_thisaccount(const char *cp
)
3141 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
3143 else if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
3144 mb
.mb_imap_account
== NULL
)
3147 rv
= !strcmp(protbase(cp
), mb
.mb_imap_account
);
3153 imap_remove(const char * volatile name
)
3155 sighandler_type
volatile saveint
, savepipe
;
3156 enum okay rv
= STOP
;
3159 if (mb
.mb_type
!= MB_IMAP
) {
3160 fprintf(stderr
, "Refusing to remove \"%s\" in disconnected mode.\n",
3165 if (!imap_thisaccount(name
)) {
3166 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
3167 "server: \"%s\" not removed.\n", name
);
3172 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3173 safe_signal(SIGINT
, &_imap_maincatch
);
3174 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3175 if (sigsetjmp(imapjmp
, 1) == 0) {
3176 if (savepipe
!= SIG_IGN
)
3177 safe_signal(SIGPIPE
, imapcatch
);
3179 rv
= imap_remove1(&mb
, imap_fileof(name
));
3181 safe_signal(SIGINT
, saveint
);
3182 safe_signal(SIGPIPE
, savepipe
);
3186 rv
= cache_remove(name
);
3195 imap_remove1(struct mailbox
*mp
, const char *name
)
3197 FILE *queuefp
= NULL
;
3200 enum okay ok
= STOP
;
3203 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3204 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3205 IMAP_OUT(o
, MB_COMD
, goto out
)
3206 while (mp
->mb_active
& MB_COMD
)
3207 ok
= imap_answer(mp
, 1);
3214 imap_rename(const char *old
, const char *new)
3216 sighandler_type saveint
, savepipe
;
3217 enum okay rv
= STOP
;
3220 if (mb
.mb_type
!= MB_IMAP
) {
3221 fprintf(stderr
, "Refusing to rename mailboxes in disconnected mode.\n");
3225 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3226 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3227 "server: \"%s\" not renamed to \"%s\".\n", old
, new);
3232 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3233 safe_signal(SIGINT
, &_imap_maincatch
);
3234 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3235 if (sigsetjmp(imapjmp
, 1) == 0) {
3236 if (savepipe
!= SIG_IGN
)
3237 safe_signal(SIGPIPE
, imapcatch
);
3239 rv
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3241 safe_signal(SIGINT
, saveint
);
3242 safe_signal(SIGPIPE
, savepipe
);
3246 rv
= cache_rename(old
, new);
3255 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3257 FILE *queuefp
= NULL
;
3260 enum okay ok
= STOP
;
3263 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3264 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1), imap_quotestr(old
),
3265 imap_quotestr(new));
3266 IMAP_OUT(o
, MB_COMD
, goto out
)
3267 while (mp
->mb_active
& MB_COMD
)
3268 ok
= imap_answer(mp
, 1);
3275 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3277 char o
[LINESIZE
], *newname
, *buf
, *bp
, *cp
, iob
[4096];
3278 size_t bufsize
, buflen
, cnt
;
3279 long offs
, offs1
, offs2
, octets
;
3280 int twice
, gotcha
= 0;
3281 FILE *queuefp
= NULL
;
3282 enum okay ok
= OKAY
, rok
= OKAY
;
3285 buf
= smalloc(bufsize
= LINESIZE
);
3288 while ((offs1
= ftell(fp
)) >= 0 &&
3289 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0) != NULL
) {
3290 for (bp
= buf
; *bp
!= ' '; ++bp
) /* strip old tag */
3295 if ((offs
= ftell(fp
)) < 0)
3298 snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3299 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3301 while (digitchar(*cp
))
3307 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3309 IMAP_OUT(o
, MB_COMD
, continue)
3310 while (mp
->mb_active
& MB_COMD
)
3311 ok
= imap_answer(mp
, twice
);
3312 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3314 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3315 imap_copyuid(mp
, NULL
, imap_unquotestr(newname
));
3317 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3318 IMAP_OUT(o
, MB_COMD
, continue)
3319 while (mp
->mb_active
& MB_COMD
)
3320 ok
= imap_answer(mp
, 1);
3323 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3324 if ((cp
= strrchr(bp
, '{')) == NULL
)
3326 octets
= atol(&cp
[1]) + 2;
3327 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3329 IMAP_OUT(o
, MB_COMD
, continue)
3330 while (mp
->mb_active
& MB_COMD
) {
3331 ok
= imap_answer(mp
, twice
);
3332 if (response_type
== RESPONSE_CONT
)
3336 if (twice
++ == 0 && fseek(fp
, offs
, SEEK_SET
) >= 0)
3340 while (octets
> 0) {
3341 size_t n
= (UICMP(z
, octets
, >, sizeof iob
)
3342 ? sizeof iob
: (size_t)octets
);
3344 if (n
!= fread(iob
, 1, n
, fp
))
3346 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3348 swrite(&mp
->mb_sock
, "");
3349 while (mp
->mb_active
& MB_COMD
) {
3350 ok
= imap_answer(mp
, 0);
3351 if (response_status
== RESPONSE_NO
&& twice
++ == 0) {
3352 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3357 if (response_status
== RESPONSE_OK
&& mp
->mb_flags
& MB_UIDPLUS
) {
3358 if ((offs2
= ftell(fp
)) < 0)
3360 fseek(fp
, offs1
, SEEK_SET
);
3361 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3362 (void)fseek(fp
, offs2
, SEEK_SET
);
3368 fprintf(stderr
, "Invalid command in IMAP cache queue: \"%s\"\n", bp
);
3373 snprintf(o
, sizeof o
, "%s CREATE %s\r\n", tag(1), newname
);
3374 IMAP_OUT(o
, MB_COMD
, continue)
3375 while (mp
->mb_active
& MB_COMD
)
3376 ok
= imap_answer(mp
, 1);
3382 ftruncate(fileno(fp
), 0);
3390 imap_strex(char const *cp
, char const **xp
)
3399 for (cq
= cp
+ 1; *cq
!= '\0'; ++cq
) {
3402 else if (*cq
== '"')
3408 n
= salloc(cq
- cp
+ 2);
3409 memcpy(n
, cp
, cq
- cp
+1);
3410 n
[cq
- cp
+ 1] = '\0';
3419 check_expunged(void)
3424 if (expunged_messages
> 0) {
3425 fprintf(stderr
, "Command not executed - messages have been expunged\n");
3438 int rv
, omsgCount
= msgCount
;
3442 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3443 fprintf(stderr
, "Already connected.\n");
3448 var_clear_allow_undefined
= TRU1
;
3449 ok_bclear(disconnected
);
3451 cp
= protbase(mailname
);
3452 if (strncmp(cp
, "imap://", 7) == 0)
3454 else if (strncmp(cp
, "imaps://", 8) == 0)
3456 if ((cq
= strchr(cp
, ':')) != NULL
)
3459 vok_bclear(savecat("disconnected-", cp
));
3460 var_clear_allow_undefined
= FAL0
;
3462 if (mb
.mb_type
== MB_CACHE
) {
3463 imap_setfile1(mailname
, 0, edit
, 1);
3464 if (msgCount
> omsgCount
)
3465 newmailinfo(omsgCount
);
3474 cdisconnect(void *vp
)
3476 int rv
= 1, *msgvec
= vp
;
3479 if (mb
.mb_type
== MB_CACHE
) {
3480 fprintf(stderr
, "Not connected.\n");
3483 if (mb
.mb_type
== MB_IMAP
) {
3484 if (cached_uidvalidity(&mb
) == 0) {
3485 fprintf(stderr
, "The current mailbox is not cached.\n");
3492 ok_bset(disconnected
, TRU1
);
3493 if (mb
.mb_type
== MB_IMAP
) {
3494 sclose(&mb
.mb_sock
);
3495 imap_setfile1(mailname
, 0, edit
, 1);
3506 int rv
= 1, *msgvec
= vp
, *ip
;
3510 if (mb
.mb_type
!= MB_IMAP
) {
3511 fprintf(stderr
, "Not connected to an IMAP server.\n");
3514 if (cached_uidvalidity(&mb
) == 0) {
3515 fprintf(stderr
, "The current mailbox is not cached.\n");
3519 for (ip
= msgvec
; *ip
; ++ip
) {
3520 mp
= &message
[*ip
- 1];
3521 if (!(mp
->m_have
& HAVE_BODY
))
3531 disconnected(const char *file
)
3537 if (ok_blook(disconnected
))
3540 cp
= protbase(file
);
3541 if (strncmp(cp
, "imap://", 7) == 0)
3543 else if (strncmp(cp
, "imaps://", 8) == 0)
3550 if ((cq
= strchr(cp
, ':')) != NULL
)
3552 vp
= ac_alloc(vs
= strlen(cp
) + 14);
3553 snprintf(vp
, vs
, "disconnected-%s", cp
);
3562 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
3564 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
3572 while (PTRCMP(omp
, <, omessage
+ omsgCount
) &&
3573 PTRCMP(nmp
, <, message
+ msgCount
)) {
3574 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
3576 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
3578 if (omp
->m_uid
== nmp
->m_uid
) {
3579 hf
= nmp
->m_flag
& MHIDDEN
;
3580 if (transparent
&& mb
.mb_type
== MB_IMAP
)
3581 omp
->m_flag
&= ~MHIDDEN
;
3583 if (transparent
&& mb
.mb_type
== MB_CACHE
)
3584 nmp
[-1].m_flag
|= hf
;
3585 } else if (omp
->m_uid
< nmp
->m_uid
)
3592 prevdot
= newprevdot
;
3598 imap_read_date_time(const char *cp
)
3602 int i
, year
, month
, day
, hour
, minute
, second
, sign
= -1;
3605 /* "25-Jul-2004 15:33:44 +0200"
3607 * 0 5 10 15 20 25 */
3608 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3610 day
= strtol(&cp
[1], NULL
, 10);
3612 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3614 if (month_names
[++i
][0] == '\0')
3618 year
= strtol(&cp
[8], NULL
, 10);
3619 hour
= strtol(&cp
[13], NULL
, 10);
3620 minute
= strtol(&cp
[16], NULL
, 10);
3621 second
= strtol(&cp
[19], NULL
, 10);
3622 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) == (time_t)-1)
3636 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3639 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3649 imap_make_date_time(time_t t
)
3653 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3656 tzdiff
= t
- mktime(gmtime(&t
));
3657 tzdiff_hour
= (int)(tzdiff
/ 60);
3658 tzdiff_min
= tzdiff_hour
% 60;
3660 tmptr
= localtime(&t
);
3661 if (tmptr
->tm_isdst
> 0)
3663 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3664 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
], tmptr
->tm_year
+ 1900,
3665 tmptr
->tm_hour
, tmptr
->tm_min
, tmptr
->tm_sec
, tzdiff_hour
, tzdiff_min
);
3669 #endif /* HAVE_IMAP */
3672 imap_read_date(char const *cp
)
3674 time_t t
= (time_t)-1;
3675 int year
, month
, day
, i
, tzdiff
;
3682 day
= strtol(cp
, &xp
, 10);
3683 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3687 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3689 if (month_names
[++i
][0] == '\0')
3695 year
= strtol(&xp
[4], &yp
, 10);
3696 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3698 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3700 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3702 tzdiff
= t
- mktime(gmtime(&t
));
3703 tmptr
= localtime(&t
);
3704 if (tmptr
->tm_isdst
> 0)
3713 imap_quotestr(char const *s
)
3718 np
= n
= salloc(2 * strlen(s
) + 3);
3721 if (*s
== '"' || *s
== '\\')
3732 imap_unquotestr(char const *s
)
3742 np
= n
= salloc(strlen(s
) + 1);
3756 /* vim:set fenc=utf-8:s-it-mode */