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