2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * Mail -- a mail program
59 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
60 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
61 * TODO may leave child running if fdopen() fails! */
66 #define POP3_ANSWER() if (pop3_answer(mp) == STOP) \
68 #define POP3_OUT(x, y) if (pop3_finish(mp) == STOP) \
71 fprintf(stderr, ">>> %s", x); \
72 mp->mb_active |= (y); \
73 if (swrite(&mp->mb_sock, x) == STOP) \
77 static size_t pop3bufsize
;
78 static sigjmp_buf pop3jmp
;
79 static sighandler_type savealrm
;
81 static struct termios otio
;
82 static int pop3keepalive
;
83 static volatile int pop3lock
;
85 static void pop3_timer_off(void);
86 static enum okay
pop3_answer(struct mailbox
*mp
);
87 static enum okay
pop3_finish(struct mailbox
*mp
);
88 static void pop3catch(int s
);
89 static void maincatch(int s
);
90 static enum okay
pop3_noop1(struct mailbox
*mp
);
91 static void pop3alarm(int s
);
92 static enum okay
pop3_pass(struct mailbox
*mp
, const char *pass
);
94 static char *pop3_find_timestamp(const char *bp
);
95 static enum okay
pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
,
97 static enum okay
pop3_apop1(struct mailbox
*mp
,
98 const char *user
, const char *xp
);
99 static int pop3_use_apop(const char *uhp
);
101 static int pop3_use_starttls(const char *uhp
);
102 static enum okay
pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
103 const char *uhp
, const char *xserver
);
104 static enum okay
pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
);
105 static enum okay
pop3_list(struct mailbox
*mp
, int n
, size_t *size
);
106 static void pop3_init(struct mailbox
*mp
, int n
);
107 static void pop3_dates(struct mailbox
*mp
);
108 static void pop3_setptr(struct mailbox
*mp
);
109 static char *pop3_have_password(const char *server
);
110 static enum okay
pop3_get(struct mailbox
*mp
, struct message
*m
,
112 static enum okay
pop3_exit(struct mailbox
*mp
);
113 static enum okay
pop3_delete(struct mailbox
*mp
, int n
);
114 static enum okay
pop3_update(struct mailbox
*mp
);
119 if (pop3keepalive
> 0) {
121 safe_signal(SIGALRM
, savealrm
);
126 pop3_answer(struct mailbox
*mp
)
131 retry
: if ((sz
= sgetline(&pop3buf
, &pop3bufsize
, NULL
, &mp
->mb_sock
)) > 0) {
132 if ((mp
->mb_active
& (MB_COMD
|MB_MULT
)) == MB_MULT
)
135 fputs(pop3buf
, stderr
);
139 mp
->mb_active
&= ~MB_COMD
;
143 mp
->mb_active
= MB_NONE
;
144 fprintf(stderr
, catgets(catd
, CATSET
, 218,
145 "POP3 error: %s"), pop3buf
);
149 * If the answer starts neither with '+' nor with
150 * '-', it must be part of a multiline response,
151 * e. g. because the user interrupted a file
152 * download. Get lines until a single dot appears.
154 multiline
: while (pop3buf
[0] != '.' || pop3buf
[1] != '\r' ||
155 pop3buf
[2] != '\n' ||
156 pop3buf
[3] != '\0') {
157 sz
= sgetline(&pop3buf
, &pop3bufsize
,
162 mp
->mb_active
&= ~MB_MULT
;
163 if (mp
->mb_active
!= MB_NONE
)
168 mp
->mb_active
= MB_NONE
;
174 pop3_finish(struct mailbox
*mp
)
176 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
!= MB_NONE
)
185 tcsetattr(0, TCSADRAIN
, &otio
);
188 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
189 siglongjmp(pop3jmp
, 1);
192 fprintf(stderr
, "Received SIGPIPE during POP3 operation\n");
201 if (interrupts
++ == 0) {
202 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
209 pop3_noop1(struct mailbox
*mp
)
211 POP3_OUT("NOOP\r\n", MB_COMD
)
220 sighandler_type saveint
, savepipe
;
225 verbose
= value("verbose") != NULL
;
227 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
228 safe_signal(SIGINT
, maincatch
);
229 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
230 if (sigsetjmp(pop3jmp
, 1) == 0) {
231 if (savepipe
!= SIG_IGN
)
232 safe_signal(SIGPIPE
, pop3catch
);
233 ok
= pop3_noop1(&mb
);
235 safe_signal(SIGINT
, saveint
);
236 safe_signal(SIGPIPE
, savepipe
);
245 sighandler_type saveint
;
246 sighandler_type savepipe
;
249 if (pop3lock
++ == 0) {
250 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
251 safe_signal(SIGINT
, maincatch
);
252 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
253 if (sigsetjmp(pop3jmp
, 1)) {
254 safe_signal(SIGINT
, saveint
);
255 safe_signal(SIGPIPE
, savepipe
);
258 if (savepipe
!= SIG_IGN
)
259 safe_signal(SIGPIPE
, pop3catch
);
260 if (pop3_noop1(&mb
) != OKAY
) {
261 safe_signal(SIGINT
, saveint
);
262 safe_signal(SIGPIPE
, savepipe
);
265 safe_signal(SIGINT
, saveint
);
266 safe_signal(SIGPIPE
, savepipe
);
268 brk
: alarm(pop3keepalive
);
273 pop3_pass(struct mailbox
*mp
, const char *pass
)
277 snprintf(o
, sizeof o
, "PASS %s\r\n", pass
);
285 pop3_find_timestamp(const char *bp
)
291 if ((cp
= strchr(bp
, '<')) == NULL
)
293 for (ep
= cp
; *ep
; ep
++) {
294 if (spacechar(*ep
&0377))
298 else if (*ep
== '>') {
306 rp
= salloc(ep
- cp
+ 2);
307 memcpy(rp
, cp
, ep
- cp
+ 1);
308 rp
[ep
- cp
+ 1] = '\0';
313 pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
, const char *ts
)
315 char *user
, *catp
, *xp
;
316 unsigned char digest
[16];
319 retry
: if (xuser
== NULL
) {
320 if ((user
= getuser()) == NULL
)
325 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
328 catp
= savecat(ts
, pass
);
330 MD5Update(&ctx
, (unsigned char *)catp
, strlen(catp
));
331 MD5Final(digest
, &ctx
);
332 xp
= md5tohex(digest
);
333 if (pop3_apop1(mp
, user
, xp
) == STOP
) {
341 pop3_apop1(struct mailbox
*mp
, const char *user
, const char *xp
)
345 snprintf(o
, sizeof o
, "APOP %s %s\r\n", user
, xp
);
352 pop3_use_apop(const char *uhp
)
356 if (value("pop3-use-apop"))
358 var
= savecat("pop3-use-apop-", uhp
);
359 return value(var
) != NULL
;
364 pop3_use_starttls(const char *uhp
)
368 if (value("pop3-use-starttls"))
370 var
= savecat("pop3-use-starttls-", uhp
);
371 return value(var
) != NULL
;
375 pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
376 const char *uhp
, const char *xserver
)
378 char o
[LINESIZE
], *user
, *server
, *cp
;
385 if (pop3_use_apop(uhp
)) {
386 if ((ts
= pop3_find_timestamp(pop3buf
)) == NULL
) {
387 fprintf(stderr
, tr(276,
388 "Could not determine timestamp from "
389 "server greeting. Can't use APOP.\n"));
394 if ((cp
= strchr(xserver
, ':')) != NULL
) {
395 server
= salloc(cp
- xserver
+ 1);
396 memcpy(server
, xserver
, cp
- xserver
);
397 server
[cp
- xserver
] = '\0';
399 server
= (char *)xserver
;
401 if (mp
->mb_sock
.s_use_ssl
== 0 && pop3_use_starttls(uhp
)) {
402 POP3_OUT("STLS\r\n", MB_COMD
)
404 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
408 if (pop3_use_starttls(uhp
)) {
409 fprintf(stderr
, "No SSL support compiled in.\n");
412 #endif /* !USE_SSL */
415 return pop3_apop(mp
, xuser
, pass
, ts
);
417 retry
: if (xuser
== NULL
) {
418 if ((user
= getuser()) == NULL
)
422 snprintf(o
, sizeof o
, "USER %s\r\n", user
);
426 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
429 if (pop3_pass(mp
, pass
) == STOP
) {
437 pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
)
442 POP3_OUT("STAT\r\n", MB_COMD
);
444 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
445 while (*cp
&& spacechar(*cp
& 0377))
448 *count
= (int)strtol(cp
, NULL
, 10);
449 while (*cp
&& !spacechar(*cp
& 0377))
451 while (*cp
&& spacechar(*cp
& 0377))
454 *size
= (int)strtol(cp
, NULL
, 10);
460 fprintf(stderr
, catgets(catd
, CATSET
, 260,
461 "invalid POP3 STAT response: %s\n"), pop3buf
);
466 pop3_list(struct mailbox
*mp
, int n
, size_t *size
)
468 char o
[LINESIZE
], *cp
;
470 snprintf(o
, sizeof o
, "LIST %u\r\n", n
);
473 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
474 while (*cp
&& spacechar(*cp
& 0377))
476 while (*cp
&& !spacechar(*cp
& 0377))
478 while (*cp
&& spacechar(*cp
& 0377))
481 *size
= (size_t)strtol(cp
, NULL
, 10);
488 pop3_init(struct mailbox
*mp
, int n
)
490 struct message
*m
= &message
[n
];
493 m
->m_flag
= MUSED
|MNEW
|MNOFROM
|MNEWEST
;
496 pop3_list(mp
, m
- message
+ 1, &m
->m_xsize
);
497 if ((cp
= hfield("status", m
)) != NULL
) {
498 while (*cp
!= '\0') {
510 pop3_dates(struct mailbox
*mp
)
515 for (i
= 0; i
< msgCount
; i
++)
516 substdate(&message
[i
]);
520 pop3_setptr(struct mailbox
*mp
)
524 message
= scalloc(msgCount
+ 1, sizeof *message
);
525 for (i
= 0; i
< msgCount
; i
++)
528 message
[msgCount
].m_size
= 0;
529 message
[msgCount
].m_lines
= 0;
534 pop3_have_password(const char *server
)
538 var
= ac_alloc(strlen(server
) + 10);
539 strcpy(var
, "password-");
540 strcpy(&var
[9], server
);
541 if ((cp
= value(var
)) != NULL
)
548 pop3_setfile(const char *server
, int newmail
, int isedit
)
551 sighandler_type saveint
;
552 sighandler_type savepipe
;
554 const char *cp
, *sp
= server
, *pass
, *uhp
;
562 if (strncmp(sp
, "pop3://", 7) == 0) {
566 } else if (strncmp(sp
, "pop3s://", 8) == 0) {
572 pass
= pop3_have_password(uhp
);
573 if ((cp
= last_at_before_slash(sp
)) != NULL
) {
574 user
= salloc(cp
- sp
+ 1);
575 memcpy(user
, sp
, cp
- sp
);
576 user
[cp
- sp
] = '\0';
581 verbose
= value("verbose") != NULL
;
582 if (sopen(sp
, &so
, use_ssl
, uhp
, use_ssl
? "pop3s" : "pop3",
588 if (mb
.mb_sock
.s_fd
>= 0)
599 mb
.mb_type
= MB_VOID
;
602 saveint
= safe_signal(SIGINT
, SIG_IGN
);
603 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
604 if (sigsetjmp(pop3jmp
, 1)) {
606 safe_signal(SIGINT
, saveint
);
607 safe_signal(SIGPIPE
, savepipe
);
611 if (saveint
!= SIG_IGN
)
612 safe_signal(SIGINT
, pop3catch
);
613 if (savepipe
!= SIG_IGN
)
614 safe_signal(SIGPIPE
, pop3catch
);
615 if ((cp
= value("pop3-keepalive")) != NULL
) {
616 if ((pop3keepalive
= strtol(cp
, NULL
, 10)) > 0) {
617 savealrm
= safe_signal(SIGALRM
, pop3alarm
);
618 alarm(pop3keepalive
);
621 mb
.mb_sock
.s_desc
= "POP3";
622 mb
.mb_sock
.s_onclose
= pop3_timer_off
;
623 if (pop3_user(&mb
, user
, pass
, uhp
, sp
) != OKAY
||
624 pop3_stat(&mb
, &mailsize
, &msgCount
) != OKAY
) {
627 safe_signal(SIGINT
, saveint
);
628 safe_signal(SIGPIPE
, savepipe
);
632 mb
.mb_type
= MB_POP3
;
633 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
637 safe_signal(SIGINT
, saveint
);
638 safe_signal(SIGPIPE
, savepipe
);
640 if (!edit
&& msgCount
== 0) {
641 if (mb
.mb_type
== MB_POP3
&& value("emptystart") == NULL
)
642 fprintf(stderr
, catgets(catd
, CATSET
, 258,
643 "No mail at %s\n"), server
);
650 pop3_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
652 sighandler_type saveint
= SIG_IGN
;
653 sighandler_type savepipe
= SIG_IGN
;
655 char o
[LINESIZE
], *line
= NULL
, *lp
;
656 size_t linesize
= 0, linelen
, size
;
657 int number
= m
- message
+ 1;
658 int emptyline
= 0, lines
;
665 verbose
= value("verbose") != NULL
;
666 if (mp
->mb_sock
.s_fd
< 0) {
667 fprintf(stderr
, catgets(catd
, CATSET
, 219,
668 "POP3 connection already closed.\n"));
671 if (pop3lock
++ == 0) {
672 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
673 safe_signal(SIGINT
, maincatch
);
674 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
675 if (sigsetjmp(pop3jmp
, 1)) {
676 safe_signal(SIGINT
, saveint
);
677 safe_signal(SIGPIPE
, savepipe
);
681 if (savepipe
!= SIG_IGN
)
682 safe_signal(SIGPIPE
, pop3catch
);
684 fseek(mp
->mb_otf
, 0L, SEEK_END
);
685 offset
= ftell(mp
->mb_otf
);
686 retry
: switch (need
) {
688 snprintf(o
, sizeof o
, "TOP %u 0\r\n", number
);
691 snprintf(o
, sizeof o
, "RETR %u\r\n", number
);
696 POP3_OUT(o
, MB_COMD
|MB_MULT
)
697 if (pop3_answer(mp
) == STOP
) {
698 if (need
== NEED_HEADER
) {
700 * The TOP POP3 command is optional, so retry
701 * with the entire message.
712 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
713 if (line
[0] == '.' && line
[1] == '\r' && line
[2] == '\n' &&
715 mp
->mb_active
&= ~MB_MULT
;
718 if (line
[0] == '.') {
724 * Need to mask 'From ' lines. This cannot be done properly
725 * since some servers pass them as 'From ' and others as
726 * '>From '. Although one could identify the first kind of
727 * server in principle, it is not possible to identify the
728 * second as '>From ' may also come from a server of the
729 * first type as actual data. So do what is absolutely
730 * necessary only - mask 'From '.
732 * If the line is the first line of the message header, it
733 * is likely a real 'From ' line. In this case, it is just
734 * ignored since it violates all standards.
736 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
737 lp
[3] == 'm' && lp
[4] == ' ') {
739 fputc('>', mp
->mb_otf
);
745 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
746 lp
[linelen
-2] == '\r')) {
747 emptyline
= linelen
<= 2;
749 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
750 fputc('\n', mp
->mb_otf
);
754 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
760 * This is very ugly; but some POP3 daemons don't end a
761 * message with \r\n\r\n, and we need \n\n for mbox format.
763 fputc('\n', mp
->mb_otf
);
769 m
->m_block
= mailx_blockof(offset
);
770 m
->m_offset
= mailx_offsetof(offset
);
774 m
->m_have
|= HAVE_HEADER
;
777 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
778 m
->m_xlines
= m
->m_lines
;
779 m
->m_xsize
= m
->m_size
;
786 if (saveint
!= SIG_IGN
)
787 safe_signal(SIGINT
, saveint
);
788 if (savepipe
!= SIG_IGN
)
789 safe_signal(SIGPIPE
, savepipe
);
797 pop3_header(struct message
*m
)
799 return pop3_get(&mb
, m
, NEED_HEADER
);
804 pop3_body(struct message
*m
)
806 return pop3_get(&mb
, m
, NEED_BODY
);
810 pop3_exit(struct mailbox
*mp
)
812 POP3_OUT("QUIT\r\n", MB_COMD
)
818 pop3_delete(struct mailbox
*mp
, int n
)
822 snprintf(o
, sizeof o
, "DELE %u\r\n", n
);
829 pop3_update(struct mailbox
*mp
)
831 FILE *readstat
= NULL
;
833 int dodel
, c
, gotcha
, held
;
836 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
841 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
842 if (m
->m_flag
& MBOX
)
848 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
849 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
852 if ((id
= hfield("message-id", m
)) != NULL
||
853 (id
= hfield("article-id", m
)) != NULL
)
854 fprintf(readstat
, "%s\n", id
);
857 dodel
= m
->m_flag
& MDELETED
;
859 dodel
= !((m
->m_flag
&MPRESERVE
) ||
860 (m
->m_flag
&MTOUCH
) == 0);
863 pop3_delete(mp
, m
- message
+ 1);
868 if (readstat
!= NULL
)
870 if (gotcha
&& edit
) {
871 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
872 printf(value("bsdcompat") || value("bsdmsgs") ?
873 catgets(catd
, CATSET
, 170, "complete\n") :
874 catgets(catd
, CATSET
, 212, "updated.\n"));
875 } else if (held
&& !edit
) {
877 printf(catgets(catd
, CATSET
, 155,
878 "Held 1 message in %s\n"), mailname
);
880 printf(catgets(catd
, CATSET
, 156,
881 "Held %d messages in %s\n"), held
, mailname
);
890 sighandler_type saveint
;
891 sighandler_type savepipe
;
893 verbose
= value("verbose") != NULL
;
894 if (mb
.mb_sock
.s_fd
< 0) {
895 fprintf(stderr
, catgets(catd
, CATSET
, 219,
896 "POP3 connection already closed.\n"));
900 saveint
= safe_signal(SIGINT
, SIG_IGN
);
901 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
902 if (sigsetjmp(pop3jmp
, 1)) {
903 safe_signal(SIGINT
, saveint
);
904 safe_signal(SIGPIPE
, saveint
);
908 if (saveint
!= SIG_IGN
)
909 safe_signal(SIGINT
, pop3catch
);
910 if (savepipe
!= SIG_IGN
)
911 safe_signal(SIGPIPE
, pop3catch
);
915 safe_signal(SIGINT
, saveint
);
916 safe_signal(SIGPIPE
, savepipe
);
919 #else /* !USE_POP3 */
923 fprintf(stderr
, catgets(catd
, CATSET
, 216,
924 "No POP3 support compiled in.\n"));
928 pop3_setfile(const char *server
, int newmail
, int isedit
)
938 pop3_header(struct message
*mp
)
946 pop3_body(struct message
*mp
)
965 #endif /* USE_POP3 */