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>
60 #define IMAP_ANSWER() { \
61 if (mp->mb_type != MB_CACHE) { \
62 enum okay ok = OKAY; \
63 while (mp->mb_active & MB_COMD) \
64 ok = imap_answer(mp, 1); \
69 /* TODO IMAP_OUT() simply returns instead of doing "actioN" if imap_finish()
70 * TODO fails, which leaves behind leaks in, e.g., imap_append1()!
71 * TODO IMAP_XOUT() was added due to this, but (1) needs to be used everywhere
72 * TODO and (2) doesn't handle all I/O errors itself, yet, too.
73 * TODO I.e., that should be a function, not a macro ... or so.
74 * TODO This entire module needs MASSIVE work! */
75 #define IMAP_OUT(X,Y,ACTION) IMAP_XOUT(X, Y, ACTION, return STOP)
76 #define IMAP_XOUT(X,Y,ACTIONERR,ACTIONBAIL)\
78 if (mp->mb_type != MB_CACHE) {\
79 if (imap_finish(mp) == STOP) {\
82 if (options & OPT_VERBOSE)\
83 fprintf(stderr, ">>> %s", X);\
85 if (swrite(&mp->mb_sock, X) == STOP) {\
94 static struct record
{
95 struct record
*rec_next
;
96 unsigned long rec_count
;
121 static char *responded_tag
;
122 static char *responded_text
;
123 static char *responded_other_text
;
124 static long responded_other_number
;
130 MAILBOX_DATA_MAILBOX
,
135 MESSAGE_DATA_EXPUNGE
,
138 RESPONSE_OTHER_UNKNOWN
141 static enum list_attributes
{
143 LIST_NOINFERIORS
= 001,
149 static int list_hierarchy_delimiter
;
150 static char *list_name
;
153 struct list_item
*l_next
;
156 enum list_attributes l_attr
;
162 static char *imapbuf
; /* TODO not static, use pool */
163 static size_t imapbufsize
;
164 static sigjmp_buf imapjmp
;
165 static sighandler_type savealrm
;
166 static int imapkeepalive
;
167 static long had_exists
= -1;
168 static long had_expunge
= -1;
169 static long expunged_messages
;
170 static volatile int imaplock
;
172 static int same_imap_account
;
174 static void imap_other_get(char *pp
);
175 static void imap_response_get(const char **cp
);
176 static void imap_response_parse(void);
177 static enum okay
imap_answer(struct mailbox
*mp
, int errprnt
);
178 static enum okay
imap_parse_list(void);
179 static enum okay
imap_finish(struct mailbox
*mp
);
180 static void imap_timer_off(void);
181 static void imapcatch(int s
);
182 static void _imap_maincatch(int s
);
183 static enum okay
imap_noop1(struct mailbox
*mp
);
184 static void rec_queue(enum rec_type type
, unsigned long cnt
);
185 static enum okay
rec_dequeue(void);
186 static void rec_rmqueue(void);
187 static void imapalarm(int s
);
188 static int imap_use_starttls(const char *uhp
);
189 static enum okay
imap_preauth(struct mailbox
*mp
, const char *xserver
,
191 static enum okay
imap_capability(struct mailbox
*mp
);
192 static enum okay
imap_auth(struct mailbox
*mp
, const char *uhp
,
193 char *xuser
, char *pass
);
195 static enum okay
imap_cram_md5(struct mailbox
*mp
,
196 char *xuser
, char *xpass
);
198 static enum okay
imap_login(struct mailbox
*mp
, char *xuser
, char *xpass
);
200 static enum okay
imap_gss(struct mailbox
*mp
, char *user
);
202 static enum okay
imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
);
203 static void imap_init(struct mailbox
*mp
, int n
);
204 static void imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
,
206 static void imap_split(char **server
, const char **sp
, int *use_ssl
,
207 const char **cp
, char const **uhp
, char const **mbx
,
208 char **pass
, char **user
);
209 static int imap_setfile1(const char *xserver
, int nmail
, int isedit
,
211 static int imap_fetchdata(struct mailbox
*mp
, struct message
*m
,
212 size_t expected
, int need
,
213 const char *head
, size_t headsize
, long headlines
);
214 static void imap_putstr(struct mailbox
*mp
, struct message
*m
,
216 const char *head
, size_t headsize
, long headlines
);
217 static enum okay
imap_get(struct mailbox
*mp
, struct message
*m
,
219 static void commitmsg(struct mailbox
*mp
, struct message
*to
,
220 struct message
*from
, enum havespec have
);
221 static enum okay
imap_fetchheaders(struct mailbox
*mp
, struct message
*m
,
223 static enum okay
imap_exit(struct mailbox
*mp
);
224 static enum okay
imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int
226 static enum okay
imap_close(struct mailbox
*mp
);
227 static enum okay
imap_update(struct mailbox
*mp
);
228 static enum okay
imap_store(struct mailbox
*mp
, struct message
*m
,
229 int n
, int c
, const char *sp
, int needstat
);
230 static enum okay
imap_unstore(struct message
*m
, int n
, const char *flag
);
231 static const char *tag(int new);
232 static char *imap_putflags(int f
);
233 static void imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
);
234 static enum okay
imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
235 off_t off1
, long xsize
, enum mflag flag
, time_t t
);
236 static enum okay
imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
);
237 static enum okay
imap_list1(struct mailbox
*mp
, const char *base
,
238 struct list_item
**list
, struct list_item
**lend
, int level
);
239 static enum okay
imap_list(struct mailbox
*mp
, const char *base
,
240 int strip
, FILE *fp
);
241 static void dopr(FILE *fp
);
242 static enum okay
imap_copy1(struct mailbox
*mp
, struct message
*m
, int n
,
244 static enum okay
imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
245 unsigned long *olduid
, unsigned long *newuid
);
246 static enum okay
imap_appenduid_parse(const char *cp
,
247 unsigned long *uidvalidity
, unsigned long *uid
);
248 static enum okay
imap_copyuid(struct mailbox
*mp
, struct message
*m
,
250 static enum okay
imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
,
251 long off1
, long xsize
, long size
, long lines
,
252 int flag
, const char *name
);
253 static enum okay
imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
);
254 static enum okay
imap_search2(struct mailbox
*mp
, struct message
*m
,
255 int cnt
, const char *spec
, int f
);
256 static enum okay
imap_remove1(struct mailbox
*mp
, const char *name
);
257 static enum okay
imap_rename1(struct mailbox
*mp
, const char *old
,
259 static char *imap_strex(char const *cp
, char const **xp
);
260 static enum okay
check_expunged(void);
263 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
;
308 imap_response_get(const char **cp
)
310 if (ascncasecmp(*cp
, "OK ", 3) == 0) {
312 response_status
= RESPONSE_OK
;
313 } else if (ascncasecmp(*cp
, "NO ", 3) == 0) {
315 response_status
= RESPONSE_NO
;
316 } else if (ascncasecmp(*cp
, "BAD ", 4) == 0) {
318 response_status
= RESPONSE_BAD
;
319 } else if (ascncasecmp(*cp
, "PREAUTH ", 8) == 0) {
321 response_status
= RESPONSE_PREAUTH
;
322 } else if (ascncasecmp(*cp
, "BYE ", 4) == 0) {
324 response_status
= RESPONSE_BYE
;
326 response_status
= RESPONSE_OTHER
;
330 imap_response_parse(void)
332 static char *parsebuf
; /* TODO Use pool */
333 static size_t parsebufsize
;
334 const char *ip
= imapbuf
;
337 if (parsebufsize
< imapbufsize
)
338 parsebuf
= srealloc(parsebuf
, parsebufsize
= imapbufsize
);
339 memcpy(parsebuf
, imapbuf
, strlen(imapbuf
) + 1);
343 response_type
= RESPONSE_CONT
;
358 imap_response_get(&ip
);
359 pp
= &parsebuf
[ip
- imapbuf
];
360 switch (response_status
) {
362 response_type
= RESPONSE_FATAL
;
365 response_type
= RESPONSE_DATA
;
369 responded_tag
= parsebuf
;
370 while (*pp
&& *pp
!= ' ')
373 response_type
= RESPONSE_ILLEGAL
;
377 while (*pp
&& *pp
== ' ')
380 response_type
= RESPONSE_ILLEGAL
;
383 ip
= &imapbuf
[pp
- parsebuf
];
384 response_type
= RESPONSE_TAGGED
;
385 imap_response_get(&ip
);
386 pp
= &parsebuf
[ip
- imapbuf
];
389 if (response_type
!= RESPONSE_CONT
&&
390 response_type
!= RESPONSE_ILLEGAL
&&
391 response_status
== RESPONSE_OTHER
)
396 imap_answer(struct mailbox
*mp
, int errprnt
)
401 if (mp
->mb_type
== MB_CACHE
)
403 again
: if (sgetline(&imapbuf
, &imapbufsize
, NULL
, &mp
->mb_sock
) > 0) {
404 if (options
& OPT_VERBOSE
)
405 fputs(imapbuf
, stderr
);
406 imap_response_parse();
407 if (response_type
== RESPONSE_ILLEGAL
)
409 if (response_type
== RESPONSE_CONT
)
411 if (response_status
== RESPONSE_OTHER
) {
412 if (response_other
== MAILBOX_DATA_EXISTS
) {
413 had_exists
= responded_other_number
;
414 rec_queue(REC_EXISTS
, responded_other_number
);
417 } else if (response_other
== MESSAGE_DATA_EXPUNGE
) {
418 rec_queue(REC_EXPUNGE
, responded_other_number
);
426 if (response_type
== RESPONSE_TAGGED
) {
427 if (asccasecmp(responded_tag
, tag(0)) == 0)
432 switch (response_status
) {
433 case RESPONSE_PREAUTH
:
434 mp
->mb_active
&= ~MB_PREAUTH
;
445 fprintf(stderr
, tr(270, "IMAP error: %s"),
448 case RESPONSE_UNKNOWN
: /* does not happen */
451 mp
->mb_active
= MB_NONE
;
459 if (response_status
!= RESPONSE_OTHER
&&
460 ascncasecmp(responded_text
, "[ALERT] ", 8) == 0)
461 fprintf(stderr
, "IMAP alert: %s", &responded_text
[8]);
463 mp
->mb_active
&= ~MB_COMD
;
466 mp
->mb_active
= MB_NONE
;
472 imap_parse_list(void)
476 cp
= responded_other_text
;
477 list_attributes
= LIST_NONE
;
479 while (*cp
&& *cp
!= ')') {
481 if (ascncasecmp(&cp
[1], "Noinferiors ", 12)
483 list_attributes
|= LIST_NOINFERIORS
;
485 } else if (ascncasecmp(&cp
[1], "Noselect ", 9)
487 list_attributes
|= LIST_NOSELECT
;
489 } else if (ascncasecmp(&cp
[1], "Marked ", 7)
491 list_attributes
|= LIST_MARKED
;
493 } else if (ascncasecmp(&cp
[1], "Unmarked ", 9)
495 list_attributes
|= LIST_UNMARKED
;
506 list_hierarchy_delimiter
= EOF
;
510 list_hierarchy_delimiter
= *cp
++ & 0377;
511 if (cp
[0] != '"' || cp
[1] != ' ')
514 } else if (cp
[0] == 'N' && cp
[1] == 'I' && cp
[2] == 'L' &&
516 list_hierarchy_delimiter
= EOF
;
522 while (*cp
&& *cp
!= '\r')
529 imap_finish(struct mailbox
*mp
)
531 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
& MB_COMD
)
539 if (imapkeepalive
> 0) {
541 safe_signal(SIGALRM
, savealrm
);
548 termios_state_reset();
551 fprintf(stderr
, tr(102, "Interrupt\n"));
552 siglongjmp(imapjmp
, 1);
555 fprintf(stderr
, tr(98,
556 "Received SIGPIPE during IMAP operation\n"));
562 _imap_maincatch(int s
)
565 if (interrupts
++ == 0) {
566 fprintf(stderr
, tr(102, "Interrupt\n"));
573 imap_noop1(struct mailbox
*mp
)
576 FILE *queuefp
= NULL
;
578 snprintf(o
, sizeof o
, "%s NOOP\r\n", tag(1));
579 IMAP_OUT(o
, MB_COMD
, return STOP
)
585 imap_fileof(char const *xcp
)
587 char const *cp
= xcp
;
591 if (cp
[0] == ':' && cp
[1] == '/' && cp
[2] == '/') {
595 if (cp
[0] == '/' && state
== 1)
607 sighandler_type
volatile oldint
, oldpipe
;
610 if (mb
.mb_type
!= MB_IMAP
)
613 if ((oldint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
614 safe_signal(SIGINT
, &_imap_maincatch
);
615 oldpipe
= safe_signal(SIGPIPE
, SIG_IGN
);
616 if (sigsetjmp(imapjmp
, 1) == 0) {
617 if (oldpipe
!= SIG_IGN
)
618 safe_signal(SIGPIPE
, imapcatch
);
619 ok
= imap_noop1(&mb
);
621 safe_signal(SIGINT
, oldint
);
622 safe_signal(SIGPIPE
, oldpipe
);
630 rec_queue(enum rec_type rt
, unsigned long cnt
)
634 rp
= scalloc(1, sizeof *rp
);
637 if (record
&& recend
) {
638 recend
->rec_next
= rp
;
641 record
= recend
= rp
;
647 struct message
*omessage
;
649 struct record
*rp
= record
, *rq
= NULL
;
650 unsigned long exists
= 0, i
;
655 message
= smalloc((msgCount
+1) * sizeof *message
);
657 memcpy(message
, omessage
, msgCount
* sizeof *message
);
658 memset(&message
[msgCount
], 0, sizeof *message
);
660 switch (rp
->rec_type
) {
662 exists
= rp
->rec_count
;
665 if (rp
->rec_count
== 0) {
669 if (rp
->rec_count
> (unsigned long)msgCount
) {
670 if (exists
== 0 || rp
->rec_count
> exists
--)
676 delcache(&mb
, &message
[rp
->rec_count
-1]);
677 memmove(&message
[rp
->rec_count
-1],
678 &message
[rp
->rec_count
],
679 (msgCount
- rp
->rec_count
+ 1) *
683 * If the message was part of a collapsed thread,
684 * the m_collapsed field of one of its ancestors
685 * should be incremented. It seems hardly possible
686 * to do this with the current message structure,
687 * though. The result is that a '+' may be shown
688 * in the header summary even if no collapsed
700 record
= recend
= NULL
;
701 if (ok
== OKAY
&& exists
> (unsigned long)msgCount
) {
702 message
= srealloc(message
,
703 (exists
+ 1) * sizeof *message
);
704 memset(&message
[msgCount
], 0,
705 (exists
- msgCount
+ 1) * sizeof *message
);
706 for (i
= msgCount
; i
< exists
; i
++)
708 imap_flags(&mb
, msgCount
+1, exists
);
723 for (rp
= record
; rp
!= NULL
;) {
724 struct record
*tmp
= rp
;
728 record
= recend
= NULL
;
735 sighandler_type
volatile saveint
, savepipe
;
738 if (imaplock
++ == 0) {
739 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
740 safe_signal(SIGINT
, &_imap_maincatch
);
741 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
742 if (sigsetjmp(imapjmp
, 1)) {
743 safe_signal(SIGINT
, saveint
);
744 safe_signal(SIGPIPE
, savepipe
);
747 if (savepipe
!= SIG_IGN
)
748 safe_signal(SIGPIPE
, imapcatch
);
749 if (imap_noop1(&mb
) != OKAY
) {
750 safe_signal(SIGINT
, saveint
);
751 safe_signal(SIGPIPE
, savepipe
);
754 safe_signal(SIGINT
, saveint
);
755 safe_signal(SIGPIPE
, savepipe
);
757 brk
: alarm(imapkeepalive
);
762 imap_use_starttls(const char *uhp
)
766 if (ok_blook(imap_use_starttls
))
768 var
= savecat("imap-use-starttls-", uhp
);
769 return vok_blook(var
);
773 imap_preauth(struct mailbox
*mp
, const char *xserver
, const char *uhp
)
777 mp
->mb_active
|= MB_PREAUTH
;
779 if ((cp
= strchr(xserver
, ':')) != NULL
) {
780 char *x
= salloc(cp
- xserver
+ 1);
781 memcpy(x
, xserver
, cp
- xserver
);
782 x
[cp
- xserver
] = '\0';
786 if (mp
->mb_sock
.s_use_ssl
== 0 && imap_use_starttls(uhp
)) {
787 FILE *queuefp
= NULL
;
790 snprintf(o
, sizeof o
, "%s STARTTLS\r\n", tag(1));
791 IMAP_OUT(o
, MB_COMD
, return STOP
)
793 if (ssl_open(xserver
, &mp
->mb_sock
, uhp
) != OKAY
)
797 if (imap_use_starttls(uhp
)) {
798 fprintf(stderr
, "No SSL support compiled in.\n");
807 imap_capability(struct mailbox
*mp
)
810 FILE *queuefp
= NULL
;
814 snprintf(o
, sizeof o
, "%s CAPABILITY\r\n", tag(1));
815 IMAP_OUT(o
, MB_COMD
, return STOP
)
816 while (mp
->mb_active
& MB_COMD
) {
817 ok
= imap_answer(mp
, 0);
818 if (response_status
== RESPONSE_OTHER
&&
819 response_other
== CAPABILITY_DATA
) {
820 cp
= responded_other_text
;
822 while (spacechar(*cp
&0377))
824 if (strncmp(cp
, "UIDPLUS", 7) == 0 &&
825 spacechar(cp
[7]&0377))
827 mp
->mb_flags
|= MB_UIDPLUS
;
828 while (*cp
&& !spacechar(*cp
&0377))
837 imap_auth(struct mailbox
*mp
, const char *uhp
, char *xuser
, char *pass
)
842 if (!(mp
->mb_active
& MB_PREAUTH
))
844 if ((auth
= ok_vlook(imap_auth
)) == NULL
) {
845 size_t i
= strlen(uhp
) + 1;
846 var
= ac_alloc(i
+ 10);
847 memcpy(var
, "imap-auth-", 10);
848 memcpy(var
+ 10, uhp
, i
);
849 auth
= vok_vlook(var
);
852 if (auth
== NULL
|| strcmp(auth
, "login") == 0)
853 return imap_login(mp
, xuser
, pass
);
854 if (strcmp(auth
, "cram-md5") == 0) {
856 return imap_cram_md5(mp
, xuser
, pass
);
858 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
862 if (strcmp(auth
, "gssapi") == 0) {
864 return imap_gss(mp
, xuser
);
866 fprintf(stderr
, tr(272, "No GSSAPI support compiled in.\n"));
870 fprintf(stderr
, tr(273, "Unknown IMAP authentication method: %s\n"),
876 * Implementation of RFC 2194.
880 imap_cram_md5(struct mailbox
*mp
, char *xuser
, char *xpass
)
882 char o
[LINESIZE
], *user
, *pass
, *cp
;
883 FILE *queuefp
= NULL
;
889 if (! getcredentials(&user
, &pass
))
892 snprintf(o
, sizeof o
, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
893 IMAP_XOUT(o
, 0, goto jleave
, goto jleave
);
895 if (response_type
!= RESPONSE_CONT
)
898 cp
= cram_md5_string(user
, pass
, responded_text
);
899 IMAP_XOUT(cp
, MB_COMD
, goto jleave
, goto jleave
);
900 while (mp
->mb_active
& MB_COMD
)
901 ok
= imap_answer(mp
, 1);
909 #endif /* HAVE_MD5 */
912 imap_login(struct mailbox
*mp
, char *xuser
, char *xpass
)
916 FILE *queuefp
= NULL
;
922 if (! getcredentials(&user
, &pass
))
925 snprintf(o
, sizeof o
, "%s LOGIN %s %s\r\n",
926 tag(1), imap_quotestr(user
), imap_quotestr(pass
));
927 IMAP_XOUT(o
, MB_COMD
, goto jleave
, goto jleave
);
928 while (mp
->mb_active
& MB_COMD
)
929 ok
= imap_answer(mp
, 1);
939 # include "imap_gssapi.h"
943 imap_select(struct mailbox
*mp
, off_t
*size
, int *cnt
, const char *mbx
)
948 FILE *queuefp
= NULL
;
951 mp
->mb_uidvalidity
= 0;
952 snprintf(o
, sizeof o
, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx
));
953 IMAP_OUT(o
, MB_COMD
, return STOP
)
954 while (mp
->mb_active
& MB_COMD
) {
955 ok
= imap_answer(mp
, 1);
956 if (response_status
!= RESPONSE_OTHER
&&
957 (cp
= asccasestr(responded_text
,
958 "[UIDVALIDITY ")) != NULL
)
959 mp
->mb_uidvalidity
= atol(&cp
[13]);
961 *cnt
= had_exists
> 0 ? had_exists
: 0;
962 if (response_status
!= RESPONSE_OTHER
&&
963 ascncasecmp(responded_text
, "[READ-ONLY] ", 12)
970 imap_flags(struct mailbox
*mp
, unsigned X
, unsigned Y
)
973 FILE *queuefp
= NULL
;
976 unsigned x
= X
, y
= Y
, n
;
978 snprintf(o
, sizeof o
, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x
, y
);
979 IMAP_OUT(o
, MB_COMD
, return STOP
)
980 while (mp
->mb_active
& MB_COMD
) {
982 if (response_status
== RESPONSE_OTHER
&&
983 response_other
== MESSAGE_DATA_FETCH
) {
984 n
= responded_other_number
;
991 if ((cp
= asccasestr(responded_other_text
, "FLAGS ")) != NULL
) {
996 imap_getflags(cp
, &cp
, &m
->m_flag
);
998 if ((cp
= asccasestr(responded_other_text
, "UID ")) != NULL
)
999 m
->m_uid
= strtoul(&cp
[4], NULL
, 10);
1000 getcache1(mp
, m
, NEED_UNSPEC
, 1);
1001 m
->m_flag
&= ~MHIDDEN
;
1003 while (x
<= y
&& message
[x
-1].m_xsize
&& message
[x
-1].m_time
)
1005 while (y
> x
&& message
[y
-1].m_xsize
&& message
[y
-1].m_time
)
1008 snprintf(o
, sizeof o
,
1009 "%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
1011 IMAP_OUT(o
, MB_COMD
, return STOP
)
1012 while (mp
->mb_active
& MB_COMD
) {
1014 if (response_status
== RESPONSE_OTHER
&&
1015 response_other
== MESSAGE_DATA_FETCH
) {
1016 n
= responded_other_number
;
1022 if ((cp
= asccasestr(responded_other_text
,
1023 "RFC822.SIZE ")) != NULL
)
1024 m
->m_xsize
= strtol(&cp
[12], NULL
, 10);
1025 if ((cp
= asccasestr(responded_other_text
,
1026 "INTERNALDATE ")) != NULL
)
1027 m
->m_time
= imap_read_date_time(&cp
[13]);
1030 for (n
= X
; n
<= Y
; n
++)
1031 putcache(mp
, &message
[n
-1]);
1036 imap_init(struct mailbox
*mp
, int n
)
1038 struct message
*m
= &message
[n
];
1041 m
->m_flag
= MUSED
|MNOFROM
;
1047 imap_setptr(struct mailbox
*mp
, int nmail
, int transparent
, int *prevcount
)
1049 struct message
*omessage
= 0;
1050 int i
, omsgCount
= 0;
1051 enum okay dequeued
= STOP
;
1053 if (nmail
|| transparent
) {
1055 omsgCount
= msgCount
;
1058 dequeued
= rec_dequeue();
1059 if (had_exists
>= 0) {
1060 if (dequeued
!= OKAY
)
1061 msgCount
= had_exists
;
1064 if (had_expunge
>= 0) {
1065 if (dequeued
!= OKAY
)
1066 msgCount
-= had_expunge
;
1069 if (nmail
&& expunged_messages
)
1070 printf("Expunged %ld message%s.\n",
1072 expunged_messages
!= 1 ? "s" : "");
1073 *prevcount
= omsgCount
- expunged_messages
;
1074 expunged_messages
= 0;
1076 fputs("IMAP error: Negative message count\n", stderr
);
1079 if (dequeued
!= OKAY
) {
1080 message
= scalloc(msgCount
+ 1, sizeof *message
);
1081 for (i
= 0; i
< msgCount
; i
++)
1083 if (!nmail
&& mp
->mb_type
== MB_IMAP
)
1086 imap_flags(mp
, 1, msgCount
);
1087 message
[msgCount
].m_size
= 0;
1088 message
[msgCount
].m_lines
= 0;
1091 if (nmail
|| transparent
)
1092 transflags(omessage
, omsgCount
, transparent
);
1098 imap_split(char **server
, const char **sp
, int *use_ssl
, const char **cp
,
1099 char const **uhp
, char const **mbx
, char **pass
, char **user
)
1102 if (strncmp(*sp
, "imap://", 7) == 0) {
1106 } else if (strncmp(*sp
, "imaps://", 8) == 0) {
1111 *use_ssl
= 0; /* (silence CC) */
1112 if ((*cp
= strchr(*sp
, '/')) != NULL
&& (*cp
)[1] != '\0') {
1113 char *x
= savestr(*sp
);
1114 x
[*cp
- *sp
] = '\0';
1119 (*server
)[*cp
- *server
] = '\0';
1123 *pass
= lookup_password_for_token(*uhp
);
1124 if ((*cp
= last_at_before_slash(*uhp
)) != NULL
) {
1125 *user
= salloc(*cp
- *uhp
+ 1);
1126 memcpy(*user
, *uhp
, *cp
- *uhp
);
1127 (*user
)[*cp
- *uhp
] = '\0';
1129 *user
= urlxdec(*user
);
1137 imap_setfile(const char *xserver
, int nmail
, int isedit
)
1139 return imap_setfile1(xserver
, nmail
, isedit
, 0);
1143 imap_setfile1(const char *xserver
, int nmail
, int isedit
,
1144 int volatile transparent
)
1147 sighandler_type
volatile saveint
, savepipe
;
1148 char *server
, *user
, *pass
, *acc
;
1149 char const *cp
, *sp
, * volatile mbx
, *uhp
;
1150 int use_ssl
= 0, prevcount
= 0;
1151 enum mbflags same_flags
;
1153 server
= savestr(xserver
);
1155 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1156 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1157 if (saveint
!= SIG_IGN
)
1158 safe_signal(SIGINT
, imapcatch
);
1159 if (savepipe
!= SIG_IGN
)
1160 safe_signal(SIGPIPE
, imapcatch
);
1164 same_flags
= mb
.mb_flags
;
1165 same_imap_account
= 0;
1166 sp
= protbase(server
);
1167 if (mb
.mb_imap_account
&& mb
.mb_type
== MB_IMAP
) {
1168 if (mb
.mb_sock
.s_fd
> 0 &&
1169 strcmp(mb
.mb_imap_account
, sp
) == 0 &&
1170 disconnected(mb
.mb_imap_account
) == 0)
1171 same_imap_account
= 1;
1176 imap_split(&server
, &sp
, &use_ssl
, &cp
, &uhp
, &xmbx
, &pass
, &user
);
1180 if (!same_imap_account
) {
1181 if (!disconnected(acc
) &&
1182 sopen(sp
, &so
, use_ssl
, uhp
,
1183 use_ssl
? "imaps" : "imap",
1184 (options
& OPT_VERBOSE
) != 0) != OKAY
) {
1192 edit
= (isedit
!= 0);
1193 if (mb
.mb_imap_account
!= NULL
)
1194 free(mb
.mb_imap_account
);
1195 mb
.mb_imap_account
= acc
;
1196 if (!same_imap_account
) {
1197 if (mb
.mb_sock
.s_fd
>= 0)
1198 sclose(&mb
.mb_sock
);
1200 same_imap_account
= 0;
1210 if (mb
.mb_imap_mailbox
!= NULL
)
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)) {
1221 /* Not safe to use &so; save to use mb.mb_sock?? :-( TODO */
1222 sclose(&mb
.mb_sock
);
1223 safe_signal(SIGINT
, saveint
);
1224 safe_signal(SIGPIPE
, savepipe
);
1226 mb
.mb_type
= MB_VOID
;
1227 mb
.mb_active
= MB_NONE
;
1230 if (saveint
!= SIG_IGN
)
1231 safe_signal(SIGINT
, imapcatch
);
1232 if (savepipe
!= SIG_IGN
)
1233 safe_signal(SIGPIPE
, imapcatch
);
1234 if (mb
.mb_sock
.s_fd
< 0) {
1235 if (disconnected(mb
.mb_imap_account
)) {
1236 if (cache_setptr(transparent
) == STOP
)
1238 "Mailbox \"%s\" is not cached.\n",
1242 if ((cp
= ok_vlook(imap_keepalive
)) != NULL
) {
1243 if ((imapkeepalive
= strtol(cp
, NULL
, 10)) > 0) {
1244 savealrm
= safe_signal(SIGALRM
, imapalarm
);
1245 alarm(imapkeepalive
);
1249 mb
.mb_sock
.s_desc
= "IMAP";
1250 mb
.mb_sock
.s_onclose
= imap_timer_off
;
1251 if (imap_preauth(&mb
, sp
, uhp
) != OKAY
||
1252 imap_auth(&mb
, uhp
, user
, pass
) != OKAY
) {
1253 sclose(&mb
.mb_sock
);
1255 safe_signal(SIGINT
, saveint
);
1256 safe_signal(SIGPIPE
, savepipe
);
1260 } else /* same account */
1261 mb
.mb_flags
|= same_flags
;
1262 mb
.mb_perm
= (options
& OPT_R_FLAG
) ? 0 : MB_DELE
;
1263 mb
.mb_type
= MB_IMAP
;
1265 if (imap_select(&mb
, &mailsize
, &msgCount
, mbx
) != OKAY
) {
1266 /*sclose(&mb.mb_sock);
1268 safe_signal(SIGINT
, saveint
);
1269 safe_signal(SIGPIPE
, savepipe
);
1271 mb
.mb_type
= MB_VOID
;
1275 imap_setptr(&mb
, nmail
, transparent
, &prevcount
);
1276 done
: setmsize(msgCount
);
1277 if (!nmail
&& !transparent
)
1279 safe_signal(SIGINT
, saveint
);
1280 safe_signal(SIGPIPE
, savepipe
);
1282 if (!nmail
&& mb
.mb_type
== MB_IMAP
)
1283 purgecache(&mb
, message
, msgCount
);
1284 if ((nmail
|| transparent
) && mb
.mb_sorted
) {
1288 if (!nmail
&& !edit
&& msgCount
== 0) {
1289 if ((mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
) &&
1290 !ok_blook(emptystart
))
1291 fprintf(stderr
, tr(258, "No mail at %s\n"), server
);
1295 newmailinfo(prevcount
);
1300 imap_fetchdata(struct mailbox
*mp
, struct message
*m
, size_t expected
,
1302 const char *head
, size_t headsize
, long headlines
)
1304 char *line
= NULL
, *lp
;
1305 size_t linesize
= 0, linelen
, size
= 0;
1306 int emptyline
= 0, lines
= 0, excess
= 0;
1309 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1310 offset
= ftell(mp
->mb_otf
);
1312 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1313 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
1315 if (linelen
> expected
) {
1316 excess
= linelen
- expected
;
1320 * Need to mask 'From ' lines. This cannot be done properly
1321 * since some servers pass them as 'From ' and others as
1322 * '>From '. Although one could identify the first kind of
1323 * server in principle, it is not possible to identify the
1324 * second as '>From ' may also come from a server of the
1325 * first type as actual data. So do what is absolutely
1326 * necessary only - mask 'From '.
1328 * If the line is the first line of the message header, it
1329 * is likely a real 'From ' line. In this case, it is just
1330 * ignored since it violates all standards.
1331 * TODO can the latter *really* happen??
1335 * Since we simply copy over data without doing any transfer
1336 * encoding reclassification/adjustment we *have* to perform
1337 * RFC 4155 compliant From_ quoting here
1339 if (is_head(lp
, linelen
)) {
1340 if (lines
+ headlines
== 0)
1342 fputc('>', mp
->mb_otf
);
1345 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
1346 lp
[linelen
-2] == '\r')) {
1347 emptyline
= linelen
<= 2;
1349 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
1350 size
+= linelen
- 1;
1353 fputc('\n', mp
->mb_otf
);
1356 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
1360 skip
: if ((expected
-= linelen
) <= 0)
1365 * This is very ugly; but some IMAP daemons don't end a
1366 * message with \r\n\r\n, and we need \n\n for mbox format.
1368 fputc('\n', mp
->mb_otf
);
1374 m
->m_size
= size
+ headsize
;
1375 m
->m_lines
= lines
+ headlines
;
1376 m
->m_block
= mailx_blockof(offset
);
1377 m
->m_offset
= mailx_offsetof(offset
);
1380 m
->m_have
|= HAVE_HEADER
;
1383 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1384 m
->m_xlines
= m
->m_lines
;
1385 m
->m_xsize
= m
->m_size
;
1394 imap_putstr(struct mailbox
*mp
, struct message
*m
, const char *str
,
1395 const char *head
, size_t headsize
, long headlines
)
1401 fseek(mp
->mb_otf
, 0L, SEEK_END
);
1402 offset
= ftell(mp
->mb_otf
);
1404 fwrite(head
, 1, headsize
, mp
->mb_otf
);
1406 fwrite(str
, 1, len
, mp
->mb_otf
);
1407 fputc('\n', mp
->mb_otf
);
1412 m
->m_size
= headsize
+ len
;
1413 m
->m_lines
= headlines
+ 1;
1414 m
->m_block
= mailx_blockof(offset
);
1415 m
->m_offset
= mailx_offsetof(offset
);
1416 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1417 m
->m_xlines
= m
->m_lines
;
1418 m
->m_xsize
= m
->m_size
;
1423 imap_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1427 sighandler_type
volatile saveint
= SIG_IGN
, savepipe
= SIG_IGN
;
1428 char *volatile head
= NULL
;
1429 char const *cp
= NULL
, *loc
= NULL
,
1430 *volatile item
= NULL
, *volatile resp
= NULL
;
1432 size_t volatile headsize
= 0;
1433 int number
= m
- message
+ 1;
1434 enum okay ok
= STOP
;
1435 FILE *queuefp
= NULL
;
1436 long volatile headlines
= 0;
1438 unsigned long u
= 0;
1440 if (getcache(mp
, m
, need
) == OKAY
)
1442 if (mp
->mb_type
== MB_CACHE
) {
1443 fprintf(stderr
, "Message %u not available.\n", number
);
1446 if (mp
->mb_sock
.s_fd
< 0) {
1447 fprintf(stderr
, "IMAP connection closed.\n");
1452 resp
= item
= "RFC822.HEADER";
1455 item
= "BODY.PEEK[]";
1457 if (m
->m_flag
& HAVE_HEADER
&& m
->m_size
) {
1458 char *hdr
= smalloc(m
->m_size
);
1460 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
1461 m
->m_offset
), SEEK_SET
) < 0 ||
1462 fread(hdr
, 1, m
->m_size
, mp
->mb_itf
)
1468 headsize
= m
->m_size
;
1469 headlines
= m
->m_lines
;
1470 item
= "BODY.PEEK[TEXT]";
1471 resp
= "BODY[TEXT]";
1478 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1479 if (sigsetjmp(imapjmp
, 1)) {
1480 safe_signal(SIGINT
, saveint
);
1481 safe_signal(SIGPIPE
, savepipe
);
1485 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1486 safe_signal(SIGINT
, &_imap_maincatch
);
1487 if (savepipe
!= SIG_IGN
)
1488 safe_signal(SIGPIPE
, imapcatch
);
1490 snprintf(o
, sizeof o
,
1491 "%s UID FETCH %lu (%s)\r\n",
1492 tag(1), m
->m_uid
, item
);
1494 if (check_expunged() == STOP
)
1496 snprintf(o
, sizeof o
,
1497 "%s FETCH %u (%s)\r\n",
1498 tag(1), number
, item
);
1500 IMAP_OUT(o
, MB_COMD
, goto out
)
1502 ok
= imap_answer(mp
, 1);
1505 if (response_status
!= RESPONSE_OTHER
||
1506 response_other
!= MESSAGE_DATA_FETCH
)
1508 if ((loc
= asccasestr(responded_other_text
, resp
)) == NULL
)
1511 if ((cp
= asccasestr(responded_other_text
, "UID "))) {
1519 n
= responded_other_number
;
1520 if ((cp
= strrchr(responded_other_text
, '{')) == NULL
) {
1521 if (m
->m_uid
? m
->m_uid
!= u
: n
!= number
)
1523 if ((cp
= strchr(loc
, '"')) != NULL
) {
1524 cp
= imap_unquotestr(cp
);
1525 imap_putstr(mp
, m
, cp
,
1526 head
, headsize
, headlines
);
1528 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
1529 m
->m_xlines
= m
->m_lines
;
1530 m
->m_xsize
= m
->m_size
;
1534 expected
= atol(&cp
[1]);
1535 if (m
->m_uid
? n
== 0 && m
->m_uid
!= u
: n
!= number
) {
1536 imap_fetchdata(mp
, NULL
, expected
, need
, NULL
, 0, 0);
1540 imap_fetchdata(mp
, &mt
, expected
, need
,
1541 head
, headsize
, headlines
);
1543 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1546 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1547 &mp
->mb_sock
) > 0) {
1548 if (options
& OPT_VERBOSE
)
1549 fputs(imapbuf
, stderr
);
1550 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1552 if (u
== m
->m_uid
) {
1553 commitmsg(mp
, m
, &mt
, mt
.m_have
);
1559 out
: while (mp
->mb_active
& MB_COMD
)
1560 ok
= imap_answer(mp
, 1);
1561 if (saveint
!= SIG_IGN
)
1562 safe_signal(SIGINT
, saveint
);
1563 if (savepipe
!= SIG_IGN
)
1564 safe_signal(SIGPIPE
, savepipe
);
1576 imap_header(struct message
*m
)
1578 return imap_get(&mb
, m
, NEED_HEADER
);
1583 imap_body(struct message
*m
)
1585 return imap_get(&mb
, m
, NEED_BODY
);
1589 commitmsg(struct mailbox
*mp
, struct message
*tomp
, struct message
*frommp
,
1592 tomp
->m_size
= frommp
->m_size
;
1593 tomp
->m_lines
= frommp
->m_lines
;
1594 tomp
->m_block
= frommp
->m_block
;
1595 tomp
->m_offset
= frommp
->m_offset
;
1596 tomp
->m_have
= have
;
1597 if (have
& HAVE_BODY
) {
1598 tomp
->m_xlines
= frommp
->m_lines
;
1599 tomp
->m_xsize
= frommp
->m_size
;
1609 int topp
/* bot > topp */
1618 FILE *queuefp
= NULL
;
1621 snprintf(o
, sizeof o
,
1622 "%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
1623 tag(1), m
[bot
-1].m_uid
, m
[topp
-1].m_uid
);
1625 if (check_expunged() == STOP
)
1627 snprintf(o
, sizeof o
,
1628 "%s FETCH %u:%u (RFC822.HEADER)\r\n",
1631 IMAP_OUT(o
, MB_COMD
, return STOP
)
1633 ok
= imap_answer(mp
, 1);
1634 if (response_status
!= RESPONSE_OTHER
)
1636 if (response_other
!= MESSAGE_DATA_FETCH
)
1638 if (ok
== STOP
|| (cp
=strrchr(responded_other_text
, '{')) == 0)
1640 if (asccasestr(responded_other_text
, "RFC822.HEADER") == NULL
)
1642 expected
= atol(&cp
[1]);
1643 if (m
[bot
-1].m_uid
) {
1644 if ((cp
=asccasestr(responded_other_text
, "UID "))) {
1646 for (n
= bot
; n
<= topp
; n
++)
1647 if ((unsigned long)u
== m
[n
-1].m_uid
)
1650 imap_fetchdata(mp
, NULL
, expected
,
1658 n
= responded_other_number
;
1659 if (n
<= 0 || n
> msgCount
) {
1660 imap_fetchdata(mp
, NULL
, expected
, NEED_HEADER
,
1665 imap_fetchdata(mp
, &mt
, expected
, NEED_HEADER
, NULL
, 0, 0);
1666 if (n
>= 0 && !(m
[n
-1].m_have
& HAVE_HEADER
))
1667 commitmsg(mp
, &m
[n
-1], &mt
, HAVE_HEADER
);
1668 if (n
== -1 && sgetline(&imapbuf
, &imapbufsize
, NULL
,
1669 &mp
->mb_sock
) > 0) {
1670 if (options
& OPT_VERBOSE
)
1671 fputs(imapbuf
, stderr
);
1672 if ((cp
= asccasestr(imapbuf
, "UID ")) != NULL
) {
1674 for (n
= bot
; n
<= topp
; n
++)
1675 if ((unsigned long)u
== m
[n
-1].m_uid
)
1677 if (n
<= topp
&& !(m
[n
-1].m_have
& HAVE_HEADER
))
1678 commitmsg(mp
, &m
[n
-1], &mt
,HAVE_HEADER
);
1683 while (mp
->mb_active
& MB_COMD
)
1684 ok
= imap_answer(mp
, 1);
1689 imap_getheaders(int volatile bot
, int topp
)
1691 sighandler_type saveint
, savepipe
;
1692 /* XXX enum okay ok = STOP;*/
1695 if (mb
.mb_type
== MB_CACHE
)
1699 if (topp
> msgCount
)
1701 for (i
= bot
; i
< topp
; i
++) {
1702 if (message
[i
-1].m_have
& HAVE_HEADER
||
1703 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1709 for (i
= topp
; i
> bot
; i
--) {
1710 if (message
[i
-1].m_have
& HAVE_HEADER
||
1711 getcache(&mb
, &message
[i
-1], NEED_HEADER
)
1720 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1721 safe_signal(SIGINT
, &_imap_maincatch
);
1722 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1723 if (sigsetjmp(imapjmp
, 1) == 0) {
1724 if (savepipe
!= SIG_IGN
)
1725 safe_signal(SIGPIPE
, imapcatch
);
1726 for (i
= bot
; i
<= topp
; i
+= chunk
) {
1727 int j
= i
+ chunk
- 1;
1728 /*ok = */imap_fetchheaders(&mb
, message
, i
,
1729 j
< topp
? j
: topp
);
1734 safe_signal(SIGINT
, saveint
);
1735 safe_signal(SIGPIPE
, savepipe
);
1740 __imap_exit(struct mailbox
*mp
)
1743 FILE *queuefp
= NULL
;
1745 mp
->mb_active
|= MB_BYE
;
1746 snprintf(o
, sizeof o
, "%s LOGOUT\r\n", tag(1));
1747 IMAP_OUT(o
, MB_COMD
, return STOP
)
1753 imap_exit(struct mailbox
*mp
)
1755 enum okay ret
= __imap_exit(mp
);
1756 #if 0 /* TODO the option today: memory leak(s) and halfway reuse or nottin */
1757 free(mp
->mb_imap_account
);
1758 free(mp
->mb_imap_mailbox
);
1759 if (mp
->mb_cache_directory
!= NULL
)
1760 free(mp
->mb_cache_directory
);
1761 #ifndef HAVE_DEBUG /* TODO ASSERT LEGACY */
1762 mp
->mb_imap_account
=
1763 mp
->mb_imap_mailbox
=
1764 mp
->mb_cache_directory
= "";
1766 mp
->mb_imap_account
= NULL
; /* for assert legacy time.. */
1767 mp
->mb_imap_mailbox
= NULL
;
1768 mp
->mb_cache_directory
= NULL
;
1771 sclose(&mp
->mb_sock
);
1776 imap_delete(struct mailbox
*mp
, int n
, struct message
*m
, int needstat
)
1778 imap_store(mp
, m
, n
, '+', "\\Deleted", needstat
);
1779 if (mp
->mb_type
== MB_IMAP
)
1785 imap_close(struct mailbox
*mp
)
1788 FILE *queuefp
= NULL
;
1790 snprintf(o
, sizeof o
, "%s CLOSE\r\n", tag(1));
1791 IMAP_OUT(o
, MB_COMD
, return STOP
)
1797 imap_update(struct mailbox
*mp
)
1800 int dodel
, c
, gotcha
= 0, held
= 0, modflags
= 0, needstat
, stored
= 0;
1802 if (!edit
&& mp
->mb_perm
!= 0) {
1804 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
1805 if (m
->m_flag
& MBOX
)
1809 if (makembox() == STOP
)
1812 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
1813 if (mp
->mb_perm
== 0) {
1816 dodel
= m
->m_flag
& MDELETED
;
1818 dodel
= !((m
->m_flag
&MPRESERVE
) ||
1819 (m
->m_flag
&MTOUCH
) == 0);
1822 * Fetch the result after around each 800 STORE commands
1823 * sent (approx. 32k data sent). Otherwise, servers will
1824 * try to flush the return queue at some point, leading
1825 * to a deadlock if we are still writing commands but not
1826 * reading their results.
1828 needstat
= stored
> 0 && stored
% 800 == 0;
1830 * Even if this message has been deleted, continue
1831 * to set further flags. This is necessary to support
1832 * Gmail semantics, where "delete" actually means
1833 * "archive", and the flags are applied to the copy
1836 if ((m
->m_flag
&(MREAD
|MSTATUS
)) == (MREAD
|MSTATUS
)) {
1837 imap_store(mp
, m
, m
-message
+1,
1838 '+', "\\Seen", needstat
);
1841 if (m
->m_flag
& MFLAG
) {
1842 imap_store(mp
, m
, m
-message
+1,
1843 '+', "\\Flagged", needstat
);
1846 if (m
->m_flag
& MUNFLAG
) {
1847 imap_store(mp
, m
, m
-message
+1,
1848 '-', "\\Flagged", needstat
);
1851 if (m
->m_flag
& MANSWER
) {
1852 imap_store(mp
, m
, m
-message
+1,
1853 '+', "\\Answered", needstat
);
1856 if (m
->m_flag
& MUNANSWER
) {
1857 imap_store(mp
, m
, m
-message
+1,
1858 '-', "\\Answered", needstat
);
1861 if (m
->m_flag
& MDRAFT
) {
1862 imap_store(mp
, m
, m
-message
+1,
1863 '+', "\\Draft", needstat
);
1866 if (m
->m_flag
& MUNDRAFT
) {
1867 imap_store(mp
, m
, m
-message
+1,
1868 '-', "\\Draft", needstat
);
1872 imap_delete(mp
, m
-message
+1, m
, needstat
);
1875 } else if (mp
->mb_type
!= MB_CACHE
||
1876 (! edit
&& ! (m
->m_flag
&(MBOXED
|MSAVED
|MDELETED
))) ||
1877 (m
->m_flag
& (MBOXED
|MPRESERVE
|MTOUCH
)) ==
1878 (MPRESERVE
|MTOUCH
) ||
1879 (edit
&& ! (m
->m_flag
& MDELETED
)))
1881 if (m
->m_flag
& MNEW
) {
1883 m
->m_flag
|= MSTATUS
;
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(tr(168, "\"%s\" "), displayname
);
1899 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
1900 ? tr(170, "complete\n") : tr(212, "updated.\n"));
1901 } else if (held
&& !edit
&& mp
->mb_perm
!= 0) {
1903 printf(tr(155, "Held 1 message in %s\n"), displayname
);
1905 printf(tr(156, "Held %d messages in %s\n"), held
,
1915 sighandler_type
volatile saveint
, savepipe
;
1917 if (mb
.mb_type
== MB_CACHE
) {
1921 if (mb
.mb_sock
.s_fd
< 0) {
1922 fprintf(stderr
, "IMAP connection closed.\n");
1926 saveint
= safe_signal(SIGINT
, SIG_IGN
);
1927 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1928 if (sigsetjmp(imapjmp
, 1)) {
1929 safe_signal(SIGINT
, saveint
);
1930 safe_signal(SIGPIPE
, saveint
);
1934 if (saveint
!= SIG_IGN
)
1935 safe_signal(SIGINT
, imapcatch
);
1936 if (savepipe
!= SIG_IGN
)
1937 safe_signal(SIGPIPE
, imapcatch
);
1939 if (!same_imap_account
) {
1942 safe_signal(SIGINT
, saveint
);
1943 safe_signal(SIGPIPE
, savepipe
);
1948 imap_store(struct mailbox
*mp
, struct message
*m
, int n
,
1949 int c
, const char *sp
, int needstat
)
1952 FILE *queuefp
= NULL
;
1954 if (mp
->mb_type
== MB_CACHE
&& (queuefp
= cache_queue(mp
)) == NULL
)
1957 snprintf(o
, sizeof o
,
1958 "%s UID STORE %lu %cFLAGS (%s)\r\n",
1959 tag(1), m
->m_uid
, c
, sp
);
1961 if (check_expunged() == STOP
)
1963 snprintf(o
, sizeof o
,
1964 "%s STORE %u %cFLAGS (%s)\r\n",
1967 IMAP_OUT(o
, MB_COMD
, return STOP
)
1971 mb
.mb_active
&= ~MB_COMD
;
1972 if (queuefp
!= NULL
)
1978 imap_undelete(struct message
*m
, int n
)
1980 return imap_unstore(m
, n
, "\\Deleted");
1984 imap_unread(struct message
*m
, int n
)
1986 return imap_unstore(m
, n
, "\\Seen");
1990 imap_unstore(struct message
*m
, int n
, const char *flag
)
1992 sighandler_type saveint
, savepipe
;
1993 enum okay ok
= STOP
;
1999 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2000 safe_signal(SIGINT
, &_imap_maincatch
);
2001 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2002 if (sigsetjmp(imapjmp
, 1) == 0) {
2003 if (savepipe
!= SIG_IGN
)
2004 safe_signal(SIGPIPE
, imapcatch
);
2005 ok
= imap_store(&mb
, m
, n
, '-', flag
, 1);
2007 safe_signal(SIGINT
, saveint
);
2008 safe_signal(SIGPIPE
, savepipe
);
2023 snprintf(ts
, sizeof ts
, "T%lu", n
);
2030 sighandler_type saveint
, savepipe
;
2032 enum okay ok
= STOP
;
2033 struct mailbox
*mp
= &mb
;
2034 FILE *queuefp
= NULL
;
2039 if (mp
->mb_type
!= MB_IMAP
) {
2040 printf("Not operating on an IMAP mailbox.\n");
2044 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2045 safe_signal(SIGINT
, &_imap_maincatch
);
2046 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2047 if (sigsetjmp(imapjmp
, 1) == 0) {
2048 if (savepipe
!= SIG_IGN
)
2049 safe_signal(SIGPIPE
, imapcatch
);
2050 snprintf(o
, sizeof o
, "%s %s\r\n", tag(1), (char *)vp
);
2051 IMAP_OUT(o
, MB_COMD
, goto out
)
2052 while (mp
->mb_active
& MB_COMD
) {
2053 ok
= imap_answer(mp
, 0);
2054 fputs(responded_text
, stdout
);
2057 out
: safe_signal(SIGINT
, saveint
);
2058 safe_signal(SIGPIPE
, savepipe
);
2066 imap_newmail(int nmail
)
2068 if (nmail
&& had_exists
< 0 && had_expunge
< 0) {
2073 if (had_exists
== msgCount
&& had_expunge
< 0)
2075 * Some servers always respond with EXISTS to NOOP. If
2076 * the mailbox has been changed but the number of messages
2077 * has not, an EXPUNGE must also had been sent; otherwise,
2078 * nothing has changed.
2081 return had_expunge
>= 0 ? 2 : had_exists
>= 0 ? 1 : 0;
2085 imap_putflags(int f
)
2090 bp
= buf
= salloc(100);
2091 if (f
& (MREAD
|MFLAGGED
|MANSWERED
|MDRAFTED
)) {
2096 for (cp
= "\\Seen"; *cp
; cp
++)
2102 for (cp
= "\\Flagged"; *cp
; cp
++)
2105 if (f
& MANSWERED
) {
2108 for (cp
= "\\Answered"; *cp
; cp
++)
2114 for (cp
= "\\Draft"; *cp
; cp
++)
2125 imap_getflags(const char *cp
, char const **xp
, enum mflag
*f
)
2127 while (*cp
!= ')') {
2129 if (ascncasecmp(cp
, "\\Seen", 5) == 0)
2131 else if (ascncasecmp(cp
, "\\Recent", 7) == 0)
2133 else if (ascncasecmp(cp
, "\\Deleted", 8) == 0)
2135 else if (ascncasecmp(cp
, "\\Flagged", 8) == 0)
2137 else if (ascncasecmp(cp
, "\\Answered", 9) == 0)
2139 else if (ascncasecmp(cp
, "\\Draft", 6) == 0)
2149 imap_append1(struct mailbox
*mp
, const char *name
, FILE *fp
,
2150 off_t off1
, long xsize
, enum mflag flag
, time_t t
)
2154 size_t bufsize
, buflen
, cnt
;
2155 enum okay ok
= STOP
;
2156 long size
, lines
, ysize
;
2158 FILE *queuefp
= NULL
;
2160 if (mp
->mb_type
== MB_CACHE
) {
2161 queuefp
= cache_queue(mp
);
2162 if (queuefp
== NULL
)
2166 buf
= smalloc(bufsize
= LINESIZE
);
2168 again
: size
= xsize
;
2170 if (fseek(fp
, off1
, SEEK_SET
) < 0) {
2174 snprintf(o
, sizeof o
, "%s APPEND %s %s%s {%ld}\r\n",
2175 tag(1), imap_quotestr(name
),
2176 imap_putflags(flag
),
2177 imap_make_date_time(t
),
2179 IMAP_XOUT(o
, MB_COMD
, goto out
, ok
= STOP
;goto out
)
2180 while (mp
->mb_active
& MB_COMD
) {
2181 ok
= imap_answer(mp
, twice
);
2182 if (response_type
== RESPONSE_CONT
)
2185 if (mp
->mb_type
!= MB_CACHE
&& ok
== STOP
) {
2193 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2196 buf
[buflen
-1] = '\r';
2198 if (mp
->mb_type
!= MB_CACHE
)
2199 swrite1(&mp
->mb_sock
, buf
, buflen
+1, 1);
2201 fwrite(buf
, 1, buflen
+1, queuefp
);
2204 if (mp
->mb_type
!= MB_CACHE
)
2205 swrite(&mp
->mb_sock
, "\r\n");
2207 fputs("\r\n", queuefp
);
2208 while (mp
->mb_active
& MB_COMD
) {
2209 ok
= imap_answer(mp
, 0);
2210 if (response_status
== RESPONSE_NO
/*&&
2211 ascncasecmp(responded_text,
2212 "[TRYCREATE] ", 12) == 0*/) {
2213 trycreate
: if (twice
++) {
2217 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
2219 imap_quotestr(name
));
2220 IMAP_XOUT(o
, MB_COMD
, goto out
, ok
= STOP
;goto out
)
2221 while (mp
->mb_active
& MB_COMD
)
2222 ok
= imap_answer(mp
, 1);
2225 imap_created_mailbox
++;
2227 } else if (ok
!= OKAY
)
2228 fprintf(stderr
, tr(270, "IMAP error: %s"),
2230 else if (response_status
== RESPONSE_OK
&&
2231 mp
->mb_flags
& MB_UIDPLUS
)
2232 imap_appenduid(mp
, fp
, t
, off1
, xsize
, ysize
, lines
,
2235 out
: if (queuefp
!= NULL
)
2242 imap_append0(struct mailbox
*mp
, const char *name
, FILE *fp
)
2244 char *buf
, *bp
, *lp
;
2245 size_t bufsize
, buflen
, cnt
;
2246 off_t off1
= -1, offs
;
2248 int flag
= MNEW
|MNEWEST
;
2253 buf
= smalloc(bufsize
= LINESIZE
);
2259 bp
= fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 1);
2260 if (bp
== NULL
|| strncmp(buf
, "From ", 5) == 0) {
2261 if (off1
!= (off_t
)-1) {
2262 ok
=imap_append1(mp
, name
, fp
, off1
,
2266 fseek(fp
, offs
+buflen
, SEEK_SET
);
2268 off1
= offs
+ buflen
;
2273 tim
= unixtime(buf
);
2277 if (bp
&& buf
[0] == '\n')
2279 else if (bp
&& inhead
&& ascncasecmp(buf
, "status", 6) == 0) {
2281 while (whitechar(*lp
&0377))
2284 while (*++lp
!= '\0')
2293 } else if (bp
&& inhead
&&
2294 ascncasecmp(buf
, "x-status", 8) == 0) {
2296 while (whitechar(*lp
&0377))
2299 while (*++lp
!= '\0')
2312 } while (bp
!= NULL
);
2318 imap_append(const char *xserver
, FILE *fp
)
2320 sighandler_type
volatile saveint
, savepipe
;
2321 char *server
, *user
, *pass
;
2322 char const *sp
, *cp
, * volatile mbx
, *uhp
;
2323 int volatile use_ssl
;
2324 enum okay ok
= STOP
;
2326 server
= savestr(xserver
);
2330 imap_split(&server
, &sp
, &xus
, &cp
, &uhp
, &xmbx
, &pass
, &user
);
2336 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2337 safe_signal(SIGINT
, &_imap_maincatch
);
2338 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2339 if (sigsetjmp(imapjmp
, 1))
2341 if (savepipe
!= SIG_IGN
)
2342 safe_signal(SIGPIPE
, imapcatch
);
2343 if ((mb
.mb_type
== MB_CACHE
|| mb
.mb_sock
.s_fd
> 0) &&
2344 mb
.mb_imap_account
&&
2345 strcmp(protbase(server
), mb
.mb_imap_account
) == 0) {
2346 ok
= imap_append0(&mb
, mbx
, fp
);
2351 memset(&mx
, 0, sizeof mx
);
2352 if (disconnected(server
) == 0) {
2353 if (sopen(sp
, &mx
.mb_sock
, use_ssl
, uhp
,
2354 use_ssl
? "imaps" : "imap",
2355 (options
& OPT_VERBOSE
) != 0) != OKAY
)
2357 mx
.mb_sock
.s_desc
= "IMAP";
2358 mx
.mb_type
= MB_IMAP
;
2359 mx
.mb_imap_account
= (char *)protbase(server
);
2360 /* TODO the code now did
2361 * TODO mx.mb_imap_mailbox = mbx;
2362 * TODO though imap_mailbox is sfree()d and mbx
2363 * TODO is possibly even a constant
2364 * TODO i changed this to sstrdup() sofar, as is used
2365 * TODO somewhere else in this file for this! */
2366 mx
.mb_imap_mailbox
= sstrdup(mbx
);
2367 if (imap_preauth(&mx
, sp
, uhp
) != OKAY
||
2368 imap_auth(&mx
, uhp
, user
, pass
)!=OKAY
) {
2369 sclose(&mx
.mb_sock
);
2372 ok
= imap_append0(&mx
, mbx
, fp
);
2375 mx
.mb_imap_account
= (char *)protbase(server
);
2376 mx
.mb_imap_mailbox
= sstrdup(mbx
); /* TODO as above */
2377 mx
.mb_type
= MB_CACHE
;
2378 ok
= imap_append0(&mx
, mbx
, fp
);
2382 out
: safe_signal(SIGINT
, saveint
);
2383 safe_signal(SIGPIPE
, savepipe
);
2391 imap_list1(struct mailbox
*mp
, const char *base
, struct list_item
**list
,
2392 struct list_item
**lend
, int level
)
2395 enum okay ok
= STOP
;
2398 FILE *queuefp
= NULL
;
2399 struct list_item
*lp
;
2401 *list
= *lend
= NULL
;
2402 snprintf(o
, sizeof o
, "%s LIST %s %%\r\n",
2403 tag(1), imap_quotestr(base
));
2404 IMAP_OUT(o
, MB_COMD
, return STOP
)
2405 while (mp
->mb_active
& MB_COMD
) {
2406 ok
= imap_answer(mp
, 1);
2407 if (response_status
== RESPONSE_OTHER
&&
2408 response_other
== MAILBOX_DATA_LIST
&&
2409 imap_parse_list() == OKAY
) {
2410 cp
= imap_unquotestr(list_name
);
2411 lp
= csalloc(1, sizeof *lp
);
2413 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2415 lp
->l_base
= *cp
? cp
: savestr(base
);
2416 lp
->l_attr
= list_attributes
;
2417 lp
->l_level
= level
+1;
2418 lp
->l_delim
= list_hierarchy_delimiter
;
2419 if (*list
&& *lend
) {
2420 (*lend
)->l_next
= lp
;
2430 imap_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
2432 struct list_item
*list
, *lend
, *lp
, *lx
, *ly
;
2438 depth
= (cp
= ok_vlook(imap_list_depth
)) != NULL
? atoi(cp
) : 2;
2439 if (imap_list1(mp
, base
, &list
, &lend
, 0) == STOP
)
2441 if (list
== NULL
|| lend
== NULL
)
2443 for (lp
= list
; lp
; lp
= lp
->l_next
)
2444 if (lp
->l_delim
!= '/' && lp
->l_delim
!= EOF
&&
2445 lp
->l_level
< depth
&&
2446 (lp
->l_attr
&LIST_NOINFERIORS
) == 0) {
2447 cp
= salloc((n
= strlen(lp
->l_name
)) + 2);
2448 memcpy(cp
, lp
->l_name
, n
);
2449 cp
[n
] = lp
->l_delim
;
2451 if (imap_list1(mp
, cp
, &lx
, &ly
, lp
->l_level
) == OKAY
&&
2453 lp
->l_has_children
= 1;
2454 if (strcmp(cp
, lx
->l_name
) == 0)
2462 for (lp
= list
; lp
; lp
= lp
->l_next
) {
2465 for (bp
= base
; *bp
&& *bp
== *cp
; bp
++)
2469 if ((lp
->l_attr
&LIST_NOSELECT
) == 0)
2470 fprintf(fp
, "%s\n", *cp
? cp
: base
);
2471 else if (lp
->l_has_children
== 0)
2472 fprintf(fp
, "%s%c\n", *cp
? cp
: base
,
2473 lp
->l_delim
!= EOF
? lp
->l_delim
: '\n');
2479 imap_folders(const char * volatile name
, int strip
)
2481 sighandler_type saveint
, savepipe
;
2482 const char *fold
, *cp
, *sp
;
2486 cp
= protbase(name
);
2487 sp
= mb
.mb_imap_account
;
2488 if (sp
== NULL
|| strcmp(cp
, sp
)) {
2489 fprintf(stderr
, tr(502,
2490 "Cannot perform `folders' but when on the very IMAP "
2491 "account; the current one is\n `%s' -- "
2492 "try `folders @'.\n"),
2493 (sp
!= NULL
) ? sp
: tr(503, "[NONE]"));
2496 fold
= imap_fileof(name
);
2497 if (options
& OPT_TTYOUT
) {
2498 if ((fp
= Ftemp(&tempfn
, "Ri", "w+", 0600, 1)) == NULL
) {
2507 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2508 safe_signal(SIGINT
, &_imap_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
);
2520 if (options
& OPT_TTYOUT
)
2525 if (options
& OPT_TTYOUT
) {
2530 fprintf(stderr
, "Folder not found.\n");
2533 safe_signal(SIGINT
, saveint
);
2534 safe_signal(SIGPIPE
, savepipe
);
2535 if (options
& OPT_TTYOUT
)
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(XSHELL
, 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
= imap_fileof(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
;
2671 (void)imap_finish(mp
);
2677 imap_copy(struct message
*m
, int n
, const char *name
)
2679 sighandler_type saveint
, savepipe
;
2680 enum okay ok
= STOP
;
2686 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2687 safe_signal(SIGINT
, &_imap_maincatch
);
2688 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2689 if (sigsetjmp(imapjmp
, 1) == 0) {
2690 if (savepipe
!= SIG_IGN
)
2691 safe_signal(SIGPIPE
, imapcatch
);
2692 ok
= imap_copy1(&mb
, m
, n
, name
);
2694 safe_signal(SIGINT
, saveint
);
2695 safe_signal(SIGPIPE
, savepipe
);
2703 imap_copyuid_parse(const char *cp
, unsigned long *uidvalidity
,
2704 unsigned long *olduid
, unsigned long *newuid
)
2708 *uidvalidity
= strtoul(cp
, &xp
, 10);
2709 *olduid
= strtoul(xp
, &yp
, 10);
2710 *newuid
= strtoul(yp
, &zp
, 10);
2711 return *uidvalidity
&& *olduid
&& *newuid
&& xp
> cp
&& *xp
== ' ' &&
2712 yp
> xp
&& *yp
== ' ' && zp
> yp
&& *zp
== ']';
2716 imap_appenduid_parse(const char *cp
, unsigned long *uidvalidity
,
2721 *uidvalidity
= strtoul(cp
, &xp
, 10);
2722 *uid
= strtoul(xp
, &yp
, 10);
2723 return *uidvalidity
&& *uid
&& xp
> cp
&& *xp
== ' ' &&
2724 yp
> xp
&& *yp
== ']';
2728 imap_copyuid(struct mailbox
*mp
, struct message
*m
, const char *name
)
2731 unsigned long uidvalidity
, olduid
, newuid
;
2735 if ((cp
= asccasestr(responded_text
, "[COPYUID ")) == NULL
||
2736 imap_copyuid_parse(&cp
[9], &uidvalidity
,
2737 &olduid
, &newuid
) == STOP
)
2740 xmb
.mb_cache_directory
= NULL
;
2741 xmb
.mb_imap_mailbox
= savestr(name
);
2742 xmb
.mb_uidvalidity
= uidvalidity
;
2745 memset(&xm
, 0, sizeof xm
);
2747 if (getcache1(mp
, &xm
, NEED_UNSPEC
, 3) != OKAY
)
2749 getcache(mp
, &xm
, NEED_HEADER
);
2750 getcache(mp
, &xm
, NEED_BODY
);
2752 if ((m
->m_flag
& HAVE_HEADER
) == 0)
2753 getcache(mp
, m
, NEED_HEADER
);
2754 if ((m
->m_flag
& HAVE_BODY
) == 0)
2755 getcache(mp
, m
, NEED_BODY
);
2759 xm
.m_flag
&= ~MFULLYCACHED
;
2760 putcache(&xmb
, &xm
);
2765 imap_appenduid(struct mailbox
*mp
, FILE *fp
, time_t t
, long off1
,
2766 long xsize
, long size
, long lines
, int flag
, const char *name
)
2769 unsigned long uidvalidity
, uid
;
2773 if ((cp
= asccasestr(responded_text
, "[APPENDUID ")) == NULL
||
2774 imap_appenduid_parse(&cp
[11], &uidvalidity
,
2778 xmb
.mb_cache_directory
= NULL
;
2779 xmb
.mb_imap_mailbox
= savestr(name
);
2780 xmb
.mb_uidvalidity
= uidvalidity
;
2781 xmb
.mb_otf
= xmb
.mb_itf
= fp
;
2783 memset(&xm
, 0, sizeof xm
);
2784 xm
.m_flag
= (flag
& MREAD
) | MNEW
;
2786 xm
.m_block
= mailx_blockof(off1
);
2787 xm
.m_offset
= mailx_offsetof(off1
);
2790 xm
.m_lines
= xm
.m_xlines
= lines
;
2792 xm
.m_have
= HAVE_HEADER
|HAVE_BODY
;
2793 putcache(&xmb
, &xm
);
2798 imap_appenduid_cached(struct mailbox
*mp
, FILE *fp
)
2802 long size
, xsize
, ysize
, lines
;
2803 enum mflag flag
= MNEW
;
2804 char *name
, *buf
, *bp
, *tempCopy
;
2806 size_t bufsize
, buflen
, cnt
;
2807 enum okay ok
= STOP
;
2809 buf
= smalloc(bufsize
= LINESIZE
);
2812 if (fgetline(&buf
, &bufsize
, &cnt
, &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
, &cnt
, &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 cnt
,
2858 const char *spec
, int f
)
2862 FILE *queuefp
= NULL
;
2863 enum okay ok
= STOP
;
2870 for (cp
= spec
; *cp
; cp
++)
2873 cp
= charset_get_lc();
2875 if (asccasecmp(cp
, "utf-8")) {
2879 if ((it
= n_iconv_open("utf-8", cp
)) != (iconv_t
)-1) {
2880 sz
= strlen(spec
) + 1;
2881 nsp
= nspec
= salloc(nsz
= 6*strlen(spec
) + 1);
2882 if (n_iconv_buf(it
, &spec
, &sz
, &nsp
, &nsz
,
2883 FAL0
) == 0 && sz
== 0) {
2891 cp
= imap_quotestr(cp
);
2892 cs
= salloc(n
= strlen(cp
) + 10);
2893 snprintf(cs
, n
, "CHARSET %s ", cp
);
2896 o
= ac_alloc(osize
= strlen(spec
) + 60);
2897 snprintf(o
, osize
, "%s UID SEARCH %s%s\r\n", tag(1), cs
, spec
);
2898 IMAP_OUT(o
, MB_COMD
, goto out
)
2899 while (mp
->mb_active
& MB_COMD
) {
2900 ok
= imap_answer(mp
, 0);
2901 if (response_status
== RESPONSE_OTHER
&&
2902 response_other
== MAILBOX_DATA_SEARCH
) {
2903 xp
= responded_other_text
;
2904 while (*xp
&& *xp
!= '\r') {
2905 n
= strtoul(xp
, &xp
, 10);
2906 for (i
= 0; i
< cnt
; i
++)
2907 if (m
[i
].m_uid
== n
&&
2908 (m
[i
].m_flag
&MHIDDEN
)
2911 (m
[i
].m_flag
&MDELETED
)
2922 imap_search1(const char *volatile spec
, int f
)
2924 sighandler_type saveint
, savepipe
;
2925 enum okay ok
= STOP
;
2930 if (mb
.mb_type
!= MB_IMAP
)
2933 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2934 safe_signal(SIGINT
, &_imap_maincatch
);
2935 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2936 if (sigsetjmp(imapjmp
, 1) == 0) {
2937 if (savepipe
!= SIG_IGN
)
2938 safe_signal(SIGPIPE
, imapcatch
);
2939 ok
= imap_search2(&mb
, message
, msgCount
, spec
, f
);
2941 safe_signal(SIGINT
, saveint
);
2942 safe_signal(SIGPIPE
, savepipe
);
2950 imap_thisaccount(const char *cp
)
2952 if (mb
.mb_type
!= MB_CACHE
&& mb
.mb_type
!= MB_IMAP
)
2954 if ((mb
.mb_type
!= MB_CACHE
&& mb
.mb_sock
.s_fd
< 0) ||
2955 mb
.mb_imap_account
== NULL
)
2957 return strcmp(protbase(cp
), mb
.mb_imap_account
) == 0;
2961 imap_remove(const char * volatile name
)
2963 sighandler_type
volatile saveint
, savepipe
;
2964 enum okay ok
= STOP
;
2966 if (mb
.mb_type
!= MB_IMAP
) {
2967 fprintf(stderr
, "Refusing to remove \"%s\" "
2968 "in disconnected mode.\n", name
);
2971 if (!imap_thisaccount(name
)) {
2972 fprintf(stderr
, "Can only remove mailboxes on current IMAP "
2973 "server: \"%s\" not removed.\n", name
);
2977 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
2978 safe_signal(SIGINT
, &_imap_maincatch
);
2979 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
2980 if (sigsetjmp(imapjmp
, 1) == 0) {
2981 if (savepipe
!= SIG_IGN
)
2982 safe_signal(SIGPIPE
, imapcatch
);
2983 ok
= imap_remove1(&mb
, imap_fileof(name
));
2985 safe_signal(SIGINT
, saveint
);
2986 safe_signal(SIGPIPE
, savepipe
);
2989 ok
= cache_remove(name
);
2996 imap_remove1(struct mailbox
*mp
, const char *name
)
2998 FILE *queuefp
= NULL
;
3001 enum okay ok
= STOP
;
3003 o
= ac_alloc(os
= 2*strlen(name
) + 100);
3004 snprintf(o
, os
, "%s DELETE %s\r\n", tag(1), imap_quotestr(name
));
3005 IMAP_OUT(o
, MB_COMD
, goto out
)
3006 while (mp
->mb_active
& MB_COMD
)
3007 ok
= imap_answer(mp
, 1);
3013 imap_rename(const char *old
, const char *new)
3015 sighandler_type saveint
, savepipe
;
3016 enum okay ok
= STOP
;
3021 if (mb
.mb_type
!= MB_IMAP
) {
3022 fprintf(stderr
, "Refusing to rename mailboxes "
3023 "in disconnected mode.\n");
3026 if (!imap_thisaccount(old
) || !imap_thisaccount(new)) {
3027 fprintf(stderr
, "Can only rename mailboxes on current IMAP "
3028 "server: \"%s\" not renamed to \"%s\".\n",
3033 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
3034 safe_signal(SIGINT
, &_imap_maincatch
);
3035 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
3036 if (sigsetjmp(imapjmp
, 1) == 0) {
3037 if (savepipe
!= SIG_IGN
)
3038 safe_signal(SIGPIPE
, imapcatch
);
3039 ok
= imap_rename1(&mb
, imap_fileof(old
), imap_fileof(new));
3041 safe_signal(SIGINT
, saveint
);
3042 safe_signal(SIGPIPE
, savepipe
);
3045 ok
= cache_rename(old
, new);
3052 imap_rename1(struct mailbox
*mp
, const char *old
, const char *new)
3054 FILE *queuefp
= NULL
;
3057 enum okay ok
= STOP
;
3059 o
= ac_alloc(os
= 2*strlen(old
) + 2*strlen(new) + 100);
3060 snprintf(o
, os
, "%s RENAME %s %s\r\n", tag(1),
3061 imap_quotestr(old
), imap_quotestr(new));
3062 IMAP_OUT(o
, MB_COMD
, goto out
)
3063 while (mp
->mb_active
& MB_COMD
)
3064 ok
= imap_answer(mp
, 1);
3070 imap_dequeue(struct mailbox
*mp
, FILE *fp
)
3072 FILE *queuefp
= NULL
;
3073 char o
[LINESIZE
], *newname
;
3074 char *buf
, *bp
, *cp
, iob
[4096];
3075 size_t bufsize
, buflen
, cnt
;
3076 enum okay ok
= OKAY
, rok
= OKAY
;
3077 long offs
, offs1
, offs2
, octets
;
3078 int twice
, gotcha
= 0;
3080 buf
= smalloc(bufsize
= LINESIZE
);
3083 while ((offs1
= ftell(fp
)) >= 0 &&
3084 fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fp
, 0)
3086 for (bp
= buf
; *bp
!= ' '; bp
++); /* strip old tag */
3090 if ((offs
= ftell(fp
)) < 0)
3092 again
: snprintf(o
, sizeof o
, "%s %s", tag(1), bp
);
3093 if (ascncasecmp(bp
, "UID COPY ", 9) == 0) {
3095 while (digitchar(*cp
&0377))
3101 if ((newname
= imap_strex(cp
, NULL
)) == NULL
)
3103 IMAP_OUT(o
, MB_COMD
, continue)
3104 while (mp
->mb_active
& MB_COMD
)
3105 ok
= imap_answer(mp
, twice
);
3106 if (response_status
== RESPONSE_NO
&& twice
++ == 0)
3108 if (response_status
== RESPONSE_OK
&&
3109 mp
->mb_flags
& MB_UIDPLUS
) {
3110 imap_copyuid(mp
, NULL
,
3111 imap_unquotestr(newname
));
3113 } else if (ascncasecmp(bp
, "UID STORE ", 10) == 0) {
3114 IMAP_OUT(o
, MB_COMD
, continue)
3115 while (mp
->mb_active
& MB_COMD
)
3116 ok
= imap_answer(mp
, 1);
3119 } else if (ascncasecmp(bp
, "APPEND ", 7) == 0) {
3120 if ((cp
= strrchr(bp
, '{')) == NULL
)
3122 octets
= atol(&cp
[1]) + 2;
3123 if ((newname
= imap_strex(&bp
[7], NULL
)) == NULL
)
3125 IMAP_OUT(o
, MB_COMD
, continue)
3126 while (mp
->mb_active
& MB_COMD
) {
3127 ok
= imap_answer(mp
, twice
);
3128 if (response_type
== RESPONSE_CONT
)
3133 fseek(fp
, offs
, SEEK_SET
) >= 0)
3137 while (octets
> 0) {
3138 size_t n
= (size_t)octets
> sizeof iob
3139 ? sizeof iob
: (size_t)octets
;
3141 if (n
!= fread(iob
, 1, n
, fp
))
3143 swrite1(&mp
->mb_sock
, iob
, n
, 1);
3145 swrite(&mp
->mb_sock
, "");
3146 while (mp
->mb_active
& MB_COMD
) {
3147 ok
= imap_answer(mp
, 0);
3148 if (response_status
== RESPONSE_NO
&&
3150 if (fseek(fp
, offs
, SEEK_SET
) < 0)
3155 if (response_status
== RESPONSE_OK
&&
3156 mp
->mb_flags
& MB_UIDPLUS
) {
3157 if ((offs2
= ftell(fp
)) < 0)
3159 fseek(fp
, offs1
, SEEK_SET
);
3160 if (imap_appenduid_cached(mp
, fp
) == STOP
) {
3161 (void)fseek(fp
, offs2
, SEEK_SET
);
3166 fail
: fprintf(stderr
,
3167 "Invalid command in IMAP cache queue: \"%s\"\n",
3173 snprintf(o
, sizeof o
, "%s CREATE %s\r\n",
3175 IMAP_OUT(o
, MB_COMD
, continue)
3176 while (mp
->mb_active
& MB_COMD
)
3177 ok
= imap_answer(mp
, 1);
3183 ftruncate(fileno(fp
), 0);
3191 imap_strex(char const *cp
, char const **xp
)
3198 for (cq
= &cp
[1]; *cq
; cq
++) {
3201 else if (*cq
== '"')
3206 n
= salloc(cq
- cp
+ 2);
3207 memcpy(n
, cp
, cq
- cp
+ 1);
3208 n
[cq
- cp
+ 1] = '\0';
3215 check_expunged(void)
3217 if (expunged_messages
> 0) {
3219 "Command not executed - messages have been expunged\n");
3230 int omsgCount
= msgCount
;
3233 if (mb
.mb_type
== MB_IMAP
&& mb
.mb_sock
.s_fd
> 0) {
3234 fprintf(stderr
, "Already connected.\n");
3237 var_clear_allow_undefined
= TRU1
;
3238 ok_bclear(disconnected
);
3239 cp
= protbase(mailname
);
3240 if (strncmp(cp
, "imap://", 7) == 0)
3242 else if (strncmp(cp
, "imaps://", 8) == 0)
3244 if ((cq
= strchr(cp
, ':')) != NULL
)
3246 vok_bclear(savecat("disconnected-", cp
));
3247 var_clear_allow_undefined
= FAL0
;
3248 if (mb
.mb_type
== MB_CACHE
) {
3249 imap_setfile1(mailname
, 0, edit
, 1);
3250 if (msgCount
> omsgCount
)
3251 newmailinfo(omsgCount
);
3257 cdisconnect(void *vp
)
3261 if (mb
.mb_type
== MB_CACHE
) {
3262 fprintf(stderr
, "Not connected.\n");
3264 } else if (mb
.mb_type
== MB_IMAP
) {
3265 if (cached_uidvalidity(&mb
) == 0) {
3266 fprintf(stderr
, "The current mailbox is not cached.\n");
3272 ok_bset(disconnected
, TRU1
);
3273 if (mb
.mb_type
== MB_IMAP
) {
3274 sclose(&mb
.mb_sock
);
3275 imap_setfile1(mailname
, 0, edit
, 1);
3283 int *msgvec
= vp
, *ip
;
3286 if (mb
.mb_type
!= MB_IMAP
) {
3287 fprintf(stderr
, "Not connected to an IMAP server.\n");
3290 if (cached_uidvalidity(&mb
) == 0) {
3291 fprintf(stderr
, "The current mailbox is not cached.\n");
3294 for (ip
= msgvec
; *ip
; ip
++) {
3295 mp
= &message
[*ip
-1];
3296 if (!(mp
->m_have
& HAVE_BODY
))
3303 disconnected(const char *file
)
3308 if (ok_blook(disconnected
))
3310 cp
= protbase(file
);
3311 if (strncmp(cp
, "imap://", 7) == 0)
3313 else if (strncmp(cp
, "imaps://", 8) == 0)
3317 if ((cq
= strchr(cp
, ':')) != NULL
)
3319 vp
= ac_alloc(vs
= strlen(cp
) + 14);
3320 snprintf(vp
, vs
, "disconnected-%s", cp
);
3327 transflags(struct message
*omessage
, long omsgCount
, int transparent
)
3329 struct message
*omp
, *nmp
, *newdot
, *newprevdot
;
3336 while (omp
< &omessage
[omsgCount
] &&
3337 nmp
< &message
[msgCount
]) {
3338 if (dot
&& nmp
->m_uid
== dot
->m_uid
)
3340 if (prevdot
&& nmp
->m_uid
== prevdot
->m_uid
)
3342 if (omp
->m_uid
== nmp
->m_uid
) {
3343 hf
= nmp
->m_flag
& MHIDDEN
;
3344 if (transparent
&& mb
.mb_type
== MB_IMAP
)
3345 omp
->m_flag
&= ~MHIDDEN
;
3347 if (transparent
&& mb
.mb_type
== MB_CACHE
)
3348 nmp
[-1].m_flag
|= hf
;
3349 } else if (omp
->m_uid
< nmp
->m_uid
)
3356 prevdot
= newprevdot
;
3361 imap_read_date_time(const char *cp
)
3364 int i
, year
, month
, day
, hour
, minute
, second
;
3369 * "25-Jul-2004 15:33:44 +0200"
3373 if (cp
[0] != '"' || strlen(cp
) < 28 || cp
[27] != '"')
3375 day
= strtol(&cp
[1], NULL
, 10);
3377 if (ascncasecmp(&cp
[4], month_names
[i
], 3) == 0)
3379 if (month_names
[++i
][0] == '\0')
3383 year
= strtol(&cp
[8], NULL
, 10);
3384 hour
= strtol(&cp
[13], NULL
, 10);
3385 minute
= strtol(&cp
[16], NULL
, 10);
3386 second
= strtol(&cp
[19], NULL
, 10);
3387 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
3402 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
3405 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
3413 imap_make_date_time(time_t t
)
3417 int tzdiff
, tzdiff_hour
, tzdiff_min
;
3419 tzdiff
= t
- mktime(gmtime(&t
));
3420 tzdiff_hour
= (int)(tzdiff
/ 60);
3421 tzdiff_min
= tzdiff_hour
% 60;
3423 tmptr
= localtime(&t
);
3424 if (tmptr
->tm_isdst
> 0)
3426 snprintf(s
, sizeof s
, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
3428 month_names
[tmptr
->tm_mon
],
3429 tmptr
->tm_year
+ 1900,
3437 #endif /* HAVE_IMAP */
3440 imap_read_date(const char *cp
)
3443 int year
, month
, day
, i
, tzdiff
;
3449 day
= strtol(cp
, &xp
, 10);
3450 if (day
<= 0 || day
> 31 || *xp
++ != '-')
3453 if (ascncasecmp(xp
, month_names
[i
], 3) == 0)
3455 if (month_names
[++i
][0] == '\0')
3461 year
= strtol(&xp
[4], &yp
, 10);
3462 if (year
< 1970 || year
> 2037 || yp
!= &xp
[8])
3464 if (yp
[0] != '\0' && (yp
[1] != '"' || yp
[2] != '\0'))
3466 if ((t
= combinetime(year
, month
, day
, 0, 0, 0)) == (time_t)-1)
3468 tzdiff
= t
- mktime(gmtime(&t
));
3469 tmptr
= localtime(&t
);
3470 if (tmptr
->tm_isdst
> 0)
3477 imap_quotestr(const char *s
)
3481 np
= n
= salloc(2 * strlen(s
) + 3);
3484 if (*s
== '"' || *s
== '\\')
3494 imap_unquotestr(const char *s
)
3500 np
= n
= salloc(strlen(s
) + 1);