2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
8 * Gunnar Ritter. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gunnar Ritter
21 * and his contributors.
22 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 static char sccsid
[] = "@(#)pop3.c 2.43 (gritter) 3/4/06";
57 * Mail -- a mail program
65 #define POP3_ANSWER() if (pop3_answer(mp) == STOP) \
67 #define POP3_OUT(x, y) if (pop3_finish(mp) == STOP) \
70 fprintf(stderr, ">>> %s", x); \
71 mp->mb_active |= (y); \
72 if (swrite(&mp->mb_sock, x) == STOP) \
76 static size_t pop3bufsize
;
77 static sigjmp_buf pop3jmp
;
78 static sighandler_type savealrm
;
80 static struct termios otio
;
81 static int pop3keepalive
;
82 static volatile int pop3lock
;
84 static void pop3_timer_off(void);
85 static enum okay
pop3_answer(struct mailbox
*mp
);
86 static enum okay
pop3_finish(struct mailbox
*mp
);
87 static void pop3catch(int s
);
88 static void maincatch(int s
);
89 static enum okay
pop3_noop1(struct mailbox
*mp
);
90 static void pop3alarm(int s
);
91 static enum okay
pop3_pass(struct mailbox
*mp
, const char *pass
);
92 static char *pop3_find_timestamp(const char *bp
);
93 static enum okay
pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
,
95 static enum okay
pop3_apop1(struct mailbox
*mp
,
96 const char *user
, const char *xp
);
97 static int pop3_use_starttls(const char *uhp
);
98 static int pop3_use_apop(const char *uhp
);
99 static enum okay
pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
100 const char *uhp
, const char *xserver
);
101 static enum okay
pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
);
102 static enum okay
pop3_list(struct mailbox
*mp
, int n
, size_t *size
);
103 static void pop3_init(struct mailbox
*mp
, int n
);
104 static void pop3_dates(struct mailbox
*mp
);
105 static void pop3_setptr(struct mailbox
*mp
);
106 static char *pop3_have_password(const char *server
);
107 static enum okay
pop3_get(struct mailbox
*mp
, struct message
*m
,
109 static enum okay
pop3_exit(struct mailbox
*mp
);
110 static enum okay
pop3_delete(struct mailbox
*mp
, int n
);
111 static enum okay
pop3_update(struct mailbox
*mp
);
116 if (pop3keepalive
> 0) {
118 safe_signal(SIGALRM
, savealrm
);
123 pop3_answer(struct mailbox
*mp
)
128 retry
: if ((sz
= sgetline(&pop3buf
, &pop3bufsize
, NULL
, &mp
->mb_sock
)) > 0) {
129 if ((mp
->mb_active
& (MB_COMD
|MB_MULT
)) == MB_MULT
)
132 fputs(pop3buf
, stderr
);
136 mp
->mb_active
&= ~MB_COMD
;
140 mp
->mb_active
= MB_NONE
;
141 fprintf(stderr
, catgets(catd
, CATSET
, 218,
142 "POP3 error: %s"), pop3buf
);
146 * If the answer starts neither with '+' nor with
147 * '-', it must be part of a multiline response,
148 * e. g. because the user interrupted a file
149 * download. Get lines until a single dot appears.
151 multiline
: while (pop3buf
[0] != '.' || pop3buf
[1] != '\r' ||
152 pop3buf
[2] != '\n' ||
153 pop3buf
[3] != '\0') {
154 sz
= sgetline(&pop3buf
, &pop3bufsize
,
159 mp
->mb_active
&= ~MB_MULT
;
160 if (mp
->mb_active
!= MB_NONE
)
165 mp
->mb_active
= MB_NONE
;
171 pop3_finish(struct mailbox
*mp
)
173 while (mp
->mb_sock
.s_fd
> 0 && mp
->mb_active
!= MB_NONE
)
182 tcsetattr(0, TCSADRAIN
, &otio
);
185 fprintf(stderr
, catgets(catd
, CATSET
, 102, "Interrupt\n"));
186 siglongjmp(pop3jmp
, 1);
189 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
;
244 if (pop3lock
++ == 0) {
245 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
246 safe_signal(SIGINT
, maincatch
);
247 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
248 if (sigsetjmp(pop3jmp
, 1)) {
249 safe_signal(SIGINT
, saveint
);
250 safe_signal(SIGPIPE
, savepipe
);
253 if (savepipe
!= SIG_IGN
)
254 safe_signal(SIGPIPE
, pop3catch
);
255 if (pop3_noop1(&mb
) != OKAY
) {
256 safe_signal(SIGINT
, saveint
);
257 safe_signal(SIGPIPE
, savepipe
);
260 safe_signal(SIGINT
, saveint
);
261 safe_signal(SIGPIPE
, savepipe
);
263 brk
: alarm(pop3keepalive
);
268 pop3_pass(struct mailbox
*mp
, const char *pass
)
272 snprintf(o
, sizeof o
, "PASS %s\r\n", pass
);
279 pop3_find_timestamp(const char *bp
)
285 if ((cp
= strchr(bp
, '<')) == NULL
)
287 for (ep
= cp
; *ep
; ep
++) {
288 if (spacechar(*ep
&0377))
292 else if (*ep
== '>') {
300 rp
= salloc(ep
- cp
+ 2);
301 memcpy(rp
, cp
, ep
- cp
+ 1);
302 rp
[ep
- cp
+ 1] = '\0';
307 pop3_apop(struct mailbox
*mp
, char *xuser
, const char *pass
, const char *ts
)
309 char *user
, *catp
, *xp
;
310 unsigned char digest
[16];
313 retry
: if (xuser
== NULL
) {
314 if ((user
= getuser()) == NULL
)
319 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
322 catp
= savecat(ts
, pass
);
324 MD5Update(&ctx
, (unsigned char *)catp
, strlen(catp
));
325 MD5Final(digest
, &ctx
);
326 xp
= md5tohex(digest
);
327 if (pop3_apop1(mp
, user
, xp
) == STOP
) {
335 pop3_apop1(struct mailbox
*mp
, const char *user
, const char *xp
)
339 snprintf(o
, sizeof o
, "APOP %s %s\r\n", user
, xp
);
346 pop3_use_starttls(const char *uhp
)
350 if (value("pop3-use-starttls"))
352 var
= savecat("pop3-use-starttls-", uhp
);
353 return value(var
) != NULL
;
357 pop3_use_apop(const char *uhp
)
361 if (value("pop3-use-apop"))
363 var
= savecat("pop3-use-apop-", uhp
);
364 return value(var
) != NULL
;
368 pop3_user(struct mailbox
*mp
, char *xuser
, const char *pass
,
369 const char *uhp
, const char *xserver
)
371 char o
[LINESIZE
], *user
, *ts
= NULL
, *server
, *cp
;
374 if (pop3_use_apop(uhp
)) {
375 if ((ts
= pop3_find_timestamp(pop3buf
)) == NULL
) {
376 fprintf(stderr
, "Could not determine timestamp from "
377 "server greeting. Impossible to use APOP.\n");
381 if ((cp
= strchr(xserver
, ':')) != NULL
) {
382 server
= salloc(cp
- xserver
+ 1);
383 memcpy(server
, xserver
, cp
- xserver
);
384 server
[cp
- xserver
] = '\0';
386 server
= (char *)xserver
;
388 if (mp
->mb_sock
.s_use_ssl
== 0 && pop3_use_starttls(uhp
)) {
389 POP3_OUT("STLS\r\n", MB_COMD
)
391 if (ssl_open(server
, &mp
->mb_sock
, uhp
) != OKAY
)
395 if (pop3_use_starttls(uhp
)) {
396 fprintf(stderr
, "No SSL support compiled in.\n");
399 #endif /* !USE_SSL */
401 return pop3_apop(mp
, xuser
, pass
, ts
);
402 retry
: if (xuser
== NULL
) {
403 if ((user
= getuser()) == NULL
)
407 snprintf(o
, sizeof o
, "USER %s\r\n", user
);
411 if ((pass
= getpassword(&otio
, &reset_tio
, NULL
)) == NULL
)
414 if (pop3_pass(mp
, pass
) == STOP
) {
422 pop3_stat(struct mailbox
*mp
, off_t
*size
, int *count
)
427 POP3_OUT("STAT\r\n", MB_COMD
);
429 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
430 while (*cp
&& spacechar(*cp
& 0377))
433 *count
= (int)strtol(cp
, NULL
, 10);
434 while (*cp
&& !spacechar(*cp
& 0377))
436 while (*cp
&& spacechar(*cp
& 0377))
439 *size
= (int)strtol(cp
, NULL
, 10);
445 fprintf(stderr
, catgets(catd
, CATSET
, 260,
446 "invalid POP3 STAT response: %s\n"), pop3buf
);
451 pop3_list(struct mailbox
*mp
, int n
, size_t *size
)
453 char o
[LINESIZE
], *cp
;
455 snprintf(o
, sizeof o
, "LIST %u\r\n", n
);
458 for (cp
= pop3buf
; *cp
&& !spacechar(*cp
& 0377); cp
++);
459 while (*cp
&& spacechar(*cp
& 0377))
461 while (*cp
&& !spacechar(*cp
& 0377))
463 while (*cp
&& spacechar(*cp
& 0377))
466 *size
= (size_t)strtol(cp
, NULL
, 10);
473 pop3_init(struct mailbox
*mp
, int n
)
475 struct message
*m
= &message
[n
];
478 m
->m_flag
= MUSED
|MNEW
|MNOFROM
|MNEWEST
;
481 pop3_list(mp
, m
- message
+ 1, &m
->m_xsize
);
482 if ((cp
= hfield("status", m
)) != NULL
) {
483 while (*cp
!= '\0') {
495 pop3_dates(struct mailbox
*mp
)
499 for (i
= 0; i
< msgCount
; i
++)
500 substdate(&message
[i
]);
504 pop3_setptr(struct mailbox
*mp
)
508 message
= scalloc(msgCount
+ 1, sizeof *message
);
509 for (i
= 0; i
< msgCount
; i
++)
512 message
[msgCount
].m_size
= 0;
513 message
[msgCount
].m_lines
= 0;
518 pop3_have_password(const char *server
)
522 var
= ac_alloc(strlen(server
) + 10);
523 strcpy(var
, "password-");
524 strcpy(&var
[9], server
);
525 if ((cp
= value(var
)) != NULL
)
532 pop3_setfile(const char *server
, int newmail
, int isedit
)
535 sighandler_type saveint
;
536 sighandler_type savepipe
;
538 const char *cp
, *sp
= server
, *pass
, *uhp
;
546 if (strncmp(sp
, "pop3://", 7) == 0) {
550 } else if (strncmp(sp
, "pop3s://", 8) == 0) {
556 pass
= pop3_have_password(uhp
);
557 if ((cp
= last_at_before_slash(sp
)) != NULL
) {
558 user
= salloc(cp
- sp
+ 1);
559 memcpy(user
, sp
, cp
- sp
);
560 user
[cp
- sp
] = '\0';
565 verbose
= value("verbose") != NULL
;
566 if (sopen(sp
, &so
, use_ssl
, uhp
, use_ssl
? "pop3s" : "pop3",
572 if (mb
.mb_sock
.s_fd
>= 0)
583 mb
.mb_type
= MB_VOID
;
586 saveint
= safe_signal(SIGINT
, SIG_IGN
);
587 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
588 if (sigsetjmp(pop3jmp
, 1)) {
590 safe_signal(SIGINT
, saveint
);
591 safe_signal(SIGPIPE
, savepipe
);
595 if (saveint
!= SIG_IGN
)
596 safe_signal(SIGINT
, pop3catch
);
597 if (savepipe
!= SIG_IGN
)
598 safe_signal(SIGPIPE
, pop3catch
);
599 if ((cp
= value("pop3-keepalive")) != NULL
) {
600 if ((pop3keepalive
= strtol(cp
, NULL
, 10)) > 0) {
601 savealrm
= safe_signal(SIGALRM
, pop3alarm
);
602 alarm(pop3keepalive
);
605 mb
.mb_sock
.s_desc
= "POP3";
606 mb
.mb_sock
.s_onclose
= pop3_timer_off
;
607 if (pop3_user(&mb
, user
, pass
, uhp
, sp
) != OKAY
||
608 pop3_stat(&mb
, &mailsize
, &msgCount
) != OKAY
) {
611 safe_signal(SIGINT
, saveint
);
612 safe_signal(SIGPIPE
, savepipe
);
616 mb
.mb_type
= MB_POP3
;
617 mb
.mb_perm
= Rflag
? 0 : MB_DELE
;
621 safe_signal(SIGINT
, saveint
);
622 safe_signal(SIGPIPE
, savepipe
);
624 if (!edit
&& msgCount
== 0) {
625 if (mb
.mb_type
== MB_POP3
&& value("emptystart") == NULL
)
626 fprintf(stderr
, catgets(catd
, CATSET
, 258,
627 "No mail at %s\n"), server
);
634 pop3_get(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
636 sighandler_type saveint
= SIG_IGN
;
637 sighandler_type savepipe
= SIG_IGN
;
639 char o
[LINESIZE
], *line
= NULL
, *lp
;
640 size_t linesize
= 0, linelen
, size
;
641 int number
= m
- message
+ 1;
642 int emptyline
= 0, lines
;
649 verbose
= value("verbose") != NULL
;
650 if (mp
->mb_sock
.s_fd
< 0) {
651 fprintf(stderr
, catgets(catd
, CATSET
, 219,
652 "POP3 connection already closed.\n"));
655 if (pop3lock
++ == 0) {
656 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
657 safe_signal(SIGINT
, maincatch
);
658 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
659 if (sigsetjmp(pop3jmp
, 1)) {
660 safe_signal(SIGINT
, saveint
);
661 safe_signal(SIGPIPE
, savepipe
);
665 if (savepipe
!= SIG_IGN
)
666 safe_signal(SIGPIPE
, pop3catch
);
668 fseek(mp
->mb_otf
, 0L, SEEK_END
);
669 offset
= ftell(mp
->mb_otf
);
670 retry
: switch (need
) {
672 snprintf(o
, sizeof o
, "TOP %u 0\r\n", number
);
675 snprintf(o
, sizeof o
, "RETR %u\r\n", number
);
680 POP3_OUT(o
, MB_COMD
|MB_MULT
)
681 if (pop3_answer(mp
) == STOP
) {
682 if (need
== NEED_HEADER
) {
684 * The TOP POP3 command is optional, so retry
685 * with the entire message.
696 while (sgetline(&line
, &linesize
, &linelen
, &mp
->mb_sock
) > 0) {
697 if (line
[0] == '.' && line
[1] == '\r' && line
[2] == '\n' &&
699 mp
->mb_active
&= ~MB_MULT
;
702 if (line
[0] == '.') {
708 * Need to mask 'From ' lines. This cannot be done properly
709 * since some servers pass them as 'From ' and others as
710 * '>From '. Although one could identify the first kind of
711 * server in principle, it is not possible to identify the
712 * second as '>From ' may also come from a server of the
713 * first type as actual data. So do what is absolutely
714 * necessary only - mask 'From '.
716 * If the line is the first line of the message header, it
717 * is likely a real 'From ' line. In this case, it is just
718 * ignored since it violates all standards.
720 if (lp
[0] == 'F' && lp
[1] == 'r' && lp
[2] == 'o' &&
721 lp
[3] == 'm' && lp
[4] == ' ') {
723 fputc('>', mp
->mb_otf
);
729 if (lp
[linelen
-1] == '\n' && (linelen
== 1 ||
730 lp
[linelen
-2] == '\r')) {
731 emptyline
= linelen
<= 2;
733 fwrite(lp
, 1, linelen
- 2, mp
->mb_otf
);
734 fputc('\n', mp
->mb_otf
);
738 fwrite(lp
, 1, linelen
, mp
->mb_otf
);
744 * This is very ugly; but some POP3 daemons don't end a
745 * message with \r\n\r\n, and we need \n\n for mbox format.
747 fputc('\n', mp
->mb_otf
);
753 m
->m_block
= mailx_blockof(offset
);
754 m
->m_offset
= mailx_offsetof(offset
);
758 m
->m_have
|= HAVE_HEADER
;
761 m
->m_have
|= HAVE_HEADER
|HAVE_BODY
;
762 m
->m_xlines
= m
->m_lines
;
763 m
->m_xsize
= m
->m_size
;
770 if (saveint
!= SIG_IGN
)
771 safe_signal(SIGINT
, saveint
);
772 if (savepipe
!= SIG_IGN
)
773 safe_signal(SIGPIPE
, savepipe
);
781 pop3_header(struct message
*m
)
783 return pop3_get(&mb
, m
, NEED_HEADER
);
788 pop3_body(struct message
*m
)
790 return pop3_get(&mb
, m
, NEED_BODY
);
794 pop3_exit(struct mailbox
*mp
)
796 POP3_OUT("QUIT\r\n", MB_COMD
)
802 pop3_delete(struct mailbox
*mp
, int n
)
806 snprintf(o
, sizeof o
, "DELE %u\r\n", n
);
813 pop3_update(struct mailbox
*mp
)
815 FILE *readstat
= NULL
;
817 int dodel
, c
, gotcha
, held
;
820 if ((readstat
= Zopen(Tflag
, "w", NULL
)) == NULL
)
825 for (m
= &message
[0], c
= 0; m
< &message
[msgCount
]; m
++) {
826 if (m
->m_flag
& MBOX
)
832 for (m
= &message
[0], gotcha
=0, held
=0; m
< &message
[msgCount
]; m
++) {
833 if (readstat
!= NULL
&& (m
->m_flag
& (MREAD
|MDELETED
)) != 0) {
836 if ((id
= hfield("message-id", m
)) != NULL
||
837 (id
= hfield("article-id", m
)) != NULL
)
838 fprintf(readstat
, "%s\n", id
);
841 dodel
= m
->m_flag
& MDELETED
;
843 dodel
= !((m
->m_flag
&MPRESERVE
) ||
844 (m
->m_flag
&MTOUCH
) == 0);
847 pop3_delete(mp
, m
- message
+ 1);
852 if (readstat
!= NULL
)
854 if (gotcha
&& edit
) {
855 printf(catgets(catd
, CATSET
, 168, "\"%s\" "), mailname
);
856 printf(value("bsdcompat") || value("bsdmsgs") ?
857 catgets(catd
, CATSET
, 170, "complete\n") :
858 catgets(catd
, CATSET
, 212, "updated.\n"));
859 } else if (held
&& !edit
) {
861 printf(catgets(catd
, CATSET
, 155,
862 "Held 1 message in %s\n"), mailname
);
864 printf(catgets(catd
, CATSET
, 156,
865 "Held %d messages in %s\n"), held
, mailname
);
874 sighandler_type saveint
;
875 sighandler_type savepipe
;
877 verbose
= value("verbose") != NULL
;
878 if (mb
.mb_sock
.s_fd
< 0) {
879 fprintf(stderr
, catgets(catd
, CATSET
, 219,
880 "POP3 connection already closed.\n"));
884 saveint
= safe_signal(SIGINT
, SIG_IGN
);
885 savepipe
= safe_signal(SIGPIPE
, SIG_IGN
);
886 if (sigsetjmp(pop3jmp
, 1)) {
887 safe_signal(SIGINT
, saveint
);
888 safe_signal(SIGPIPE
, saveint
);
892 if (saveint
!= SIG_IGN
)
893 safe_signal(SIGINT
, pop3catch
);
894 if (savepipe
!= SIG_IGN
)
895 safe_signal(SIGPIPE
, pop3catch
);
899 safe_signal(SIGINT
, saveint
);
900 safe_signal(SIGPIPE
, savepipe
);
903 #else /* !USE_POP3 */
907 fprintf(stderr
, catgets(catd
, CATSET
, 216,
908 "No POP3 support compiled in.\n"));
912 pop3_setfile(const char *server
, int newmail
, int isedit
)
919 pop3_header(struct message
*mp
)
926 pop3_body(struct message
*mp
)
944 #endif /* USE_POP3 */