1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ POP3 (RFCs 1939, 2595) client.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 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
43 typedef int avoid_empty_file_compiler_warning
;
51 #define POP3_ANSWER() POP3_XANSWER(return STOP);
52 #define POP3_XANSWER(ACTIONSTOP) \
54 if (pop3_answer(mp) == STOP) {\
59 #define POP3_OUT(X,Y) POP3_XOUT(X, Y, return STOP);
60 #define POP3_XOUT(X,Y,ACTIONSTOP) \
62 if (pop3_finish(mp) == STOP) {\
65 if (options & OPT_VERBOSE)\
66 fprintf(stderr, ">>> %s", X);\
68 if (swrite(&mp->mb_sock, X) == STOP) {\
74 static size_t pop3bufsize
;
75 static sigjmp_buf pop3jmp
;
76 static sighandler_type savealrm
;
77 static int pop3keepalive
;
78 static volatile int pop3lock
;
80 /* Perform entire login handshake */
81 static enum okay
_pop3_login(struct mailbox
*mp
, char *xuser
, char *pass
,
82 char const *uhp
, char const *xserver
);
84 /* APOP: get greeting credential or NULL */
86 static char * _pop3_lookup_apop_timestamp(char const *bp
);
89 static bool_t
_pop3_use_starttls(char const *uhp
);
91 /* APOP: shall we use it (for *uhp*)? */
92 static bool_t
_pop3_no_apop(char const *uhp
);
94 /* Several authentication methods */
95 static enum okay
_pop3_auth_plain(struct mailbox
*mp
, char *xuser
,
98 static enum okay
_pop3_auth_apop(struct mailbox
*mp
, char *xuser
,
99 char *pass
, char const *ts
);
102 static void pop3_timer_off(void);
103 static enum okay
pop3_answer(struct mailbox
*mp
);
104 static enum okay
pop3_finish(struct mailbox
*mp
);
105 static void pop3catch(int s
);
106 static void maincatch(int s
);
107 static enum okay
pop3_noop1(struct mailbox
*mp
);
108 static void pop3alarm(int s
);
109 static enum okay
pop3_stat(struct mailbox
*mp
, off_t
*size
, int *cnt
);
110 static enum okay
pop3_list(struct mailbox
*mp
, int n
, size_t *size
);
111 static void pop3_init(struct mailbox
*mp
, int n
);
112 static void pop3_dates(struct mailbox
*mp
);
113 static void pop3_setptr(struct mailbox
*mp
);
114 static enum okay
pop3_get(struct mailbox
*mp
, struct message
*m
,
116 static enum okay
pop3_exit(struct mailbox
*mp
);
117 static enum okay
pop3_delete(struct mailbox
*mp
, int n
);
118 static enum okay
pop3_update(struct mailbox
*mp
);
121 _pop3_login(struct mailbox
*mp
, char *xuser
, char *pass
, char const *uhp
,
130 /* Get the greeting, check wether APOP is advertised */
131 POP3_XANSWER(goto jleave
);
133 ts
= _pop3_lookup_apop_timestamp(pop3buf
);
136 if ((cp
= strchr(xserver
, ':')) != NULL
) { /* TODO GENERIC URI PARSE! */
137 size_t l
= (size_t)(cp
- xserver
);
138 char *x
= salloc(l
+ 1);
139 memcpy(x
, xserver
, l
);
144 /* If not yet secured, can we upgrade to TLS? */
146 if (mp
->mb_sock
.s_use_ssl
== 0 && _pop3_use_starttls(uhp
)) {
147 POP3_XOUT("STLS\r\n", MB_COMD
, goto jleave
);
148 POP3_XANSWER(goto jleave
);
149 if (ssl_open(xserver
, &mp
->mb_sock
, uhp
) != OKAY
)
153 if (_pop3_use_starttls(uhp
)) {
154 fprintf(stderr
, "No SSL support compiled in.\n");
159 /* Use the APOP single roundtrip? */
160 if (! _pop3_no_apop(uhp
)) {
163 rv
= _pop3_auth_apop(mp
, xuser
, pass
, ts
);
165 fprintf(stderr
, tr(276,
166 "POP3 `APOP' authentication failed, "
167 "maybe try setting *pop3-no-apop*\n"));
171 if (options
& OPT_VERBOSE
)
172 fprintf(stderr
, tr(204, "No POP3 `APOP' support "
173 "available, sending password in clear text\n"));
175 rv
= _pop3_auth_plain(mp
, xuser
, pass
);
181 _pop3_lookup_apop_timestamp(char const *bp
)
185 * A POP3 server which implements the APOP command will include
186 * a timestamp in its banner greeting. The syntax of the timestamp
187 * corresponds to the `msg-id' in [RFC822]
189 * msg-id = "<" addr-spec ">"
190 * addr-spec = local-part "@" domain
197 if ((cp
= strchr(bp
, '<')) == NULL
)
200 /* xxx What about malformed APOP timestamp (<@>) here? */
201 for (ep
= cp
; *ep
; ep
++) {
206 else if (*ep
== '>') {
215 tl
= (size_t)(++ep
- cp
);
224 _pop3_use_starttls(char const *uhp
)
228 if (value("pop3-use-starttls"))
230 var
= savecat("pop3-use-starttls-", uhp
);
231 return value(var
) != NULL
;
235 _pop3_no_apop(char const *uhp
)
239 if (! (ret
= boption("pop3-no-apop"))) {
240 #define __S "pop3-no-apop-"
241 #define __SL sizeof(__S)
242 size_t i
= strlen(uhp
);
243 char *var
= ac_alloc(i
+ __SL
);
244 memcpy(var
, __S
, __SL
- 1);
245 memcpy(var
+ __SL
- 1, uhp
, i
+ 1);
256 _pop3_auth_apop(struct mailbox
*mp
, char *xuser
, char *pass
, char const *ts
)
259 unsigned char digest
[16];
260 char hex
[MD5TOHEX_SIZE
];
265 for (tl
= strlen(ts
);;) {
267 if (! getcredentials(&user
, &pass
))
271 MD5Update(&ctx
, (unsigned char*)UNCONST(ts
), tl
);
272 MD5Update(&ctx
, (unsigned char*)pass
, strlen(pass
));
273 MD5Final(digest
, &ctx
);
274 md5tohex(hex
, digest
);
277 cp
= ac_alloc(5 + i
+ 1 + MD5TOHEX_SIZE
+ 3);
279 memcpy(cp
, "APOP ", 5);
280 memcpy(cp
+ 5, user
, i
);
283 memcpy(cp
+ i
, hex
, MD5TOHEX_SIZE
);
285 memcpy(cp
+ i
, "\r\n\0", 3);
286 POP3_XOUT(cp
, MB_COMD
, goto jcont
);
287 POP3_XANSWER(goto jcont
);
297 #endif /* HAVE_MD5 */
300 _pop3_auth_plain(struct mailbox
*mp
, char *xuser
, char *pass
)
306 /* The USER/PASS plain text version */
309 if (! getcredentials(&user
, &pass
))
314 cp
= ac_alloc(MAX(ul
, pl
) + 5 + 2 +1);
316 memcpy(cp
, "USER ", 5);
317 memcpy(cp
+ 5, user
, ul
);
318 memcpy(cp
+ 5 + ul
, "\r\n\0", 3);
319 POP3_XOUT(cp
, MB_COMD
, goto jcont
);
320 POP3_XANSWER(goto jcont
);
322 memcpy(cp
, "PASS ", 5);
323 memcpy(cp
+ 5, pass
, pl
);
324 memcpy(cp
+ 5 + pl
, "\r\n\0", 3);
325 POP3_XOUT(cp
, MB_COMD
, goto jcont
);
326 POP3_XANSWER(goto jcont
);
340 if (pop3keepalive
> 0) {
342 safe_signal(SIGALRM
, savealrm
);
347 pop3_answer(struct mailbox
*mp
)
352 retry
: if ((sz
= sgetline(&pop3buf
, &pop3bufsize
, NULL
, &mp
->mb_sock
)) > 0) {
353 if ((mp
->mb_active
& (MB_COMD
|MB_MULT
)) == MB_MULT
)
355 if (options
& OPT_VERBOSE
)
356 fputs(pop3buf
, stderr
);
360 mp
->mb_active
&= ~MB_COMD
;
364 mp
->mb_active
= MB_NONE
;
365 fprintf(stderr
, tr(218, "POP3 error: %s"), pop3buf
);
369 * If the answer starts neither with '+' nor with
370 * '-', it must be part of a multiline response,
371 * e. g. because the user interrupted a file
372 * download. Get lines until a single dot appears.
374 multiline
: while (pop3buf
[0] != '.' || pop3buf
[1] != '\r' ||
375 pop3buf
[2] != '\n' ||
376 pop3buf
[3] != '\0') {
377 sz
= sgetline(&pop3buf
, &pop3bufsize
,
382 mp
->mb_active
&= ~MB_MULT
;
383 if (mp
->mb_active
!= MB_NONE
)
388 mp
->mb_active
= MB_NONE
;
394 pop3_finish(struct mailbox
*mp
)
396 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
!= MB_NONE
)
397 (void)pop3_answer(mp
);
404 termios_state_reset();
407 fprintf(stderr
, tr(102, "Interrupt\n"));
408 siglongjmp(pop3jmp
, 1);
411 fprintf(stderr
, "Received SIGPIPE during POP3 operation\n");
420 if (interrupts
++ == 0) {
421 fprintf(stderr
, tr(102, "Interrupt\n"));
428 pop3_noop1(struct mailbox
*mp
)
430 POP3_OUT("NOOP\r\n", MB_COMD
)
439 sighandler_type saveint
, savepipe
;
445 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
446 safe_signal(SIGINT
, maincatch
);
447 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
448 if (sigsetjmp(pop3jmp
, 1) == 0) {
449 if (savepipe
!= SIG_IGN
)
450 safe_signal(SIGPIPE
, pop3catch
);
451 ok
= pop3_noop1(&mb
);
453 safe_signal(SIGINT
, saveint
);
454 safe_signal(SIGPIPE
, savepipe
);
463 sighandler_type saveint
;
464 sighandler_type savepipe
;
467 if (pop3lock
++ == 0) {
468 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
469 safe_signal(SIGINT
, maincatch
);
470 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
471 if (sigsetjmp(pop3jmp
, 1)) {
472 safe_signal(SIGINT
, saveint
);
473 safe_signal(SIGPIPE
, savepipe
);
476 if (savepipe
!= SIG_IGN
)
477 safe_signal(SIGPIPE
, pop3catch
);
478 if (pop3_noop1(&mb
) != OKAY
) {
479 safe_signal(SIGINT
, saveint
);
480 safe_signal(SIGPIPE
, savepipe
);
483 safe_signal(SIGINT
, saveint
);
484 safe_signal(SIGPIPE
, savepipe
);
486 brk
: alarm(pop3keepalive
);
491 pop3_stat(struct mailbox
*mp
, off_t
*size
, int *cnt
)
496 POP3_OUT("STAT\r\n", MB_COMD
)
498 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
499 while (*cp
&& spacechar(*cp
& 0377))
502 *cnt
= (int)strtol(cp
, NULL
, 10);
503 while (*cp
&& !spacechar(*cp
& 0377))
505 while (*cp
&& spacechar(*cp
& 0377))
508 *size
= (int)strtol(cp
, NULL
, 10);
514 fprintf(stderr
, tr(260, "invalid POP3 STAT response: %s\n"),
520 pop3_list(struct mailbox
*mp
, int n
, size_t *size
)
522 char o
[LINESIZE
], *cp
;
524 snprintf(o
, sizeof o
, "LIST %u\r\n", n
);
527 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
528 while (*cp
&& spacechar(*cp
& 0377))
530 while (*cp
&& !spacechar(*cp
& 0377))
532 while (*cp
&& spacechar(*cp
& 0377))
535 *size
= (size_t)strtol(cp
, NULL
, 10);
542 pop3_init(struct mailbox
*mp
, int n
)
544 struct message
*m
= &message
[n
];
547 m
->m_flag
= MUSED
|MNEW
|MNOFROM
|MNEWEST
;
550 pop3_list(mp
, m
- message
+ 1, &m
->m_xsize
);
551 if ((cp
= hfield1("status", m
)) != NULL
) {
552 while (*cp
!= '\0') {
564 pop3_dates(struct mailbox
*mp
)
569 for (i
= 0; i
< msgCount
; i
++)
570 substdate(&message
[i
]);
574 pop3_setptr(struct mailbox
*mp
)
578 message
= scalloc(msgCount
+ 1, sizeof *message
);
579 for (i
= 0; i
< msgCount
; i
++)
582 message
[msgCount
].m_size
= 0;
583 message
[msgCount
].m_lines
= 0;
588 pop3_setfile(const char *server
, int nmail
, int isedit
)
591 sighandler_type saveint
;
592 sighandler_type savepipe
;
593 char *volatile user
, *pass
;
594 const char *cp
, *uhp
, *volatile sp
= server
;
599 if (strncmp(sp
, "pop3://", 7) == 0) {
603 } else if (strncmp(sp
, "pop3s://", 8) == 0) {
609 pass
= lookup_password_for_token(uhp
);
610 if ((cp
= last_at_before_slash(sp
)) != NULL
) {
611 user
= salloc(cp
- sp
+ 1);
612 memcpy(user
, sp
, cp
- sp
);
613 user
[cp
- sp
] = '\0';
615 user
= urlxdec(user
);
618 if (sopen(sp
, &so
, use_ssl
, uhp
, use_ssl
? "pop3s" : "pop3",
619 (options
& OPT_VERBOSE
) != 0) != OKAY
) {
623 edit
= (isedit
!= 0);
624 if (mb
.mb_sock
.s_fd
>= 0)
635 mb
.mb_type
= MB_VOID
;
638 saveint
= safe_signal(SIGINT
, SIG_IGN
);
639 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
640 if (sigsetjmp(pop3jmp
, 1)) {
642 safe_signal(SIGINT
, saveint
);
643 safe_signal(SIGPIPE
, savepipe
);
647 if (saveint
!= SIG_IGN
)
648 safe_signal(SIGINT
, pop3catch
);
649 if (savepipe
!= SIG_IGN
)
650 safe_signal(SIGPIPE
, pop3catch
);
651 if ((cp
= value("pop3-keepalive")) != NULL
) {
652 if ((pop3keepalive
= strtol(cp
, NULL
, 10)) > 0) {
653 savealrm
= safe_signal(SIGALRM
, pop3alarm
);
654 alarm(pop3keepalive
);
657 mb
.mb_sock
.s_desc
= "POP3";
658 mb
.mb_sock
.s_onclose
= pop3_timer_off
;
659 if (_pop3_login(&mb
, user
, pass
, uhp
, sp
) != OKAY
||
660 pop3_stat(&mb
, &mailsize
, &msgCount
) != OKAY
) {
663 safe_signal(SIGINT
, saveint
);
664 safe_signal(SIGPIPE
, savepipe
);
668 mb
.mb_type
= MB_POP3
;
669 mb
.mb_perm
= (options
& OPT_R_FLAG
) ? 0 : MB_DELE
;
673 safe_signal(SIGINT
, saveint
);
674 safe_signal(SIGPIPE
, savepipe
);
676 if (!edit
&& msgCount
== 0) {
677 if (mb
.mb_type
== MB_POP3
&& value("emptystart") == NULL
)
678 fprintf(stderr
, tr(258, "No mail at %s\n"), server
);
685 pop3_get(struct mailbox
*mp
, struct message
*m
, enum needspec
volatile need
)
687 sighandler_type
volatile saveint
= SIG_IGN
, savepipe
= SIG_IGN
;
689 char o
[LINESIZE
], *line
= NULL
, *lp
;
690 size_t linesize
= 0, linelen
, size
;
691 int number
= m
- message
+ 1, emptyline
= 0, lines
;
698 if (mp
->mb_sock
.s_fd
< 0) {
699 fprintf(stderr
, tr(219, "POP3 connection already closed.\n"));
702 if (pop3lock
++ == 0) {
703 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
704 safe_signal(SIGINT
, maincatch
);
705 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
706 if (sigsetjmp(pop3jmp
, 1)) {
707 safe_signal(SIGINT
, saveint
);
708 safe_signal(SIGPIPE
, savepipe
);
712 if (savepipe
!= SIG_IGN
)
713 safe_signal(SIGPIPE
, pop3catch
);
715 fseek(mp
->mb_otf
, 0L, SEEK_END
);
716 offset
= ftell(mp
->mb_otf
);
717 retry
: switch (need
) {
719 snprintf(o
, sizeof o
, "TOP %u 0\r\n", number
);
722 snprintf(o
, sizeof o
, "RETR %u\r\n", number
);
727 POP3_OUT(o
, MB_COMD
|MB_MULT
)
728 if (pop3_answer(mp
) == STOP
) {
729 if (need
== NEED_HEADER
) {
731 * The TOP POP3 command is optional, so retry
732 * with the entire message.
743 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
744 if (line
[0] == '.' && line
[1] == '\r' && line
[2] == '\n' &&
746 mp
->mb_active
&= ~MB_MULT
;
749 if (line
[0] == '.') {
755 * Need to mask 'From ' lines. This cannot be done properly
756 * since some servers pass them as 'From ' and others as
757 * '>From '. Although one could identify the first kind of
758 * server in principle, it is not possible to identify the
759 * second as '>From ' may also come from a server of the
760 * first type as actual data. So do what is absolutely
761 * necessary only - mask 'From '.
763 * If the line is the first line of the message header, it
764 * is likely a real 'From ' line. In this case, it is just
765 * ignored since it violates all standards.
766 * TODO i have *never* seen the latter?!?!?
770 * Since we simply copy over data without doing any transfer
771 * encoding reclassification/adjustment we *have* to perform
772 * RFC 4155 compliant From_ quoting here
774 if (is_head(lp
, linelen
)) {
777 fputc('>', mp
->mb_otf
);
781 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
782 lp
[linelen
-2] == '\r')) {
783 emptyline
= linelen
<= 2;
785 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
786 fputc('\n', mp
->mb_otf
);
790 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
796 * This is very ugly; but some POP3 daemons don't end a
797 * message with \r\n\r\n, and we need \n\n for mbox format.
799 fputc('\n', mp
->mb_otf
);
805 m
->m_block
= mailx_blockof(offset
);
806 m
->m_offset
= mailx_offsetof(offset
);
810 m
->m_have
|= HAVE_HEADER
;
813 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
814 m
->m_xlines
= m
->m_lines
;
815 m
->m_xsize
= m
->m_size
;
822 if (saveint
!= SIG_IGN
)
823 safe_signal(SIGINT
, saveint
);
824 if (savepipe
!= SIG_IGN
)
825 safe_signal(SIGPIPE
, savepipe
);
833 pop3_header(struct message
*m
)
835 return pop3_get(&mb
, m
,
836 boption("pop3-bulk-load") ? NEED_BODY
: NEED_HEADER
);
840 pop3_body(struct message
*m
)
842 return pop3_get(&mb
, m
, NEED_BODY
);
846 pop3_exit(struct mailbox
*mp
)
848 POP3_OUT("QUIT\r\n", MB_COMD
)
854 pop3_delete(struct mailbox
*mp
, int n
)
858 snprintf(o
, sizeof o
, "DELE %u\r\n", n
);
865 pop3_update(struct mailbox
*mp
)
868 int dodel
, c
, gotcha
, held
;
872 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
873 if (m
->m_flag
& MBOX
)
879 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
881 dodel
= m
->m_flag
& MDELETED
;
883 dodel
= !((m
->m_flag
&MPRESERVE
) ||
884 (m
->m_flag
&MTOUCH
) == 0);
887 pop3_delete(mp
, m
- message
+ 1);
892 if (gotcha
&& edit
) {
893 printf(tr(168, "\"%s\" "), displayname
);
894 printf((value("bsdcompat") || value("bsdmsgs"))
895 ? tr(170, "complete\n") : tr(212, "updated.\n"));
896 } else if (held
&& !edit
) {
898 printf(tr(155, "Held 1 message in %s\n"), displayname
);
900 printf(tr(156, "Held %d messages in %s\n"), held
,
910 sighandler_type saveint
;
911 sighandler_type savepipe
;
913 if (mb
.mb_sock
.s_fd
< 0) {
914 fprintf(stderr
, tr(219,
915 "POP3 connection already closed.\n"));
919 saveint
= safe_signal(SIGINT
, SIG_IGN
);
920 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
921 if (sigsetjmp(pop3jmp
, 1)) {
922 safe_signal(SIGINT
, saveint
);
923 safe_signal(SIGPIPE
, saveint
);
927 if (saveint
!= SIG_IGN
)
928 safe_signal(SIGINT
, pop3catch
);
929 if (savepipe
!= SIG_IGN
)
930 safe_signal(SIGPIPE
, pop3catch
);
934 safe_signal(SIGINT
, saveint
);
935 safe_signal(SIGPIPE
, savepipe
);
938 #endif /* HAVE_POP3 */