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