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) 1980, 1993
8  *	The Regents of the University of California.  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 the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its 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 THE REGENTS 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 THE REGENTS 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[] = "@(#)fio.c	2.73 (gritter) 1/7/08";
42 #endif
43 #endif /* not lint */
44 
45 #include "rcv.h"
46 #include <sys/stat.h>
47 #include <sys/file.h>
48 #include <sys/wait.h>
49 #ifdef	HAVE_WORDEXP
50 #include <wordexp.h>
51 #endif	/* HAVE_WORDEXP */
52 #include <unistd.h>
53 
54 #if defined (USE_NSS)
55 #include <nss.h>
56 #include <ssl.h>
57 #elif defined (USE_OPENSSL)
58 #include <openssl/ssl.h>
59 #include <openssl/err.h>
60 #include <openssl/x509v3.h>
61 #include <openssl/x509.h>
62 #include <openssl/rand.h>
63 #endif	/* USE_SSL */
64 #ifdef	HAVE_SOCKETS
65 #include <sys/socket.h>
66 #include <netdb.h>
67 #include <netinet/in.h>
68 #ifdef	HAVE_ARPA_INET_H
69 #include <arpa/inet.h>
70 #endif	/* HAVE_ARPA_INET_H */
71 #endif	/* HAVE_SOCKETS */
72 
73 #include <errno.h>
74 #include "extern.h"
75 
76 /*
77  * Mail -- a mail program
78  *
79  * File I/O.
80  */
81 
82 static void makemessage(void);
83 static void append(struct message *mp);
84 static char *globname(char *name);
85 static size_t length_of_line(const char *line, size_t linesize);
86 static char *fgetline_byone(char **line, size_t *linesize, size_t *llen,
87 		FILE *fp, int appendnl, size_t n);
88 static enum okay get_header(struct message *mp);
89 
90 /*
91  * Set up the input pointers while copying the mail file into /tmp.
92  */
93 void
setptr(FILE * ibuf,off_t offset)94 setptr(FILE *ibuf, off_t offset)
95 {
96 	int c;
97 	size_t count;
98 	char *cp, *cp2;
99 	struct message this;
100 	int maybe, inhead, thiscnt;
101 	char *linebuf = NULL;
102 	size_t linesize = 0, filesize;
103 	int broken_mbox = value("broken-mbox") != NULL;
104 
105 	maybe = 1;
106 	inhead = 0;
107 	thiscnt = 0;
108 	memset(&this, 0, sizeof this);
109 	this.m_flag = MUSED|MNEW|MNEWEST;
110 	filesize = mailsize - offset;
111 	for (;;) {
112 		if (fgetline(&linebuf, &linesize, &filesize, &count, ibuf, 0)
113 				== NULL) {
114 			this.m_xsize = this.m_size;
115 			this.m_xlines = this.m_lines;
116 			this.m_have = HAVE_HEADER|HAVE_BODY;
117 			if (thiscnt > 0)
118 				append(&this);
119 			makemessage();
120 			if (linebuf)
121 				free(linebuf);
122 			return;
123 		}
124 #ifdef	notdef
125 		if (linebuf[0] == '\0')
126 			linebuf[0] = '.';
127 #endif
128 		fwrite(linebuf, sizeof *linebuf, count, mb.mb_otf);
129 		if (ferror(mb.mb_otf)) {
130 			perror("/tmp");
131 			exit(1);
132 		}
133 		if (linebuf[count - 1] == '\n')
134 			linebuf[count - 1] = '\0';
135 		if (maybe && linebuf[0] == 'F' && is_head(linebuf, count)) {
136 			this.m_xsize = this.m_size;
137 			this.m_xlines = this.m_lines;
138 			this.m_have = HAVE_HEADER|HAVE_BODY;
139 			if (thiscnt++ > 0)
140 				append(&this);
141 			msgCount++;
142 			this.m_flag = MUSED|MNEW|MNEWEST;
143 			this.m_size = 0;
144 			this.m_lines = 0;
145 			this.m_block = mailx_blockof(offset);
146 			this.m_offset = mailx_offsetof(offset);
147 			inhead = 1;
148 		} else if (linebuf[0] == 0) {
149 			inhead = 0;
150 		} else if (inhead) {
151 			for (cp = linebuf, cp2 = "status";; cp++) {
152 				if ((c = *cp2++) == 0) {
153 					while (c = *cp++, whitechar(c));
154 					if (cp[-1] != ':')
155 						break;
156 					while ((c = *cp++) != '\0')
157 						if (c == 'R')
158 							this.m_flag |= MREAD;
159 						else if (c == 'O')
160 							this.m_flag &= ~MNEW;
161 					break;
162 				}
163 				if (*cp != c && *cp != upperconv(c))
164 					break;
165 			}
166 			for (cp = linebuf, cp2 = "x-status";; cp++) {
167 				if ((c = *cp2++) == 0) {
168 					while (c = *cp++, whitechar(c));
169 					if (cp[-1] != ':')
170 						break;
171 					while ((c = *cp++) != '\0')
172 						if (c == 'F')
173 							this.m_flag |= MFLAGGED;
174 						else if (c == 'A')
175 							this.m_flag|=MANSWERED;
176 						else if (c == 'T')
177 							this.m_flag|=MDRAFTED;
178 					break;
179 				}
180 				if (*cp != c && *cp != upperconv(c))
181 					break;
182 			}
183 		}
184 		offset += count;
185 		this.m_size += count;
186 		this.m_lines++;
187 		if (!broken_mbox)
188 			maybe = linebuf[0] == 0;
189 	}
190 	/*NOTREACHED*/
191 }
192 
193 /*
194  * Drop the passed line onto the passed output buffer.
195  * If a write error occurs, return -1, else the count of
196  * characters written, including the newline.
197  */
198 int
putline(FILE * obuf,char * linebuf,size_t count)199 putline(FILE *obuf, char *linebuf, size_t count)
200 {
201 	fwrite(linebuf, sizeof *linebuf, count, obuf);
202 	putc('\n', obuf);
203 	if (ferror(obuf))
204 		return (-1);
205 	return (count + 1);
206 }
207 
208 /*
209  * Read up a line from the specified input into the line
210  * buffer.  Return the number of characters read.  Do not
211  * include the newline at the end.
212  *
213  * n is the number of characters already read.
214  */
215 int
readline_restart(FILE * ibuf,char ** linebuf,size_t * linesize,size_t n)216 readline_restart(FILE *ibuf, char **linebuf, size_t *linesize, size_t n)
217 {
218 	long sz;
219 
220 	clearerr(ibuf);
221 	/*
222 	 * Interrupts will cause trouble if we are inside a stdio call. As
223 	 * this is only relevant if input comes from a terminal, we can simply
224 	 * bypass it by read() then.
225 	 */
226 	if (fileno(ibuf) == 0 && is_a_tty[0]) {
227 		if (*linebuf == NULL || *linesize < LINESIZE + n + 1)
228 			*linebuf = srealloc(*linebuf,
229 					*linesize = LINESIZE + n + 1);
230 		for (;;) {
231 			if (n >= *linesize - 128)
232 				*linebuf = srealloc(*linebuf, *linesize += 256);
233 again:
234 			sz = read(0, *linebuf + n, *linesize - n - 1);
235 			if (sz > 0) {
236 				n += sz;
237 				(*linebuf)[n] = '\0';
238 				if (n > 0 && (*linebuf)[n - 1] == '\n')
239 					break;
240 			} else {
241 				if (sz < 0 && errno == EINTR)
242 					goto again;
243 				if (n > 0) {
244 					if ((*linebuf)[n - 1] != '\n') {
245 						(*linebuf)[n++] = '\n';
246 						(*linebuf)[n] = '\0';
247 					}
248 					break;
249 				} else
250 					return -1;
251 			}
252 		}
253 	} else {
254 		/*
255 		 * Not reading from standard input or standard input not
256 		 * a terminal. We read one char at a time as it is the
257 		 * only way to get lines with embedded NUL characters in
258 		 * standard stdio.
259 		 */
260 		if (fgetline_byone(linebuf, linesize, &n, ibuf, 1, n) == NULL)
261 			return -1;
262 	}
263 	if (n > 0 && (*linebuf)[n - 1] == '\n')
264 		(*linebuf)[--n] = '\0';
265 	return n;
266 }
267 
268 /*
269  * Return a file buffer all ready to read up the
270  * passed message pointer.
271  */
272 FILE *
setinput(struct mailbox * mp,struct message * m,enum needspec need)273 setinput(struct mailbox *mp, struct message *m, enum needspec need)
274 {
275 	enum okay ok = STOP;
276 
277 	switch (need) {
278 	case NEED_HEADER:
279 		if (m->m_have & HAVE_HEADER)
280 			ok = OKAY;
281 		else
282 			ok = get_header(m);
283 		break;
284 	case NEED_BODY:
285 		if (m->m_have & HAVE_BODY)
286 			ok = OKAY;
287 		else
288 			ok = get_body(m);
289 		break;
290 	case NEED_UNSPEC:
291 		ok = OKAY;
292 		break;
293 	}
294 	if (ok != OKAY)
295 		return NULL;
296 	fflush(mp->mb_otf);
297 	if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
298 					m->m_offset), SEEK_SET) < 0) {
299 		perror("fseek");
300 		panic(catgets(catd, CATSET, 77, "temporary file seek"));
301 	}
302 	return (mp->mb_itf);
303 }
304 
305 struct message *
setdot(struct message * mp)306 setdot(struct message *mp)
307 {
308 	if (dot != mp) {
309 		prevdot = dot;
310 		did_print_dot = 0;
311 	}
312 	dot = mp;
313 	uncollapse1(dot, 0);
314 	return dot;
315 }
316 
317 /*
318  * Take the data out of the passed ghost file and toss it into
319  * a dynamically allocated message structure.
320  */
321 static void
makemessage(void)322 makemessage(void)
323 {
324 	if (msgCount == 0)
325 		append(NULL);
326 	setdot(message);
327 	message[msgCount].m_size = 0;
328 	message[msgCount].m_lines = 0;
329 }
330 
331 /*
332  * Append the passed message descriptor onto the message structure.
333  */
334 static void
append(struct message * mp)335 append(struct message *mp)
336 {
337 	if (msgCount + 1 >= msgspace)
338 		message = srealloc(message, (msgspace += 64) * sizeof *message);
339 	if (msgCount > 0)
340 		message[msgCount - 1] = *mp;
341 }
342 
343 /*
344  * Delete a file, but only if the file is a plain file.
345  */
346 int
rm(char * name)347 rm(char *name)
348 {
349 	struct stat sb;
350 
351 	if (stat(name, &sb) < 0)
352 		return(-1);
353 	if (!S_ISREG(sb.st_mode)) {
354 		errno = EISDIR;
355 		return(-1);
356 	}
357 	return(unlink(name));
358 }
359 
360 static int sigdepth;		/* depth of holdsigs() */
361 static sigset_t nset, oset;
362 /*
363  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
364  */
365 void
holdsigs(void)366 holdsigs(void)
367 {
368 
369 	if (sigdepth++ == 0) {
370 		sigemptyset(&nset);
371 		sigaddset(&nset, SIGHUP);
372 		sigaddset(&nset, SIGINT);
373 		sigaddset(&nset, SIGQUIT);
374 		sigprocmask(SIG_BLOCK, &nset, &oset);
375 	}
376 }
377 
378 /*
379  * Release signals SIGHUP, SIGINT, and SIGQUIT.
380  */
381 void
relsesigs(void)382 relsesigs(void)
383 {
384 
385 	if (--sigdepth == 0)
386 		sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
387 }
388 
389 /*
390  * Determine the size of the file possessed by
391  * the passed buffer.
392  */
393 off_t
fsize(FILE * iob)394 fsize(FILE *iob)
395 {
396 	struct stat sbuf;
397 
398 	if (fstat(fileno(iob), &sbuf) < 0)
399 		return 0;
400 	return sbuf.st_size;
401 }
402 
403 /*
404  * Evaluate the string given as a new mailbox name.
405  * Supported meta characters:
406  *	%	for my system mail box
407  *	%user	for user's system mail box
408  *	#	for previous file
409  *	&	invoker's mbox file
410  *	+file	file in folder directory
411  *	any shell meta character
412  * Return the file name as a dynamic string.
413  */
414 char *
expand(char * name)415 expand(char *name)
416 {
417 	char xname[PATHSIZE];
418 	char foldbuf[PATHSIZE];
419 	struct shortcut *sh;
420 
421 	/*
422 	 * The order of evaluation is "%" and "#" expand into constants.
423 	 * "&" can expand into "+".  "+" can expand into shell meta characters.
424 	 * Shell meta characters expand into constants.
425 	 * This way, we make no recursive expansion.
426 	 */
427 	if ((sh = get_shortcut(name)) != NULL)
428 		name = sh->sh_long;
429 next:
430 	switch (*name) {
431 	case '%':
432 		if (name[1] == ':' && name[2]) {
433 			name = &name[2];
434 			goto next;
435 		}
436 		findmail(name[1] ? name + 1 : myname, name[1] != '\0' || uflag,
437 				xname, sizeof xname);
438 		return savestr(xname);
439 	case '#':
440 		if (name[1] != 0)
441 			break;
442 		if (prevfile[0] == 0) {
443 			printf(catgets(catd, CATSET, 80, "No previous file\n"));
444 			return NULL;
445 		}
446 		return savestr(prevfile);
447 	case '&':
448 		if (name[1] == 0 && (name = value("MBOX")) == NULL)
449 			name = "~/mbox";
450 		/* fall through */
451 	}
452 	if (name[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
453 		snprintf(xname, sizeof xname, "%s/%s", protbase(mailname),
454 				&name[1]);
455 		name = savestr(xname);
456 	}
457 	if (name[0] == '+' && getfold(foldbuf, sizeof foldbuf) >= 0) {
458 		if (which_protocol(foldbuf) == PROTO_IMAP &&
459 				strcmp(foldbuf, protbase(foldbuf)))
460 		snprintf(xname, sizeof xname, "%s%s", foldbuf, name+1);
461 		else
462 			snprintf(xname, sizeof xname, "%s/%s", foldbuf, name+1);
463 		name = savestr(xname);
464 		if (foldbuf[0] == '%' && foldbuf[1] == ':')
465 			goto next;
466 	}
467 	/* catch the most common shell meta character */
468 	if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
469 		snprintf(xname, sizeof xname, "%s%s", homedir, name + 1);
470 		name = savestr(xname);
471 	}
472 	if (!anyof(name, "|&;<>~{}()[]*?$`'\"\\"))
473 		return name;
474 	if (which_protocol(name) == PROTO_FILE)
475 		return globname(name);
476 	else
477 		return name;
478 }
479 
480 static char *
globname(char * name)481 globname(char *name)
482 {
483 #ifdef	HAVE_WORDEXP
484 	wordexp_t we;
485 	char *cp;
486 	sigset_t nset;
487 	int i;
488 
489 	/*
490 	 * Some systems (notably Open UNIX 8.0.0) fork a shell for
491 	 * wordexp() and wait for it; waiting will fail if our SIGCHLD
492 	 * handler is active.
493 	 */
494 	sigemptyset(&nset);
495 	sigaddset(&nset, SIGCHLD);
496 	sigprocmask(SIG_BLOCK, &nset, NULL);
497 	i = wordexp(name, &we, 0);
498 	sigprocmask(SIG_UNBLOCK, &nset, NULL);
499 	switch (i) {
500 	case 0:
501 		break;
502 	case WRDE_NOSPACE:
503 		fprintf(stderr, catgets(catd, CATSET, 83,
504 				"\"%s\": Expansion buffer overflow.\n"), name);
505 		return NULL;
506 	case WRDE_BADCHAR:
507 	case WRDE_SYNTAX:
508 	default:
509 		fprintf(stderr, catgets(catd, CATSET, 242,
510 				"Syntax error in \"%s\"\n"), name);
511 		return NULL;
512 	}
513 	switch (we.we_wordc) {
514 	case 1:
515 		cp = savestr(we.we_wordv[0]);
516 		break;
517 	case 0:
518 		fprintf(stderr, catgets(catd, CATSET, 82,
519 					"\"%s\": No match.\n"), name);
520 		cp = NULL;
521 		break;
522 	default:
523 		fprintf(stderr, catgets(catd, CATSET, 84,
524 				"\"%s\": Ambiguous.\n"), name);
525 		cp = NULL;
526 	}
527 	wordfree(&we);
528 	return cp;
529 #else	/* !HAVE_WORDEXP */
530 	char xname[PATHSIZE];
531 	char cmdbuf[PATHSIZE];		/* also used for file names */
532 	int pid, l;
533 	char *cp, *shell;
534 	int pivec[2];
535 	extern int wait_status;
536 	struct stat sbuf;
537 
538 	if (pipe(pivec) < 0) {
539 		perror("pipe");
540 		return name;
541 	}
542 	snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
543 	if ((shell = value("SHELL")) == NULL)
544 		shell = SHELL;
545 	pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
546 	if (pid < 0) {
547 		close(pivec[0]);
548 		close(pivec[1]);
549 		return NULL;
550 	}
551 	close(pivec[1]);
552 again:
553 	l = read(pivec[0], xname, sizeof xname);
554 	if (l < 0) {
555 		if (errno == EINTR)
556 			goto again;
557 		perror("read");
558 		close(pivec[0]);
559 		return NULL;
560 	}
561 	close(pivec[0]);
562 	if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
563 		fprintf(stderr, catgets(catd, CATSET, 81,
564 				"\"%s\": Expansion failed.\n"), name);
565 		return NULL;
566 	}
567 	if (l == 0) {
568 		fprintf(stderr, catgets(catd, CATSET, 82,
569 					"\"%s\": No match.\n"), name);
570 		return NULL;
571 	}
572 	if (l == sizeof xname) {
573 		fprintf(stderr, catgets(catd, CATSET, 83,
574 				"\"%s\": Expansion buffer overflow.\n"), name);
575 		return NULL;
576 	}
577 	xname[l] = 0;
578 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
579 		;
580 	cp[1] = '\0';
581 	if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
582 		fprintf(stderr, catgets(catd, CATSET, 84,
583 				"\"%s\": Ambiguous.\n"), name);
584 		return NULL;
585 	}
586 	return savestr(xname);
587 #endif	/* !HAVE_WORDEXP */
588 }
589 
590 /*
591  * Determine the current folder directory name.
592  */
593 int
getfold(char * name,int size)594 getfold(char *name, int size)
595 {
596 	char *folder;
597 	enum protocol	p;
598 
599 	if ((folder = value("folder")) == NULL)
600 		return (-1);
601 	if (*folder == '/' || (p = which_protocol(folder)) != PROTO_FILE &&
602 			p != PROTO_MAILDIR) {
603 		strncpy(name, folder, size);
604 		name[size-1]='\0';
605 	} else {
606 		snprintf(name, size, "%s/%s", homedir, folder);
607 	}
608 	return (0);
609 }
610 
611 /*
612  * Return the name of the dead.letter file.
613  */
614 char *
getdeadletter(void)615 getdeadletter(void)
616 {
617 	char *cp;
618 
619 	if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
620 		cp = expand("~/dead.letter");
621 	else if (*cp != '/') {
622 		char *buf;
623 		size_t sz;
624 
625 		buf = ac_alloc(sz = strlen(cp) + 3);
626 		snprintf(buf, sz, "~/%s", cp);
627 		snprintf(buf, sz, "~/%s", cp);
628 		cp = expand(buf);
629 		ac_free(buf);
630 	}
631 	return cp;
632 }
633 
634 /*
635  * line is a buffer with the result of fgets(). Returns the first
636  * newline or the last character read.
637  */
638 static size_t
length_of_line(const char * line,size_t linesize)639 length_of_line(const char *line, size_t linesize)
640 {
641 	register size_t i;
642 
643 	/*
644 	 * Last character is always '\0' and was added by fgets.
645 	 */
646 	linesize--;
647 	for (i = 0; i < linesize; i++)
648 		if (line[i] == '\n')
649 			break;
650 	return i < linesize ? i + 1 : linesize;
651 }
652 
653 /*
654  * fgets replacement to handle lines of arbitrary size and with
655  * embedded \0 characters.
656  * line - line buffer. *line be NULL.
657  * linesize - allocated size of line buffer.
658  * count - maximum characters to read. May be NULL.
659  * llen - length_of_line(*line).
660  * fp - input FILE.
661  * appendnl - always terminate line with \n, append if necessary.
662  */
663 char *
fgetline(char ** line,size_t * linesize,size_t * count,size_t * llen,FILE * fp,int appendnl)664 fgetline(char **line, size_t *linesize, size_t *count, size_t *llen,
665 		FILE *fp, int appendnl)
666 {
667 	long i_llen, sz;
668 
669 	if (count == NULL)
670 		/*
671 		 * If we have no count, we cannot determine where the
672 		 * characters returned by fgets() end if there was no
673 		 * newline. We have to read one character at one.
674 		 */
675 		return fgetline_byone(line, linesize, llen, fp, appendnl, 0);
676 	if (*line == NULL || *linesize < LINESIZE)
677 		*line = srealloc(*line, *linesize = LINESIZE);
678 	sz = *linesize <= *count ? *linesize : *count + 1;
679 	if (sz <= 1 || fgets(*line, sz, fp) == NULL)
680 		/*
681 		 * Leave llen untouched; it is used to determine whether
682 		 * the last line was \n-terminated in some callers.
683 		 */
684 		return NULL;
685 	i_llen = length_of_line(*line, sz);
686 	*count -= i_llen;
687 	while ((*line)[i_llen - 1] != '\n') {
688 		*line = srealloc(*line, *linesize += 256);
689 		sz = *linesize - i_llen;
690 		sz = (sz <= *count ? sz : *count + 1);
691 		if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
692 			if (appendnl) {
693 				(*line)[i_llen++] = '\n';
694 				(*line)[i_llen] = '\0';
695 			}
696 			break;
697 		}
698 		sz = length_of_line(&(*line)[i_llen], sz);
699 		i_llen += sz;
700 		*count -= sz;
701 	}
702 	if (llen)
703 		*llen = i_llen;
704 	return *line;
705 }
706 
707 /*
708  * Read a line, one character at once.
709  */
710 static char *
fgetline_byone(char ** line,size_t * linesize,size_t * llen,FILE * fp,int appendnl,size_t n)711 fgetline_byone(char **line, size_t *linesize, size_t *llen,
712 		FILE *fp, int appendnl, size_t n)
713 {
714 	int c;
715 
716 	if (*line == NULL || *linesize < LINESIZE + n + 1)
717 		*line = srealloc(*line, *linesize = LINESIZE + n + 1);
718 	for (;;) {
719 		if (n >= *linesize - 128)
720 			*line = srealloc(*line, *linesize += 256);
721 		c = getc(fp);
722 		if (c != EOF) {
723 			(*line)[n++] = c;
724 			(*line)[n] = '\0';
725 			if (c == '\n')
726 				break;
727 		} else {
728 			if (n > 0) {
729 				if (appendnl) {
730 					(*line)[n++] = '\n';
731 					(*line)[n] = '\0';
732 				}
733 				break;
734 			} else
735 				return NULL;
736 		}
737 	}
738 	if (llen)
739 		*llen = n;
740 	return *line;
741 }
742 
743 static enum okay
get_header(struct message * mp)744 get_header(struct message *mp)
745 {
746 	switch (mb.mb_type) {
747 	case MB_FILE:
748 	case MB_MAILDIR:
749 		return OKAY;
750 	case MB_POP3:
751 		return pop3_header(mp);
752 	case MB_IMAP:
753 	case MB_CACHE:
754 		return imap_header(mp);
755 	case MB_VOID:
756 		return STOP;
757 	}
758 	/*NOTREACHED*/
759 	return STOP;
760 }
761 
762 enum okay
get_body(struct message * mp)763 get_body(struct message *mp)
764 {
765 	switch (mb.mb_type) {
766 	case MB_FILE:
767 	case MB_MAILDIR:
768 		return OKAY;
769 	case MB_POP3:
770 		return pop3_body(mp);
771 	case MB_IMAP:
772 	case MB_CACHE:
773 		return imap_body(mp);
774 	case MB_VOID:
775 		return STOP;
776 	}
777 	/*NOTREACHED*/
778 	return STOP;
779 }
780 
781 #ifdef	HAVE_SOCKETS
782 static long xwrite(int fd, const char *data, size_t sz);
783 
784 static long
xwrite(int fd,const char * data,size_t sz)785 xwrite(int fd, const char *data, size_t sz)
786 {
787 	long wo, wt = 0;
788 
789 	do {
790 		if ((wo = write(fd, data + wt, sz - wt)) < 0) {
791 			if (errno == EINTR)
792 				continue;
793 			else
794 				return -1;
795 		}
796 		wt += wo;
797 	} while (wt < sz);
798 	return sz;
799 }
800 
801 int
sclose(struct sock * sp)802 sclose(struct sock *sp)
803 {
804 	int	i;
805 
806 	if (sp->s_fd > 0) {
807 		if (sp->s_onclose != NULL)
808 			(*sp->s_onclose)();
809 #if defined (USE_NSS)
810 		if (sp->s_use_ssl) {
811 			sp->s_use_ssl = 0;
812 			i = PR_Close(sp->s_prfd) == PR_SUCCESS ? 0 : -1;
813 			sp->s_prfd = NULL;
814 		} else
815 #elif defined (USE_OPENSSL)
816 		if (sp->s_use_ssl) {
817 			sp->s_use_ssl = 0;
818 			SSL_shutdown(sp->s_ssl);
819 			SSL_free(sp->s_ssl);
820 			sp->s_ssl = NULL;
821 			SSL_CTX_free(sp->s_ctx);
822 			sp->s_ctx = NULL;
823 		}
824 #endif	/* USE_SSL */
825 		{
826 			i = close(sp->s_fd);
827 		}
828 		sp->s_fd = -1;
829 		return i;
830 	}
831 	sp->s_fd = -1;
832 	return 0;
833 }
834 
835 enum okay
swrite(struct sock * sp,const char * data)836 swrite(struct sock *sp, const char *data)
837 {
838 	return swrite1(sp, data, strlen(data), 0);
839 }
840 
841 enum okay
swrite1(struct sock * sp,const char * data,int sz,int use_buffer)842 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
843 {
844 	int	x;
845 
846 	if (use_buffer > 0) {
847 		int	di;
848 		enum okay	ok;
849 
850 		if (sp->s_wbuf == NULL) {
851 			sp->s_wbufsize = 4096;
852 			sp->s_wbuf = smalloc(sp->s_wbufsize);
853 			sp->s_wbufpos = 0;
854 		}
855 		while (sp->s_wbufpos + sz > sp->s_wbufsize) {
856 			di = sp->s_wbufsize - sp->s_wbufpos;
857 			sz -= di;
858 			if (sp->s_wbufpos > 0) {
859 				memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
860 				ok = swrite1(sp, sp->s_wbuf,
861 						sp->s_wbufsize, -1);
862 			} else
863 				ok = swrite1(sp, data,
864 						sp->s_wbufsize, -1);
865 			if (ok != OKAY)
866 				return STOP;
867 			data += di;
868 			sp->s_wbufpos = 0;
869 		}
870 		if (sz == sp->s_wbufsize) {
871 			ok = swrite1(sp, data, sp->s_wbufsize, -1);
872 			if (ok != OKAY)
873 				return STOP;
874 		} else if (sz) {
875 			memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
876 			sp->s_wbufpos += sz;
877 		}
878 		return OKAY;
879 	} else if (use_buffer == 0 && sp->s_wbuf != NULL &&
880 			sp->s_wbufpos > 0) {
881 		x = sp->s_wbufpos;
882 		sp->s_wbufpos = 0;
883 		if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
884 			return STOP;
885 	}
886 	if (sz == 0)
887 		return OKAY;
888 #if defined (USE_NSS)
889 	if (sp->s_use_ssl) {
890 		x = PR_Write(sp->s_prfd, data, sz);
891 	} else
892 #elif defined (USE_OPENSSL)
893 	if (sp->s_use_ssl) {
894 ssl_retry:	x = SSL_write(sp->s_ssl, data, sz);
895 		if (x < 0) {
896 			switch (SSL_get_error(sp->s_ssl, x)) {
897 			case SSL_ERROR_WANT_READ:
898 			case SSL_ERROR_WANT_WRITE:
899 				goto ssl_retry;
900 			}
901 		}
902 	} else
903 #endif	/* USE_SSL */
904 	{
905 		x = xwrite(sp->s_fd, data, sz);
906 	}
907 	if (x != sz) {
908 		char	o[512];
909 		snprintf(o, sizeof o, "%s write error",
910 				sp->s_desc ? sp->s_desc : "socket");
911 #if defined (USE_NSS)
912 		sp->s_use_ssl ? nss_gen_err("%s", o) : perror(o);
913 #elif defined (USE_OPENSSL)
914 		sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
915 #else	/* !USE_SSL */
916 		perror(o);
917 #endif	/* !USE_SSL */
918 		if (x < 0)
919 			sclose(sp);
920 		return STOP;
921 	}
922 	return OKAY;
923 }
924 
925 int
sgetline(char ** line,size_t * linesize,size_t * linelen,struct sock * sp)926 sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp)
927 {
928 	char	*lp = *line;
929 
930 	if (sp->s_rsz < 0) {
931 		sclose(sp);
932 		return sp->s_rsz;
933 	}
934 	do {
935 		if (*line == NULL || lp > &(*line)[*linesize - 128]) {
936 			size_t diff = lp - *line;
937 			*line = srealloc(*line, *linesize += 256);
938 			lp = &(*line)[diff];
939 		}
940 		if (sp->s_rbufptr == NULL ||
941 				sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
942 #if defined (USE_NSS)
943 			if (sp->s_use_ssl) {
944 				if ((sp->s_rsz = PR_Read(sp->s_prfd,
945 						sp->s_rbuf,
946 						sizeof sp->s_rbuf)) <= 0) {
947 					if (sp->s_rsz < 0) {
948 						char	o[512];
949 						snprintf(o, sizeof o, "%s",
950 							sp->s_desc ?
951 								sp->s_desc :
952 								"socket");
953 						nss_gen_err("%s", o);
954 					}
955 					break;
956 				}
957 			} else
958 #elif defined (USE_OPENSSL)
959 			if (sp->s_use_ssl) {
960 		ssl_retry:	if ((sp->s_rsz = SSL_read(sp->s_ssl,
961 						sp->s_rbuf,
962 						sizeof sp->s_rbuf)) <= 0) {
963 					if (sp->s_rsz < 0) {
964 						char	o[512];
965 						switch(SSL_get_error(sp->s_ssl,
966 							sp->s_rsz)) {
967 						case SSL_ERROR_WANT_READ:
968 						case SSL_ERROR_WANT_WRITE:
969 							goto ssl_retry;
970 						}
971 						snprintf(o, sizeof o, "%s",
972 							sp->s_desc ?
973 								sp->s_desc :
974 								"socket");
975 						ssl_gen_err("%s", o);
976 
977 					}
978 					break;
979 				}
980 			} else
981 #endif	/* USE_SSL */
982 			{
983 			again:	if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
984 						sizeof sp->s_rbuf)) <= 0) {
985 					if (sp->s_rsz < 0) {
986 						char	o[512];
987 						if (errno == EINTR)
988 							goto again;
989 						snprintf(o, sizeof o, "%s",
990 							sp->s_desc ?
991 								sp->s_desc :
992 								"socket");
993 						perror(o);
994 					}
995 					break;
996 				}
997 			}
998 			sp->s_rbufptr = sp->s_rbuf;
999 		}
1000 	} while ((*lp++ = *sp->s_rbufptr++) != '\n');
1001 	*lp = '\0';
1002 	if (linelen)
1003 		*linelen = lp - *line;
1004 	return lp - *line;
1005 }
1006 
1007 enum okay
sopen(const char * xserver,struct sock * sp,int use_ssl,const char * uhp,const char * portstr,int verbose)1008 sopen(const char *xserver, struct sock *sp, int use_ssl,
1009 		const char *uhp, const char *portstr, int verbose)
1010 {
1011 #ifdef	HAVE_IPv6_FUNCS
1012 	char	hbuf[NI_MAXHOST];
1013 	struct addrinfo	hints, *res0, *res;
1014 #else	/* !HAVE_IPv6_FUNCS */
1015 	struct sockaddr_in	servaddr;
1016 	struct in_addr	**pptr;
1017 	struct hostent	*hp;
1018 	struct servent	*ep;
1019 	unsigned short	port = 0;
1020 #endif	/* !HAVE_IPv6_FUNCS */
1021 	int	sockfd;
1022 	char	*cp;
1023 	char	*server = (char *)xserver;
1024 
1025 	if ((cp = strchr(server, ':')) != NULL) {
1026 		portstr = &cp[1];
1027 #ifndef	HAVE_IPv6_FUNCS
1028 		port = strtol(portstr, NULL, 10);
1029 #endif	/* HAVE_IPv6_FUNCS */
1030 		server = salloc(cp - xserver + 1);
1031 		memcpy(server, xserver, cp - xserver);
1032 		server[cp - xserver] = '\0';
1033 	}
1034 #ifdef	HAVE_IPv6_FUNCS
1035 	memset(&hints, 0, sizeof hints);
1036 	hints.ai_socktype = SOCK_STREAM;
1037 	if (verbose)
1038 		fprintf(stderr, "Resolving host %s . . .", server);
1039 	if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1040 		fprintf(stderr, catgets(catd, CATSET, 252,
1041 				"Could not resolve host: %s\n"), server);
1042 		return STOP;
1043 	} else if (verbose)
1044 		fprintf(stderr, " done.\n");
1045 	sockfd = -1;
1046 	for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1047 		if (verbose) {
1048 			if (getnameinfo(res->ai_addr, res->ai_addrlen,
1049 						hbuf, sizeof hbuf, NULL, 0,
1050 						NI_NUMERICHOST) != 0)
1051 				strcpy(hbuf, "unknown host");
1052 			fprintf(stderr, catgets(catd, CATSET, 192,
1053 					"Connecting to %s . . ."), hbuf);
1054 		}
1055 		if ((sockfd = socket(res->ai_family, res->ai_socktype,
1056 				res->ai_protocol)) >= 0) {
1057 			if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1058 				close(sockfd);
1059 				sockfd = -1;
1060 			}
1061 		}
1062 	}
1063 	if (sockfd < 0) {
1064 		perror(catgets(catd, CATSET, 254, "could not connect"));
1065 		freeaddrinfo(res0);
1066 		return STOP;
1067 	}
1068 	freeaddrinfo(res0);
1069 #else	/* !HAVE_IPv6_FUNCS */
1070 	if (port == 0) {
1071 		if ((ep = getservbyname((char *)portstr, "tcp")) == NULL) {
1072 			if (equal(portstr, "smtp"))
1073 				port = htons(25);
1074 			else if (equal(portstr, "smtps"))
1075 				port = htons(465);
1076 			else if (equal(portstr, "imap"))
1077 				port = htons(143);
1078 			else if (equal(portstr, "imaps"))
1079 				port = htons(993);
1080 			else if (equal(portstr, "pop3"))
1081 				port = htons(110);
1082 			else if (equal(portstr, "pop3s"))
1083 				port = htons(995);
1084 			else {
1085 				fprintf(stderr, catgets(catd, CATSET, 251,
1086 					"Unknown service: %s\n"), portstr);
1087 				return STOP;
1088 			}
1089 		} else
1090 			port = ep->s_port;
1091 	} else
1092 		port = htons(port);
1093 	if (verbose)
1094 		fprintf(stderr, "Resolving host %s . . .", server);
1095 	if ((hp = gethostbyname(server)) == NULL) {
1096 		fprintf(stderr, catgets(catd, CATSET, 252,
1097 				"Could not resolve host: %s\n"), server);
1098 		return STOP;
1099 	} else if (verbose)
1100 		fprintf(stderr, " done.\n");
1101 	pptr = (struct in_addr **)hp->h_addr_list;
1102 	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1103 		perror(catgets(catd, CATSET, 253, "could not create socket"));
1104 		return STOP;
1105 	}
1106 	memset(&servaddr, 0, sizeof servaddr);
1107 	servaddr.sin_family = AF_INET;
1108 	servaddr.sin_port = port;
1109 	memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1110 	if (verbose)
1111 		fprintf(stderr, catgets(catd, CATSET, 192,
1112 				"Connecting to %s . . ."), inet_ntoa(**pptr));
1113 	if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1114 			!= 0) {
1115 		perror(catgets(catd, CATSET, 254, "could not connect"));
1116 		return STOP;
1117 	}
1118 #endif	/* !HAVE_IPv6_FUNCS */
1119 	if (verbose)
1120 		fputs(catgets(catd, CATSET, 193, " connected.\n"), stderr);
1121 	memset(sp, 0, sizeof *sp);
1122 	sp->s_fd = sockfd;
1123 #if defined (USE_SSL)
1124 	if (use_ssl) {
1125 		enum okay ok;
1126 
1127 		if ((ok = ssl_open(server, sp, uhp)) != OKAY)
1128 			sclose(sp);
1129 		return ok;
1130 	}
1131 #endif	/* USE_SSL */
1132 	return OKAY;
1133 }
1134 #endif	/* HAVE_SOCKETS */
1135