Allow *write* with no arguments..
[s-mailx.git] / fio.c
blob7b8b4988d6480b6d681f297358b5798ab0ffcd6d
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;
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 the string given as a new mailbox name.
399 * Supported meta characters:
400 * % for my system mail box
401 * %user for user's system mail box
402 * # for previous file
403 * & invoker's mbox file
404 * +file file in folder directory
405 * any shell meta character
406 * Return the file name as a dynamic string.
408 char *
409 expand(char *name)
411 char xname[PATHSIZE];
412 char foldbuf[PATHSIZE];
413 struct shortcut *sh;
416 * The order of evaluation is "%" and "#" expand into constants.
417 * "&" can expand into "+". "+" can expand into shell meta characters.
418 * Shell meta characters expand into constants.
419 * This way, we make no recursive expansion.
421 if ((sh = get_shortcut(name)) != NULL)
422 name = sh->sh_long;
423 next:
424 switch (*name) {
425 case '%':
426 if (name[1] == ':' && name[2]) {
427 name = &name[2];
428 goto next;
430 findmail(name[1] ? name + 1 : myname, name[1] != '\0' || uflag,
431 xname, sizeof xname);
432 return savestr(xname);
433 case '#':
434 if (name[1] != 0)
435 break;
436 if (prevfile[0] == 0) {
437 printf(catgets(catd, CATSET, 80, "No previous file\n"));
438 return NULL;
440 return savestr(prevfile);
441 case '&':
442 if (name[1] == 0 && (name = value("MBOX")) == NULL)
443 name = "~/mbox";
444 /* fall through */
446 if (name[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
447 snprintf(xname, sizeof xname, "%s/%s", protbase(mailname),
448 &name[1]);
449 name = savestr(xname);
451 if (name[0] == '+' && getfold(foldbuf, sizeof foldbuf) >= 0) {
452 snprintf(xname, sizeof xname,
453 ((which_protocol(foldbuf) == PROTO_IMAP &&
454 strcmp(foldbuf, protbase(foldbuf)))
455 ? "%s%s" : "%s/%s"),
456 foldbuf, name+1);
457 name = savestr(xname);
458 if (foldbuf[0] == '%' && foldbuf[1] == ':')
459 goto next;
461 /* catch the most common shell meta character */
462 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
463 snprintf(xname, sizeof xname, "%s%s", homedir, name + 1);
464 name = savestr(xname);
466 if (!anyof(name, "|&;<>~{}()[]*?$`'\"\\"))
467 return name;
468 if (which_protocol(name) == PROTO_FILE)
469 return globname(name);
470 else
471 return name;
474 static char *
475 globname(char *name)
477 #ifdef HAVE_WORDEXP
478 wordexp_t we;
479 char *cp;
480 sigset_t nset;
481 int i;
484 * Some systems (notably Open UNIX 8.0.0) fork a shell for
485 * wordexp() and wait for it; waiting will fail if our SIGCHLD
486 * handler is active.
488 sigemptyset(&nset);
489 sigaddset(&nset, SIGCHLD);
490 sigprocmask(SIG_BLOCK, &nset, NULL);
491 i = wordexp(name, &we, 0);
492 sigprocmask(SIG_UNBLOCK, &nset, NULL);
493 switch (i) {
494 case 0:
495 break;
496 case WRDE_NOSPACE:
497 fprintf(stderr, catgets(catd, CATSET, 83,
498 "\"%s\": Expansion buffer overflow.\n"), name);
499 return NULL;
500 case WRDE_BADCHAR:
501 case WRDE_SYNTAX:
502 default:
503 fprintf(stderr, catgets(catd, CATSET, 242,
504 "Syntax error in \"%s\"\n"), name);
505 return NULL;
507 switch (we.we_wordc) {
508 case 1:
509 cp = savestr(we.we_wordv[0]);
510 break;
511 case 0:
512 fprintf(stderr, catgets(catd, CATSET, 82,
513 "\"%s\": No match.\n"), name);
514 cp = NULL;
515 break;
516 default:
517 fprintf(stderr, catgets(catd, CATSET, 84,
518 "\"%s\": Ambiguous.\n"), name);
519 cp = NULL;
521 wordfree(&we);
522 return cp;
523 #else /* !HAVE_WORDEXP */
524 char xname[PATHSIZE];
525 char cmdbuf[PATHSIZE]; /* also used for file names */
526 int pid, l;
527 char *cp, *shell;
528 int pivec[2];
529 extern int wait_status;
530 struct stat sbuf;
532 if (pipe(pivec) < 0) {
533 perror("pipe");
534 return name;
536 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
537 if ((shell = value("SHELL")) == NULL)
538 shell = SHELL;
539 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
540 if (pid < 0) {
541 close(pivec[0]);
542 close(pivec[1]);
543 return NULL;
545 close(pivec[1]);
546 again:
547 l = read(pivec[0], xname, sizeof xname);
548 if (l < 0) {
549 if (errno == EINTR)
550 goto again;
551 perror("read");
552 close(pivec[0]);
553 return NULL;
555 close(pivec[0]);
556 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
557 fprintf(stderr, catgets(catd, CATSET, 81,
558 "\"%s\": Expansion failed.\n"), name);
559 return NULL;
561 if (l == 0) {
562 fprintf(stderr, catgets(catd, CATSET, 82,
563 "\"%s\": No match.\n"), name);
564 return NULL;
566 if (l == sizeof xname) {
567 fprintf(stderr, catgets(catd, CATSET, 83,
568 "\"%s\": Expansion buffer overflow.\n"), name);
569 return NULL;
571 xname[l] = 0;
572 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
574 cp[1] = '\0';
575 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
576 fprintf(stderr, catgets(catd, CATSET, 84,
577 "\"%s\": Ambiguous.\n"), name);
578 return NULL;
580 return savestr(xname);
581 #endif /* !HAVE_WORDEXP */
585 * Determine the current folder directory name.
588 getfold(char *name, int size)
590 char *folder;
591 enum protocol p;
593 if ((folder = value("folder")) == NULL)
594 return (-1);
595 if (*folder == '/' || ((p = which_protocol(folder)) != PROTO_FILE &&
596 p != PROTO_MAILDIR)) {
597 strncpy(name, folder, size);
598 name[size-1]='\0';
599 } else {
600 snprintf(name, size, "%s/%s", homedir, folder);
602 return (0);
606 * Return the name of the dead.letter file.
608 char *
609 getdeadletter(void)
611 char *cp;
613 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
614 cp = expand("~/dead.letter");
615 else if (*cp != '/') {
616 char *buf;
617 size_t sz;
619 buf = ac_alloc(sz = strlen(cp) + 3);
620 snprintf(buf, sz, "~/%s", cp);
621 snprintf(buf, sz, "~/%s", cp);
622 cp = expand(buf);
623 ac_free(buf);
625 return cp;
629 * line is a buffer with the result of fgets(). Returns the first
630 * newline or the last character read.
632 static size_t
633 length_of_line(const char *line, size_t linesize)
635 register size_t i;
638 * Last character is always '\0' and was added by fgets.
640 linesize--;
641 for (i = 0; i < linesize; i++)
642 if (line[i] == '\n')
643 break;
644 return i < linesize ? i + 1 : linesize;
648 * fgets replacement to handle lines of arbitrary size and with
649 * embedded \0 characters.
650 * line - line buffer. *line be NULL.
651 * linesize - allocated size of line buffer.
652 * count - maximum characters to read. May be NULL.
653 * llen - length_of_line(*line).
654 * fp - input FILE.
655 * appendnl - always terminate line with \n, append if necessary.
657 char *
658 fgetline(char **line, size_t *linesize, size_t *count, size_t *llen,
659 FILE *fp, int appendnl)
661 size_t i_llen, sz;
663 if (count == NULL)
665 * If we have no count, we cannot determine where the
666 * characters returned by fgets() end if there was no
667 * newline. We have to read one character at one.
669 return fgetline_byone(line, linesize, llen, fp, appendnl, 0);
670 if (*line == NULL || *linesize < LINESIZE)
671 *line = srealloc(*line, *linesize = LINESIZE);
672 sz = *linesize <= *count ? *linesize : *count + 1;
673 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
675 * Leave llen untouched; it is used to determine whether
676 * the last line was \n-terminated in some callers.
678 return NULL;
679 i_llen = length_of_line(*line, sz);
680 *count -= i_llen;
681 while ((*line)[i_llen - 1] != '\n') {
682 *line = srealloc(*line, *linesize += 256);
683 sz = *linesize - i_llen;
684 sz = (sz <= *count ? sz : *count + 1);
685 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
686 if (appendnl) {
687 (*line)[i_llen++] = '\n';
688 (*line)[i_llen] = '\0';
690 break;
692 sz = length_of_line(&(*line)[i_llen], sz);
693 i_llen += sz;
694 *count -= sz;
696 if (llen)
697 *llen = i_llen;
698 return *line;
702 * Read a line, one character at once.
704 static char *
705 fgetline_byone(char **line, size_t *linesize, size_t *llen,
706 FILE *fp, int appendnl, size_t n)
708 int c;
710 if (*line == NULL || *linesize < LINESIZE + n + 1)
711 *line = srealloc(*line, *linesize = LINESIZE + n + 1);
712 for (;;) {
713 if (n >= *linesize - 128)
714 *line = srealloc(*line, *linesize += 256);
715 c = getc(fp);
716 if (c != EOF) {
717 (*line)[n++] = c;
718 (*line)[n] = '\0';
719 if (c == '\n')
720 break;
721 } else {
722 if (n > 0) {
723 if (appendnl) {
724 (*line)[n++] = '\n';
725 (*line)[n] = '\0';
727 break;
728 } else
729 return NULL;
732 if (llen)
733 *llen = n;
734 return *line;
737 static enum okay
738 get_header(struct message *mp)
740 switch (mb.mb_type) {
741 case MB_FILE:
742 case MB_MAILDIR:
743 return OKAY;
744 case MB_POP3:
745 return pop3_header(mp);
746 case MB_IMAP:
747 case MB_CACHE:
748 return imap_header(mp);
749 case MB_VOID:
750 return STOP;
752 /*NOTREACHED*/
753 return STOP;
756 enum okay
757 get_body(struct message *mp)
759 switch (mb.mb_type) {
760 case MB_FILE:
761 case MB_MAILDIR:
762 return OKAY;
763 case MB_POP3:
764 return pop3_body(mp);
765 case MB_IMAP:
766 case MB_CACHE:
767 return imap_body(mp);
768 case MB_VOID:
769 return STOP;
771 /*NOTREACHED*/
772 return STOP;
775 #ifdef HAVE_SOCKETS
776 static long xwrite(int fd, const char *data, size_t sz);
778 static long
779 xwrite(int fd, const char *data, size_t sz)
781 long wo;
782 size_t wt = 0;
784 do {
785 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
786 if (errno == EINTR)
787 continue;
788 else
789 return -1;
791 wt += wo;
792 } while (wt < sz);
793 return sz;
797 sclose(struct sock *sp)
799 int i;
801 if (sp->s_fd > 0) {
802 if (sp->s_onclose != NULL)
803 (*sp->s_onclose)();
804 #if defined (USE_NSS)
805 if (sp->s_use_ssl) {
806 sp->s_use_ssl = 0;
807 i = PR_Close(sp->s_prfd) == PR_SUCCESS ? 0 : -1;
808 sp->s_prfd = NULL;
809 } else
810 #elif defined (USE_OPENSSL)
811 if (sp->s_use_ssl) {
812 sp->s_use_ssl = 0;
813 SSL_shutdown(sp->s_ssl);
814 SSL_free(sp->s_ssl);
815 sp->s_ssl = NULL;
816 SSL_CTX_free(sp->s_ctx);
817 sp->s_ctx = NULL;
819 #endif /* USE_SSL */
821 i = close(sp->s_fd);
823 sp->s_fd = -1;
824 return i;
826 sp->s_fd = -1;
827 return 0;
830 enum okay
831 swrite(struct sock *sp, const char *data)
833 return swrite1(sp, data, strlen(data), 0);
836 enum okay
837 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
839 int x;
841 if (use_buffer > 0) {
842 int di;
843 enum okay ok;
845 if (sp->s_wbuf == NULL) {
846 sp->s_wbufsize = 4096;
847 sp->s_wbuf = smalloc(sp->s_wbufsize);
848 sp->s_wbufpos = 0;
850 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
851 di = sp->s_wbufsize - sp->s_wbufpos;
852 sz -= di;
853 if (sp->s_wbufpos > 0) {
854 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
855 ok = swrite1(sp, sp->s_wbuf,
856 sp->s_wbufsize, -1);
857 } else
858 ok = swrite1(sp, data,
859 sp->s_wbufsize, -1);
860 if (ok != OKAY)
861 return STOP;
862 data += di;
863 sp->s_wbufpos = 0;
865 if (sz == sp->s_wbufsize) {
866 ok = swrite1(sp, data, sp->s_wbufsize, -1);
867 if (ok != OKAY)
868 return STOP;
869 } else if (sz) {
870 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
871 sp->s_wbufpos += sz;
873 return OKAY;
874 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
875 sp->s_wbufpos > 0) {
876 x = sp->s_wbufpos;
877 sp->s_wbufpos = 0;
878 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
879 return STOP;
881 if (sz == 0)
882 return OKAY;
883 #if defined (USE_NSS)
884 if (sp->s_use_ssl) {
885 x = PR_Write(sp->s_prfd, data, sz);
886 } else
887 #elif defined (USE_OPENSSL)
888 if (sp->s_use_ssl) {
889 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
890 if (x < 0) {
891 switch (SSL_get_error(sp->s_ssl, x)) {
892 case SSL_ERROR_WANT_READ:
893 case SSL_ERROR_WANT_WRITE:
894 goto ssl_retry;
897 } else
898 #endif /* USE_SSL */
900 x = xwrite(sp->s_fd, data, sz);
902 if (x != sz) {
903 char o[512];
904 snprintf(o, sizeof o, "%s write error",
905 sp->s_desc ? sp->s_desc : "socket");
906 #if defined (USE_NSS)
907 sp->s_use_ssl ? nss_gen_err("%s", o) : perror(o);
908 #elif defined (USE_OPENSSL)
909 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
910 #else /* !USE_SSL */
911 perror(o);
912 #endif /* !USE_SSL */
913 if (x < 0)
914 sclose(sp);
915 return STOP;
917 return OKAY;
921 sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp)
923 char *lp = *line;
925 if (sp->s_rsz < 0) {
926 sclose(sp);
927 return sp->s_rsz;
929 do {
930 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
931 size_t diff = lp - *line;
932 *line = srealloc(*line, *linesize += 256);
933 lp = &(*line)[diff];
935 if (sp->s_rbufptr == NULL ||
936 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
937 #if defined (USE_NSS)
938 if (sp->s_use_ssl) {
939 if ((sp->s_rsz = PR_Read(sp->s_prfd,
940 sp->s_rbuf,
941 sizeof sp->s_rbuf)) <= 0) {
942 if (sp->s_rsz < 0) {
943 char o[512];
944 snprintf(o, sizeof o, "%s",
945 sp->s_desc ?
946 sp->s_desc :
947 "socket");
948 nss_gen_err("%s", o);
950 break;
952 } else
953 #elif defined (USE_OPENSSL)
954 if (sp->s_use_ssl) {
955 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
956 sp->s_rbuf,
957 sizeof sp->s_rbuf)) <= 0) {
958 if (sp->s_rsz < 0) {
959 char o[512];
960 switch(SSL_get_error(sp->s_ssl,
961 sp->s_rsz)) {
962 case SSL_ERROR_WANT_READ:
963 case SSL_ERROR_WANT_WRITE:
964 goto ssl_retry;
966 snprintf(o, sizeof o, "%s",
967 sp->s_desc ?
968 sp->s_desc :
969 "socket");
970 ssl_gen_err("%s", o);
973 break;
975 } else
976 #endif /* USE_SSL */
978 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
979 sizeof sp->s_rbuf)) <= 0) {
980 if (sp->s_rsz < 0) {
981 char o[512];
982 if (errno == EINTR)
983 goto again;
984 snprintf(o, sizeof o, "%s",
985 sp->s_desc ?
986 sp->s_desc :
987 "socket");
988 perror(o);
990 break;
993 sp->s_rbufptr = sp->s_rbuf;
995 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
996 *lp = '\0';
997 if (linelen)
998 *linelen = lp - *line;
999 return lp - *line;
1002 enum okay
1003 sopen(const char *xserver, struct sock *sp, int use_ssl,
1004 const char *uhp, const char *portstr, int verbose)
1006 #ifdef USE_IPV6
1007 char hbuf[NI_MAXHOST];
1008 struct addrinfo hints, *res0, *res;
1009 #else
1010 struct sockaddr_in servaddr;
1011 struct in_addr **pptr;
1012 struct hostent *hp;
1013 struct servent *ep;
1014 unsigned short port = 0;
1015 #endif
1016 int sockfd;
1017 char *cp;
1018 char *server = (char *)xserver;
1019 (void)use_ssl;
1020 (void)uhp;
1022 if ((cp = strchr(server, ':')) != NULL) {
1023 portstr = &cp[1];
1024 #ifndef USE_IPV6
1025 port = strtol(portstr, NULL, 10);
1026 #endif
1027 server = salloc(cp - xserver + 1);
1028 memcpy(server, xserver, cp - xserver);
1029 server[cp - xserver] = '\0';
1031 #ifdef USE_IPV6
1032 memset(&hints, 0, sizeof hints);
1033 hints.ai_socktype = SOCK_STREAM;
1034 if (verbose)
1035 fprintf(stderr, "Resolving host %s . . .", server);
1036 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1037 fprintf(stderr, catgets(catd, CATSET, 252,
1038 "Could not resolve host: %s\n"), server);
1039 return STOP;
1040 } else if (verbose)
1041 fprintf(stderr, " done.\n");
1042 sockfd = -1;
1043 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1044 if (verbose) {
1045 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1046 hbuf, sizeof hbuf, NULL, 0,
1047 NI_NUMERICHOST) != 0)
1048 strcpy(hbuf, "unknown host");
1049 fprintf(stderr, catgets(catd, CATSET, 192,
1050 "Connecting to %s:%s . . ."),
1051 hbuf, portstr);
1053 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1054 res->ai_protocol)) >= 0) {
1055 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1056 close(sockfd);
1057 sockfd = -1;
1061 if (sockfd < 0) {
1062 perror(catgets(catd, CATSET, 254, "could not connect"));
1063 freeaddrinfo(res0);
1064 return STOP;
1066 freeaddrinfo(res0);
1067 #else /* USE_IPV6 */
1068 if (port == 0) {
1069 if (strcmp(portstr, "smtp") == 0)
1070 port = htons(25);
1071 else if (strcmp(portstr, "smtps") == 0)
1072 port = htons(465);
1073 else if (strcmp(portstr, "imap") == 0)
1074 port = htons(143);
1075 else if (strcmp(portstr, "imaps") == 0)
1076 port = htons(993);
1077 else if (strcmp(portstr, "pop3") == 0)
1078 port = htons(110);
1079 else if (strcmp(portstr, "pop3s") == 0)
1080 port = htons(995);
1081 else if ((ep = getservbyname((char *)portstr, "tcp")) != NULL)
1082 port = ep->s_port;
1083 else {
1084 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1085 portstr);
1086 return STOP;
1088 } else
1089 port = htons(port);
1090 if (verbose)
1091 fprintf(stderr, "Resolving host %s . . .", server);
1092 if ((hp = gethostbyname(server)) == NULL) {
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 pptr = (struct in_addr **)hp->h_addr_list;
1099 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1100 perror(catgets(catd, CATSET, 253, "could not create socket"));
1101 return STOP;
1103 memset(&servaddr, 0, sizeof servaddr);
1104 servaddr.sin_family = AF_INET;
1105 servaddr.sin_port = port;
1106 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1107 if (verbose)
1108 fprintf(stderr, catgets(catd, CATSET, 192,
1109 "Connecting to %s:%d . . ."),
1110 inet_ntoa(**pptr), ntohs(port));
1111 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1112 != 0) {
1113 perror(catgets(catd, CATSET, 254, "could not connect"));
1114 return STOP;
1116 #endif /* USE_IPV6 */
1117 if (verbose)
1118 fputs(catgets(catd, CATSET, 193, " connected.\n"), stderr);
1119 memset(sp, 0, sizeof *sp);
1120 sp->s_fd = sockfd;
1121 #ifdef USE_SSL
1122 if (use_ssl) {
1123 enum okay ok;
1125 if ((ok = ssl_open(server, sp, uhp)) != OKAY)
1126 sclose(sp);
1127 return ok;
1129 #endif
1130 return OKAY;
1132 #endif /* HAVE_SOCKETS */