INSTALL: more on v14.5
[s-mailx.git] / fio.c
blobaa19225d3218fe0cc1d50575e41dafa6e91c6abc
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ File I/O.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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 HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/wait.h>
46 #include <fcntl.h>
48 #ifdef HAVE_WORDEXP
49 # include <wordexp.h>
50 #endif
52 #ifdef HAVE_SOCKETS
53 # include <sys/socket.h>
55 # include <netdb.h>
57 # include <netinet/in.h>
59 # ifdef HAVE_ARPA_INET_H
60 # include <arpa/inet.h>
61 # endif
62 #endif
64 #ifdef HAVE_OPENSSL
65 # include <openssl/err.h>
66 # include <openssl/rand.h>
67 # include <openssl/ssl.h>
68 # include <openssl/x509v3.h>
69 # include <openssl/x509.h>
70 #endif
72 struct {
73 FILE *s_file; /* File we were in. */
74 enum condition s_cond; /* Saved state of conditionals */
75 int s_loading; /* Loading .mailrc, etc. */
76 #define SSTACK 20
77 } _sstack[SSTACK];
78 static size_t _ssp; /* Top of file stack */
79 static FILE * _input;
81 /* Locate the user's mailbox file (where new, unread mail is queued) */
82 static void _findmail(char *buf, size_t bufsize, char const *user,
83 bool_t force);
85 /* Perform shell meta character expansion */
86 static char * _globname(char const *name, enum fexp_mode fexpm);
88 /* *line* is a buffer with the result of fgets().
89 * Returns the first newline or the last character read */
90 static size_t _length_of_line(const char *line, size_t linesize);
92 /* Read a line, one character at a time */
93 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
94 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
96 static void makemessage(void);
97 static void _fio_append(struct message *mp);
98 static enum okay get_header(struct message *mp);
100 static void
101 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
103 char *cp;
105 if (strcmp(user, myname) == 0 && ! force &&
106 (cp = value("folder")) != NULL) {
107 switch (which_protocol(cp)) {
108 case PROTO_IMAP:
109 if (strcmp(cp, protbase(cp)) != 0)
110 goto jcopy;
111 snprintf(buf, bufsize, "%s/INBOX", cp);
112 goto jleave;
113 default:
114 break;
118 if (force || (cp = value("MAIL")) == NULL)
119 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
120 else {
121 jcopy:
122 n_strlcpy(buf, cp, bufsize);
124 jleave:
128 static char *
129 _globname(char const *name, enum fexp_mode fexpm)
131 #ifdef HAVE_WORDEXP
132 wordexp_t we;
133 char *cp = NULL;
134 sigset_t nset;
135 int i;
137 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
138 * and wait, which will fail if our SIGCHLD handler is active */
139 sigemptyset(&nset);
140 sigaddset(&nset, SIGCHLD);
141 sigprocmask(SIG_BLOCK, &nset, NULL);
143 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
144 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
145 memset(&we, 0, sizeof we);
146 i = wordexp(name, &we, 0);
147 sigprocmask(SIG_UNBLOCK, &nset, NULL);
149 switch (i) {
150 case 0:
151 break;
152 case WRDE_NOSPACE:
153 if (! (fexpm & FEXP_SILENT))
154 fprintf(stderr,
155 tr(83, "\"%s\": Expansion buffer overflow.\n"),
156 name);
157 goto jleave;
158 case WRDE_BADCHAR:
159 case WRDE_SYNTAX:
160 default:
161 if (! (fexpm & FEXP_SILENT))
162 fprintf(stderr, tr(242, "Syntax error in \"%s\"\n"),
163 name);
164 goto jleave;
167 switch (we.we_wordc) {
168 case 1:
169 cp = savestr(we.we_wordv[0]);
170 break;
171 case 0:
172 if (! (fexpm & FEXP_SILENT))
173 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
174 break;
175 default:
176 if (fexpm & FEXP_MULTIOK) {
177 size_t j, l;
179 for (l = 0, j = 0; j < we.we_wordc; ++j)
180 l += strlen(we.we_wordv[j]) + 1;
181 ++l;
182 cp = salloc(l);
183 for (l = 0, j = 0; j < we.we_wordc; ++j) {
184 size_t x = strlen(we.we_wordv[j]);
185 memcpy(cp + l, we.we_wordv[j], x);
186 l += x;
187 cp[l++] = ' ';
189 cp[l] = '\0';
190 } else if (! (fexpm & FEXP_SILENT))
191 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
192 break;
194 jleave:
195 wordfree(&we);
196 return cp;
198 #else /* !HAVE_WORDEXP */
199 extern int wait_status;
201 struct stat sbuf;
202 char xname[MAXPATHLEN], cmdbuf[MAXPATHLEN], /* also used for files */
203 *cp, *shellp;
204 int pid, l, pivec[2];
206 if (pipe(pivec) < 0) {
207 perror("pipe");
208 return NULL;
210 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
211 if ((shellp = value("SHELL")) == NULL)
212 shellp = UNCONST(SHELL);
213 pid = start_command(shellp, 0, -1, pivec[1], "-c", cmdbuf, NULL);
214 if (pid < 0) {
215 close(pivec[0]);
216 close(pivec[1]);
217 return NULL;
219 close(pivec[1]);
221 again:
222 l = read(pivec[0], xname, sizeof xname);
223 if (l < 0) {
224 if (errno == EINTR)
225 goto again;
226 perror("read");
227 close(pivec[0]);
228 return NULL;
230 close(pivec[0]);
231 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
232 if (! (fexpm & FEXP_SILENT))
233 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"),
234 name);
235 return NULL;
237 if (l == 0) {
238 if (! (fexpm & FEXP_SILENT))
239 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
240 return NULL;
242 if (l == sizeof xname) {
243 if (! (fexpm & FEXP_SILENT))
244 fprintf(stderr,
245 tr(83, "\"%s\": Expansion buffer overflow.\n"),
246 name);
247 return NULL;
249 xname[l] = 0;
250 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
252 cp[1] = '\0';
253 if (! (fexpm & FEXP_MULTIOK) && strchr(xname, ' ') &&
254 stat(xname, &sbuf) < 0) {
255 if (! (fexpm & FEXP_SILENT))
256 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
257 return NULL;
259 return savestr(xname);
260 #endif /* !HAVE_WORDEXP */
264 * line is a buffer with the result of fgets(). Returns the first
265 * newline or the last character read.
267 static size_t
268 _length_of_line(const char *line, size_t linesize)
270 size_t i;
272 /* Last character is always '\0' and was added by fgets() */
273 for (--linesize, i = 0; i < linesize; i++)
274 if (line[i] == '\n')
275 break;
276 return (i < linesize) ? i + 1 : linesize;
279 static char *
280 _fgetline_byone(char **line, size_t *linesize, size_t *llen,
281 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS)
283 int c;
285 assert(*linesize == 0 || *line != NULL);
286 for (;;) {
287 if (*linesize <= LINESIZE || n >= *linesize - 128) {
288 *linesize += ((*line == NULL)
289 ? LINESIZE + n + 1 : 256);
290 *line = (srealloc_safe)(*line, *linesize
291 SMALLOC_DEBUG_ARGSCALL);
293 c = getc(fp);
294 if (c != EOF) {
295 (*line)[n++] = c;
296 (*line)[n] = '\0';
297 if (c == '\n')
298 break;
299 } else {
300 if (n > 0) {
301 if (appendnl) {
302 (*line)[n++] = '\n';
303 (*line)[n] = '\0';
305 break;
306 } else
307 return NULL;
310 if (llen)
311 *llen = n;
312 return *line;
315 FL char *
316 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen,
317 FILE *fp, int appendnl SMALLOC_DEBUG_ARGS)
319 size_t i_llen, sz;
321 if (cnt == NULL)
323 * If we have no count, we cannot determine where the
324 * characters returned by fgets() end if there was no
325 * newline. We have to read one character at one.
327 return _fgetline_byone(line, linesize, llen, fp, appendnl, 0
328 SMALLOC_DEBUG_ARGSCALL);
329 if (*line == NULL || *linesize < LINESIZE)
330 *line = (srealloc_safe)(*line, *linesize = LINESIZE
331 SMALLOC_DEBUG_ARGSCALL);
332 sz = *linesize <= *cnt ? *linesize : *cnt + 1;
333 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
335 * Leave llen untouched; it is used to determine whether
336 * the last line was \n-terminated in some callers.
338 return NULL;
339 i_llen = _length_of_line(*line, sz);
340 *cnt -= i_llen;
341 while ((*line)[i_llen - 1] != '\n') {
342 *line = (srealloc_safe)(*line, *linesize += 256
343 SMALLOC_DEBUG_ARGSCALL);
344 sz = *linesize - i_llen;
345 sz = (sz <= *cnt ? sz : *cnt + 1);
346 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
347 if (appendnl) {
348 (*line)[i_llen++] = '\n';
349 (*line)[i_llen] = '\0';
351 break;
353 sz = _length_of_line(&(*line)[i_llen], sz);
354 i_llen += sz;
355 *cnt -= sz;
357 if (llen)
358 *llen = i_llen;
359 return *line;
362 FL int
363 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
364 SMALLOC_DEBUG_ARGS)
366 /* TODO readline_restart(): always *appends* LF just to strip it again;
367 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
368 long sz;
370 clearerr(ibuf);
372 * Interrupts will cause trouble if we are inside a stdio call. As
373 * this is only relevant if input comes from a terminal, we can simply
374 * bypass it by read() then.
376 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
377 assert(*linesize == 0 || *linebuf != NULL);
378 for (;;) {
379 if (*linesize <= LINESIZE || n >= *linesize - 128) {
380 *linesize += ((*linebuf == NULL)
381 ? LINESIZE + n + 1 : 256);
382 *linebuf = (srealloc_safe)(*linebuf, *linesize
383 SMALLOC_DEBUG_ARGSCALL);
385 again:
386 sz = read(0, *linebuf + n, *linesize - n - 1);
387 if (sz > 0) {
388 n += sz;
389 (*linebuf)[n] = '\0';
390 if (n > 0 && (*linebuf)[n - 1] == '\n')
391 break;
392 } else {
393 if (sz < 0 && errno == EINTR)
394 goto again;
395 if (n > 0) {
396 if ((*linebuf)[n - 1] != '\n') {
397 (*linebuf)[n++] = '\n';
398 (*linebuf)[n] = '\0';
400 break;
401 } else
402 return -1;
405 } else {
407 * Not reading from standard input or standard input not
408 * a terminal. We read one char at a time as it is the
409 * only way to get lines with embedded NUL characters in
410 * standard stdio.
412 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
413 SMALLOC_DEBUG_ARGSCALL) == NULL)
414 return -1;
416 if (n > 0 && (*linebuf)[n - 1] == '\n')
417 (*linebuf)[--n] = '\0';
418 return n;
421 FL int
422 (readline_input)(enum lned_mode lned, char const *prompt, char **linebuf,
423 size_t *linesize SMALLOC_DEBUG_ARGS)
425 FILE *ifile = (_input != NULL) ? _input : stdin;
426 bool_t doprompt, dotty;
427 int n;
429 doprompt = (!sourcing && (options & OPT_INTERACTIVE));
430 dotty = (doprompt && !boption("line-editor-disable"));
431 if (!doprompt)
432 prompt = NULL;
433 else if (prompt == NULL)
434 prompt = getprompt();
436 for (n = 0;;) {
437 if (dotty) {
438 assert(ifile == stdin);
439 n = (tty_readline)(prompt, linebuf, linesize, n
440 SMALLOC_DEBUG_ARGSCALL);
441 } else {
442 if (prompt != NULL && *prompt != '\0') {
443 fputs(prompt, stdout);
444 fflush(stdout);
446 n = (readline_restart)(ifile, linebuf, linesize, n
447 SMALLOC_DEBUG_ARGSCALL);
449 if (n <= 0)
450 break;
452 * POSIX says:
453 * An unquoted <backslash> at the end of a command line
454 * shall be discarded and the next line shall continue the
455 * command.
457 if ((lned & LNED_LF_ESC) && (*linebuf)[n - 1] == '\\') {
458 (*linebuf)[--n] = '\0';
459 if (prompt != NULL && *prompt != '\0')
460 prompt = ".. "; /* XXX PS2 .. */
461 continue;
463 if (dotty && (lned & LNED_HIST_ADD))
464 tty_addhist(*linebuf);
465 break;
467 return n;
470 FL char *
471 readstr_input(char const *prompt, char const *string) /* FIXME SIGS<->leaks */
473 /* TODO readstr_input(): linebuf pool */
474 size_t linesize = 0, slen;
475 char *linebuf = NULL, *rv = NULL;
476 bool_t doprompt, dotty;
478 doprompt = (!sourcing && (options & OPT_INTERACTIVE));
479 dotty = (doprompt && !boption("line-editor-disable"));
480 if (!doprompt)
481 prompt = NULL;
482 else if (prompt == NULL)
483 prompt = getprompt();
485 /* If STDIN is not a terminal, simply read from it */
486 if (dotty) {
487 slen = (string != NULL) ? strlen(string) : 0;
488 if (slen) {
489 linesize = slen + LINESIZE + 1;
490 linebuf = smalloc_safe(linesize);
491 if (slen)
492 memcpy(linebuf, string, slen + 1);
494 if (tty_readline(prompt, &linebuf, &linesize, slen) >= 0)
495 rv = linebuf;
496 } else {
497 if (prompt != NULL && *prompt != '\0') {
498 fputs(prompt, stdout);
499 fflush(stdout);
501 linesize = slen = 0;
502 linebuf = NULL;
503 if (readline_restart(stdin, &linebuf, &linesize, slen) >= 0)
504 rv = linebuf;
507 if (rv != NULL)
508 rv = (*rv == '\0') ? NULL : savestr(rv);
509 if (linebuf != NULL)
510 free(linebuf);
511 return rv;
514 * Set up the input pointers while copying the mail file into /tmp.
516 FL void
517 setptr(FILE *ibuf, off_t offset)
519 int c;
520 char *cp, *linebuf = NULL;
521 char const *cp2;
522 struct message this;
523 int maybe, inhead, thiscnt;
524 size_t linesize = 0, filesize, cnt;
526 maybe = 1;
527 inhead = 0;
528 thiscnt = 0;
529 memset(&this, 0, sizeof this);
530 this.m_flag = MUSED|MNEW|MNEWEST;
531 filesize = mailsize - offset;
532 offset = ftell(mb.mb_otf);
533 for (;;) {
534 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0)
535 == NULL) {
536 this.m_xsize = this.m_size;
537 this.m_xlines = this.m_lines;
538 this.m_have = HAVE_HEADER|HAVE_BODY;
539 if (thiscnt > 0)
540 _fio_append(&this);
541 makemessage();
542 if (linebuf)
543 free(linebuf);
544 return;
546 #ifdef notdef
547 if (linebuf[0] == '\0')
548 linebuf[0] = '.';
549 #endif
550 /* XXX Convert CRLF to LF; this should be rethought in that
551 * XXX CRLF input should possibly end as CRLF output? */
552 if (cnt >= 2 && linebuf[cnt - 1] == '\n' &&
553 linebuf[cnt - 2] == '\r')
554 linebuf[--cnt - 1] = '\n';
555 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
556 if (ferror(mb.mb_otf)) {
557 perror("/tmp");
558 exit(1);
560 if (linebuf[cnt - 1] == '\n')
561 linebuf[cnt - 1] = '\0';
562 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt)) {
563 /* TODO
564 * TODO char date[FROM_DATEBUF];
565 * TODO extract_date_from_from_(linebuf, cnt, date);
566 * TODO this.m_time = 10000;
568 this.m_xsize = this.m_size;
569 this.m_xlines = this.m_lines;
570 this.m_have = HAVE_HEADER|HAVE_BODY;
571 if (thiscnt++ > 0)
572 _fio_append(&this);
573 msgCount++;
574 this.m_flag = MUSED|MNEW|MNEWEST;
575 this.m_size = 0;
576 this.m_lines = 0;
577 this.m_block = mailx_blockof(offset);
578 this.m_offset = mailx_offsetof(offset);
579 inhead = 1;
580 } else if (linebuf[0] == 0) {
581 inhead = 0;
582 } else if (inhead) {
583 for (cp = linebuf, cp2 = "status";; cp++) {
584 if ((c = *cp2++) == 0) {
585 while (c = *cp++, whitechar(c));
586 if (cp[-1] != ':')
587 break;
588 while ((c = *cp++) != '\0')
589 if (c == 'R')
590 this.m_flag |= MREAD;
591 else if (c == 'O')
592 this.m_flag &= ~MNEW;
593 break;
595 if (*cp != c && *cp != upperconv(c))
596 break;
598 for (cp = linebuf, cp2 = "x-status";; cp++) {
599 if ((c = *cp2++) == 0) {
600 while (c = *cp++, whitechar(c));
601 if (cp[-1] != ':')
602 break;
603 while ((c = *cp++) != '\0')
604 if (c == 'F')
605 this.m_flag |= MFLAGGED;
606 else if (c == 'A')
607 this.m_flag|=MANSWERED;
608 else if (c == 'T')
609 this.m_flag|=MDRAFTED;
610 break;
612 if (*cp != c && *cp != upperconv(c))
613 break;
616 offset += cnt;
617 this.m_size += cnt;
618 this.m_lines++;
619 maybe = linebuf[0] == 0;
621 /*NOTREACHED*/
625 * Drop the passed line onto the passed output buffer.
626 * If a write error occurs, return -1, else the count of
627 * characters written, including the newline.
629 FL int
630 putline(FILE *obuf, char *linebuf, size_t cnt)
632 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
633 putc('\n', obuf);
634 if (ferror(obuf))
635 return (-1);
636 return (cnt + 1);
640 * Return a file buffer all ready to read up the
641 * passed message pointer.
643 FL FILE *
644 setinput(struct mailbox *mp, struct message *m, enum needspec need)
646 enum okay ok = STOP;
648 switch (need) {
649 case NEED_HEADER:
650 if (m->m_have & HAVE_HEADER)
651 ok = OKAY;
652 else
653 ok = get_header(m);
654 break;
655 case NEED_BODY:
656 if (m->m_have & HAVE_BODY)
657 ok = OKAY;
658 else
659 ok = get_body(m);
660 break;
661 case NEED_UNSPEC:
662 ok = OKAY;
663 break;
665 if (ok != OKAY)
666 return NULL;
667 fflush(mp->mb_otf);
668 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
669 m->m_offset), SEEK_SET) < 0) {
670 perror("fseek");
671 panic(tr(77, "temporary file seek"));
673 return (mp->mb_itf);
676 FL struct message *
677 setdot(struct message *mp)
679 if (dot != mp) {
680 prevdot = dot;
681 did_print_dot = FAL0;
683 dot = mp;
684 uncollapse1(dot, 0);
685 return dot;
689 * Take the data out of the passed ghost file and toss it into
690 * a dynamically allocated message structure.
692 static void
693 makemessage(void)
695 if (msgCount == 0)
696 _fio_append(NULL);
697 setdot(message);
698 message[msgCount].m_size = 0;
699 message[msgCount].m_lines = 0;
703 * Append the passed message descriptor onto the message structure.
705 static void
706 _fio_append(struct message *mp)
708 if (msgCount + 1 >= msgspace)
709 message = srealloc(message, (msgspace += 64) * sizeof *message);
710 if (msgCount > 0)
711 message[msgCount - 1] = *mp;
715 * Delete a file, but only if the file is a plain file.
717 FL int
718 rm(char *name) /* TODO TOCTOU; but i'm out of ideas today */
720 struct stat sb;
721 int ret = -1;
723 if (stat(name, &sb) < 0)
725 else if (! S_ISREG(sb.st_mode))
726 errno = EISDIR;
727 else
728 ret = unlink(name);
729 return ret;
732 static int _fio_sigdepth; /* depth of holdsigs() */
733 static sigset_t _fio_nset, _fio_oset;
735 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
737 FL void
738 holdsigs(void)
741 if (_fio_sigdepth++ == 0) {
742 sigemptyset(&_fio_nset);
743 sigaddset(&_fio_nset, SIGHUP);
744 sigaddset(&_fio_nset, SIGINT);
745 sigaddset(&_fio_nset, SIGQUIT);
746 sigprocmask(SIG_BLOCK, &_fio_nset, &_fio_oset);
751 * Release signals SIGHUP, SIGINT, and SIGQUIT.
753 FL void
754 relsesigs(void)
756 if (--_fio_sigdepth == 0)
757 sigprocmask(SIG_SETMASK, &_fio_oset, NULL);
761 * Determine the size of the file possessed by
762 * the passed buffer.
764 FL off_t
765 fsize(FILE *iob)
767 struct stat sbuf;
769 if (fstat(fileno(iob), &sbuf) < 0)
770 return 0;
771 return sbuf.st_size;
774 FL char *
775 fexpand(char const *name, enum fexp_mode fexpm)
777 char cbuf[MAXPATHLEN], *res;
778 struct str s;
779 struct shortcut *sh;
780 bool_t dyn;
783 * The order of evaluation is "%" and "#" expand into constants.
784 * "&" can expand into "+". "+" can expand into shell meta characters.
785 * Shell meta characters expand into constants.
786 * This way, we make no recursive expansion.
788 res = UNCONST(name);
789 if (! (fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
790 res = sh->sh_long;
792 if (fexpm & FEXP_SHELL) {
793 dyn = FAL0;
794 goto jshell;
796 jnext:
797 dyn = FAL0;
798 switch (*res) {
799 case '%':
800 if (res[1] == ':' && res[2] != '\0') {
801 res = &res[2];
802 goto jnext;
804 _findmail(cbuf, sizeof cbuf,
805 (res[1] != '\0') ? res + 1 : myname,
806 (res[1] != '\0' || (options & OPT_u_FLAG)));
807 res = cbuf;
808 goto jislocal;
809 case '#':
810 if (res[1] != '\0')
811 break;
812 if (prevfile[0] == '\0') {
813 fprintf(stderr, tr(80, "No previous file\n"));
814 res = NULL;
815 goto jleave;
817 res = prevfile;
818 goto jislocal;
819 case '&':
820 if (res[1] == '\0') {
821 if ((res = value("MBOX")) == NULL)
822 res = UNCONST("~/mbox");
823 else if (res[0] != '&' || res[1] != '\0')
824 goto jnext;
826 break;
829 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
830 res = str_concat_csvl(&s,
831 protbase(mailname), "/", res + 1, NULL)->s;
832 dyn = TRU1;
835 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
836 size_t i = strlen(cbuf);
838 res = str_concat_csvl(&s, cbuf,
839 (i > 0 && cbuf[i - 1] == '/') ? "" : "/",
840 res + 1, NULL)->s;
841 dyn = TRU1;
843 if (res[0] == '%' && res[1] == ':') {
844 res += 2;
845 goto jnext;
849 /* Catch the most common shell meta character */
850 jshell:
851 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
852 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
853 dyn = TRU1;
856 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
857 which_protocol(res) == PROTO_FILE) {
858 res = _globname(res, fexpm);
859 dyn = TRU1;
860 goto jleave;
863 jislocal:
864 if (fexpm & FEXP_LOCAL)
865 switch (which_protocol(res)) {
866 case PROTO_FILE:
867 case PROTO_MAILDIR: /* XXX Really? ok MAILDIR for local? */
868 break;
869 default:
870 fprintf(stderr, tr(280,
871 "`%s': only a local file or directory may "
872 "be used\n"), name);
873 res = NULL;
874 break;
876 jleave:
877 if (res && ! dyn)
878 res = savestr(res);
879 return res;
882 FL void
883 demail(void)
886 if (value("keep") != NULL || rm(mailname) < 0) {
887 int fd = open(mailname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
888 if (fd >= 0)
889 close(fd);
893 FL bool_t
894 var_folder_updated(char const *name, char **store)
896 char rv = TRU1;
897 char *folder, *unres = NULL, *res = NULL;
899 if ((folder = UNCONST(name)) == NULL)
900 goto jleave;
902 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
903 /* XXX This *only* works because we do NOT
904 * XXX update environment variables via the "set" mechanism */
905 if (folder[0] == '%' && folder[1] == ':')
906 folder += 2;
907 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
908 goto jleave;
910 switch (which_protocol(folder)) {
911 case PROTO_POP3:
912 /* Ooops. This won't work */
913 fprintf(stderr, tr(501, "`folder' cannot be set to a flat, "
914 "readonly POP3 account\n"));
915 rv = FAL0;
916 goto jleave;
917 case PROTO_IMAP:
918 /* Simply assign what we have, even including `%:' prefix */
919 if (folder != name)
920 goto jvcopy;
921 goto jleave;
922 default:
923 /* Further expansion desired */
924 break;
927 /* All non-absolute paths are relative to our home directory */
928 if (*folder != '/') {
929 size_t l1 = strlen(homedir), l2 = strlen(folder);
930 unres = ac_alloc(l1 + l2 + 2);
931 memcpy(unres, homedir, l1);
932 unres[l1] = '/';
933 memcpy(unres + l1 + 1, folder, l2);
934 unres[l1 + 1 + l2] = '\0';
935 folder = unres;
938 /* Since lex.c:_update_mailname() uses realpath(3) if available to
939 * avoid that we loose track of our currently open folder in case we
940 * chdir away, but still checks the leading path portion against
941 * getfold() to be able to abbreviate to the +FOLDER syntax if
942 * possible, we need to realpath(3) the folder, too */
943 #ifdef HAVE_REALPATH
944 res = ac_alloc(MAXPATHLEN);
945 if (realpath(folder, res) == NULL)
946 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
947 else
948 folder = res;
949 #endif
951 jvcopy:
952 *store = sstrdup(folder);
954 if (res != NULL)
955 ac_free(res);
956 if (unres != NULL)
957 ac_free(unres);
958 jleave:
959 return rv;
962 FL bool_t
963 getfold(char *name, size_t size)
965 char const *folder;
967 if ((folder = value("folder")) != NULL)
968 (void)n_strlcpy(name, folder, size);
969 return (folder != NULL);
973 * Return the name of the dead.letter file.
975 FL char const *
976 getdeadletter(void)
978 char const *cp;
980 if ((cp = value("DEAD")) == NULL ||
981 (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
982 cp = fexpand("~/dead.letter", FEXP_LOCAL|FEXP_SHELL);
983 else if (*cp != '/') {
984 size_t sz = strlen(cp) + 3;
985 char *buf = ac_alloc(sz);
987 snprintf(buf, sz, "~/%s", cp);
988 cp = fexpand(buf, FEXP_LOCAL|FEXP_SHELL);
989 ac_free(buf);
991 if (cp == NULL)
992 cp = "dead.letter";
993 return cp;
996 static enum okay
997 get_header(struct message *mp)
999 (void)mp;
1000 switch (mb.mb_type) {
1001 case MB_FILE:
1002 case MB_MAILDIR:
1003 return (OKAY);
1004 #ifdef HAVE_POP3
1005 case MB_POP3:
1006 return (pop3_header(mp));
1007 #endif
1008 #ifdef HAVE_IMAP
1009 case MB_IMAP:
1010 case MB_CACHE:
1011 return imap_header(mp);
1012 #endif
1013 case MB_VOID:
1014 default:
1015 return (STOP);
1019 FL enum okay
1020 get_body(struct message *mp)
1022 (void)mp;
1023 switch (mb.mb_type) {
1024 case MB_FILE:
1025 case MB_MAILDIR:
1026 return (OKAY);
1027 #ifdef HAVE_POP3
1028 case MB_POP3:
1029 return (pop3_body(mp));
1030 #endif
1031 #ifdef HAVE_IMAP
1032 case MB_IMAP:
1033 case MB_CACHE:
1034 return imap_body(mp);
1035 #endif
1036 case MB_VOID:
1037 default:
1038 return (STOP);
1042 #ifdef HAVE_SOCKETS
1043 static long xwrite(int fd, const char *data, size_t sz);
1045 static long
1046 xwrite(int fd, const char *data, size_t sz)
1048 long wo;
1049 size_t wt = 0;
1051 do {
1052 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
1053 if (errno == EINTR)
1054 continue;
1055 else
1056 return -1;
1058 wt += wo;
1059 } while (wt < sz);
1060 return sz;
1063 FL int
1064 sclose(struct sock *sp)
1066 int i;
1068 if (sp->s_fd > 0) {
1069 if (sp->s_onclose != NULL)
1070 (*sp->s_onclose)();
1071 #ifdef HAVE_OPENSSL
1072 if (sp->s_use_ssl) {
1073 sp->s_use_ssl = 0;
1074 SSL_shutdown(sp->s_ssl);
1075 SSL_free(sp->s_ssl);
1076 sp->s_ssl = NULL;
1077 SSL_CTX_free(sp->s_ctx);
1078 sp->s_ctx = NULL;
1080 #endif
1082 i = close(sp->s_fd);
1084 sp->s_fd = -1;
1085 return i;
1087 sp->s_fd = -1;
1088 return 0;
1091 FL enum okay
1092 swrite(struct sock *sp, const char *data)
1094 return swrite1(sp, data, strlen(data), 0);
1097 FL enum okay
1098 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
1100 int x;
1102 if (use_buffer > 0) {
1103 int di;
1104 enum okay ok;
1106 if (sp->s_wbuf == NULL) {
1107 sp->s_wbufsize = 4096;
1108 sp->s_wbuf = smalloc(sp->s_wbufsize);
1109 sp->s_wbufpos = 0;
1111 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1112 di = sp->s_wbufsize - sp->s_wbufpos;
1113 sz -= di;
1114 if (sp->s_wbufpos > 0) {
1115 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1116 ok = swrite1(sp, sp->s_wbuf,
1117 sp->s_wbufsize, -1);
1118 } else
1119 ok = swrite1(sp, data,
1120 sp->s_wbufsize, -1);
1121 if (ok != OKAY)
1122 return STOP;
1123 data += di;
1124 sp->s_wbufpos = 0;
1126 if (sz == sp->s_wbufsize) {
1127 ok = swrite1(sp, data, sp->s_wbufsize, -1);
1128 if (ok != OKAY)
1129 return STOP;
1130 } else if (sz) {
1131 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1132 sp->s_wbufpos += sz;
1134 return OKAY;
1135 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
1136 sp->s_wbufpos > 0) {
1137 x = sp->s_wbufpos;
1138 sp->s_wbufpos = 0;
1139 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
1140 return STOP;
1142 if (sz == 0)
1143 return OKAY;
1144 #ifdef HAVE_OPENSSL
1145 if (sp->s_use_ssl) {
1146 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
1147 if (x < 0) {
1148 switch (SSL_get_error(sp->s_ssl, x)) {
1149 case SSL_ERROR_WANT_READ:
1150 case SSL_ERROR_WANT_WRITE:
1151 goto ssl_retry;
1154 } else
1155 #endif
1157 x = xwrite(sp->s_fd, data, sz);
1159 if (x != sz) {
1160 char o[512];
1161 snprintf(o, sizeof o, "%s write error",
1162 sp->s_desc ? sp->s_desc : "socket");
1163 #ifdef HAVE_OPENSSL
1164 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1165 #else
1166 perror(o);
1167 #endif
1168 if (x < 0)
1169 sclose(sp);
1170 return STOP;
1172 return OKAY;
1175 FL enum okay
1176 sopen(const char *xserver, struct sock *sp, int use_ssl,
1177 const char *uhp, const char *portstr, int verbose)
1179 #ifdef HAVE_SO_SNDTIMEO
1180 struct timeval tv;
1181 #endif
1182 #ifdef HAVE_SO_LINGER
1183 struct linger li;
1184 #endif
1185 #ifdef HAVE_IPV6
1186 char hbuf[NI_MAXHOST];
1187 struct addrinfo hints, *res0, *res;
1188 #else
1189 struct sockaddr_in servaddr;
1190 struct in_addr **pptr;
1191 struct hostent *hp;
1192 struct servent *ep;
1193 unsigned short port = 0;
1194 #endif
1195 int sockfd;
1196 char *cp;
1197 char *server = UNCONST(xserver);
1198 (void)use_ssl;
1199 (void)uhp;
1201 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1202 portstr = &cp[1];
1203 #ifndef HAVE_IPV6
1204 port = strtol(portstr, NULL, 10);
1205 #endif
1206 server = salloc(cp - xserver + 1);
1207 memcpy(server, xserver, cp - xserver);
1208 server[cp - xserver] = '\0';
1211 /* Connect timeouts after 30 seconds */
1212 #ifdef HAVE_SO_SNDTIMEO
1213 tv.tv_sec = 30;
1214 tv.tv_usec = 0;
1215 #endif
1217 #ifdef HAVE_IPV6
1218 if (verbose)
1219 fprintf(stderr, "Resolving host %s . . .", server);
1220 memset(&hints, 0, sizeof hints);
1221 hints.ai_socktype = SOCK_STREAM;
1222 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1223 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1224 return STOP;
1225 } else if (verbose)
1226 fprintf(stderr, tr(500, " done.\n"));
1228 sockfd = -1;
1229 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1230 if (verbose) {
1231 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1232 hbuf, sizeof hbuf, NULL, 0,
1233 NI_NUMERICHOST) != 0)
1234 strcpy(hbuf, "unknown host");
1235 fprintf(stderr, tr(192,
1236 "%sConnecting to %s:%s . . ."),
1237 (res == res0) ? "" : "\n",
1238 hbuf, portstr);
1240 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1241 res->ai_protocol)) >= 0) {
1242 # ifdef HAVE_SO_SNDTIMEO
1243 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,
1244 &tv, sizeof tv);
1245 # endif
1246 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1247 close(sockfd);
1248 sockfd = -1;
1252 if (sockfd < 0) {
1253 perror(tr(254, " could not connect"));
1254 freeaddrinfo(res0);
1255 return STOP;
1257 freeaddrinfo(res0);
1259 #else /* HAVE_IPV6 */
1260 if (port == 0) {
1261 if (strcmp(portstr, "smtp") == 0)
1262 port = htons(25);
1263 else if (strcmp(portstr, "smtps") == 0)
1264 port = htons(465);
1265 # ifdef HAVE_IMAP
1266 else if (strcmp(portstr, "imap") == 0)
1267 port = htons(143);
1268 else if (strcmp(portstr, "imaps") == 0)
1269 port = htons(993);
1270 # endif
1271 # ifdef HAVE_POP3
1272 else if (strcmp(portstr, "pop3") == 0)
1273 port = htons(110);
1274 else if (strcmp(portstr, "pop3s") == 0)
1275 port = htons(995);
1276 # endif
1277 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1278 port = ep->s_port;
1279 else {
1280 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1281 portstr);
1282 return (STOP);
1284 } else
1285 port = htons(port);
1287 if (verbose)
1288 fprintf(stderr, "Resolving host %s . . .", server);
1289 if ((hp = gethostbyname(server)) == NULL) {
1290 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1291 return STOP;
1292 } else if (verbose)
1293 fprintf(stderr, tr(500, " done.\n"));
1295 pptr = (struct in_addr **)hp->h_addr_list;
1296 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1297 perror(tr(253, "could not create socket"));
1298 return STOP;
1300 memset(&servaddr, 0, sizeof servaddr);
1301 servaddr.sin_family = AF_INET;
1302 servaddr.sin_port = port;
1303 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1304 if (verbose)
1305 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1306 "", inet_ntoa(**pptr), ntohs(port));
1308 # ifdef HAVE_SO_SNDTIMEO
1309 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1310 # endif
1311 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1312 != 0) {
1313 perror(tr(254, " could not connect"));
1314 return STOP;
1316 #endif /* HAVE_IPV6 */
1317 if (verbose)
1318 fputs(tr(193, " connected.\n"), stderr);
1320 /* And the regular timeouts */
1321 #ifdef HAVE_SO_SNDTIMEO
1322 tv.tv_sec = 42;
1323 tv.tv_usec = 0;
1324 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1325 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1326 #endif
1327 #ifdef HAVE_SO_LINGER
1328 li.l_onoff = 1;
1329 li.l_linger = 42;
1330 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1331 #endif
1333 memset(sp, 0, sizeof *sp);
1334 sp->s_fd = sockfd;
1335 #ifdef HAVE_SSL
1336 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1337 sclose(sp);
1338 return STOP;
1340 #endif
1341 return OKAY;
1344 FL int
1345 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1346 SMALLOC_DEBUG_ARGS)
1348 char *lp = *line;
1350 if (sp->s_rsz < 0) {
1351 sclose(sp);
1352 return sp->s_rsz;
1354 do {
1355 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
1356 size_t diff = lp - *line;
1357 *line = (srealloc)(*line, *linesize += 256
1358 SMALLOC_DEBUG_ARGSCALL);
1359 lp = &(*line)[diff];
1361 if (sp->s_rbufptr == NULL ||
1362 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
1363 #ifdef HAVE_OPENSSL
1364 if (sp->s_use_ssl) {
1365 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
1366 sp->s_rbuf,
1367 sizeof sp->s_rbuf)) <= 0) {
1368 if (sp->s_rsz < 0) {
1369 char o[512];
1370 switch(SSL_get_error(sp->s_ssl,
1371 sp->s_rsz)) {
1372 case SSL_ERROR_WANT_READ:
1373 case SSL_ERROR_WANT_WRITE:
1374 goto ssl_retry;
1376 snprintf(o, sizeof o, "%s",
1377 sp->s_desc ?
1378 sp->s_desc :
1379 "socket");
1380 ssl_gen_err("%s", o);
1383 break;
1385 } else
1386 #endif
1388 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
1389 sizeof sp->s_rbuf)) <= 0) {
1390 if (sp->s_rsz < 0) {
1391 char o[512];
1392 if (errno == EINTR)
1393 goto again;
1394 snprintf(o, sizeof o, "%s",
1395 sp->s_desc ?
1396 sp->s_desc :
1397 "socket");
1398 perror(o);
1400 break;
1403 sp->s_rbufptr = sp->s_rbuf;
1405 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1406 *lp = '\0';
1407 if (linelen)
1408 *linelen = lp - *line;
1409 return lp - *line;
1411 #endif /* HAVE_SOCKETS */
1413 FL void
1414 load(char const *name)
1416 FILE *in, *oldin;
1418 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1419 return;
1420 oldin = _input;
1421 _input = in;
1422 loading = TRU1;
1423 sourcing = TRU1;
1424 commands();
1425 loading = FAL0;
1426 sourcing = FAL0;
1427 _input = oldin;
1428 Fclose(in);
1431 FL int
1432 csource(void *v)
1434 int rv = 1;
1435 char **arglist = v;
1436 FILE *fi;
1437 char *cp;
1439 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1440 goto jleave;
1441 if ((fi = Fopen(cp, "r")) == NULL) {
1442 perror(cp);
1443 goto jleave;
1445 if (_ssp >= SSTACK - 1) {
1446 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1447 Fclose(fi);
1448 goto jleave;
1451 _sstack[_ssp].s_file = _input;
1452 _sstack[_ssp].s_cond = cond;
1453 _sstack[_ssp].s_loading = loading;
1454 ++_ssp;
1455 loading = FAL0;
1456 cond = CANY;
1457 _input = fi;
1458 sourcing = TRU1;
1459 rv = 0;
1460 jleave:
1461 return rv;
1464 FL int
1465 unstack(void)
1467 int rv = 1;
1469 if (_ssp <= 0) {
1470 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1471 sourcing = FAL0;
1472 goto jleave;
1475 Fclose(_input);
1476 if (cond != CANY)
1477 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1478 --_ssp;
1479 cond = _sstack[_ssp].s_cond;
1480 loading = _sstack[_ssp].s_loading;
1481 _input = _sstack[_ssp].s_file;
1482 if (_ssp == 0)
1483 sourcing = loading;
1484 rv = 0;
1485 jleave:
1486 return rv;