expand(),file_expand(): use a shared implementation..
[s-mailx.git] / fio.c
blob435e7e5769cdc6832a6f568d6afade44ade0e886
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.
78 * Evaluate the string given as a new mailbox name.
79 * Supported meta characters:
80 * % for my system mail box
81 * %user for user's system mail box
82 * # for previous file
83 * & invoker's mbox file
84 * +file file in folder directory
85 * any shell meta character
86 * Return the file name as a dynamic string.
88 static char * _expand(char const *name, int only_local);
89 /* Perform shell meta character expansion */
90 static char * _globname(char const *name);
92 static void makemessage(void);
93 static void append(struct message *mp);
94 static size_t length_of_line(const char *line, size_t linesize);
95 static char *fgetline_byone(char **line, size_t *linesize, size_t *llen,
96 FILE *fp, int appendnl, size_t n);
97 static enum okay get_header(struct message *mp);
99 static char *
100 _expand(char const *name, int only_local)
102 char cbuf[PATHSIZE], *res;
103 struct str s;
104 struct shortcut *sh;
105 int dyn;
108 * The order of evaluation is "%" and "#" expand into constants.
109 * "&" can expand into "+". "+" can expand into shell meta characters.
110 * Shell meta characters expand into constants.
111 * This way, we make no recursive expansion.
113 res = (char*)name;
114 if ((sh = get_shortcut(res)) != NULL)
115 res = sh->sh_long;
117 next: dyn = 0;
118 switch (*res) {
119 case '%':
120 if (res[1] == ':' && res[2]) {
121 res = &res[2];
122 goto next;
124 findmail((res[1] ? res + 1 : myname),
125 (res[1] != '\0' || uflag), cbuf, sizeof cbuf);
126 res = cbuf;
127 goto jleave;
128 case '#':
129 if (res[1] != 0)
130 break;
131 if (prevfile[0] == 0) {
132 fprintf(stderr, tr(80, "No previous file\n"));
133 res = NULL;
134 } else
135 res = prevfile;
136 goto jleave;
137 case '&':
138 if (res[1] == 0 && (res = value("MBOX")) == NULL)
139 res = "~/mbox";
140 break;
143 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
144 res = str_concat_csvl(&s,
145 protbase(mailname), "/", res + 1, NULL)->s;
146 dyn = 1;
149 if (res[0] == '+' && getfold(cbuf, sizeof cbuf) >= 0) {
150 res = str_concat_csvl(&s,
151 cbuf, ((which_protocol(cbuf) == PROTO_IMAP &&
152 strcmp(cbuf, protbase(cbuf))) /* XXX */
153 ? "" : "/"),
154 res + 1, NULL)->s;
155 dyn = 1;
156 if (cbuf[0] == '%' && cbuf[1] == ':')
157 goto next;
160 /* Catch the most common shell meta character */
161 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
162 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
163 dyn = 1;
166 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
167 which_protocol(res) == PROTO_FILE) {
168 res = _globname(res);
169 dyn = 1;
170 goto jleave;
173 if (only_local)
174 switch (which_protocol(res)) {
175 case PROTO_FILE:
176 case PROTO_MAILDIR: /* XXX Really? ok MAILDIR for local? */
177 break;
178 default:
179 fprintf(stderr, tr(280,
180 "\"%s\": only a local file or directory may "
181 "be used\n"), name);
182 res = NULL;
183 break;
186 jleave:
187 if (res && ! dyn)
188 res = savestr(res);
189 return (res);
192 static char *
193 _globname(char const *name)
195 #ifdef HAVE_WORDEXP
196 wordexp_t we;
197 char *cp;
198 sigset_t nset;
199 int i;
202 * Some systems (notably Open UNIX 8.0.0) fork a shell for
203 * wordexp() and wait for it; waiting will fail if our SIGCHLD
204 * handler is active.
206 sigemptyset(&nset);
207 sigaddset(&nset, SIGCHLD);
208 sigprocmask(SIG_BLOCK, &nset, NULL);
209 i = wordexp(name, &we, 0);
210 sigprocmask(SIG_UNBLOCK, &nset, NULL);
212 switch (i) {
213 case 0:
214 break;
215 case WRDE_NOSPACE:
216 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"),
217 name);
218 return NULL;
219 case WRDE_BADCHAR:
220 case WRDE_SYNTAX:
221 default:
222 fprintf(stderr, tr(242, "Syntax error in \"%s\"\n"), name);
223 return NULL;
226 switch (we.we_wordc) {
227 case 1:
228 cp = savestr(we.we_wordv[0]);
229 break;
230 case 0:
231 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
232 cp = NULL;
233 break;
234 default:
235 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
236 cp = NULL;
239 wordfree(&we);
240 return cp;
242 #else /* !HAVE_WORDEXP */
243 char xname[PATHSIZE];
244 char cmdbuf[PATHSIZE]; /* also used for file names */
245 int pid, l;
246 char *cp, *shell;
247 int pivec[2];
248 extern int wait_status;
249 struct stat sbuf;
251 if (pipe(pivec) < 0) {
252 perror("pipe");
253 return name;
255 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
256 if ((shell = value("SHELL")) == NULL)
257 shell = SHELL;
258 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
259 if (pid < 0) {
260 close(pivec[0]);
261 close(pivec[1]);
262 return NULL;
264 close(pivec[1]);
265 again:
266 l = read(pivec[0], xname, sizeof xname);
267 if (l < 0) {
268 if (errno == EINTR)
269 goto again;
270 perror("read");
271 close(pivec[0]);
272 return NULL;
274 close(pivec[0]);
275 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
276 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"), name);
277 return NULL;
279 if (l == 0) {
280 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
281 return NULL;
283 if (l == sizeof xname) {
284 fprintf(stderr, tr(83, "\"%s\": Expansion buffer overflow.\n"),
285 name);
286 return NULL;
288 xname[l] = 0;
289 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
291 cp[1] = '\0';
292 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
293 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
294 return NULL;
296 return savestr(xname);
297 #endif /* !HAVE_WORDEXP */
301 * Set up the input pointers while copying the mail file into /tmp.
303 void
304 setptr(FILE *ibuf, off_t offset)
306 int c;
307 size_t count;
308 char *cp, *cp2;
309 struct message this;
310 int maybe, inhead, thiscnt;
311 char *linebuf = NULL;
312 size_t linesize = 0, filesize;
314 maybe = 1;
315 inhead = 0;
316 thiscnt = 0;
317 memset(&this, 0, sizeof this);
318 this.m_flag = MUSED|MNEW|MNEWEST;
319 filesize = mailsize - offset;
320 offset = ftell(mb.mb_otf);
321 for (;;) {
322 if (fgetline(&linebuf, &linesize, &filesize, &count, ibuf, 0)
323 == NULL) {
324 this.m_xsize = this.m_size;
325 this.m_xlines = this.m_lines;
326 this.m_have = HAVE_HEADER|HAVE_BODY;
327 if (thiscnt > 0)
328 append(&this);
329 makemessage();
330 if (linebuf)
331 free(linebuf);
332 return;
334 #ifdef notdef
335 if (linebuf[0] == '\0')
336 linebuf[0] = '.';
337 #endif
338 fwrite(linebuf, sizeof *linebuf, count, mb.mb_otf);
339 if (ferror(mb.mb_otf)) {
340 perror("/tmp");
341 exit(1);
343 if (linebuf[count - 1] == '\n')
344 linebuf[count - 1] = '\0';
345 if (maybe && linebuf[0] == 'F' && is_head(linebuf, count)) {
346 this.m_xsize = this.m_size;
347 this.m_xlines = this.m_lines;
348 this.m_have = HAVE_HEADER|HAVE_BODY;
349 if (thiscnt++ > 0)
350 append(&this);
351 msgCount++;
352 this.m_flag = MUSED|MNEW|MNEWEST;
353 this.m_size = 0;
354 this.m_lines = 0;
355 this.m_block = mailx_blockof(offset);
356 this.m_offset = mailx_offsetof(offset);
357 inhead = 1;
358 } else if (linebuf[0] == 0) {
359 inhead = 0;
360 } else if (inhead) {
361 for (cp = linebuf, cp2 = "status";; cp++) {
362 if ((c = *cp2++) == 0) {
363 while (c = *cp++, whitechar(c));
364 if (cp[-1] != ':')
365 break;
366 while ((c = *cp++) != '\0')
367 if (c == 'R')
368 this.m_flag |= MREAD;
369 else if (c == 'O')
370 this.m_flag &= ~MNEW;
371 break;
373 if (*cp != c && *cp != upperconv(c))
374 break;
376 for (cp = linebuf, cp2 = "x-status";; cp++) {
377 if ((c = *cp2++) == 0) {
378 while (c = *cp++, whitechar(c));
379 if (cp[-1] != ':')
380 break;
381 while ((c = *cp++) != '\0')
382 if (c == 'F')
383 this.m_flag |= MFLAGGED;
384 else if (c == 'A')
385 this.m_flag|=MANSWERED;
386 else if (c == 'T')
387 this.m_flag|=MDRAFTED;
388 break;
390 if (*cp != c && *cp != upperconv(c))
391 break;
394 offset += count;
395 this.m_size += count;
396 this.m_lines++;
397 maybe = linebuf[0] == 0;
399 /*NOTREACHED*/
403 * Drop the passed line onto the passed output buffer.
404 * If a write error occurs, return -1, else the count of
405 * characters written, including the newline.
408 putline(FILE *obuf, char *linebuf, size_t count)
410 fwrite(linebuf, sizeof *linebuf, count, obuf);
411 putc('\n', obuf);
412 if (ferror(obuf))
413 return (-1);
414 return (count + 1);
418 * Read up a line from the specified input into the line
419 * buffer. Return the number of characters read. Do not
420 * include the newline at the end.
422 * n is the number of characters already read.
425 readline_restart(FILE *ibuf, char **linebuf, size_t *linesize, size_t n)
427 long sz;
429 clearerr(ibuf);
431 * Interrupts will cause trouble if we are inside a stdio call. As
432 * this is only relevant if input comes from a terminal, we can simply
433 * bypass it by read() then.
435 if (fileno(ibuf) == 0 && is_a_tty[0]) {
436 if (*linebuf == NULL || *linesize < LINESIZE + n + 1)
437 *linebuf = srealloc(*linebuf,
438 *linesize = LINESIZE + n + 1);
439 for (;;) {
440 if (n >= *linesize - 128)
441 *linebuf = srealloc(*linebuf, *linesize += 256);
442 again:
443 sz = read(0, *linebuf + n, *linesize - n - 1);
444 if (sz > 0) {
445 n += sz;
446 (*linebuf)[n] = '\0';
447 if (n > 0 && (*linebuf)[n - 1] == '\n')
448 break;
449 } else {
450 if (sz < 0 && errno == EINTR)
451 goto again;
452 if (n > 0) {
453 if ((*linebuf)[n - 1] != '\n') {
454 (*linebuf)[n++] = '\n';
455 (*linebuf)[n] = '\0';
457 break;
458 } else
459 return -1;
462 } else {
464 * Not reading from standard input or standard input not
465 * a terminal. We read one char at a time as it is the
466 * only way to get lines with embedded NUL characters in
467 * standard stdio.
469 if (fgetline_byone(linebuf, linesize, &n, ibuf, 1, n) == NULL)
470 return -1;
472 if (n > 0 && (*linebuf)[n - 1] == '\n')
473 (*linebuf)[--n] = '\0';
474 return n;
478 * Return a file buffer all ready to read up the
479 * passed message pointer.
481 FILE *
482 setinput(struct mailbox *mp, struct message *m, enum needspec need)
484 enum okay ok = STOP;
486 switch (need) {
487 case NEED_HEADER:
488 if (m->m_have & HAVE_HEADER)
489 ok = OKAY;
490 else
491 ok = get_header(m);
492 break;
493 case NEED_BODY:
494 if (m->m_have & HAVE_BODY)
495 ok = OKAY;
496 else
497 ok = get_body(m);
498 break;
499 case NEED_UNSPEC:
500 ok = OKAY;
501 break;
503 if (ok != OKAY)
504 return NULL;
505 fflush(mp->mb_otf);
506 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
507 m->m_offset), SEEK_SET) < 0) {
508 perror("fseek");
509 panic(catgets(catd, CATSET, 77, "temporary file seek"));
511 return (mp->mb_itf);
514 struct message *
515 setdot(struct message *mp)
517 if (dot != mp) {
518 prevdot = dot;
519 did_print_dot = 0;
521 dot = mp;
522 uncollapse1(dot, 0);
523 return dot;
527 * Take the data out of the passed ghost file and toss it into
528 * a dynamically allocated message structure.
530 static void
531 makemessage(void)
533 if (msgCount == 0)
534 append(NULL);
535 setdot(message);
536 message[msgCount].m_size = 0;
537 message[msgCount].m_lines = 0;
541 * Append the passed message descriptor onto the message structure.
543 static void
544 append(struct message *mp)
546 if (msgCount + 1 >= msgspace)
547 message = srealloc(message, (msgspace += 64) * sizeof *message);
548 if (msgCount > 0)
549 message[msgCount - 1] = *mp;
553 * Delete a file, but only if the file is a plain file.
556 rm(char *name)
558 struct stat sb;
560 if (stat(name, &sb) < 0)
561 return(-1);
562 if (!S_ISREG(sb.st_mode)) {
563 errno = EISDIR;
564 return(-1);
566 return(unlink(name));
569 static int sigdepth; /* depth of holdsigs() */
570 static sigset_t nset, oset;
572 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
574 void
575 holdsigs(void)
578 if (sigdepth++ == 0) {
579 sigemptyset(&nset);
580 sigaddset(&nset, SIGHUP);
581 sigaddset(&nset, SIGINT);
582 sigaddset(&nset, SIGQUIT);
583 sigprocmask(SIG_BLOCK, &nset, &oset);
588 * Release signals SIGHUP, SIGINT, and SIGQUIT.
590 void
591 relsesigs(void)
593 if (--sigdepth == 0)
594 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
598 * Determine the size of the file possessed by
599 * the passed buffer.
601 off_t
602 fsize(FILE *iob)
604 struct stat sbuf;
606 if (fstat(fileno(iob), &sbuf) < 0)
607 return 0;
608 return sbuf.st_size;
612 * Just like expand(), but expanded file must be FILE or DIR
614 char *
615 file_expand(char const *name)
617 return (_expand(name, 1));
621 * Evaluate the string given as a new mailbox name.
622 * Supported meta characters:
623 * % for my system mail box
624 * %user for user's system mail box
625 * # for previous file
626 * & invoker's mbox file
627 * +file file in folder directory
628 * any shell meta character
629 * Return the file name as a dynamic string.
631 char *
632 expand(char const *name)
634 return (_expand(name, 0));
638 * Determine the current folder directory name.
641 getfold(char *name, int size)
643 char *folder;
644 enum protocol p;
646 if ((folder = value("folder")) == NULL)
647 return (-1);
648 if (*folder == '/' || ((p = which_protocol(folder)) != PROTO_FILE &&
649 p != PROTO_MAILDIR)) {
650 strncpy(name, folder, size);
651 name[size-1]='\0';
652 } else {
653 snprintf(name, size, "%s/%s", homedir, folder);
655 return (0);
659 * Return the name of the dead.letter file.
661 char *
662 getdeadletter(void)
664 char *cp;
666 if ((cp = value("DEAD")) == NULL || (cp = file_expand(cp)) == NULL)
667 cp = file_expand("~/dead.letter");
668 else if (*cp != '/') {
669 size_t sz = strlen(cp) + 3;
670 char *buf = ac_alloc(sz);
672 snprintf(buf, sz, "~/%s", cp);
673 cp = file_expand(buf);
674 ac_free(buf);
676 if (cp == NULL)
677 cp = "dead.letter";
678 return (cp);
682 * line is a buffer with the result of fgets(). Returns the first
683 * newline or the last character read.
685 static size_t
686 length_of_line(const char *line, size_t linesize)
688 register size_t i;
691 * Last character is always '\0' and was added by fgets.
693 linesize--;
694 for (i = 0; i < linesize; i++)
695 if (line[i] == '\n')
696 break;
697 return i < linesize ? i + 1 : linesize;
701 * fgets replacement to handle lines of arbitrary size and with
702 * embedded \0 characters.
703 * line - line buffer. *line be NULL.
704 * linesize - allocated size of line buffer.
705 * count - maximum characters to read. May be NULL.
706 * llen - length_of_line(*line).
707 * fp - input FILE.
708 * appendnl - always terminate line with \n, append if necessary.
710 char *
711 fgetline(char **line, size_t *linesize, size_t *count, size_t *llen,
712 FILE *fp, int appendnl)
714 size_t i_llen, sz;
716 if (count == NULL)
718 * If we have no count, we cannot determine where the
719 * characters returned by fgets() end if there was no
720 * newline. We have to read one character at one.
722 return fgetline_byone(line, linesize, llen, fp, appendnl, 0);
723 if (*line == NULL || *linesize < LINESIZE)
724 *line = srealloc(*line, *linesize = LINESIZE);
725 sz = *linesize <= *count ? *linesize : *count + 1;
726 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
728 * Leave llen untouched; it is used to determine whether
729 * the last line was \n-terminated in some callers.
731 return NULL;
732 i_llen = length_of_line(*line, sz);
733 *count -= i_llen;
734 while ((*line)[i_llen - 1] != '\n') {
735 *line = srealloc(*line, *linesize += 256);
736 sz = *linesize - i_llen;
737 sz = (sz <= *count ? sz : *count + 1);
738 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
739 if (appendnl) {
740 (*line)[i_llen++] = '\n';
741 (*line)[i_llen] = '\0';
743 break;
745 sz = length_of_line(&(*line)[i_llen], sz);
746 i_llen += sz;
747 *count -= sz;
749 if (llen)
750 *llen = i_llen;
751 return *line;
755 * Read a line, one character at once.
757 static char *
758 fgetline_byone(char **line, size_t *linesize, size_t *llen,
759 FILE *fp, int appendnl, size_t n)
761 int c;
763 if (*line == NULL || *linesize < LINESIZE + n + 1)
764 *line = srealloc(*line, *linesize = LINESIZE + n + 1);
765 for (;;) {
766 if (n >= *linesize - 128)
767 *line = srealloc(*line, *linesize += 256);
768 c = getc(fp);
769 if (c != EOF) {
770 (*line)[n++] = c;
771 (*line)[n] = '\0';
772 if (c == '\n')
773 break;
774 } else {
775 if (n > 0) {
776 if (appendnl) {
777 (*line)[n++] = '\n';
778 (*line)[n] = '\0';
780 break;
781 } else
782 return NULL;
785 if (llen)
786 *llen = n;
787 return *line;
790 static enum okay
791 get_header(struct message *mp)
793 switch (mb.mb_type) {
794 case MB_FILE:
795 case MB_MAILDIR:
796 return OKAY;
797 case MB_POP3:
798 return pop3_header(mp);
799 case MB_IMAP:
800 case MB_CACHE:
801 return imap_header(mp);
802 case MB_VOID:
803 return STOP;
805 /*NOTREACHED*/
806 return STOP;
809 enum okay
810 get_body(struct message *mp)
812 switch (mb.mb_type) {
813 case MB_FILE:
814 case MB_MAILDIR:
815 return OKAY;
816 case MB_POP3:
817 return pop3_body(mp);
818 case MB_IMAP:
819 case MB_CACHE:
820 return imap_body(mp);
821 case MB_VOID:
822 return STOP;
824 /*NOTREACHED*/
825 return STOP;
828 #ifdef HAVE_SOCKETS
829 static long xwrite(int fd, const char *data, size_t sz);
831 static long
832 xwrite(int fd, const char *data, size_t sz)
834 long wo;
835 size_t wt = 0;
837 do {
838 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
839 if (errno == EINTR)
840 continue;
841 else
842 return -1;
844 wt += wo;
845 } while (wt < sz);
846 return sz;
850 sclose(struct sock *sp)
852 int i;
854 if (sp->s_fd > 0) {
855 if (sp->s_onclose != NULL)
856 (*sp->s_onclose)();
857 #if defined (USE_NSS)
858 if (sp->s_use_ssl) {
859 sp->s_use_ssl = 0;
860 i = PR_Close(sp->s_prfd) == PR_SUCCESS ? 0 : -1;
861 sp->s_prfd = NULL;
862 } else
863 #elif defined (USE_OPENSSL)
864 if (sp->s_use_ssl) {
865 sp->s_use_ssl = 0;
866 SSL_shutdown(sp->s_ssl);
867 SSL_free(sp->s_ssl);
868 sp->s_ssl = NULL;
869 SSL_CTX_free(sp->s_ctx);
870 sp->s_ctx = NULL;
872 #endif /* USE_SSL */
874 i = close(sp->s_fd);
876 sp->s_fd = -1;
877 return i;
879 sp->s_fd = -1;
880 return 0;
883 enum okay
884 swrite(struct sock *sp, const char *data)
886 return swrite1(sp, data, strlen(data), 0);
889 enum okay
890 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
892 int x;
894 if (use_buffer > 0) {
895 int di;
896 enum okay ok;
898 if (sp->s_wbuf == NULL) {
899 sp->s_wbufsize = 4096;
900 sp->s_wbuf = smalloc(sp->s_wbufsize);
901 sp->s_wbufpos = 0;
903 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
904 di = sp->s_wbufsize - sp->s_wbufpos;
905 sz -= di;
906 if (sp->s_wbufpos > 0) {
907 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
908 ok = swrite1(sp, sp->s_wbuf,
909 sp->s_wbufsize, -1);
910 } else
911 ok = swrite1(sp, data,
912 sp->s_wbufsize, -1);
913 if (ok != OKAY)
914 return STOP;
915 data += di;
916 sp->s_wbufpos = 0;
918 if (sz == sp->s_wbufsize) {
919 ok = swrite1(sp, data, sp->s_wbufsize, -1);
920 if (ok != OKAY)
921 return STOP;
922 } else if (sz) {
923 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
924 sp->s_wbufpos += sz;
926 return OKAY;
927 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
928 sp->s_wbufpos > 0) {
929 x = sp->s_wbufpos;
930 sp->s_wbufpos = 0;
931 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
932 return STOP;
934 if (sz == 0)
935 return OKAY;
936 #if defined (USE_NSS)
937 if (sp->s_use_ssl) {
938 x = PR_Write(sp->s_prfd, data, sz);
939 } else
940 #elif defined (USE_OPENSSL)
941 if (sp->s_use_ssl) {
942 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
943 if (x < 0) {
944 switch (SSL_get_error(sp->s_ssl, x)) {
945 case SSL_ERROR_WANT_READ:
946 case SSL_ERROR_WANT_WRITE:
947 goto ssl_retry;
950 } else
951 #endif /* USE_SSL */
953 x = xwrite(sp->s_fd, data, sz);
955 if (x != sz) {
956 char o[512];
957 snprintf(o, sizeof o, "%s write error",
958 sp->s_desc ? sp->s_desc : "socket");
959 #if defined (USE_NSS)
960 sp->s_use_ssl ? nss_gen_err("%s", o) : perror(o);
961 #elif defined (USE_OPENSSL)
962 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
963 #else /* !USE_SSL */
964 perror(o);
965 #endif /* !USE_SSL */
966 if (x < 0)
967 sclose(sp);
968 return STOP;
970 return OKAY;
974 sgetline(char **line, size_t *linesize, size_t *linelen, struct sock *sp)
976 char *lp = *line;
978 if (sp->s_rsz < 0) {
979 sclose(sp);
980 return sp->s_rsz;
982 do {
983 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
984 size_t diff = lp - *line;
985 *line = srealloc(*line, *linesize += 256);
986 lp = &(*line)[diff];
988 if (sp->s_rbufptr == NULL ||
989 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
990 #if defined (USE_NSS)
991 if (sp->s_use_ssl) {
992 if ((sp->s_rsz = PR_Read(sp->s_prfd,
993 sp->s_rbuf,
994 sizeof sp->s_rbuf)) <= 0) {
995 if (sp->s_rsz < 0) {
996 char o[512];
997 snprintf(o, sizeof o, "%s",
998 sp->s_desc ?
999 sp->s_desc :
1000 "socket");
1001 nss_gen_err("%s", o);
1003 break;
1005 } else
1006 #elif defined (USE_OPENSSL)
1007 if (sp->s_use_ssl) {
1008 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
1009 sp->s_rbuf,
1010 sizeof sp->s_rbuf)) <= 0) {
1011 if (sp->s_rsz < 0) {
1012 char o[512];
1013 switch(SSL_get_error(sp->s_ssl,
1014 sp->s_rsz)) {
1015 case SSL_ERROR_WANT_READ:
1016 case SSL_ERROR_WANT_WRITE:
1017 goto ssl_retry;
1019 snprintf(o, sizeof o, "%s",
1020 sp->s_desc ?
1021 sp->s_desc :
1022 "socket");
1023 ssl_gen_err("%s", o);
1026 break;
1028 } else
1029 #endif /* USE_SSL */
1031 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
1032 sizeof sp->s_rbuf)) <= 0) {
1033 if (sp->s_rsz < 0) {
1034 char o[512];
1035 if (errno == EINTR)
1036 goto again;
1037 snprintf(o, sizeof o, "%s",
1038 sp->s_desc ?
1039 sp->s_desc :
1040 "socket");
1041 perror(o);
1043 break;
1046 sp->s_rbufptr = sp->s_rbuf;
1048 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1049 *lp = '\0';
1050 if (linelen)
1051 *linelen = lp - *line;
1052 return lp - *line;
1055 enum okay
1056 sopen(const char *xserver, struct sock *sp, int use_ssl,
1057 const char *uhp, const char *portstr, int verbose)
1059 #ifdef USE_IPV6
1060 char hbuf[NI_MAXHOST];
1061 struct addrinfo hints, *res0, *res;
1062 #else
1063 struct sockaddr_in servaddr;
1064 struct in_addr **pptr;
1065 struct hostent *hp;
1066 struct servent *ep;
1067 unsigned short port = 0;
1068 #endif
1069 int sockfd;
1070 char *cp;
1071 char *server = (char *)xserver;
1072 (void)use_ssl;
1073 (void)uhp;
1075 if ((cp = strchr(server, ':')) != NULL) {
1076 portstr = &cp[1];
1077 #ifndef USE_IPV6
1078 port = strtol(portstr, NULL, 10);
1079 #endif
1080 server = salloc(cp - xserver + 1);
1081 memcpy(server, xserver, cp - xserver);
1082 server[cp - xserver] = '\0';
1084 #ifdef USE_IPV6
1085 memset(&hints, 0, sizeof hints);
1086 hints.ai_socktype = SOCK_STREAM;
1087 if (verbose)
1088 fprintf(stderr, "Resolving host %s . . .", server);
1089 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1090 fprintf(stderr, catgets(catd, CATSET, 252,
1091 "Could not resolve host: %s\n"), server);
1092 return STOP;
1093 } else if (verbose)
1094 fprintf(stderr, " done.\n");
1095 sockfd = -1;
1096 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1097 if (verbose) {
1098 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1099 hbuf, sizeof hbuf, NULL, 0,
1100 NI_NUMERICHOST) != 0)
1101 strcpy(hbuf, "unknown host");
1102 fprintf(stderr, catgets(catd, CATSET, 192,
1103 "Connecting to %s:%s . . ."),
1104 hbuf, portstr);
1106 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1107 res->ai_protocol)) >= 0) {
1108 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1109 close(sockfd);
1110 sockfd = -1;
1114 if (sockfd < 0) {
1115 perror(catgets(catd, CATSET, 254, "could not connect"));
1116 freeaddrinfo(res0);
1117 return STOP;
1119 freeaddrinfo(res0);
1120 #else /* USE_IPV6 */
1121 if (port == 0) {
1122 if (strcmp(portstr, "smtp") == 0)
1123 port = htons(25);
1124 else if (strcmp(portstr, "smtps") == 0)
1125 port = htons(465);
1126 else if (strcmp(portstr, "imap") == 0)
1127 port = htons(143);
1128 else if (strcmp(portstr, "imaps") == 0)
1129 port = htons(993);
1130 else if (strcmp(portstr, "pop3") == 0)
1131 port = htons(110);
1132 else if (strcmp(portstr, "pop3s") == 0)
1133 port = htons(995);
1134 else if ((ep = getservbyname((char *)portstr, "tcp")) != NULL)
1135 port = ep->s_port;
1136 else {
1137 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1138 portstr);
1139 return STOP;
1141 } else
1142 port = htons(port);
1143 if (verbose)
1144 fprintf(stderr, "Resolving host %s . . .", server);
1145 if ((hp = gethostbyname(server)) == NULL) {
1146 fprintf(stderr, catgets(catd, CATSET, 252,
1147 "Could not resolve host: %s\n"), server);
1148 return STOP;
1149 } else if (verbose)
1150 fprintf(stderr, " done.\n");
1151 pptr = (struct in_addr **)hp->h_addr_list;
1152 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1153 perror(catgets(catd, CATSET, 253, "could not create socket"));
1154 return STOP;
1156 memset(&servaddr, 0, sizeof servaddr);
1157 servaddr.sin_family = AF_INET;
1158 servaddr.sin_port = port;
1159 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1160 if (verbose)
1161 fprintf(stderr, catgets(catd, CATSET, 192,
1162 "Connecting to %s:%d . . ."),
1163 inet_ntoa(**pptr), ntohs(port));
1164 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1165 != 0) {
1166 perror(catgets(catd, CATSET, 254, "could not connect"));
1167 return STOP;
1169 #endif /* USE_IPV6 */
1170 if (verbose)
1171 fputs(catgets(catd, CATSET, 193, " connected.\n"), stderr);
1172 memset(sp, 0, sizeof *sp);
1173 sp->s_fd = sockfd;
1174 #ifdef USE_SSL
1175 if (use_ssl) {
1176 enum okay ok;
1178 if ((ok = ssl_open(server, sp, uhp)) != OKAY)
1179 sclose(sp);
1180 return ok;
1182 #endif
1183 return OKAY;
1185 #endif /* HAVE_SOCKETS */