outof(): Mail NULL fix..
[s-mailx.git] / fio.c
blob2dcb3dcb243ab95658a77a8c35eafdc2d0b27049
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "rcv.h"
41 #include <sys/stat.h>
42 #include <sys/file.h>
43 #include <sys/wait.h>
44 #ifdef HAVE_WORDEXP
45 #include <wordexp.h>
46 #endif /* HAVE_WORDEXP */
47 #include <unistd.h>
49 #if defined (USE_NSS)
50 #include <nss.h>
51 #include <ssl.h>
52 #elif defined (USE_OPENSSL)
53 #include <openssl/ssl.h>
54 #include <openssl/err.h>
55 #include <openssl/x509v3.h>
56 #include <openssl/x509.h>
57 #include <openssl/rand.h>
58 #endif /* USE_SSL */
59 #ifdef HAVE_SOCKETS
60 #include <sys/socket.h>
61 #include <netdb.h>
62 #include <netinet/in.h>
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
65 #endif /* HAVE_ARPA_INET_H */
66 #endif /* HAVE_SOCKETS */
68 #include <errno.h>
69 #include "extern.h"
72 * Mail -- a mail program
74 * File I/O.
77 static void makemessage(void);
78 static void append(struct message *mp);
79 static char *globname(char const*name);
80 static size_t length_of_line(const char *line, size_t linesize);
81 static char *fgetline_byone(char **line, size_t *linesize, size_t *llen,
82 FILE *fp, int appendnl, size_t n);
83 static enum okay get_header(struct message *mp);
86 * Set up the input pointers while copying the mail file into /tmp.
88 void
89 setptr(FILE *ibuf, off_t offset)
91 int c;
92 size_t count;
93 char *cp, *cp2;
94 struct message this;
95 int maybe, inhead, thiscnt;
96 char *linebuf = NULL;
97 size_t linesize = 0, filesize;
99 maybe = 1;
100 inhead = 0;
101 thiscnt = 0;
102 memset(&this, 0, sizeof this);
103 this.m_flag = MUSED|MNEW|MNEWEST;
104 filesize = mailsize - offset;
105 offset = ftell(mb.mb_otf);
106 for (;;) {
107 if (fgetline(&linebuf, &linesize, &filesize, &count, ibuf, 0)
108 == NULL) {
109 this.m_xsize = this.m_size;
110 this.m_xlines = this.m_lines;
111 this.m_have = HAVE_HEADER|HAVE_BODY;
112 if (thiscnt > 0)
113 append(&this);
114 makemessage();
115 if (linebuf)
116 free(linebuf);
117 return;
119 #ifdef notdef
120 if (linebuf[0] == '\0')
121 linebuf[0] = '.';
122 #endif
123 fwrite(linebuf, sizeof *linebuf, count, mb.mb_otf);
124 if (ferror(mb.mb_otf)) {
125 perror("/tmp");
126 exit(1);
128 if (linebuf[count - 1] == '\n')
129 linebuf[count - 1] = '\0';
130 if (maybe && linebuf[0] == 'F' && is_head(linebuf, count)) {
131 this.m_xsize = this.m_size;
132 this.m_xlines = this.m_lines;
133 this.m_have = HAVE_HEADER|HAVE_BODY;
134 if (thiscnt++ > 0)
135 append(&this);
136 msgCount++;
137 this.m_flag = MUSED|MNEW|MNEWEST;
138 this.m_size = 0;
139 this.m_lines = 0;
140 this.m_block = mailx_blockof(offset);
141 this.m_offset = mailx_offsetof(offset);
142 inhead = 1;
143 } else if (linebuf[0] == 0) {
144 inhead = 0;
145 } else if (inhead) {
146 for (cp = linebuf, cp2 = "status";; cp++) {
147 if ((c = *cp2++) == 0) {
148 while (c = *cp++, whitechar(c));
149 if (cp[-1] != ':')
150 break;
151 while ((c = *cp++) != '\0')
152 if (c == 'R')
153 this.m_flag |= MREAD;
154 else if (c == 'O')
155 this.m_flag &= ~MNEW;
156 break;
158 if (*cp != c && *cp != upperconv(c))
159 break;
161 for (cp = linebuf, cp2 = "x-status";; cp++) {
162 if ((c = *cp2++) == 0) {
163 while (c = *cp++, whitechar(c));
164 if (cp[-1] != ':')
165 break;
166 while ((c = *cp++) != '\0')
167 if (c == 'F')
168 this.m_flag |= MFLAGGED;
169 else if (c == 'A')
170 this.m_flag|=MANSWERED;
171 else if (c == 'T')
172 this.m_flag|=MDRAFTED;
173 break;
175 if (*cp != c && *cp != upperconv(c))
176 break;
179 offset += count;
180 this.m_size += count;
181 this.m_lines++;
182 maybe = linebuf[0] == 0;
184 /*NOTREACHED*/
188 * Drop the passed line onto the passed output buffer.
189 * If a write error occurs, return -1, else the count of
190 * characters written, including the newline.
193 putline(FILE *obuf, char *linebuf, size_t count)
195 fwrite(linebuf, sizeof *linebuf, count, obuf);
196 putc('\n', obuf);
197 if (ferror(obuf))
198 return (-1);
199 return (count + 1);
203 * Read up a line from the specified input into the line
204 * buffer. Return the number of characters read. Do not
205 * include the newline at the end.
207 * n is the number of characters already read.
210 readline_restart(FILE *ibuf, char **linebuf, size_t *linesize, size_t n)
212 long sz;
214 clearerr(ibuf);
216 * Interrupts will cause trouble if we are inside a stdio call. As
217 * this is only relevant if input comes from a terminal, we can simply
218 * bypass it by read() then.
220 if (fileno(ibuf) == 0 && is_a_tty[0]) {
221 if (*linebuf == NULL || *linesize < LINESIZE + n + 1)
222 *linebuf = srealloc(*linebuf,
223 *linesize = LINESIZE + n + 1);
224 for (;;) {
225 if (n >= *linesize - 128)
226 *linebuf = srealloc(*linebuf, *linesize += 256);
227 again:
228 sz = read(0, *linebuf + n, *linesize - n - 1);
229 if (sz > 0) {
230 n += sz;
231 (*linebuf)[n] = '\0';
232 if (n > 0 && (*linebuf)[n - 1] == '\n')
233 break;
234 } else {
235 if (sz < 0 && errno == EINTR)
236 goto again;
237 if (n > 0) {
238 if ((*linebuf)[n - 1] != '\n') {
239 (*linebuf)[n++] = '\n';
240 (*linebuf)[n] = '\0';
242 break;
243 } else
244 return -1;
247 } else {
249 * Not reading from standard input or standard input not
250 * a terminal. We read one char at a time as it is the
251 * only way to get lines with embedded NUL characters in
252 * standard stdio.
254 if (fgetline_byone(linebuf, linesize, &n, ibuf, 1, n) == NULL)
255 return -1;
257 if (n > 0 && (*linebuf)[n - 1] == '\n')
258 (*linebuf)[--n] = '\0';
259 return n;
263 * Return a file buffer all ready to read up the
264 * passed message pointer.
266 FILE *
267 setinput(struct mailbox *mp, struct message *m, enum needspec need)
269 enum okay ok = STOP;
271 switch (need) {
272 case NEED_HEADER:
273 if (m->m_have & HAVE_HEADER)
274 ok = OKAY;
275 else
276 ok = get_header(m);
277 break;
278 case NEED_BODY:
279 if (m->m_have & HAVE_BODY)
280 ok = OKAY;
281 else
282 ok = get_body(m);
283 break;
284 case NEED_UNSPEC:
285 ok = OKAY;
286 break;
288 if (ok != OKAY)
289 return NULL;
290 fflush(mp->mb_otf);
291 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
292 m->m_offset), SEEK_SET) < 0) {
293 perror("fseek");
294 panic(catgets(catd, CATSET, 77, "temporary file seek"));
296 return (mp->mb_itf);
299 struct message *
300 setdot(struct message *mp)
302 if (dot != mp) {
303 prevdot = dot;
304 did_print_dot = 0;
306 dot = mp;
307 uncollapse1(dot, 0);
308 return dot;
312 * Take the data out of the passed ghost file and toss it into
313 * a dynamically allocated message structure.
315 static void
316 makemessage(void)
318 if (msgCount == 0)
319 append(NULL);
320 setdot(message);
321 message[msgCount].m_size = 0;
322 message[msgCount].m_lines = 0;
326 * Append the passed message descriptor onto the message structure.
328 static void
329 append(struct message *mp)
331 if (msgCount + 1 >= msgspace)
332 message = srealloc(message, (msgspace += 64) * sizeof *message);
333 if (msgCount > 0)
334 message[msgCount - 1] = *mp;
338 * Delete a file, but only if the file is a plain file.
341 rm(char *name)
343 struct stat sb;
345 if (stat(name, &sb) < 0)
346 return(-1);
347 if (!S_ISREG(sb.st_mode)) {
348 errno = EISDIR;
349 return(-1);
351 return(unlink(name));
354 static int sigdepth; /* depth of holdsigs() */
355 static sigset_t nset, oset;
357 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
359 void
360 holdsigs(void)
363 if (sigdepth++ == 0) {
364 sigemptyset(&nset);
365 sigaddset(&nset, SIGHUP);
366 sigaddset(&nset, SIGINT);
367 sigaddset(&nset, SIGQUIT);
368 sigprocmask(SIG_BLOCK, &nset, &oset);
373 * Release signals SIGHUP, SIGINT, and SIGQUIT.
375 void
376 relsesigs(void)
379 if (--sigdepth == 0)
380 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
384 * Determine the size of the file possessed by
385 * the passed buffer.
387 off_t
388 fsize(FILE *iob)
390 struct stat sbuf;
392 if (fstat(fileno(iob), &sbuf) < 0)
393 return 0;
394 return sbuf.st_size;
398 * Evaluate a string and expand file meta characters:
399 * +file file in folder directory
400 * any shell meta character
401 * Return the file name as a dynamic string.
403 char *
404 file_expand(char const*name)
406 char foldbuf[PATHSIZE];
407 struct str s;
408 struct shortcut *sh;
409 char const*orig = name;
411 if ((sh = get_shortcut(name)) != NULL)
412 name = sh->sh_long;
414 s.s = NULL;
416 if (name[0] == '+' && getfold(foldbuf, sizeof foldbuf) >= 0) {
417 switch (which_protocol(foldbuf)) {
418 /* XXX file_expand(): really "ok!" PROTO_MAILDIR misuse? */
419 case PROTO_FILE:
420 case PROTO_MAILDIR:
421 (void)str_concat_csvl(&s, foldbuf, "/", name + 1, NULL);
422 if (s.l >= PATHSIZE) {
423 fprintf(stderr, tr(83,
424 "\"%s\": Expansion buffer overflow.\n"),
425 orig);
426 return (NULL);
428 name = s.s;
429 break;
430 default:
431 fprintf(stderr, tr(280, "\"%s\": only a local file or "
432 "directory may be used\n"), orig);
433 return (NULL);
437 /* Catch the most common shell meta character */
438 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
439 (void)str_concat_csvl(&s, homedir, name + 1, NULL);
440 if (s.l >= PATHSIZE) {
441 fprintf(stderr, tr(83,
442 "\"%s\": Expansion buffer overflow.\n"),
443 orig);
444 return (NULL);
446 name = s.s;
449 if (name[0] != '|' && anyof(name, "|&;<>~{}()[]*?$`'\"\\") &&
450 which_protocol(name) == PROTO_FILE)
451 return (globname(name));
452 return (s.s ? s.s : savestr(name));
456 * Evaluate the string given as a new mailbox name.
457 * Supported meta characters:
458 * % for my system mail box
459 * %user for user's system mail box
460 * # for previous file
461 * & invoker's mbox file
462 * +file file in folder directory
463 * any shell meta character
464 * Return the file name as a dynamic string.
466 char *
467 expand(char *name)
469 char xname[PATHSIZE];
470 char foldbuf[PATHSIZE];
471 struct shortcut *sh;
474 * The order of evaluation is "%" and "#" expand into constants.
475 * "&" can expand into "+". "+" can expand into shell meta characters.
476 * Shell meta characters expand into constants.
477 * This way, we make no recursive expansion.
479 if ((sh = get_shortcut(name)) != NULL)
480 name = sh->sh_long;
481 next:
482 switch (*name) {
483 case '%':
484 if (name[1] == ':' && name[2]) {
485 name = &name[2];
486 goto next;
488 findmail(name[1] ? name + 1 : myname, name[1] != '\0' || uflag,
489 xname, sizeof xname);
490 return savestr(xname);
491 case '#':
492 if (name[1] != 0)
493 break;
494 if (prevfile[0] == 0) {
495 printf(catgets(catd, CATSET, 80, "No previous file\n"));
496 return NULL;
498 return savestr(prevfile);
499 case '&':
500 if (name[1] == 0 && (name = value("MBOX")) == NULL)
501 name = "~/mbox";
502 /* fall through */
504 if (name[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
505 snprintf(xname, sizeof xname, "%s/%s", protbase(mailname),
506 &name[1]);
507 name = savestr(xname);
509 if (name[0] == '+' && getfold(foldbuf, sizeof foldbuf) >= 0) {
510 snprintf(xname, sizeof xname,
511 ((which_protocol(foldbuf) == PROTO_IMAP &&
512 strcmp(foldbuf, protbase(foldbuf)))
513 ? "%s%s" : "%s/%s"),
514 foldbuf, name+1);
515 name = savestr(xname);
516 if (foldbuf[0] == '%' && foldbuf[1] == ':')
517 goto next;
519 /* catch the most common shell meta character */
520 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
521 snprintf(xname, sizeof xname, "%s%s", homedir, name + 1);
522 name = savestr(xname);
524 if (!anyof(name, "|&;<>~{}()[]*?$`'\"\\"))
525 return name;
526 if (which_protocol(name) == PROTO_FILE)
527 return globname(name);
528 else
529 return name;
532 static char *
533 globname(char const*name)
535 #ifdef HAVE_WORDEXP
536 wordexp_t we;
537 char *cp;
538 sigset_t nset;
539 int i;
542 * Some systems (notably Open UNIX 8.0.0) fork a shell for
543 * wordexp() and wait for it; waiting will fail if our SIGCHLD
544 * handler is active.
546 sigemptyset(&nset);
547 sigaddset(&nset, SIGCHLD);
548 sigprocmask(SIG_BLOCK, &nset, NULL);
549 i = wordexp(name, &we, 0);
550 sigprocmask(SIG_UNBLOCK, &nset, NULL);
551 switch (i) {
552 case 0:
553 break;
554 case WRDE_NOSPACE:
555 fprintf(stderr, catgets(catd, CATSET, 83,
556 "\"%s\": Expansion buffer overflow.\n"), name);
557 return NULL;
558 case WRDE_BADCHAR:
559 case WRDE_SYNTAX:
560 default:
561 fprintf(stderr, catgets(catd, CATSET, 242,
562 "Syntax error in \"%s\"\n"), name);
563 return NULL;
565 switch (we.we_wordc) {
566 case 1:
567 cp = savestr(we.we_wordv[0]);
568 break;
569 case 0:
570 fprintf(stderr, catgets(catd, CATSET, 82,
571 "\"%s\": No match.\n"), name);
572 cp = NULL;
573 break;
574 default:
575 fprintf(stderr, catgets(catd, CATSET, 84,
576 "\"%s\": Ambiguous.\n"), name);
577 cp = NULL;
579 wordfree(&we);
580 return cp;
581 #else /* !HAVE_WORDEXP */
582 char xname[PATHSIZE];
583 char cmdbuf[PATHSIZE]; /* also used for file names */
584 int pid, l;
585 char *cp, *shell;
586 int pivec[2];
587 extern int wait_status;
588 struct stat sbuf;
590 if (pipe(pivec) < 0) {
591 perror("pipe");
592 return name;
594 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
595 if ((shell = value("SHELL")) == NULL)
596 shell = SHELL;
597 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
598 if (pid < 0) {
599 close(pivec[0]);
600 close(pivec[1]);
601 return NULL;
603 close(pivec[1]);
604 again:
605 l = read(pivec[0], xname, sizeof xname);
606 if (l < 0) {
607 if (errno == EINTR)
608 goto again;
609 perror("read");
610 close(pivec[0]);
611 return NULL;
613 close(pivec[0]);
614 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
615 fprintf(stderr, catgets(catd, CATSET, 81,
616 "\"%s\": Expansion failed.\n"), name);
617 return NULL;
619 if (l == 0) {
620 fprintf(stderr, catgets(catd, CATSET, 82,
621 "\"%s\": No match.\n"), name);
622 return NULL;
624 if (l == sizeof xname) {
625 fprintf(stderr, catgets(catd, CATSET, 83,
626 "\"%s\": Expansion buffer overflow.\n"), name);
627 return NULL;
629 xname[l] = 0;
630 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
632 cp[1] = '\0';
633 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
634 fprintf(stderr, catgets(catd, CATSET, 84,
635 "\"%s\": Ambiguous.\n"), name);
636 return NULL;
638 return savestr(xname);
639 #endif /* !HAVE_WORDEXP */
643 * Determine the current folder directory name.
646 getfold(char *name, int size)
648 char *folder;
649 enum protocol p;
651 if ((folder = value("folder")) == NULL)
652 return (-1);
653 if (*folder == '/' || ((p = which_protocol(folder)) != PROTO_FILE &&
654 p != PROTO_MAILDIR)) {
655 strncpy(name, folder, size);
656 name[size-1]='\0';
657 } else {
658 snprintf(name, size, "%s/%s", homedir, folder);
660 return (0);
664 * Return the name of the dead.letter file.
666 char *
667 getdeadletter(void)
669 char *cp;
671 if ((cp = value("DEAD")) == NULL || (cp = file_expand(cp)) == NULL)
672 cp = file_expand("~/dead.letter");
673 else if (*cp != '/') {
674 size_t sz = strlen(cp) + 3;
675 char *buf = ac_alloc(sz);
677 snprintf(buf, sz, "~/%s", cp);
678 cp = file_expand(buf);
679 ac_free(buf);
681 return (cp);
685 * line is a buffer with the result of fgets(). Returns the first
686 * newline or the last character read.
688 static size_t
689 length_of_line(const char *line, size_t linesize)
691 register size_t i;
694 * Last character is always '\0' and was added by fgets.
696 linesize--;
697 for (i = 0; i < linesize; i++)
698 if (line[i] == '\n')
699 break;
700 return i < linesize ? i + 1 : linesize;
704 * fgets replacement to handle lines of arbitrary size and with
705 * embedded \0 characters.
706 * line - line buffer. *line be NULL.
707 * linesize - allocated size of line buffer.
708 * count - maximum characters to read. May be NULL.
709 * llen - length_of_line(*line).
710 * fp - input FILE.
711 * appendnl - always terminate line with \n, append if necessary.
713 char *
714 fgetline(char **line, size_t *linesize, size_t *count, size_t *llen,
715 FILE *fp, int appendnl)
717 size_t i_llen, sz;
719 if (count == NULL)
721 * If we have no count, we cannot determine where the
722 * characters returned by fgets() end if there was no
723 * newline. We have to read one character at one.
725 return fgetline_byone(line, linesize, llen, fp, appendnl, 0);
726 if (*line == NULL || *linesize < LINESIZE)
727 *line = srealloc(*line, *linesize = LINESIZE);
728 sz = *linesize <= *count ? *linesize : *count + 1;
729 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
731 * Leave llen untouched; it is used to determine whether
732 * the last line was \n-terminated in some callers.
734 return NULL;
735 i_llen = length_of_line(*line, sz);
736 *count -= i_llen;
737 while ((*line)[i_llen - 1] != '\n') {
738 *line = srealloc(*line, *linesize += 256);
739 sz = *linesize - i_llen;
740 sz = (sz <= *count ? sz : *count + 1);
741 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
742 if (appendnl) {
743 (*line)[i_llen++] = '\n';
744 (*line)[i_llen] = '\0';
746 break;
748 sz = length_of_line(&(*line)[i_llen], sz);
749 i_llen += sz;
750 *count -= sz;
752 if (llen)
753 *llen = i_llen;
754 return *line;
758 * Read a line, one character at once.
760 static char *
761 fgetline_byone(char **line, size_t *linesize, size_t *llen,
762 FILE *fp, int appendnl, size_t n)
764 int c;
766 if (*line == NULL || *linesize < LINESIZE + n + 1)
767 *line = srealloc(*line, *linesize = LINESIZE + n + 1);
768 for (;;) {
769 if (n >= *linesize - 128)
770 *line = srealloc(*line, *linesize += 256);
771 c = getc(fp);
772 if (c != EOF) {
773 (*line)[n++] = c;
774 (*line)[n] = '\0';
775 if (c == '\n')
776 break;
777 } else {
778 if (n > 0) {
779 if (appendnl) {
780 (*line)[n++] = '\n';
781 (*line)[n] = '\0';
783 break;
784 } else
785 return NULL;
788 if (llen)
789 *llen = n;
790 return *line;
793 static enum okay
794 get_header(struct message *mp)
796 switch (mb.mb_type) {
797 case MB_FILE:
798 case MB_MAILDIR:
799 return OKAY;
800 case MB_POP3:
801 return pop3_header(mp);
802 case MB_IMAP:
803 case MB_CACHE:
804 return imap_header(mp);
805 case MB_VOID:
806 return STOP;
808 /*NOTREACHED*/
809 return STOP;
812 enum okay
813 get_body(struct message *mp)
815 switch (mb.mb_type) {
816 case MB_FILE:
817 case MB_MAILDIR:
818 return OKAY;
819 case MB_POP3:
820 return pop3_body(mp);
821 case MB_IMAP:
822 case MB_CACHE:
823 return imap_body(mp);
824 case MB_VOID:
825 return STOP;
827 /*NOTREACHED*/
828 return STOP;
831 #ifdef HAVE_SOCKETS
832 static long xwrite(int fd, const char *data, size_t sz);
834 static long
835 xwrite(int fd, const char *data, size_t sz)
837 long wo;
838 size_t wt = 0;
840 do {
841 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
842 if (errno == EINTR)
843 continue;
844 else
845 return -1;
847 wt += wo;
848 } while (wt < sz);
849 return sz;
853 sclose(struct sock *sp)
855 int i;
857 if (sp->s_fd > 0) {
858 if (sp->s_onclose != NULL)
859 (*sp->s_onclose)();
860 #if defined (USE_NSS)
861 if (sp->s_use_ssl) {
862 sp->s_use_ssl = 0;
863 i = PR_Close(sp->s_prfd) == PR_SUCCESS ? 0 : -1;
864 sp->s_prfd = NULL;
865 } else
866 #elif defined (USE_OPENSSL)
867 if (sp->s_use_ssl) {
868 sp->s_use_ssl = 0;
869 SSL_shutdown(sp->s_ssl);
870 SSL_free(sp->s_ssl);
871 sp->s_ssl = NULL;
872 SSL_CTX_free(sp->s_ctx);
873 sp->s_ctx = NULL;
875 #endif /* USE_SSL */
877 i = close(sp->s_fd);
879 sp->s_fd = -1;
880 return i;
882 sp->s_fd = -1;
883 return 0;
886 enum okay
887 swrite(struct sock *sp, const char *data)
889 return swrite1(sp, data, strlen(data), 0);
892 enum okay
893 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
895 int x;
897 if (use_buffer > 0) {
898 int di;
899 enum okay ok;
901 if (sp->s_wbuf == NULL) {
902 sp->s_wbufsize = 4096;
903 sp->s_wbuf = smalloc(sp->s_wbufsize);
904 sp->s_wbufpos = 0;
906 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
907 di = sp->s_wbufsize - sp->s_wbufpos;
908 sz -= di;
909 if (sp->s_wbufpos > 0) {
910 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
911 ok = swrite1(sp, sp->s_wbuf,
912 sp->s_wbufsize, -1);
913 } else
914 ok = swrite1(sp, data,
915 sp->s_wbufsize, -1);
916 if (ok != OKAY)
917 return STOP;
918 data += di;
919 sp->s_wbufpos = 0;
921 if (sz == sp->s_wbufsize) {
922 ok = swrite1(sp, data, sp->s_wbufsize, -1);
923 if (ok != OKAY)
924 return STOP;
925 } else if (sz) {
926 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
927 sp->s_wbufpos += sz;
929 return OKAY;
930 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
931 sp->s_wbufpos > 0) {
932 x = sp->s_wbufpos;
933 sp->s_wbufpos = 0;
934 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
935 return STOP;
937 if (sz == 0)
938 return OKAY;
939 #if defined (USE_NSS)
940 if (sp->s_use_ssl) {
941 x = PR_Write(sp->s_prfd, data, sz);
942 } else
943 #elif defined (USE_OPENSSL)
944 if (sp->s_use_ssl) {
945 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
946 if (x < 0) {
947 switch (SSL_get_error(sp->s_ssl, x)) {
948 case SSL_ERROR_WANT_READ:
949 case SSL_ERROR_WANT_WRITE:
950 goto ssl_retry;
953 } else
954 #endif /* USE_SSL */
956 x = xwrite(sp->s_fd, data, sz);
958 if (x != sz) {
959 char o[512];
960 snprintf(o, sizeof o, "%s write error",
961 sp->s_desc ? sp->s_desc : "socket");
962 #if defined (USE_NSS)
963 sp->s_use_ssl ? nss_gen_err("%s", o) : perror(o);
964 #elif defined (USE_OPENSSL)
965 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
966 #else /* !USE_SSL */
967 perror(o);
968 #endif /* !USE_SSL */
969 if (x < 0)
970 sclose(sp);
971 return STOP;
973 return OKAY;
977 sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp)
979 char *lp = *line;
981 if (sp->s_rsz < 0) {
982 sclose(sp);
983 return sp->s_rsz;
985 do {
986 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
987 size_t diff = lp - *line;
988 *line = srealloc(*line, *linesize += 256);
989 lp = &(*line)[diff];
991 if (sp->s_rbufptr == NULL ||
992 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
993 #if defined (USE_NSS)
994 if (sp->s_use_ssl) {
995 if ((sp->s_rsz = PR_Read(sp->s_prfd,
996 sp->s_rbuf,
997 sizeof sp->s_rbuf)) <= 0) {
998 if (sp->s_rsz < 0) {
999 char o[512];
1000 snprintf(o, sizeof o, "%s",
1001 sp->s_desc ?
1002 sp->s_desc :
1003 "socket");
1004 nss_gen_err("%s", o);
1006 break;
1008 } else
1009 #elif defined (USE_OPENSSL)
1010 if (sp->s_use_ssl) {
1011 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
1012 sp->s_rbuf,
1013 sizeof sp->s_rbuf)) <= 0) {
1014 if (sp->s_rsz < 0) {
1015 char o[512];
1016 switch(SSL_get_error(sp->s_ssl,
1017 sp->s_rsz)) {
1018 case SSL_ERROR_WANT_READ:
1019 case SSL_ERROR_WANT_WRITE:
1020 goto ssl_retry;
1022 snprintf(o, sizeof o, "%s",
1023 sp->s_desc ?
1024 sp->s_desc :
1025 "socket");
1026 ssl_gen_err("%s", o);
1029 break;
1031 } else
1032 #endif /* USE_SSL */
1034 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
1035 sizeof sp->s_rbuf)) <= 0) {
1036 if (sp->s_rsz < 0) {
1037 char o[512];
1038 if (errno == EINTR)
1039 goto again;
1040 snprintf(o, sizeof o, "%s",
1041 sp->s_desc ?
1042 sp->s_desc :
1043 "socket");
1044 perror(o);
1046 break;
1049 sp->s_rbufptr = sp->s_rbuf;
1051 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1052 *lp = '\0';
1053 if (linelen)
1054 *linelen = lp - *line;
1055 return lp - *line;
1058 enum okay
1059 sopen(const char *xserver, struct sock *sp, int use_ssl,
1060 const char *uhp, const char *portstr, int verbose)
1062 #ifdef USE_IPV6
1063 char hbuf[NI_MAXHOST];
1064 struct addrinfo hints, *res0, *res;
1065 #else
1066 struct sockaddr_in servaddr;
1067 struct in_addr **pptr;
1068 struct hostent *hp;
1069 struct servent *ep;
1070 unsigned short port = 0;
1071 #endif
1072 int sockfd;
1073 char *cp;
1074 char *server = (char *)xserver;
1075 (void)use_ssl;
1076 (void)uhp;
1078 if ((cp = strchr(server, ':')) != NULL) {
1079 portstr = &cp[1];
1080 #ifndef USE_IPV6
1081 port = strtol(portstr, NULL, 10);
1082 #endif
1083 server = salloc(cp - xserver + 1);
1084 memcpy(server, xserver, cp - xserver);
1085 server[cp - xserver] = '\0';
1087 #ifdef USE_IPV6
1088 memset(&hints, 0, sizeof hints);
1089 hints.ai_socktype = SOCK_STREAM;
1090 if (verbose)
1091 fprintf(stderr, "Resolving host %s . . .", server);
1092 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1093 fprintf(stderr, catgets(catd, CATSET, 252,
1094 "Could not resolve host: %s\n"), server);
1095 return STOP;
1096 } else if (verbose)
1097 fprintf(stderr, " done.\n");
1098 sockfd = -1;
1099 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1100 if (verbose) {
1101 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1102 hbuf, sizeof hbuf, NULL, 0,
1103 NI_NUMERICHOST) != 0)
1104 strcpy(hbuf, "unknown host");
1105 fprintf(stderr, catgets(catd, CATSET, 192,
1106 "Connecting to %s:%s . . ."),
1107 hbuf, portstr);
1109 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1110 res->ai_protocol)) >= 0) {
1111 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1112 close(sockfd);
1113 sockfd = -1;
1117 if (sockfd < 0) {
1118 perror(catgets(catd, CATSET, 254, "could not connect"));
1119 freeaddrinfo(res0);
1120 return STOP;
1122 freeaddrinfo(res0);
1123 #else /* USE_IPV6 */
1124 if (port == 0) {
1125 if (strcmp(portstr, "smtp") == 0)
1126 port = htons(25);
1127 else if (strcmp(portstr, "smtps") == 0)
1128 port = htons(465);
1129 else if (strcmp(portstr, "imap") == 0)
1130 port = htons(143);
1131 else if (strcmp(portstr, "imaps") == 0)
1132 port = htons(993);
1133 else if (strcmp(portstr, "pop3") == 0)
1134 port = htons(110);
1135 else if (strcmp(portstr, "pop3s") == 0)
1136 port = htons(995);
1137 else if ((ep = getservbyname((char *)portstr, "tcp")) != NULL)
1138 port = ep->s_port;
1139 else {
1140 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1141 portstr);
1142 return STOP;
1144 } else
1145 port = htons(port);
1146 if (verbose)
1147 fprintf(stderr, "Resolving host %s . . .", server);
1148 if ((hp = gethostbyname(server)) == NULL) {
1149 fprintf(stderr, catgets(catd, CATSET, 252,
1150 "Could not resolve host: %s\n"), server);
1151 return STOP;
1152 } else if (verbose)
1153 fprintf(stderr, " done.\n");
1154 pptr = (struct in_addr **)hp->h_addr_list;
1155 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1156 perror(catgets(catd, CATSET, 253, "could not create socket"));
1157 return STOP;
1159 memset(&servaddr, 0, sizeof servaddr);
1160 servaddr.sin_family = AF_INET;
1161 servaddr.sin_port = port;
1162 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1163 if (verbose)
1164 fprintf(stderr, catgets(catd, CATSET, 192,
1165 "Connecting to %s:%d . . ."),
1166 inet_ntoa(**pptr), ntohs(port));
1167 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1168 != 0) {
1169 perror(catgets(catd, CATSET, 254, "could not connect"));
1170 return STOP;
1172 #endif /* USE_IPV6 */
1173 if (verbose)
1174 fputs(catgets(catd, CATSET, 193, " connected.\n"), stderr);
1175 memset(sp, 0, sizeof *sp);
1176 sp->s_fd = sockfd;
1177 #ifdef USE_SSL
1178 if (use_ssl) {
1179 enum okay ok;
1181 if ((ok = ssl_open(server, sp, uhp)) != OKAY)
1182 sclose(sp);
1183 return ok;
1185 #endif
1186 return OKAY;
1188 #endif /* HAVE_SOCKETS */