1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * Copyright (c) 2002
8  *	Gunnar Ritter.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
25  *
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
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char sccsid[] = "@(#)pop3.c	2.43 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
44 
45 #include "config.h"
46 
47 #include "rcv.h"
48 #include "extern.h"
49 #include <errno.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <time.h>
53 
54 #include "md5.h"
55 
56 /*
57  * Mail -- a mail program
58  *
59  * POP3 client.
60  */
61 
62 #ifdef	HAVE_SOCKETS
63 static int	verbose;
64 
65 #define	POP3_ANSWER()	if (pop3_answer(mp) == STOP) \
66 				return STOP;
67 #define	POP3_OUT(x, y)	if (pop3_finish(mp) == STOP) \
68 				return STOP; \
69 			if (verbose) \
70 				fprintf(stderr, ">>> %s", x); \
71 			mp->mb_active |= (y); \
72 			if (swrite(&mp->mb_sock, x) == STOP) \
73 				return STOP;
74 
75 static char	*pop3buf;
76 static size_t	pop3bufsize;
77 static sigjmp_buf	pop3jmp;
78 static sighandler_type savealrm;
79 static int	reset_tio;
80 static struct termios	otio;
81 static int	pop3keepalive;
82 static volatile int	pop3lock;
83 
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,
94 		const char *ts);
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,
108 		enum needspec need);
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);
112 
113 static void
pop3_timer_off(void)114 pop3_timer_off(void)
115 {
116 	if (pop3keepalive > 0) {
117 		alarm(0);
118 		safe_signal(SIGALRM, savealrm);
119 	}
120 }
121 
122 static enum okay
pop3_answer(struct mailbox * mp)123 pop3_answer(struct mailbox *mp)
124 {
125 	int sz;
126 	enum okay ok = STOP;
127 
128 retry:	if ((sz = sgetline(&pop3buf, &pop3bufsize, NULL, &mp->mb_sock)) > 0) {
129 		if ((mp->mb_active & (MB_COMD|MB_MULT)) == MB_MULT)
130 			goto multiline;
131 		if (verbose)
132 			fputs(pop3buf, stderr);
133 		switch (*pop3buf) {
134 		case '+':
135 			ok = OKAY;
136 			mp->mb_active &= ~MB_COMD;
137 			break;
138 		case '-':
139 			ok = STOP;
140 			mp->mb_active = MB_NONE;
141 			fprintf(stderr, catgets(catd, CATSET, 218,
142 					"POP3 error: %s"), pop3buf);
143 			break;
144 		default:
145 			/*
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.
150 			 */
151 	multiline:	 while (pop3buf[0] != '.' || pop3buf[1] != '\r' ||
152 					pop3buf[2] != '\n' ||
153 					pop3buf[3] != '\0') {
154 				sz = sgetline(&pop3buf, &pop3bufsize,
155 						NULL, &mp->mb_sock);
156 				if (sz <= 0)
157 					goto eof;
158 			}
159 			mp->mb_active &= ~MB_MULT;
160 			if (mp->mb_active != MB_NONE)
161 				goto retry;
162 		}
163 	} else {
164 	eof: 	ok = STOP;
165 		mp->mb_active = MB_NONE;
166 	}
167 	return ok;
168 }
169 
170 static enum okay
pop3_finish(struct mailbox * mp)171 pop3_finish(struct mailbox *mp)
172 {
173 	while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
174 		pop3_answer(mp);
175 	return OKAY;
176 }
177 
178 static void
pop3catch(int s)179 pop3catch(int s)
180 {
181 	if (reset_tio)
182 		tcsetattr(0, TCSADRAIN, &otio);
183 	switch (s) {
184 	case SIGINT:
185 		fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
186 		siglongjmp(pop3jmp, 1);
187 		break;
188 	case SIGPIPE:
189 		fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
190 		break;
191 	}
192 }
193 
194 static void
maincatch(int s)195 maincatch(int s)
196 {
197 	if (interrupts++ == 0) {
198 		fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
199 		return;
200 	}
201 	onintr(0);
202 }
203 
204 static enum okay
pop3_noop1(struct mailbox * mp)205 pop3_noop1(struct mailbox *mp)
206 {
207 	POP3_OUT("NOOP\r\n", MB_COMD)
208 	POP3_ANSWER()
209 	return OKAY;
210 }
211 
212 enum okay
pop3_noop(void)213 pop3_noop(void)
214 {
215 	enum okay	ok = STOP;
216 	sighandler_type	saveint, savepipe;
217 
218 	(void)&saveint;
219 	(void)&savepipe;
220 	(void)&ok;
221 	verbose = value("verbose") != NULL;
222 	pop3lock = 1;
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);
230 	}
231 	safe_signal(SIGINT, saveint);
232 	safe_signal(SIGPIPE, savepipe);
233 	pop3lock = 0;
234 	return ok;
235 }
236 
237 /*ARGSUSED*/
238 static void
pop3alarm(int s)239 pop3alarm(int s)
240 {
241 	sighandler_type	saveint;
242 	sighandler_type savepipe;
243 
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);
251 			goto brk;
252 		}
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);
258 			goto out;
259 		}
260 		safe_signal(SIGINT, saveint);
261 		safe_signal(SIGPIPE, savepipe);
262 	}
263 brk:	alarm(pop3keepalive);
264 out:	pop3lock--;
265 }
266 
267 static enum okay
pop3_pass(struct mailbox * mp,const char * pass)268 pop3_pass(struct mailbox *mp, const char *pass)
269 {
270 	char o[LINESIZE];
271 
272 	snprintf(o, sizeof o, "PASS %s\r\n", pass);
273 	POP3_OUT(o, MB_COMD)
274 	POP3_ANSWER()
275 	return OKAY;
276 }
277 
278 static char *
pop3_find_timestamp(const char * bp)279 pop3_find_timestamp(const char *bp)
280 {
281 	const char	*cp, *ep;
282 	char	*rp;
283 	int	hadat = 0;
284 
285 	if ((cp = strchr(bp, '<')) == NULL)
286 		return NULL;
287 	for (ep = cp; *ep; ep++) {
288 		if (spacechar(*ep&0377))
289 			return NULL;
290 		else if (*ep == '@')
291 			hadat = 1;
292 		else if (*ep == '>') {
293 			if (hadat != 1)
294 				return NULL;
295 			break;
296 		}
297 	}
298 	if (*ep != '>')
299 		return NULL;
300 	rp = salloc(ep - cp + 2);
301 	memcpy(rp, cp, ep - cp + 1);
302 	rp[ep - cp + 1] = '\0';
303 	return rp;
304 }
305 
306 static enum okay
pop3_apop(struct mailbox * mp,char * xuser,const char * pass,const char * ts)307 pop3_apop(struct mailbox *mp, char *xuser, const char *pass, const char *ts)
308 {
309 	char	*user, *catp, *xp;
310 	unsigned char	digest[16];
311 	MD5_CTX	ctx;
312 
313 retry:	if (xuser == NULL) {
314 		if ((user = getuser()) == NULL)
315 			return STOP;
316 	} else
317 		user = xuser;
318 	if (pass == NULL) {
319 		if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
320 			return STOP;
321 	}
322 	catp = savecat(ts, pass);
323 	MD5Init(&ctx);
324 	MD5Update(&ctx, (unsigned char *)catp, strlen(catp));
325 	MD5Final(digest, &ctx);
326 	xp = md5tohex(digest);
327 	if (pop3_apop1(mp, user, xp) == STOP) {
328 		pass = NULL;
329 		goto retry;
330 	}
331 	return OKAY;
332 }
333 
334 static enum okay
pop3_apop1(struct mailbox * mp,const char * user,const char * xp)335 pop3_apop1(struct mailbox *mp, const char *user, const char *xp)
336 {
337 	char	o[LINESIZE];
338 
339 	snprintf(o, sizeof o, "APOP %s %s\r\n", user, xp);
340 	POP3_OUT(o, MB_COMD)
341 	POP3_ANSWER()
342 	return OKAY;
343 }
344 
345 static int
pop3_use_starttls(const char * uhp)346 pop3_use_starttls(const char *uhp)
347 {
348 	char	*var;
349 
350 	if (value("pop3-use-starttls"))
351 		return 1;
352 	var = savecat("pop3-use-starttls-", uhp);
353 	return value(var) != NULL;
354 }
355 
356 static int
pop3_use_apop(const char * uhp)357 pop3_use_apop(const char *uhp)
358 {
359 	char	*var;
360 
361 	if (value("pop3-use-apop"))
362 		return 1;
363 	var = savecat("pop3-use-apop-", uhp);
364 	return value(var) != NULL;
365 }
366 
367 static enum okay
pop3_user(struct mailbox * mp,char * xuser,const char * pass,const char * uhp,const char * xserver)368 pop3_user(struct mailbox *mp, char *xuser, const char *pass,
369 		const char *uhp, const char *xserver)
370 {
371 	char o[LINESIZE], *user, *ts = NULL, *server, *cp;
372 
373 	POP3_ANSWER()
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");
378 			return STOP;
379 		}
380 	}
381 	if ((cp = strchr(xserver, ':')) != NULL) {
382 		server = salloc(cp - xserver + 1);
383 		memcpy(server, xserver, cp - xserver);
384 		server[cp - xserver] = '\0';
385 	} else
386 		server = (char *)xserver;
387 #ifdef	USE_SSL
388 	if (mp->mb_sock.s_use_ssl == 0 && pop3_use_starttls(uhp)) {
389 		POP3_OUT("STLS\r\n", MB_COMD)
390 		POP3_ANSWER()
391 		if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
392 			return STOP;
393 	}
394 #else	/* !USE_SSL */
395 	if (pop3_use_starttls(uhp)) {
396 		fprintf(stderr, "No SSL support compiled in.\n");
397 		return STOP;
398 	}
399 #endif	/* !USE_SSL */
400 	if (ts != NULL)
401 		return pop3_apop(mp, xuser, pass, ts);
402 retry:	if (xuser == NULL) {
403 		if ((user = getuser()) == NULL)
404 			return STOP;
405 	} else
406 		user = xuser;
407 	snprintf(o, sizeof o, "USER %s\r\n", user);
408 	POP3_OUT(o, MB_COMD)
409 	POP3_ANSWER()
410 	if (pass == NULL) {
411 		if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
412 			return STOP;
413 	}
414 	if (pop3_pass(mp, pass) == STOP) {
415 		pass = NULL;
416 		goto retry;
417 	}
418 	return OKAY;
419 }
420 
421 static enum okay
pop3_stat(struct mailbox * mp,off_t * size,int * count)422 pop3_stat(struct mailbox *mp, off_t *size, int *count)
423 {
424 	char *cp;
425 	enum okay ok = OKAY;
426 
427 	POP3_OUT("STAT\r\n", MB_COMD);
428 	POP3_ANSWER()
429 	for (cp = pop3buf; *cp && !spacechar(*cp & 0377); cp++);
430 	while (*cp && spacechar(*cp & 0377))
431 		cp++;
432 	if (*cp) {
433 		*count = (int)strtol(cp, NULL, 10);
434 		while (*cp && !spacechar(*cp & 0377))
435 			cp++;
436 		while (*cp && spacechar(*cp & 0377))
437 			cp++;
438 		if (*cp)
439 			*size = (int)strtol(cp, NULL, 10);
440 		else
441 			ok = STOP;
442 	} else
443 		ok = STOP;
444 	if (ok == STOP)
445 		fprintf(stderr, catgets(catd, CATSET, 260,
446 			"invalid POP3 STAT response: %s\n"), pop3buf);
447 	return ok;
448 }
449 
450 static enum okay
pop3_list(struct mailbox * mp,int n,size_t * size)451 pop3_list(struct mailbox *mp, int n, size_t *size)
452 {
453 	char o[LINESIZE], *cp;
454 
455 	snprintf(o, sizeof o, "LIST %u\r\n", n);
456 	POP3_OUT(o, MB_COMD)
457 	POP3_ANSWER()
458 	for (cp = pop3buf; *cp && !spacechar(*cp & 0377); cp++);
459 	while (*cp && spacechar(*cp & 0377))
460 		cp++;
461 	while (*cp && !spacechar(*cp & 0377))
462 		cp++;
463 	while (*cp && spacechar(*cp & 0377))
464 		cp++;
465 	if (*cp)
466 		*size = (size_t)strtol(cp, NULL, 10);
467 	else
468 		*size = 0;
469 	return OKAY;
470 }
471 
472 static void
pop3_init(struct mailbox * mp,int n)473 pop3_init(struct mailbox *mp, int n)
474 {
475 	struct message *m = &message[n];
476 	char *cp;
477 
478 	m->m_flag = MUSED|MNEW|MNOFROM|MNEWEST;
479 	m->m_block = 0;
480 	m->m_offset = 0;
481 	pop3_list(mp, m - message + 1, &m->m_xsize);
482 	if ((cp = hfield("status", m)) != NULL) {
483 		while (*cp != '\0') {
484 			if (*cp == 'R')
485 				m->m_flag |= MREAD;
486 			else if (*cp == 'O')
487 				m->m_flag &= ~MNEW;
488 			cp++;
489 		}
490 	}
491 }
492 
493 /*ARGSUSED*/
494 static void
pop3_dates(struct mailbox * mp)495 pop3_dates(struct mailbox *mp)
496 {
497 	int	i;
498 
499 	for (i = 0; i < msgCount; i++)
500 		substdate(&message[i]);
501 }
502 
503 static void
pop3_setptr(struct mailbox * mp)504 pop3_setptr(struct mailbox *mp)
505 {
506 	int i;
507 
508 	message = scalloc(msgCount + 1, sizeof *message);
509 	for (i = 0; i < msgCount; i++)
510 		pop3_init(mp, i);
511 	setdot(message);
512 	message[msgCount].m_size = 0;
513 	message[msgCount].m_lines = 0;
514 	pop3_dates(mp);
515 }
516 
517 static char *
pop3_have_password(const char * server)518 pop3_have_password(const char *server)
519 {
520 	char *var, *cp;
521 
522 	var = ac_alloc(strlen(server) + 10);
523 	strcpy(var, "password-");
524 	strcpy(&var[9], server);
525 	if ((cp = value(var)) != NULL)
526 		cp = savestr(cp);
527 	ac_free(var);
528 	return cp;
529 }
530 
531 int
pop3_setfile(const char * server,int newmail,int isedit)532 pop3_setfile(const char *server, int newmail, int isedit)
533 {
534 	struct sock	so;
535 	sighandler_type	saveint;
536 	sighandler_type savepipe;
537 	char *user;
538 	const char *cp, *sp = server, *pass, *uhp;
539 	int use_ssl = 0;
540 
541 	(void)&sp;
542 	(void)&use_ssl;
543 	(void)&user;
544 	if (newmail)
545 		return 1;
546 	if (strncmp(sp, "pop3://", 7) == 0) {
547 		sp = &sp[7];
548 		use_ssl = 0;
549 #ifdef	USE_SSL
550 	} else if (strncmp(sp, "pop3s://", 8) == 0) {
551 		sp = &sp[8];
552 		use_ssl = 1;
553 #endif	/* USE_SSL */
554 	}
555 	uhp = sp;
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';
561 		sp = &cp[1];
562 		user = strdec(user);
563 	} else
564 		user = NULL;
565 	verbose = value("verbose") != NULL;
566 	if (sopen(sp, &so, use_ssl, uhp, use_ssl ? "pop3s" : "pop3",
567 				verbose) != OKAY) {
568 		return -1;
569 	}
570 	quit();
571 	edit = isedit;
572 	if (mb.mb_sock.s_fd >= 0)
573 		sclose(&mb.mb_sock);
574 	if (mb.mb_itf) {
575 		fclose(mb.mb_itf);
576 		mb.mb_itf = NULL;
577 	}
578 	if (mb.mb_otf) {
579 		fclose(mb.mb_otf);
580 		mb.mb_otf = NULL;
581 	}
582 	initbox(server);
583 	mb.mb_type = MB_VOID;
584 	pop3lock = 1;
585 	mb.mb_sock = so;
586 	saveint = safe_signal(SIGINT, SIG_IGN);
587 	savepipe = safe_signal(SIGPIPE, SIG_IGN);
588 	if (sigsetjmp(pop3jmp, 1)) {
589 		sclose(&mb.mb_sock);
590 		safe_signal(SIGINT, saveint);
591 		safe_signal(SIGPIPE, savepipe);
592 		pop3lock = 0;
593 		return 1;
594 	}
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);
603 		}
604 	}
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) {
609 		sclose(&mb.mb_sock);
610 		pop3_timer_off();
611 		safe_signal(SIGINT, saveint);
612 		safe_signal(SIGPIPE, savepipe);
613 		pop3lock = 0;
614 		return 1;
615 	}
616 	mb.mb_type = MB_POP3;
617 	mb.mb_perm = Rflag ? 0 : MB_DELE;
618 	pop3_setptr(&mb);
619 	setmsize(msgCount);
620 	sawcom = 0;
621 	safe_signal(SIGINT, saveint);
622 	safe_signal(SIGPIPE, savepipe);
623 	pop3lock = 0;
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);
628 		return 1;
629 	}
630 	return 0;
631 }
632 
633 static enum okay
pop3_get(struct mailbox * mp,struct message * m,enum needspec need)634 pop3_get(struct mailbox *mp, struct message *m, enum needspec need)
635 {
636 	sighandler_type	saveint = SIG_IGN;
637 	sighandler_type savepipe = SIG_IGN;
638 	off_t offset;
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;
643 
644 	(void)&saveint;
645 	(void)&savepipe;
646 	(void)&number;
647 	(void)&emptyline;
648 	(void)&need;
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"));
653 		return STOP;
654 	}
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);
662 			pop3lock--;
663 			return STOP;
664 		}
665 		if (savepipe != SIG_IGN)
666 			safe_signal(SIGPIPE, pop3catch);
667 	}
668 	fseek(mp->mb_otf, 0L, SEEK_END);
669 	offset = ftell(mp->mb_otf);
670 retry:	switch (need) {
671 	case NEED_HEADER:
672 		snprintf(o, sizeof o, "TOP %u 0\r\n", number);
673 		break;
674 	case NEED_BODY:
675 		snprintf(o, sizeof o, "RETR %u\r\n", number);
676 		break;
677 	case NEED_UNSPEC:
678 		abort();
679 	}
680 	POP3_OUT(o, MB_COMD|MB_MULT)
681 	if (pop3_answer(mp) == STOP) {
682 		if (need == NEED_HEADER) {
683 			/*
684 			 * The TOP POP3 command is optional, so retry
685 			 * with the entire message.
686 			 */
687 			need = NEED_BODY;
688 			goto retry;
689 		}
690 		if (interrupts)
691 			onintr(0);
692 		return STOP;
693 	}
694 	size = 0;
695 	lines = 0;
696 	while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
697 		if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
698 				line[3] == '\0') {
699 			mp->mb_active &= ~MB_MULT;
700 			break;
701 		}
702 		if (line[0] == '.') {
703 			lp = &line[1];
704 			linelen--;
705 		} else
706 			lp = line;
707 		/*
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 '.
715 		 *
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.
719 		 */
720 		if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
721 				lp[3] == 'm' && lp[4] == ' ') {
722 			if (lines != 0) {
723 				fputc('>', mp->mb_otf);
724 				size++;
725 			} else
726 				continue;
727 		}
728 		lines++;
729 		if (lp[linelen-1] == '\n' && (linelen == 1 ||
730 					lp[linelen-2] == '\r')) {
731 			emptyline = linelen <= 2;
732 			if (linelen > 2)
733 				fwrite(lp, 1, linelen - 2, mp->mb_otf);
734 			fputc('\n', mp->mb_otf);
735 			size += linelen - 1;
736 		} else {
737 			emptyline = 0;
738 			fwrite(lp, 1, linelen, mp->mb_otf);
739 			size += linelen;
740 		}
741 	}
742 	if (!emptyline) {
743 		/*
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.
746 		 */
747 		fputc('\n', mp->mb_otf);
748 		lines++;
749 		size++;
750 	}
751 	m->m_size = size;
752 	m->m_lines = lines;
753 	m->m_block = mailx_blockof(offset);
754 	m->m_offset = mailx_offsetof(offset);
755 	fflush(mp->mb_otf);
756 	switch (need) {
757 	case NEED_HEADER:
758 		m->m_have |= HAVE_HEADER;
759 		break;
760 	case NEED_BODY:
761 		m->m_have |= HAVE_HEADER|HAVE_BODY;
762 		m->m_xlines = m->m_lines;
763 		m->m_xsize = m->m_size;
764 		break;
765 	case NEED_UNSPEC:
766 		break;
767 	}
768 	if (line)
769 		free(line);
770 	if (saveint != SIG_IGN)
771 		safe_signal(SIGINT, saveint);
772 	if (savepipe != SIG_IGN)
773 		safe_signal(SIGPIPE, savepipe);
774 	pop3lock--;
775 	if (interrupts)
776 		onintr(0);
777 	return OKAY;
778 }
779 
780 enum okay
pop3_header(struct message * m)781 pop3_header(struct message *m)
782 {
783 	return pop3_get(&mb, m, NEED_HEADER);
784 }
785 
786 
787 enum okay
pop3_body(struct message * m)788 pop3_body(struct message *m)
789 {
790 	return pop3_get(&mb, m, NEED_BODY);
791 }
792 
793 static enum okay
pop3_exit(struct mailbox * mp)794 pop3_exit(struct mailbox *mp)
795 {
796 	POP3_OUT("QUIT\r\n", MB_COMD)
797 	POP3_ANSWER()
798 	return OKAY;
799 }
800 
801 static enum okay
pop3_delete(struct mailbox * mp,int n)802 pop3_delete(struct mailbox *mp, int n)
803 {
804 	char o[LINESIZE];
805 
806 	snprintf(o, sizeof o, "DELE %u\r\n", n);
807 	POP3_OUT(o, MB_COMD)
808 	POP3_ANSWER()
809 	return OKAY;
810 }
811 
812 static enum okay
pop3_update(struct mailbox * mp)813 pop3_update(struct mailbox *mp)
814 {
815 	FILE *readstat = NULL;
816 	struct message *m;
817 	int dodel, c, gotcha, held;
818 
819 	if (Tflag != NULL) {
820 		if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
821 			Tflag = NULL;
822 	}
823 	if (!edit) {
824 		holdbits();
825 		for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
826 			if (m->m_flag & MBOX)
827 				c++;
828 		}
829 		if (c > 0)
830 			makembox();
831 	}
832 	for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
833 		if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
834 			char *id;
835 
836 			if ((id = hfield("message-id", m)) != NULL ||
837 					(id = hfield("article-id", m)) != NULL)
838 				fprintf(readstat, "%s\n", id);
839 		}
840 		if (edit) {
841 			dodel = m->m_flag & MDELETED;
842 		} else {
843 			dodel = !((m->m_flag&MPRESERVE) ||
844 					(m->m_flag&MTOUCH) == 0);
845 		}
846 		if (dodel) {
847 			pop3_delete(mp, m - message + 1);
848 			gotcha++;
849 		} else
850 			held++;
851 	}
852 	if (readstat != NULL)
853 		Fclose(readstat);
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) {
860 		if (held == 1)
861 			printf(catgets(catd, CATSET, 155,
862 				"Held 1 message in %s\n"), mailname);
863 		else if (held > 1)
864 			printf(catgets(catd, CATSET, 156,
865 				"Held %d messages in %s\n"), held, mailname);
866 	}
867 	fflush(stdout);
868 	return OKAY;
869 }
870 
871 void
pop3_quit(void)872 pop3_quit(void)
873 {
874 	sighandler_type	saveint;
875 	sighandler_type savepipe;
876 
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"));
881 		return;
882 	}
883 	pop3lock = 1;
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);
889 		pop3lock = 0;
890 		return;
891 	}
892 	if (saveint != SIG_IGN)
893 		safe_signal(SIGINT, pop3catch);
894 	if (savepipe != SIG_IGN)
895 		safe_signal(SIGPIPE, pop3catch);
896 	pop3_update(&mb);
897 	pop3_exit(&mb);
898 	sclose(&mb.mb_sock);
899 	safe_signal(SIGINT, saveint);
900 	safe_signal(SIGPIPE, savepipe);
901 	pop3lock = 0;
902 }
903 #else	/* !HAVE_SOCKETS */
904 static void
nopop3(void)905 nopop3(void)
906 {
907 	fprintf(stderr, catgets(catd, CATSET, 216,
908 				"No POP3 support compiled in.\n"));
909 }
910 
911 int
pop3_setfile(const char * server,int newmail,int isedit)912 pop3_setfile(const char *server, int newmail, int isedit)
913 {
914 	nopop3();
915 	return -1;
916 }
917 
918 enum okay
pop3_header(struct message * mp)919 pop3_header(struct message *mp)
920 {
921 	nopop3();
922 	return STOP;
923 }
924 
925 enum okay
pop3_body(struct message * mp)926 pop3_body(struct message *mp)
927 {
928 	nopop3();
929 	return STOP;
930 }
931 
932 void
pop3_quit(void)933 pop3_quit(void)
934 {
935 	nopop3();
936 }
937 
938 enum okay
pop3_noop(void)939 pop3_noop(void)
940 {
941 	nopop3();
942 	return STOP;
943 }
944 #endif	/* HAVE_SOCKETS */
945