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