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