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
62 #define POP3_ANSWER() if (pop3_answer(mp) == STOP) \
64 #define POP3_OUT(x, y) if (pop3_finish(mp) == STOP) \
67 fprintf(stderr, ">>> %s", x); \
68 mp->mb_active |= (y); \
69 if (swrite(&mp->mb_sock, x) == STOP) \
73 static size_t pop3bufsize
;
74 static sigjmp_buf pop3jmp
;
75 static sighandler_type savealrm
;
77 static struct termios otio
;
78 static int pop3keepalive
;
79 static volatile int pop3lock
;
81 static void pop3_timer_off(void);
82 static enum okay
pop3_answer(struct mailbox
*mp
);
83 static enum okay
pop3_finish(struct mailbox
*mp
);
84 static void pop3catch(int s
);
85 static void maincatch(int s
);
86 static enum okay
pop3_noop1(struct mailbox
*mp
);
87 static void pop3alarm(int s
);
88 static enum okay
pop3_pass(struct mailbox
*mp
, const char *pass
);
90 static char *pop3_find_timestamp(const char *bp
);
91 static enum okay
pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
,
93 static enum okay
pop3_apop1(struct mailbox
*mp
,
94 const char *user
, const char *xp
);
95 static int pop3_use_apop(const char *uhp
);
97 static int pop3_use_starttls(const char *uhp
);
98 static enum okay
pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
99 const char *uhp
, const char *xserver
);
100 static enum okay
pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
);
101 static enum okay
pop3_list(struct mailbox
*mp
, int n
, size_t *size
);
102 static void pop3_init(struct mailbox
*mp
, int n
);
103 static void pop3_dates(struct mailbox
*mp
);
104 static void pop3_setptr(struct mailbox
*mp
);
105 static char *pop3_have_password(const char *server
);
106 static enum okay
pop3_get(struct mailbox
*mp
, struct message
*m
,
108 static enum okay
pop3_exit(struct mailbox
*mp
);
109 static enum okay
pop3_delete(struct mailbox
*mp
, int n
);
110 static enum okay
pop3_update(struct mailbox
*mp
);
115 if (pop3keepalive
> 0) {
117 safe_signal(SIGALRM
, savealrm
);
122 pop3_answer(struct mailbox
*mp
)
127 retry
: if ((sz
= sgetline(&pop3buf
, &pop3bufsize
, NULL
, &mp
->mb_sock
)) > 0) {
128 if ((mp
->mb_active
& (MB_COMD
|MB_MULT
)) == MB_MULT
)
131 fputs(pop3buf
, stderr
);
135 mp
->mb_active
&= ~MB_COMD
;
139 mp
->mb_active
= MB_NONE
;
140 fprintf(stderr
, catgets(catd
, CATSET
, 218,
141 "POP3 error: %s"), pop3buf
);
145 * If the answer starts neither with '+' nor with
146 * '-', it must be part of a multiline response,
147 * e. g. because the user interrupted a file
148 * download. Get lines until a single dot appears.
150 multiline
: while (pop3buf
[0] != '.' || pop3buf
[1] != '\r' ||
151 pop3buf
[2] != '\n' ||
152 pop3buf
[3] != '\0') {
153 sz
= sgetline(&pop3buf
, &pop3bufsize
,
158 mp
->mb_active
&= ~MB_MULT
;
159 if (mp
->mb_active
!= MB_NONE
)
164 mp
->mb_active
= MB_NONE
;
170 pop3_finish(struct mailbox
*mp
)
172 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
!= MB_NONE
)
181 tcsetattr(0, TCSADRAIN
, &otio
);
184 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
185 siglongjmp(pop3jmp
, 1);
188 fprintf(stderr
, "Received SIGPIPE during POP3 operation\n");
197 if (interrupts
++ == 0) {
198 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
205 pop3_noop1(struct mailbox
*mp
)
207 POP3_OUT("NOOP\r\n", MB_COMD
)
216 sighandler_type saveint
, savepipe
;
221 verbose
= value("verbose") != NULL
;
223 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
224 safe_signal(SIGINT
, maincatch
);
225 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
226 if (sigsetjmp(pop3jmp
, 1) == 0) {
227 if (savepipe
!= SIG_IGN
)
228 safe_signal(SIGPIPE
, pop3catch
);
229 ok
= pop3_noop1(&mb
);
231 safe_signal(SIGINT
, saveint
);
232 safe_signal(SIGPIPE
, savepipe
);
241 sighandler_type saveint
;
242 sighandler_type savepipe
;
245 if (pop3lock
++ == 0) {
246 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
247 safe_signal(SIGINT
, maincatch
);
248 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
249 if (sigsetjmp(pop3jmp
, 1)) {
250 safe_signal(SIGINT
, saveint
);
251 safe_signal(SIGPIPE
, savepipe
);
254 if (savepipe
!= SIG_IGN
)
255 safe_signal(SIGPIPE
, pop3catch
);
256 if (pop3_noop1(&mb
) != OKAY
) {
257 safe_signal(SIGINT
, saveint
);
258 safe_signal(SIGPIPE
, savepipe
);
261 safe_signal(SIGINT
, saveint
);
262 safe_signal(SIGPIPE
, savepipe
);
264 brk
: alarm(pop3keepalive
);
269 pop3_pass(struct mailbox
*mp
, const char *pass
)
273 snprintf(o
, sizeof o
, "PASS %s\r\n", pass
);
281 pop3_find_timestamp(const char *bp
)
287 if ((cp
= strchr(bp
, '<')) == NULL
)
289 for (ep
= cp
; *ep
; ep
++) {
290 if (spacechar(*ep
&0377))
294 else if (*ep
== '>') {
302 rp
= salloc(ep
- cp
+ 2);
303 memcpy(rp
, cp
, ep
- cp
+ 1);
304 rp
[ep
- cp
+ 1] = '\0';
309 pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
, const char *ts
)
311 char *user
, *catp
, *xp
;
312 unsigned char digest
[16];
315 retry
: if (xuser
== NULL
) {
316 if ((user
= getuser()) == NULL
)
321 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
324 catp
= savecat(ts
, pass
);
326 MD5Update(&ctx
, (unsigned char *)catp
, strlen(catp
));
327 MD5Final(digest
, &ctx
);
328 xp
= md5tohex(digest
);
329 if (pop3_apop1(mp
, user
, xp
) == STOP
) {
337 pop3_apop1(struct mailbox
*mp
, const char *user
, const char *xp
)
341 snprintf(o
, sizeof o
, "APOP %s %s\r\n", user
, xp
);
348 pop3_use_apop(const char *uhp
)
352 if (value("pop3-use-apop"))
354 var
= savecat("pop3-use-apop-", uhp
);
355 return value(var
) != NULL
;
360 pop3_use_starttls(const char *uhp
)
364 if (value("pop3-use-starttls"))
366 var
= savecat("pop3-use-starttls-", uhp
);
367 return value(var
) != NULL
;
371 pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
372 const char *uhp
, const char *xserver
)
374 char o
[LINESIZE
], *user
, *server
, *cp
;
381 if (pop3_use_apop(uhp
)) {
382 if ((ts
= pop3_find_timestamp(pop3buf
)) == NULL
) {
383 fprintf(stderr
, tr(276,
384 "Could not determine timestamp from "
385 "server greeting. Can't use APOP.\n"));
390 if ((cp
= strchr(xserver
, ':')) != NULL
) {
391 server
= salloc(cp
- xserver
+ 1);
392 memcpy(server
, xserver
, cp
- xserver
);
393 server
[cp
- xserver
] = '\0';
395 server
= (char *)xserver
;
397 if (mp
->mb_sock
.s_use_ssl
== 0 && pop3_use_starttls(uhp
)) {
398 POP3_OUT("STLS\r\n", MB_COMD
)
400 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
404 if (pop3_use_starttls(uhp
)) {
405 fprintf(stderr
, "No SSL support compiled in.\n");
408 #endif /* !USE_SSL */
411 return pop3_apop(mp
, xuser
, pass
, ts
);
413 retry
: if (xuser
== NULL
) {
414 if ((user
= getuser()) == NULL
)
418 snprintf(o
, sizeof o
, "USER %s\r\n", user
);
422 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
425 if (pop3_pass(mp
, pass
) == STOP
) {
433 pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
)
438 POP3_OUT("STAT\r\n", MB_COMD
);
440 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
441 while (*cp
&& spacechar(*cp
& 0377))
444 *count
= (int)strtol(cp
, NULL
, 10);
445 while (*cp
&& !spacechar(*cp
& 0377))
447 while (*cp
&& spacechar(*cp
& 0377))
450 *size
= (int)strtol(cp
, NULL
, 10);
456 fprintf(stderr
, catgets(catd
, CATSET
, 260,
457 "invalid POP3 STAT response: %s\n"), pop3buf
);
462 pop3_list(struct mailbox
*mp
, int n
, size_t *size
)
464 char o
[LINESIZE
], *cp
;
466 snprintf(o
, sizeof o
, "LIST %u\r\n", n
);
469 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
470 while (*cp
&& spacechar(*cp
& 0377))
472 while (*cp
&& !spacechar(*cp
& 0377))
474 while (*cp
&& spacechar(*cp
& 0377))
477 *size
= (size_t)strtol(cp
, NULL
, 10);
484 pop3_init(struct mailbox
*mp
, int n
)
486 struct message
*m
= &message
[n
];
489 m
->m_flag
= MUSED
|MNEW
|MNOFROM
|MNEWEST
;
492 pop3_list(mp
, m
- message
+ 1, &m
->m_xsize
);
493 if ((cp
= hfield1("status", m
)) != NULL
) {
494 while (*cp
!= '\0') {
506 pop3_dates(struct mailbox
*mp
)
511 for (i
= 0; i
< msgCount
; i
++)
512 substdate(&message
[i
]);
516 pop3_setptr(struct mailbox
*mp
)
520 message
= scalloc(msgCount
+ 1, sizeof *message
);
521 for (i
= 0; i
< msgCount
; i
++)
524 message
[msgCount
].m_size
= 0;
525 message
[msgCount
].m_lines
= 0;
530 pop3_have_password(const char *server
)
534 var
= ac_alloc(strlen(server
) + 10);
535 strcpy(var
, "password-");
536 strcpy(&var
[9], server
);
537 if ((cp
= value(var
)) != NULL
)
544 pop3_setfile(const char *server
, int newmail
, int isedit
)
547 sighandler_type saveint
;
548 sighandler_type savepipe
;
550 const char *cp
, *uhp
, *volatile pass
, *volatile sp
= server
;
555 if (strncmp(sp
, "pop3://", 7) == 0) {
559 } else if (strncmp(sp
, "pop3s://", 8) == 0) {
565 pass
= pop3_have_password(uhp
);
566 if ((cp
= last_at_before_slash(sp
)) != NULL
) {
567 user
= salloc(cp
- sp
+ 1);
568 memcpy(user
, sp
, cp
- sp
);
569 user
[cp
- sp
] = '\0';
574 verbose
= value("verbose") != NULL
;
575 if (sopen(sp
, &so
, use_ssl
, uhp
, use_ssl
? "pop3s" : "pop3",
581 if (mb
.mb_sock
.s_fd
>= 0)
592 mb
.mb_type
= MB_VOID
;
595 saveint
= safe_signal(SIGINT
, SIG_IGN
);
596 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
597 if (sigsetjmp(pop3jmp
, 1)) {
599 safe_signal(SIGINT
, saveint
);
600 safe_signal(SIGPIPE
, savepipe
);
604 if (saveint
!= SIG_IGN
)
605 safe_signal(SIGINT
, pop3catch
);
606 if (savepipe
!= SIG_IGN
)
607 safe_signal(SIGPIPE
, pop3catch
);
608 if ((cp
= value("pop3-keepalive")) != NULL
) {
609 if ((pop3keepalive
= strtol(cp
, NULL
, 10)) > 0) {
610 savealrm
= safe_signal(SIGALRM
, pop3alarm
);
611 alarm(pop3keepalive
);
614 mb
.mb_sock
.s_desc
= "POP3";
615 mb
.mb_sock
.s_onclose
= pop3_timer_off
;
616 if (pop3_user(&mb
, user
, pass
, uhp
, sp
) != OKAY
||
617 pop3_stat(&mb
, &mailsize
, &msgCount
) != OKAY
) {
620 safe_signal(SIGINT
, saveint
);
621 safe_signal(SIGPIPE
, savepipe
);
625 mb
.mb_type
= MB_POP3
;
626 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
630 safe_signal(SIGINT
, saveint
);
631 safe_signal(SIGPIPE
, savepipe
);
633 if (!edit
&& msgCount
== 0) {
634 if (mb
.mb_type
== MB_POP3
&& value("emptystart") == NULL
)
635 fprintf(stderr
, catgets(catd
, CATSET
, 258,
636 "No mail at %s\n"), server
);
643 pop3_get(struct mailbox
*mp
, struct message
*m
, enum needspec
volatile need
)
645 sighandler_type
volatile saveint
= SIG_IGN
, savepipe
= SIG_IGN
;
647 char o
[LINESIZE
], *line
= NULL
, *lp
;
648 size_t linesize
= 0, linelen
, size
;
649 int number
= m
- message
+ 1, emptyline
= 0, lines
;
656 verbose
= value("verbose") != NULL
;
657 if (mp
->mb_sock
.s_fd
< 0) {
658 fprintf(stderr
, catgets(catd
, CATSET
, 219,
659 "POP3 connection already closed.\n"));
662 if (pop3lock
++ == 0) {
663 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
664 safe_signal(SIGINT
, maincatch
);
665 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
666 if (sigsetjmp(pop3jmp
, 1)) {
667 safe_signal(SIGINT
, saveint
);
668 safe_signal(SIGPIPE
, savepipe
);
672 if (savepipe
!= SIG_IGN
)
673 safe_signal(SIGPIPE
, pop3catch
);
675 fseek(mp
->mb_otf
, 0L, SEEK_END
);
676 offset
= ftell(mp
->mb_otf
);
677 retry
: switch (need
) {
679 snprintf(o
, sizeof o
, "TOP %u 0\r\n", number
);
682 snprintf(o
, sizeof o
, "RETR %u\r\n", number
);
687 POP3_OUT(o
, MB_COMD
|MB_MULT
)
688 if (pop3_answer(mp
) == STOP
) {
689 if (need
== NEED_HEADER
) {
691 * The TOP POP3 command is optional, so retry
692 * with the entire message.
703 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
704 if (line
[0] == '.' && line
[1] == '\r' && line
[2] == '\n' &&
706 mp
->mb_active
&= ~MB_MULT
;
709 if (line
[0] == '.') {
715 * Need to mask 'From ' lines. This cannot be done properly
716 * since some servers pass them as 'From ' and others as
717 * '>From '. Although one could identify the first kind of
718 * server in principle, it is not possible to identify the
719 * second as '>From ' may also come from a server of the
720 * first type as actual data. So do what is absolutely
721 * necessary only - mask 'From '.
723 * If the line is the first line of the message header, it
724 * is likely a real 'From ' line. In this case, it is just
725 * ignored since it violates all standards.
727 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
728 lp
[3] == 'm' && lp
[4] == ' ') {
730 fputc('>', mp
->mb_otf
);
736 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
737 lp
[linelen
-2] == '\r')) {
738 emptyline
= linelen
<= 2;
740 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
741 fputc('\n', mp
->mb_otf
);
745 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
751 * This is very ugly; but some POP3 daemons don't end a
752 * message with \r\n\r\n, and we need \n\n for mbox format.
754 fputc('\n', mp
->mb_otf
);
760 m
->m_block
= mailx_blockof(offset
);
761 m
->m_offset
= mailx_offsetof(offset
);
765 m
->m_have
|= HAVE_HEADER
;
768 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
769 m
->m_xlines
= m
->m_lines
;
770 m
->m_xsize
= m
->m_size
;
777 if (saveint
!= SIG_IGN
)
778 safe_signal(SIGINT
, saveint
);
779 if (savepipe
!= SIG_IGN
)
780 safe_signal(SIGPIPE
, savepipe
);
788 pop3_header(struct message
*m
)
790 return pop3_get(&mb
, m
, NEED_HEADER
);
795 pop3_body(struct message
*m
)
797 return pop3_get(&mb
, m
, NEED_BODY
);
801 pop3_exit(struct mailbox
*mp
)
803 POP3_OUT("QUIT\r\n", MB_COMD
)
809 pop3_delete(struct mailbox
*mp
, int n
)
813 snprintf(o
, sizeof o
, "DELE %u\r\n", n
);
820 pop3_update(struct mailbox
*mp
)
822 FILE *readstat
= NULL
;
824 int dodel
, c
, gotcha
, held
;
827 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
832 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
833 if (m
->m_flag
& MBOX
)
839 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
840 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
843 if ((id
= hfield1("message-id", m
)) != NULL
||
844 (id
= hfieldX("article-id", m
)) != NULL
)
845 fprintf(readstat
, "%s\n", id
);
848 dodel
= m
->m_flag
& MDELETED
;
850 dodel
= !((m
->m_flag
&MPRESERVE
) ||
851 (m
->m_flag
&MTOUCH
) == 0);
854 pop3_delete(mp
, m
- message
+ 1);
859 if (readstat
!= NULL
)
861 if (gotcha
&& edit
) {
862 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
863 printf(value("bsdcompat") || value("bsdmsgs") ?
864 catgets(catd
, CATSET
, 170, "complete\n") :
865 catgets(catd
, CATSET
, 212, "updated.\n"));
866 } else if (held
&& !edit
) {
868 printf(catgets(catd
, CATSET
, 155,
869 "Held 1 message in %s\n"), mailname
);
871 printf(catgets(catd
, CATSET
, 156,
872 "Held %d messages in %s\n"), held
, mailname
);
881 sighandler_type saveint
;
882 sighandler_type savepipe
;
884 verbose
= value("verbose") != NULL
;
885 if (mb
.mb_sock
.s_fd
< 0) {
886 fprintf(stderr
, catgets(catd
, CATSET
, 219,
887 "POP3 connection already closed.\n"));
891 saveint
= safe_signal(SIGINT
, SIG_IGN
);
892 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
893 if (sigsetjmp(pop3jmp
, 1)) {
894 safe_signal(SIGINT
, saveint
);
895 safe_signal(SIGPIPE
, saveint
);
899 if (saveint
!= SIG_IGN
)
900 safe_signal(SIGINT
, pop3catch
);
901 if (savepipe
!= SIG_IGN
)
902 safe_signal(SIGPIPE
, pop3catch
);
906 safe_signal(SIGINT
, saveint
);
907 safe_signal(SIGPIPE
, savepipe
);
910 #else /* !USE_POP3 */
914 fprintf(stderr
, catgets(catd
, CATSET
, 216,
915 "No POP3 support compiled in.\n"));
919 pop3_setfile(const char *server
, int newmail
, int isedit
)
929 pop3_header(struct message
*mp
)
937 pop3_body(struct message
*mp
)
956 #endif /* USE_POP3 */