nail.1, mk-mk.in: include the VERSION in the manual
[s-mailx.git] / fio.c
blob1e360ce64743a1ee4348b4a1315892a520b95316
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 #include "rcv.h"
42 #include <sys/file.h>
43 #include <sys/stat.h>
44 #include <sys/wait.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #ifdef HAVE_WORDEXP
49 # include <wordexp.h>
50 #endif
52 #ifdef HAVE_SOCKETS
53 # include <sys/socket.h>
54 # include <netdb.h>
55 # include <netinet/in.h>
56 # ifdef HAVE_ARPA_INET_H
57 # include <arpa/inet.h>
58 # endif
59 #endif
61 #ifdef HAVE_OPENSSL
62 # include <openssl/err.h>
63 # include <openssl/rand.h>
64 # include <openssl/ssl.h>
65 # include <openssl/x509v3.h>
66 # include <openssl/x509.h>
67 #endif
69 #include "extern.h"
71 struct {
72 FILE *s_file; /* File we were in. */
73 enum condition s_cond; /* Saved state of conditionals */
74 int s_loading; /* Loading .mailrc, etc. */
75 #define SSTACK 20
76 } _sstack[SSTACK];
77 static size_t _ssp; /* Top of file stack */
78 static FILE * _input;
80 /* Locate the user's mailbox file (where new, unread mail is queued) */
81 static void _findmail(char *buf, size_t bufsize, char const *user,
82 bool_t force);
84 /* Perform shell meta character expansion */
85 static char * _globname(char const *name, enum fexp_mode fexpm);
87 /* *line* is a buffer with the result of fgets().
88 * Returns the first newline or the last character read */
89 static size_t _length_of_line(const char *line, size_t linesize);
91 /* Read a line, one character at a time */
92 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
93 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
95 static void makemessage(void);
96 static void append(struct message *mp);
97 static enum okay get_header(struct message *mp);
99 static void
100 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
102 char *cp;
104 if (strcmp(user, myname) == 0 && ! force &&
105 (cp = value("folder")) != NULL) {
106 switch (which_protocol(cp)) {
107 case PROTO_IMAP:
108 if (strcmp(cp, protbase(cp)) != 0)
109 goto jcopy;
110 snprintf(buf, bufsize, "%s/INBOX", cp);
111 goto jleave;
112 default:
113 break;
117 if (force || (cp = value("MAIL")) == NULL)
118 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
119 else {
120 jcopy:
121 n_strlcpy(buf, cp, bufsize);
123 jleave:
127 static char *
128 _globname(char const *name, enum fexp_mode fexpm)
130 #ifdef HAVE_WORDEXP
131 wordexp_t we;
132 char *cp = NULL;
133 sigset_t nset;
134 int i;
137 * Some systems (notably Open UNIX 8.0.0) fork a shell for
138 * wordexp() and wait for it; waiting will fail if our SIGCHLD
139 * handler is active.
141 sigemptyset(&nset);
142 sigaddset(&nset, SIGCHLD);
143 sigprocmask(SIG_BLOCK, &nset, NULL);
144 /* Mac OS X Snow Leopard doesn't init fields on error, causing SIGSEGV
145 * in wordfree(3) */
146 # ifdef __APPLE__
147 memset(&we, 0, sizeof we);
148 # endif
149 i = wordexp(name, &we, 0);
150 sigprocmask(SIG_UNBLOCK, &nset, NULL);
152 switch (i) {
153 case 0:
154 break;
155 case WRDE_NOSPACE:
156 if (! (fexpm & FEXP_SILENT))
157 fprintf(stderr,
158 tr(83, "\"%s\": Expansion buffer overflow.\n"),
159 name);
160 goto jleave;
161 case WRDE_BADCHAR:
162 case WRDE_SYNTAX:
163 default:
164 if (! (fexpm & FEXP_SILENT))
165 fprintf(stderr, tr(242, "Syntax error in \"%s\"\n"),
166 name);
167 goto jleave;
170 switch (we.we_wordc) {
171 case 1:
172 cp = savestr(we.we_wordv[0]);
173 break;
174 case 0:
175 if (! (fexpm & FEXP_SILENT))
176 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
177 break;
178 default:
179 if (fexpm & FEXP_MULTIOK) {
180 size_t j, l;
182 for (l = 0, j = 0; j < we.we_wordc; ++j)
183 l += strlen(we.we_wordv[j]) + 1;
184 ++l;
185 cp = salloc(l);
186 for (l = 0, j = 0; j < we.we_wordc; ++j) {
187 size_t x = strlen(we.we_wordv[j]);
188 memcpy(cp + l, we.we_wordv[j], x);
189 l += x;
190 cp[l++] = ' ';
192 cp[l] = '\0';
193 } else if (! (fexpm & FEXP_SILENT))
194 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
195 break;
197 jleave:
198 wordfree(&we);
199 return cp;
201 #else /* !HAVE_WORDEXP */
202 extern int wait_status;
204 struct stat sbuf;
205 char xname[MAXPATHLEN], cmdbuf[MAXPATHLEN], /* also used for files */
206 *cp, *shell;
207 int pid, l, pivec[2];
209 if (pipe(pivec) < 0) {
210 perror("pipe");
211 return NULL;
213 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
214 if ((shell = value("SHELL")) == NULL)
215 shell = UNCONST(SHELL);
216 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
217 if (pid < 0) {
218 close(pivec[0]);
219 close(pivec[1]);
220 return NULL;
222 close(pivec[1]);
224 again:
225 l = read(pivec[0], xname, sizeof xname);
226 if (l < 0) {
227 if (errno == EINTR)
228 goto again;
229 perror("read");
230 close(pivec[0]);
231 return NULL;
233 close(pivec[0]);
234 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
235 if (! (fexpm & FEXP_SILENT))
236 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"),
237 name);
238 return NULL;
240 if (l == 0) {
241 if (! (fexpm & FEXP_SILENT))
242 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
243 return NULL;
245 if (l == sizeof xname) {
246 if (! (fexpm & FEXP_SILENT))
247 fprintf(stderr,
248 tr(83, "\"%s\": Expansion buffer overflow.\n"),
249 name);
250 return NULL;
252 xname[l] = 0;
253 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
255 cp[1] = '\0';
256 if (! (fexpm & FEXP_MULTIOK) && strchr(xname, ' ') &&
257 stat(xname, &sbuf) < 0) {
258 if (! (fexpm & FEXP_SILENT))
259 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
260 return NULL;
262 return savestr(xname);
263 #endif /* !HAVE_WORDEXP */
267 * line is a buffer with the result of fgets(). Returns the first
268 * newline or the last character read.
270 static size_t
271 _length_of_line(const char *line, size_t linesize)
273 size_t i;
275 /* Last character is always '\0' and was added by fgets() */
276 for (--linesize, i = 0; i < linesize; i++)
277 if (line[i] == '\n')
278 break;
279 return (i < linesize) ? i + 1 : linesize;
282 static char *
283 _fgetline_byone(char **line, size_t *linesize, size_t *llen,
284 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS)
286 int c;
288 if (*line == NULL || *linesize < LINESIZE + n + 1)
289 *line = (srealloc)(*line, *linesize = LINESIZE + n + 1
290 SMALLOC_DEBUG_ARGSCALL);
291 for (;;) {
292 if (n >= *linesize - 128)
293 *line = (srealloc)(*line, *linesize += 256
294 SMALLOC_DEBUG_ARGSCALL);
295 c = getc(fp);
296 if (c != EOF) {
297 (*line)[n++] = c;
298 (*line)[n] = '\0';
299 if (c == '\n')
300 break;
301 } else {
302 if (n > 0) {
303 if (appendnl) {
304 (*line)[n++] = '\n';
305 (*line)[n] = '\0';
307 break;
308 } else
309 return NULL;
312 if (llen)
313 *llen = n;
314 return *line;
317 char *
318 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen,
319 FILE *fp, int appendnl SMALLOC_DEBUG_ARGS)
321 size_t i_llen, sz;
323 if (cnt == NULL)
325 * If we have no count, we cannot determine where the
326 * characters returned by fgets() end if there was no
327 * newline. We have to read one character at one.
329 return _fgetline_byone(line, linesize, llen, fp, appendnl, 0
330 SMALLOC_DEBUG_ARGSCALL);
331 if (*line == NULL || *linesize < LINESIZE)
332 *line = (srealloc)(*line, *linesize = LINESIZE
333 SMALLOC_DEBUG_ARGSCALL);
334 sz = *linesize <= *cnt ? *linesize : *cnt + 1;
335 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
337 * Leave llen untouched; it is used to determine whether
338 * the last line was \n-terminated in some callers.
340 return NULL;
341 i_llen = _length_of_line(*line, sz);
342 *cnt -= i_llen;
343 while ((*line)[i_llen - 1] != '\n') {
344 *line = (srealloc)(*line, *linesize += 256
345 SMALLOC_DEBUG_ARGSCALL);
346 sz = *linesize - i_llen;
347 sz = (sz <= *cnt ? sz : *cnt + 1);
348 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
349 if (appendnl) {
350 (*line)[i_llen++] = '\n';
351 (*line)[i_llen] = '\0';
353 break;
355 sz = _length_of_line(&(*line)[i_llen], sz);
356 i_llen += sz;
357 *cnt -= sz;
359 if (llen)
360 *llen = i_llen;
361 return *line;
365 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
366 SMALLOC_DEBUG_ARGS)
368 /* TODO readline_restart(): always *appends* LF just to strip it again;
369 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
370 long sz;
372 clearerr(ibuf);
374 * Interrupts will cause trouble if we are inside a stdio call. As
375 * this is only relevant if input comes from a terminal, we can simply
376 * bypass it by read() then.
378 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
379 if (*linebuf == NULL || *linesize < LINESIZE + n + 1)
380 *linebuf = (srealloc)(*linebuf,
381 *linesize = LINESIZE + n + 1
382 SMALLOC_DEBUG_ARGSCALL);
383 for (;;) {
384 if (n >= *linesize - 128)
385 *linebuf = (srealloc)(*linebuf,
386 *linesize += 256
387 SMALLOC_DEBUG_ARGSCALL);
388 again:
389 sz = read(0, *linebuf + n, *linesize - n - 1);
390 if (sz > 0) {
391 n += sz;
392 (*linebuf)[n] = '\0';
393 if (n > 0 && (*linebuf)[n - 1] == '\n')
394 break;
395 } else {
396 if (sz < 0 && errno == EINTR)
397 goto again;
398 if (n > 0) {
399 if ((*linebuf)[n - 1] != '\n') {
400 (*linebuf)[n++] = '\n';
401 (*linebuf)[n] = '\0';
403 break;
404 } else
405 return -1;
408 } else {
410 * Not reading from standard input or standard input not
411 * a terminal. We read one char at a time as it is the
412 * only way to get lines with embedded NUL characters in
413 * standard stdio.
415 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
416 SMALLOC_DEBUG_ARGSCALL) == NULL)
417 return -1;
419 if (n > 0 && (*linebuf)[n - 1] == '\n')
420 (*linebuf)[--n] = '\0';
421 return n;
425 (readline_input)(enum lned_mode lned, char const *prompt, char **linebuf,
426 size_t *linesize SMALLOC_DEBUG_ARGS)
428 FILE *ifile = (_input != NULL) ? _input : stdin;
429 bool_t doprompt, dotty;
430 int n;
432 if (prompt == NULL)
433 prompt = getprompt();
434 doprompt = (! sourcing && (options & OPT_INTERACTIVE));
435 dotty = (doprompt && ! boption("line-editor-disable"));
437 for (n = 0;;) {
438 if (dotty) {
439 assert(ifile == stdin);
440 n = (tty_readline)(prompt, linebuf, linesize, n
441 SMALLOC_DEBUG_ARGSCALL);
442 } else {
443 if (doprompt && *prompt) {
444 fputs(prompt, stdout);
445 fflush(stdout);
447 n = (readline_restart)(ifile, linebuf, linesize, n
448 SMALLOC_DEBUG_ARGSCALL);
450 if (n <= 0)
451 break;
453 * POSIX says:
454 * An unquoted <backslash> at the end of a command line
455 * shall be discarded and the next line shall continue the
456 * command.
458 if ((lned & LNED_LF_ESC) && (*linebuf)[n - 1] == '\\') {
459 (*linebuf)[--n] = '\0';
460 if (*prompt)
461 prompt = ".. "; /* XXX PS2 .. */
462 continue;
464 if (dotty && (lned & LNED_HIST_ADD))
465 tty_addhist(*linebuf);
466 break;
468 return n;
471 char *
472 readstr_input(char const *prompt, char const *string) /* FIXME SIGS<->leaks */
474 /* TODO readstr_input(): linebuf pool */
475 size_t linesize = 0, slen;
476 char *linebuf = NULL, *rv = NULL;
477 bool_t doprompt, dotty;
479 if (prompt == NULL)
480 prompt = getprompt();
481 doprompt = (! sourcing && (options & OPT_INTERACTIVE));
482 dotty = (doprompt && ! boption("line-editor-disable"));
484 /* If STDIN is not a terminal, simply read from it */
485 if (dotty) {
486 slen = (string != NULL) ? strlen(string) : 0;
487 if (slen) {
488 linesize = slen + LINESIZE + 1;
489 linebuf = smalloc(linesize);
490 if (slen)
491 memcpy(linebuf, string, slen + 1);
493 if (tty_readline(prompt, &linebuf, &linesize, slen) >= 0)
494 rv = linebuf;
495 } else {
496 if (doprompt && *prompt) {
497 fputs(prompt, stdout);
498 fflush(stdout);
500 linesize = slen = 0;
501 linebuf = NULL;
502 if (readline_restart(stdin, &linebuf, &linesize, slen) >= 0)
503 rv = linebuf;
506 if (rv != NULL)
507 rv = (*rv == '\0') ? NULL : savestr(rv);
508 if (linebuf != NULL)
509 free(linebuf);
510 return rv;
513 * Set up the input pointers while copying the mail file into /tmp.
515 void
516 setptr(FILE *ibuf, off_t offset)
518 int c;
519 char *cp, *linebuf = NULL;
520 char const *cp2;
521 struct message this;
522 int maybe, inhead, thiscnt;
523 size_t linesize = 0, filesize, cnt;
525 maybe = 1;
526 inhead = 0;
527 thiscnt = 0;
528 memset(&this, 0, sizeof this);
529 this.m_flag = MUSED|MNEW|MNEWEST;
530 filesize = mailsize - offset;
531 offset = ftell(mb.mb_otf);
532 for (;;) {
533 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0)
534 == NULL) {
535 this.m_xsize = this.m_size;
536 this.m_xlines = this.m_lines;
537 this.m_have = HAVE_HEADER|HAVE_BODY;
538 if (thiscnt > 0)
539 append(&this);
540 makemessage();
541 if (linebuf)
542 free(linebuf);
543 return;
545 #ifdef notdef
546 if (linebuf[0] == '\0')
547 linebuf[0] = '.';
548 #endif
549 /* XXX Convert CRLF to LF; this should be rethought in that
550 * XXX CRLF input should possibly end as CRLF output? */
551 if (cnt >= 2 && linebuf[cnt - 1] == '\n' &&
552 linebuf[cnt - 2] == '\r')
553 linebuf[--cnt - 1] = '\n';
554 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
555 if (ferror(mb.mb_otf)) {
556 perror("/tmp");
557 exit(1);
559 if (linebuf[cnt - 1] == '\n')
560 linebuf[cnt - 1] = '\0';
561 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt)) {
562 /* TODO
563 * TODO char date[FROM_DATEBUF];
564 * TODO extract_date_from_from_(linebuf, cnt, date);
565 * TODO this.m_time = 10000;
567 this.m_xsize = this.m_size;
568 this.m_xlines = this.m_lines;
569 this.m_have = HAVE_HEADER|HAVE_BODY;
570 if (thiscnt++ > 0)
571 append(&this);
572 msgCount++;
573 this.m_flag = MUSED|MNEW|MNEWEST;
574 this.m_size = 0;
575 this.m_lines = 0;
576 this.m_block = mailx_blockof(offset);
577 this.m_offset = mailx_offsetof(offset);
578 inhead = 1;
579 } else if (linebuf[0] == 0) {
580 inhead = 0;
581 } else if (inhead) {
582 for (cp = linebuf, cp2 = "status";; cp++) {
583 if ((c = *cp2++) == 0) {
584 while (c = *cp++, whitechar(c));
585 if (cp[-1] != ':')
586 break;
587 while ((c = *cp++) != '\0')
588 if (c == 'R')
589 this.m_flag |= MREAD;
590 else if (c == 'O')
591 this.m_flag &= ~MNEW;
592 break;
594 if (*cp != c && *cp != upperconv(c))
595 break;
597 for (cp = linebuf, cp2 = "x-status";; cp++) {
598 if ((c = *cp2++) == 0) {
599 while (c = *cp++, whitechar(c));
600 if (cp[-1] != ':')
601 break;
602 while ((c = *cp++) != '\0')
603 if (c == 'F')
604 this.m_flag |= MFLAGGED;
605 else if (c == 'A')
606 this.m_flag|=MANSWERED;
607 else if (c == 'T')
608 this.m_flag|=MDRAFTED;
609 break;
611 if (*cp != c && *cp != upperconv(c))
612 break;
615 offset += cnt;
616 this.m_size += cnt;
617 this.m_lines++;
618 maybe = linebuf[0] == 0;
620 /*NOTREACHED*/
624 * Drop the passed line onto the passed output buffer.
625 * If a write error occurs, return -1, else the count of
626 * characters written, including the newline.
629 putline(FILE *obuf, char *linebuf, size_t cnt)
631 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
632 putc('\n', obuf);
633 if (ferror(obuf))
634 return (-1);
635 return (cnt + 1);
639 * Return a file buffer all ready to read up the
640 * passed message pointer.
642 FILE *
643 setinput(struct mailbox *mp, struct message *m, enum needspec need)
645 enum okay ok = STOP;
647 switch (need) {
648 case NEED_HEADER:
649 if (m->m_have & HAVE_HEADER)
650 ok = OKAY;
651 else
652 ok = get_header(m);
653 break;
654 case NEED_BODY:
655 if (m->m_have & HAVE_BODY)
656 ok = OKAY;
657 else
658 ok = get_body(m);
659 break;
660 case NEED_UNSPEC:
661 ok = OKAY;
662 break;
664 if (ok != OKAY)
665 return NULL;
666 fflush(mp->mb_otf);
667 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
668 m->m_offset), SEEK_SET) < 0) {
669 perror("fseek");
670 panic(catgets(catd, CATSET, 77, "temporary file seek"));
672 return (mp->mb_itf);
675 struct message *
676 setdot(struct message *mp)
678 if (dot != mp) {
679 prevdot = dot;
680 did_print_dot = FAL0;
682 dot = mp;
683 uncollapse1(dot, 0);
684 return dot;
688 * Take the data out of the passed ghost file and toss it into
689 * a dynamically allocated message structure.
691 static void
692 makemessage(void)
694 if (msgCount == 0)
695 append(NULL);
696 setdot(message);
697 message[msgCount].m_size = 0;
698 message[msgCount].m_lines = 0;
702 * Append the passed message descriptor onto the message structure.
704 static void
705 append(struct message *mp)
707 if (msgCount + 1 >= msgspace)
708 message = srealloc(message, (msgspace += 64) * sizeof *message);
709 if (msgCount > 0)
710 message[msgCount - 1] = *mp;
714 * Delete a file, but only if the file is a plain file.
717 rm(char *name) /* TODO TOCTOU; but i'm out of ideas today */
719 struct stat sb;
720 int ret = -1;
722 if (stat(name, &sb) < 0)
724 else if (! S_ISREG(sb.st_mode))
725 errno = EISDIR;
726 else
727 ret = unlink(name);
728 return ret;
731 static int sigdepth; /* depth of holdsigs() */
732 static sigset_t nset, oset;
734 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
736 void
737 holdsigs(void)
740 if (sigdepth++ == 0) {
741 sigemptyset(&nset);
742 sigaddset(&nset, SIGHUP);
743 sigaddset(&nset, SIGINT);
744 sigaddset(&nset, SIGQUIT);
745 sigprocmask(SIG_BLOCK, &nset, &oset);
750 * Release signals SIGHUP, SIGINT, and SIGQUIT.
752 void
753 relsesigs(void)
755 if (--sigdepth == 0)
756 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
760 * Determine the size of the file possessed by
761 * the passed buffer.
763 off_t
764 fsize(FILE *iob)
766 struct stat sbuf;
768 if (fstat(fileno(iob), &sbuf) < 0)
769 return 0;
770 return sbuf.st_size;
773 char *
774 fexpand(char const *name, enum fexp_mode fexpm)
776 char cbuf[MAXPATHLEN], *res;
777 struct str s;
778 struct shortcut *sh;
779 bool_t dyn;
782 * The order of evaluation is "%" and "#" expand into constants.
783 * "&" can expand into "+". "+" can expand into shell meta characters.
784 * Shell meta characters expand into constants.
785 * This way, we make no recursive expansion.
787 res = UNCONST(name);
788 if (! (fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
789 res = sh->sh_long;
791 if (fexpm & FEXP_SHELL) {
792 dyn = FAL0;
793 goto jshell;
795 jnext:
796 dyn = FAL0;
797 switch (*res) {
798 case '%':
799 if (res[1] == ':' && res[2] != '\0') {
800 res = &res[2];
801 goto jnext;
803 _findmail(cbuf, sizeof cbuf,
804 (res[1] != '\0') ? res + 1 : myname,
805 (res[1] != '\0' || option_u_arg != NULL));
806 res = cbuf;
807 goto jislocal;
808 case '#':
809 if (res[1] != '\0')
810 break;
811 if (prevfile[0] == '\0') {
812 fprintf(stderr, tr(80, "No previous file\n"));
813 res = NULL;
814 goto jleave;
816 res = prevfile;
817 goto jislocal;
818 case '&':
819 if (res[1] == '\0') {
820 if ((res = value("MBOX")) == NULL)
821 res = UNCONST("~/mbox");
822 else if (res[0] != '&' || res[1] != '\0')
823 goto jnext;
825 break;
828 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
829 res = str_concat_csvl(&s,
830 protbase(mailname), "/", res + 1, NULL)->s;
831 dyn = TRU1;
834 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
835 size_t i = strlen(cbuf);
837 res = str_concat_csvl(&s, cbuf,
838 (i > 0 && cbuf[i - 1] == '/') ? "" : "/",
839 res + 1, NULL)->s;
840 dyn = TRU1;
842 if (res[0] == '%' && res[1] == ':') {
843 res += 2;
844 goto jnext;
848 /* Catch the most common shell meta character */
849 jshell:
850 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
851 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
852 dyn = TRU1;
855 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
856 which_protocol(res) == PROTO_FILE) {
857 res = _globname(res, fexpm);
858 dyn = TRU1;
859 goto jleave;
862 jislocal:
863 if (fexpm & FEXP_LOCAL)
864 switch (which_protocol(res)) {
865 case PROTO_FILE:
866 case PROTO_MAILDIR: /* XXX Really? ok MAILDIR for local? */
867 break;
868 default:
869 fprintf(stderr, tr(280,
870 "`%s': only a local file or directory may "
871 "be used\n"), name);
872 res = NULL;
873 break;
875 jleave:
876 if (res && ! dyn)
877 res = savestr(res);
878 return res;
881 void
882 demail(void)
885 if (value("keep") != NULL || rm(mailname) < 0) {
886 int fd = open(mailname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
887 if (fd >= 0)
888 close(fd);
892 bool_t
893 var_folder_updated(char const *name, char **store)
895 char rv = TRU1;
896 char *folder, *unres = NULL, *res = NULL;
898 if ((folder = UNCONST(name)) == NULL)
899 goto jleave;
901 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
902 /* XXX This *only* works because we do NOT
903 * XXX update environment variables via the "set" mechanism */
904 if (folder[0] == '%' && folder[1] == ':')
905 folder += 2;
906 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
907 goto jleave;
909 switch (which_protocol(folder)) {
910 case PROTO_POP3:
911 /* Ooops. This won't work */
912 fprintf(stderr, tr(501, "`folder' cannot be set to a flat, "
913 "readonly POP3 account\n"));
914 rv = FAL0;
915 goto jleave;
916 case PROTO_IMAP:
917 /* Simply assign what we have, even including `%:' prefix */
918 if (folder != name)
919 goto jvcopy;
920 goto jleave;
921 default:
922 /* Further expansion desired */
923 break;
926 /* All non-absolute paths are relative to our home directory */
927 if (*folder != '/') {
928 size_t l1 = strlen(homedir), l2 = strlen(folder);
929 unres = ac_alloc(l1 + l2 + 2);
930 memcpy(unres, homedir, l1);
931 unres[l1] = '/';
932 memcpy(unres + l1 + 1, folder, l2);
933 unres[l1 + 1 + l2] = '\0';
934 folder = unres;
937 /* Since lex.c:_update_mailname() uses realpath(3) if available to
938 * avoid that we loose track of our currently open folder in case we
939 * chdir away, but still checks the leading path portion against
940 * getfold() to be able to abbreviate to the +FOLDER syntax if
941 * possible, we need to realpath(3) the folder, too */
942 #ifdef HAVE_REALPATH
943 res = ac_alloc(MAXPATHLEN);
944 if (realpath(folder, res) == NULL)
945 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
946 else
947 folder = res;
948 #endif
950 jvcopy:
951 *store = sstrdup(folder);
953 if (res != NULL)
954 ac_free(res);
955 if (unres != NULL)
956 ac_free(unres);
957 jleave:
958 return rv;
961 bool_t
962 getfold(char *name, size_t size)
964 char const *folder;
966 if ((folder = value("folder")) != NULL)
967 (void)n_strlcpy(name, folder, size);
968 return (folder != NULL);
972 * Return the name of the dead.letter file.
974 char const *
975 getdeadletter(void)
977 char const *cp;
979 if ((cp = value("DEAD")) == NULL ||
980 (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
981 cp = fexpand("~/dead.letter", FEXP_LOCAL|FEXP_SHELL);
982 else if (*cp != '/') {
983 size_t sz = strlen(cp) + 3;
984 char *buf = ac_alloc(sz);
986 snprintf(buf, sz, "~/%s", cp);
987 cp = fexpand(buf, FEXP_LOCAL|FEXP_SHELL);
988 ac_free(buf);
990 if (cp == NULL)
991 cp = "dead.letter";
992 return cp;
995 static enum okay
996 get_header(struct message *mp)
998 (void)mp;
999 switch (mb.mb_type) {
1000 case MB_FILE:
1001 case MB_MAILDIR:
1002 return (OKAY);
1003 #ifdef HAVE_POP3
1004 case MB_POP3:
1005 return (pop3_header(mp));
1006 #endif
1007 #ifdef HAVE_IMAP
1008 case MB_IMAP:
1009 case MB_CACHE:
1010 return imap_header(mp);
1011 #endif
1012 case MB_VOID:
1013 default:
1014 return (STOP);
1018 enum okay
1019 get_body(struct message *mp)
1021 (void)mp;
1022 switch (mb.mb_type) {
1023 case MB_FILE:
1024 case MB_MAILDIR:
1025 return (OKAY);
1026 #ifdef HAVE_POP3
1027 case MB_POP3:
1028 return (pop3_body(mp));
1029 #endif
1030 #ifdef HAVE_IMAP
1031 case MB_IMAP:
1032 case MB_CACHE:
1033 return imap_body(mp);
1034 #endif
1035 case MB_VOID:
1036 default:
1037 return (STOP);
1041 #ifdef HAVE_SOCKETS
1042 static long xwrite(int fd, const char *data, size_t sz);
1044 static long
1045 xwrite(int fd, const char *data, size_t sz)
1047 long wo;
1048 size_t wt = 0;
1050 do {
1051 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
1052 if (errno == EINTR)
1053 continue;
1054 else
1055 return -1;
1057 wt += wo;
1058 } while (wt < sz);
1059 return sz;
1063 sclose(struct sock *sp)
1065 int i;
1067 if (sp->s_fd > 0) {
1068 if (sp->s_onclose != NULL)
1069 (*sp->s_onclose)();
1070 #ifdef HAVE_OPENSSL
1071 if (sp->s_use_ssl) {
1072 sp->s_use_ssl = 0;
1073 SSL_shutdown(sp->s_ssl);
1074 SSL_free(sp->s_ssl);
1075 sp->s_ssl = NULL;
1076 SSL_CTX_free(sp->s_ctx);
1077 sp->s_ctx = NULL;
1079 #endif
1081 i = close(sp->s_fd);
1083 sp->s_fd = -1;
1084 return i;
1086 sp->s_fd = -1;
1087 return 0;
1090 enum okay
1091 swrite(struct sock *sp, const char *data)
1093 return swrite1(sp, data, strlen(data), 0);
1096 enum okay
1097 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
1099 int x;
1101 if (use_buffer > 0) {
1102 int di;
1103 enum okay ok;
1105 if (sp->s_wbuf == NULL) {
1106 sp->s_wbufsize = 4096;
1107 sp->s_wbuf = smalloc(sp->s_wbufsize);
1108 sp->s_wbufpos = 0;
1110 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1111 di = sp->s_wbufsize - sp->s_wbufpos;
1112 sz -= di;
1113 if (sp->s_wbufpos > 0) {
1114 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1115 ok = swrite1(sp, sp->s_wbuf,
1116 sp->s_wbufsize, -1);
1117 } else
1118 ok = swrite1(sp, data,
1119 sp->s_wbufsize, -1);
1120 if (ok != OKAY)
1121 return STOP;
1122 data += di;
1123 sp->s_wbufpos = 0;
1125 if (sz == sp->s_wbufsize) {
1126 ok = swrite1(sp, data, sp->s_wbufsize, -1);
1127 if (ok != OKAY)
1128 return STOP;
1129 } else if (sz) {
1130 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1131 sp->s_wbufpos += sz;
1133 return OKAY;
1134 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
1135 sp->s_wbufpos > 0) {
1136 x = sp->s_wbufpos;
1137 sp->s_wbufpos = 0;
1138 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
1139 return STOP;
1141 if (sz == 0)
1142 return OKAY;
1143 #ifdef HAVE_OPENSSL
1144 if (sp->s_use_ssl) {
1145 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
1146 if (x < 0) {
1147 switch (SSL_get_error(sp->s_ssl, x)) {
1148 case SSL_ERROR_WANT_READ:
1149 case SSL_ERROR_WANT_WRITE:
1150 goto ssl_retry;
1153 } else
1154 #endif
1156 x = xwrite(sp->s_fd, data, sz);
1158 if (x != sz) {
1159 char o[512];
1160 snprintf(o, sizeof o, "%s write error",
1161 sp->s_desc ? sp->s_desc : "socket");
1162 #ifdef HAVE_OPENSSL
1163 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1164 #else
1165 perror(o);
1166 #endif
1167 if (x < 0)
1168 sclose(sp);
1169 return STOP;
1171 return OKAY;
1174 enum okay
1175 sopen(const char *xserver, struct sock *sp, int use_ssl,
1176 const char *uhp, const char *portstr, int verbose)
1178 #ifdef HAVE_SO_SNDTIMEO
1179 struct timeval tv;
1180 #endif
1181 #ifdef HAVE_SO_LINGER
1182 struct linger li;
1183 #endif
1184 #ifdef HAVE_IPV6
1185 char hbuf[NI_MAXHOST];
1186 struct addrinfo hints, *res0, *res;
1187 #else
1188 struct sockaddr_in servaddr;
1189 struct in_addr **pptr;
1190 struct hostent *hp;
1191 struct servent *ep;
1192 unsigned short port = 0;
1193 #endif
1194 int sockfd;
1195 char *cp;
1196 char *server = UNCONST(xserver);
1197 (void)use_ssl;
1198 (void)uhp;
1200 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1201 portstr = &cp[1];
1202 #ifndef HAVE_IPV6
1203 port = strtol(portstr, NULL, 10);
1204 #endif
1205 server = salloc(cp - xserver + 1);
1206 memcpy(server, xserver, cp - xserver);
1207 server[cp - xserver] = '\0';
1210 /* Connect timeouts after 30 seconds */
1211 #ifdef HAVE_SO_SNDTIMEO
1212 tv.tv_sec = 30;
1213 tv.tv_usec = 0;
1214 #endif
1216 #ifdef HAVE_IPV6
1217 if (verbose)
1218 fprintf(stderr, "Resolving host %s . . .", server);
1219 memset(&hints, 0, sizeof hints);
1220 hints.ai_socktype = SOCK_STREAM;
1221 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1222 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1223 return STOP;
1224 } else if (verbose)
1225 fprintf(stderr, tr(500, " done.\n"));
1227 sockfd = -1;
1228 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1229 if (verbose) {
1230 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1231 hbuf, sizeof hbuf, NULL, 0,
1232 NI_NUMERICHOST) != 0)
1233 strcpy(hbuf, "unknown host");
1234 fprintf(stderr, tr(192,
1235 "%sConnecting to %s:%s . . ."),
1236 (res == res0) ? "" : "\n",
1237 hbuf, portstr);
1239 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1240 res->ai_protocol)) >= 0) {
1241 # ifdef HAVE_SO_SNDTIMEO
1242 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,
1243 &tv, sizeof tv);
1244 # endif
1245 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1246 close(sockfd);
1247 sockfd = -1;
1251 if (sockfd < 0) {
1252 perror(tr(254, " could not connect"));
1253 freeaddrinfo(res0);
1254 return STOP;
1256 freeaddrinfo(res0);
1258 #else /* HAVE_IPV6 */
1259 if (port == 0) {
1260 if (strcmp(portstr, "smtp") == 0)
1261 port = htons(25);
1262 else if (strcmp(portstr, "smtps") == 0)
1263 port = htons(465);
1264 # ifdef HAVE_IMAP
1265 else if (strcmp(portstr, "imap") == 0)
1266 port = htons(143);
1267 else if (strcmp(portstr, "imaps") == 0)
1268 port = htons(993);
1269 # endif
1270 # ifdef HAVE_POP3
1271 else if (strcmp(portstr, "pop3") == 0)
1272 port = htons(110);
1273 else if (strcmp(portstr, "pop3s") == 0)
1274 port = htons(995);
1275 # endif
1276 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1277 port = ep->s_port;
1278 else {
1279 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1280 portstr);
1281 return (STOP);
1283 } else
1284 port = htons(port);
1286 if (verbose)
1287 fprintf(stderr, "Resolving host %s . . .", server);
1288 if ((hp = gethostbyname(server)) == NULL) {
1289 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1290 return STOP;
1291 } else if (verbose)
1292 fprintf(stderr, tr(500, " done.\n"));
1294 pptr = (struct in_addr **)hp->h_addr_list;
1295 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1296 perror(tr(253, "could not create socket"));
1297 return STOP;
1299 memset(&servaddr, 0, sizeof servaddr);
1300 servaddr.sin_family = AF_INET;
1301 servaddr.sin_port = port;
1302 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1303 if (verbose)
1304 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1305 "", inet_ntoa(**pptr), ntohs(port));
1307 # ifdef HAVE_SO_SNDTIMEO
1308 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1309 # endif
1310 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1311 != 0) {
1312 perror(tr(254, " could not connect"));
1313 return STOP;
1315 #endif /* HAVE_IPV6 */
1316 if (verbose)
1317 fputs(tr(193, " connected.\n"), stderr);
1319 /* And the regular timeouts */
1320 #ifdef HAVE_SO_SNDTIMEO
1321 tv.tv_sec = 42;
1322 tv.tv_usec = 0;
1323 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1324 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1325 #endif
1326 #ifdef HAVE_SO_LINGER
1327 li.l_onoff = 1;
1328 li.l_linger = 42;
1329 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1330 #endif
1332 memset(sp, 0, sizeof *sp);
1333 sp->s_fd = sockfd;
1334 #ifdef HAVE_SSL
1335 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1336 sclose(sp);
1337 return STOP;
1339 #endif
1340 return OKAY;
1344 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1345 SMALLOC_DEBUG_ARGS)
1347 char *lp = *line;
1349 if (sp->s_rsz < 0) {
1350 sclose(sp);
1351 return sp->s_rsz;
1353 do {
1354 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
1355 size_t diff = lp - *line;
1356 *line = (srealloc)(*line, *linesize += 256
1357 SMALLOC_DEBUG_ARGSCALL);
1358 lp = &(*line)[diff];
1360 if (sp->s_rbufptr == NULL ||
1361 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
1362 #ifdef HAVE_OPENSSL
1363 if (sp->s_use_ssl) {
1364 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
1365 sp->s_rbuf,
1366 sizeof sp->s_rbuf)) <= 0) {
1367 if (sp->s_rsz < 0) {
1368 char o[512];
1369 switch(SSL_get_error(sp->s_ssl,
1370 sp->s_rsz)) {
1371 case SSL_ERROR_WANT_READ:
1372 case SSL_ERROR_WANT_WRITE:
1373 goto ssl_retry;
1375 snprintf(o, sizeof o, "%s",
1376 sp->s_desc ?
1377 sp->s_desc :
1378 "socket");
1379 ssl_gen_err("%s", o);
1382 break;
1384 } else
1385 #endif
1387 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
1388 sizeof sp->s_rbuf)) <= 0) {
1389 if (sp->s_rsz < 0) {
1390 char o[512];
1391 if (errno == EINTR)
1392 goto again;
1393 snprintf(o, sizeof o, "%s",
1394 sp->s_desc ?
1395 sp->s_desc :
1396 "socket");
1397 perror(o);
1399 break;
1402 sp->s_rbufptr = sp->s_rbuf;
1404 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1405 *lp = '\0';
1406 if (linelen)
1407 *linelen = lp - *line;
1408 return lp - *line;
1410 #endif /* HAVE_SOCKETS */
1412 void
1413 load(char const *name)
1415 FILE *in, *oldin;
1417 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1418 return;
1419 oldin = _input;
1420 _input = in;
1421 loading = TRU1;
1422 sourcing = TRU1;
1423 commands();
1424 loading = FAL0;
1425 sourcing = FAL0;
1426 _input = oldin;
1427 Fclose(in);
1431 csource(void *v)
1433 int rv = 1;
1434 char **arglist = v;
1435 FILE *fi;
1436 char *cp;
1438 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1439 goto jleave;
1440 if ((fi = Fopen(cp, "r")) == NULL) {
1441 perror(cp);
1442 goto jleave;
1444 if (_ssp >= SSTACK - 1) {
1445 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1446 Fclose(fi);
1447 goto jleave;
1450 _sstack[_ssp].s_file = _input;
1451 _sstack[_ssp].s_cond = cond;
1452 _sstack[_ssp].s_loading = loading;
1453 ++_ssp;
1454 loading = FAL0;
1455 cond = CANY;
1456 _input = fi;
1457 sourcing = TRU1;
1458 rv = 0;
1459 jleave:
1460 return rv;
1464 unstack(void)
1466 int rv = 1;
1468 if (_ssp <= 0) {
1469 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1470 sourcing = FAL0;
1471 goto jleave;
1474 Fclose(_input);
1475 if (cond != CANY)
1476 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1477 --_ssp;
1478 cond = _sstack[_ssp].s_cond;
1479 loading = _sstack[_ssp].s_loading;
1480 _input = _sstack[_ssp].s_file;
1481 if (_ssp == 0)
1482 sourcing = loading;
1483 rv = 0;
1484 jleave:
1485 return rv;