2 * Heirloom mailx - 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
42 static char sccsid
[] = "@(#)pop3.c 2.43 (gritter) 3/4/06";
58 * Mail -- a mail program
63 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
64 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
65 * TODO may leave child running if fdopen() fails! */
70 #define POP3_ANSWER() if (pop3_answer(mp) == STOP) \
72 #define POP3_OUT(x, y) if (pop3_finish(mp) == STOP) \
75 fprintf(stderr, ">>> %s", x); \
76 mp->mb_active |= (y); \
77 if (swrite(&mp->mb_sock, x) == STOP) \
81 static size_t pop3bufsize
;
82 static sigjmp_buf pop3jmp
;
83 static sighandler_type savealrm
;
85 static struct termios otio
;
86 static int pop3keepalive
;
87 static volatile int pop3lock
;
89 static void pop3_timer_off(void);
90 static enum okay
pop3_answer(struct mailbox
*mp
);
91 static enum okay
pop3_finish(struct mailbox
*mp
);
92 static void pop3catch(int s
);
93 static void maincatch(int s
);
94 static enum okay
pop3_noop1(struct mailbox
*mp
);
95 static void pop3alarm(int s
);
96 static enum okay
pop3_pass(struct mailbox
*mp
, const char *pass
);
97 static char *pop3_find_timestamp(const char *bp
);
98 static enum okay
pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
,
100 static enum okay
pop3_apop1(struct mailbox
*mp
,
101 const char *user
, const char *xp
);
102 static int pop3_use_starttls(const char *uhp
);
103 static int pop3_use_apop(const char *uhp
);
104 static enum okay
pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
105 const char *uhp
, const char *xserver
);
106 static enum okay
pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
);
107 static enum okay
pop3_list(struct mailbox
*mp
, int n
, size_t *size
);
108 static void pop3_init(struct mailbox
*mp
, int n
);
109 static void pop3_dates(struct mailbox
*mp
);
110 static void pop3_setptr(struct mailbox
*mp
);
111 static char *pop3_have_password(const char *server
);
112 static enum okay
pop3_get(struct mailbox
*mp
, struct message
*m
,
114 static enum okay
pop3_exit(struct mailbox
*mp
);
115 static enum okay
pop3_delete(struct mailbox
*mp
, int n
);
116 static enum okay
pop3_update(struct mailbox
*mp
);
121 if (pop3keepalive
> 0) {
123 safe_signal(SIGALRM
, savealrm
);
128 pop3_answer(struct mailbox
*mp
)
133 retry
: if ((sz
= sgetline(&pop3buf
, &pop3bufsize
, NULL
, &mp
->mb_sock
)) > 0) {
134 if ((mp
->mb_active
& (MB_COMD
|MB_MULT
)) == MB_MULT
)
137 fputs(pop3buf
, stderr
);
141 mp
->mb_active
&= ~MB_COMD
;
145 mp
->mb_active
= MB_NONE
;
146 fprintf(stderr
, catgets(catd
, CATSET
, 218,
147 "POP3 error: %s"), pop3buf
);
151 * If the answer starts neither with '+' nor with
152 * '-', it must be part of a multiline response,
153 * e. g. because the user interrupted a file
154 * download. Get lines until a single dot appears.
156 multiline
: while (pop3buf
[0] != '.' || pop3buf
[1] != '\r' ||
157 pop3buf
[2] != '\n' ||
158 pop3buf
[3] != '\0') {
159 sz
= sgetline(&pop3buf
, &pop3bufsize
,
164 mp
->mb_active
&= ~MB_MULT
;
165 if (mp
->mb_active
!= MB_NONE
)
170 mp
->mb_active
= MB_NONE
;
176 pop3_finish(struct mailbox
*mp
)
178 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
!= MB_NONE
)
187 tcsetattr(0, TCSADRAIN
, &otio
);
190 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
191 siglongjmp(pop3jmp
, 1);
194 fprintf(stderr
, "Received SIGPIPE during POP3 operation\n");
203 if (interrupts
++ == 0) {
204 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
211 pop3_noop1(struct mailbox
*mp
)
213 POP3_OUT("NOOP\r\n", MB_COMD
)
222 sighandler_type saveint
, savepipe
;
227 verbose
= value("verbose") != NULL
;
229 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
230 safe_signal(SIGINT
, maincatch
);
231 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
232 if (sigsetjmp(pop3jmp
, 1) == 0) {
233 if (savepipe
!= SIG_IGN
)
234 safe_signal(SIGPIPE
, pop3catch
);
235 ok
= pop3_noop1(&mb
);
237 safe_signal(SIGINT
, saveint
);
238 safe_signal(SIGPIPE
, savepipe
);
247 sighandler_type saveint
;
248 sighandler_type savepipe
;
251 if (pop3lock
++ == 0) {
252 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
253 safe_signal(SIGINT
, maincatch
);
254 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
255 if (sigsetjmp(pop3jmp
, 1)) {
256 safe_signal(SIGINT
, saveint
);
257 safe_signal(SIGPIPE
, savepipe
);
260 if (savepipe
!= SIG_IGN
)
261 safe_signal(SIGPIPE
, pop3catch
);
262 if (pop3_noop1(&mb
) != OKAY
) {
263 safe_signal(SIGINT
, saveint
);
264 safe_signal(SIGPIPE
, savepipe
);
267 safe_signal(SIGINT
, saveint
);
268 safe_signal(SIGPIPE
, savepipe
);
270 brk
: alarm(pop3keepalive
);
275 pop3_pass(struct mailbox
*mp
, const char *pass
)
279 snprintf(o
, sizeof o
, "PASS %s\r\n", pass
);
286 pop3_find_timestamp(const char *bp
)
292 if ((cp
= strchr(bp
, '<')) == NULL
)
294 for (ep
= cp
; *ep
; ep
++) {
295 if (spacechar(*ep
&0377))
299 else if (*ep
== '>') {
307 rp
= salloc(ep
- cp
+ 2);
308 memcpy(rp
, cp
, ep
- cp
+ 1);
309 rp
[ep
- cp
+ 1] = '\0';
314 pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
, const char *ts
)
316 char *user
, *catp
, *xp
;
317 unsigned char digest
[16];
320 retry
: if (xuser
== NULL
) {
321 if ((user
= getuser()) == NULL
)
326 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
329 catp
= savecat(ts
, pass
);
331 MD5Update(&ctx
, (unsigned char *)catp
, strlen(catp
));
332 MD5Final(digest
, &ctx
);
333 xp
= md5tohex(digest
);
334 if (pop3_apop1(mp
, user
, xp
) == STOP
) {
342 pop3_apop1(struct mailbox
*mp
, const char *user
, const char *xp
)
346 snprintf(o
, sizeof o
, "APOP %s %s\r\n", user
, xp
);
353 pop3_use_starttls(const char *uhp
)
357 if (value("pop3-use-starttls"))
359 var
= savecat("pop3-use-starttls-", uhp
);
360 return value(var
) != NULL
;
364 pop3_use_apop(const char *uhp
)
368 if (value("pop3-use-apop"))
370 var
= savecat("pop3-use-apop-", 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
, *ts
= NULL
, *server
, *cp
;
381 if (pop3_use_apop(uhp
)) {
382 if ((ts
= pop3_find_timestamp(pop3buf
)) == NULL
) {
383 fprintf(stderr
, "Could not determine timestamp from "
384 "server greeting. Impossible to use APOP.\n");
388 if ((cp
= strchr(xserver
, ':')) != NULL
) {
389 server
= salloc(cp
- xserver
+ 1);
390 memcpy(server
, xserver
, cp
- xserver
);
391 server
[cp
- xserver
] = '\0';
393 server
= (char *)xserver
;
395 if (mp
->mb_sock
.s_use_ssl
== 0 && pop3_use_starttls(uhp
)) {
396 POP3_OUT("STLS\r\n", MB_COMD
)
398 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
402 if (pop3_use_starttls(uhp
)) {
403 fprintf(stderr
, "No SSL support compiled in.\n");
406 #endif /* !USE_SSL */
408 return pop3_apop(mp
, xuser
, pass
, ts
);
409 retry
: if (xuser
== NULL
) {
410 if ((user
= getuser()) == NULL
)
414 snprintf(o
, sizeof o
, "USER %s\r\n", user
);
418 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
421 if (pop3_pass(mp
, pass
) == STOP
) {
429 pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
)
434 POP3_OUT("STAT\r\n", MB_COMD
);
436 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
437 while (*cp
&& spacechar(*cp
& 0377))
440 *count
= (int)strtol(cp
, NULL
, 10);
441 while (*cp
&& !spacechar(*cp
& 0377))
443 while (*cp
&& spacechar(*cp
& 0377))
446 *size
= (int)strtol(cp
, NULL
, 10);
452 fprintf(stderr
, catgets(catd
, CATSET
, 260,
453 "invalid POP3 STAT response: %s\n"), pop3buf
);
458 pop3_list(struct mailbox
*mp
, int n
, size_t *size
)
460 char o
[LINESIZE
], *cp
;
462 snprintf(o
, sizeof o
, "LIST %u\r\n", n
);
465 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
466 while (*cp
&& spacechar(*cp
& 0377))
468 while (*cp
&& !spacechar(*cp
& 0377))
470 while (*cp
&& spacechar(*cp
& 0377))
473 *size
= (size_t)strtol(cp
, NULL
, 10);
480 pop3_init(struct mailbox
*mp
, int n
)
482 struct message
*m
= &message
[n
];
485 m
->m_flag
= MUSED
|MNEW
|MNOFROM
|MNEWEST
;
488 pop3_list(mp
, m
- message
+ 1, &m
->m_xsize
);
489 if ((cp
= hfield("status", m
)) != NULL
) {
490 while (*cp
!= '\0') {
502 pop3_dates(struct mailbox
*mp
)
507 for (i
= 0; i
< msgCount
; i
++)
508 substdate(&message
[i
]);
512 pop3_setptr(struct mailbox
*mp
)
516 message
= scalloc(msgCount
+ 1, sizeof *message
);
517 for (i
= 0; i
< msgCount
; i
++)
520 message
[msgCount
].m_size
= 0;
521 message
[msgCount
].m_lines
= 0;
526 pop3_have_password(const char *server
)
530 var
= ac_alloc(strlen(server
) + 10);
531 strcpy(var
, "password-");
532 strcpy(&var
[9], server
);
533 if ((cp
= value(var
)) != NULL
)
540 pop3_setfile(const char *server
, int newmail
, int isedit
)
543 sighandler_type saveint
;
544 sighandler_type savepipe
;
546 const char *cp
, *sp
= server
, *pass
, *uhp
;
554 if (strncmp(sp
, "pop3://", 7) == 0) {
558 } else if (strncmp(sp
, "pop3s://", 8) == 0) {
564 pass
= pop3_have_password(uhp
);
565 if ((cp
= last_at_before_slash(sp
)) != NULL
) {
566 user
= salloc(cp
- sp
+ 1);
567 memcpy(user
, sp
, cp
- sp
);
568 user
[cp
- sp
] = '\0';
573 verbose
= value("verbose") != NULL
;
574 if (sopen(sp
, &so
, use_ssl
, uhp
, use_ssl
? "pop3s" : "pop3",
580 if (mb
.mb_sock
.s_fd
>= 0)
591 mb
.mb_type
= MB_VOID
;
594 saveint
= safe_signal(SIGINT
, SIG_IGN
);
595 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
596 if (sigsetjmp(pop3jmp
, 1)) {
598 safe_signal(SIGINT
, saveint
);
599 safe_signal(SIGPIPE
, savepipe
);
603 if (saveint
!= SIG_IGN
)
604 safe_signal(SIGINT
, pop3catch
);
605 if (savepipe
!= SIG_IGN
)
606 safe_signal(SIGPIPE
, pop3catch
);
607 if ((cp
= value("pop3-keepalive")) != NULL
) {
608 if ((pop3keepalive
= strtol(cp
, NULL
, 10)) > 0) {
609 savealrm
= safe_signal(SIGALRM
, pop3alarm
);
610 alarm(pop3keepalive
);
613 mb
.mb_sock
.s_desc
= "POP3";
614 mb
.mb_sock
.s_onclose
= pop3_timer_off
;
615 if (pop3_user(&mb
, user
, pass
, uhp
, sp
) != OKAY
||
616 pop3_stat(&mb
, &mailsize
, &msgCount
) != OKAY
) {
619 safe_signal(SIGINT
, saveint
);
620 safe_signal(SIGPIPE
, savepipe
);
624 mb
.mb_type
= MB_POP3
;
625 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
629 safe_signal(SIGINT
, saveint
);
630 safe_signal(SIGPIPE
, savepipe
);
632 if (!edit
&& msgCount
== 0) {
633 if (mb
.mb_type
== MB_POP3
&& value("emptystart") == NULL
)
634 fprintf(stderr
, catgets(catd
, CATSET
, 258,
635 "No mail at %s\n"), server
);
642 pop3_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
644 sighandler_type saveint
= SIG_IGN
;
645 sighandler_type savepipe
= SIG_IGN
;
647 char o
[LINESIZE
], *line
= NULL
, *lp
;
648 size_t linesize
= 0, linelen
, size
;
649 int number
= m
- message
+ 1;
650 int emptyline
= 0, lines
;
657 verbose
= value("verbose") != NULL
;
658 if (mp
->mb_sock
.s_fd
< 0) {
659 fprintf(stderr
, catgets(catd
, CATSET
, 219,
660 "POP3 connection already closed.\n"));
663 if (pop3lock
++ == 0) {
664 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
665 safe_signal(SIGINT
, maincatch
);
666 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
667 if (sigsetjmp(pop3jmp
, 1)) {
668 safe_signal(SIGINT
, saveint
);
669 safe_signal(SIGPIPE
, savepipe
);
673 if (savepipe
!= SIG_IGN
)
674 safe_signal(SIGPIPE
, pop3catch
);
676 fseek(mp
->mb_otf
, 0L, SEEK_END
);
677 offset
= ftell(mp
->mb_otf
);
678 retry
: switch (need
) {
680 snprintf(o
, sizeof o
, "TOP %u 0\r\n", number
);
683 snprintf(o
, sizeof o
, "RETR %u\r\n", number
);
688 POP3_OUT(o
, MB_COMD
|MB_MULT
)
689 if (pop3_answer(mp
) == STOP
) {
690 if (need
== NEED_HEADER
) {
692 * The TOP POP3 command is optional, so retry
693 * with the entire message.
704 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
705 if (line
[0] == '.' && line
[1] == '\r' && line
[2] == '\n' &&
707 mp
->mb_active
&= ~MB_MULT
;
710 if (line
[0] == '.') {
716 * Need to mask 'From ' lines. This cannot be done properly
717 * since some servers pass them as 'From ' and others as
718 * '>From '. Although one could identify the first kind of
719 * server in principle, it is not possible to identify the
720 * second as '>From ' may also come from a server of the
721 * first type as actual data. So do what is absolutely
722 * necessary only - mask 'From '.
724 * If the line is the first line of the message header, it
725 * is likely a real 'From ' line. In this case, it is just
726 * ignored since it violates all standards.
728 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
729 lp
[3] == 'm' && lp
[4] == ' ') {
731 fputc('>', mp
->mb_otf
);
737 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
738 lp
[linelen
-2] == '\r')) {
739 emptyline
= linelen
<= 2;
741 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
742 fputc('\n', mp
->mb_otf
);
746 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
752 * This is very ugly; but some POP3 daemons don't end a
753 * message with \r\n\r\n, and we need \n\n for mbox format.
755 fputc('\n', mp
->mb_otf
);
761 m
->m_block
= mailx_blockof(offset
);
762 m
->m_offset
= mailx_offsetof(offset
);
766 m
->m_have
|= HAVE_HEADER
;
769 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
770 m
->m_xlines
= m
->m_lines
;
771 m
->m_xsize
= m
->m_size
;
778 if (saveint
!= SIG_IGN
)
779 safe_signal(SIGINT
, saveint
);
780 if (savepipe
!= SIG_IGN
)
781 safe_signal(SIGPIPE
, savepipe
);
789 pop3_header(struct message
*m
)
791 return pop3_get(&mb
, m
, NEED_HEADER
);
796 pop3_body(struct message
*m
)
798 return pop3_get(&mb
, m
, NEED_BODY
);
802 pop3_exit(struct mailbox
*mp
)
804 POP3_OUT("QUIT\r\n", MB_COMD
)
810 pop3_delete(struct mailbox
*mp
, int n
)
814 snprintf(o
, sizeof o
, "DELE %u\r\n", n
);
821 pop3_update(struct mailbox
*mp
)
823 FILE *readstat
= NULL
;
825 int dodel
, c
, gotcha
, held
;
828 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
833 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
834 if (m
->m_flag
& MBOX
)
840 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
841 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
844 if ((id
= hfield("message-id", m
)) != NULL
||
845 (id
= hfield("article-id", m
)) != NULL
)
846 fprintf(readstat
, "%s\n", id
);
849 dodel
= m
->m_flag
& MDELETED
;
851 dodel
= !((m
->m_flag
&MPRESERVE
) ||
852 (m
->m_flag
&MTOUCH
) == 0);
855 pop3_delete(mp
, m
- message
+ 1);
860 if (readstat
!= NULL
)
862 if (gotcha
&& edit
) {
863 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
864 printf(value("bsdcompat") || value("bsdmsgs") ?
865 catgets(catd
, CATSET
, 170, "complete\n") :
866 catgets(catd
, CATSET
, 212, "updated.\n"));
867 } else if (held
&& !edit
) {
869 printf(catgets(catd
, CATSET
, 155,
870 "Held 1 message in %s\n"), mailname
);
872 printf(catgets(catd
, CATSET
, 156,
873 "Held %d messages in %s\n"), held
, mailname
);
882 sighandler_type saveint
;
883 sighandler_type savepipe
;
885 verbose
= value("verbose") != NULL
;
886 if (mb
.mb_sock
.s_fd
< 0) {
887 fprintf(stderr
, catgets(catd
, CATSET
, 219,
888 "POP3 connection already closed.\n"));
892 saveint
= safe_signal(SIGINT
, SIG_IGN
);
893 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
894 if (sigsetjmp(pop3jmp
, 1)) {
895 safe_signal(SIGINT
, saveint
);
896 safe_signal(SIGPIPE
, saveint
);
900 if (saveint
!= SIG_IGN
)
901 safe_signal(SIGINT
, pop3catch
);
902 if (savepipe
!= SIG_IGN
)
903 safe_signal(SIGPIPE
, pop3catch
);
907 safe_signal(SIGINT
, saveint
);
908 safe_signal(SIGPIPE
, savepipe
);
911 #else /* !USE_POP3 */
915 fprintf(stderr
, catgets(catd
, CATSET
, 216,
916 "No POP3 support compiled in.\n"));
920 pop3_setfile(const char *server
, int newmail
, int isedit
)
930 pop3_header(struct message
*mp
)
938 pop3_body(struct message
*mp
)
957 #endif /* USE_POP3 */