NEWS: update for s-nail-14_5_2-sort.patch
[s-mailx.git] / fio.c
blob1e7a0880b1385c99e0b6f750580d8f61c25478ff
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 - 2014 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 = ok_vlook(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 = ok_vlook(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 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
138 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
139 memset(&we, 0, sizeof we);
141 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
142 * and wait, which will fail if our SIGCHLD handler is active */
143 sigemptyset(&nset);
144 sigaddset(&nset, SIGCHLD);
145 sigprocmask(SIG_BLOCK, &nset, NULL);
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 struct stat sbuf;
200 char xname[MAXPATHLEN], cmdbuf[MAXPATHLEN], /* also used for files */
201 *cp, *shellp;
202 int pivec[2], pid, l, waits;
204 if (pipe(pivec) < 0) {
205 perror("pipe");
206 return NULL;
208 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
209 if ((shellp = ok_vlook(SHELL)) == NULL)
210 shellp = UNCONST(XSHELL);
211 pid = start_command(shellp, 0, -1, pivec[1], "-c", cmdbuf, NULL);
212 if (pid < 0) {
213 close(pivec[0]);
214 close(pivec[1]);
215 return NULL;
217 close(pivec[1]);
219 again:
220 l = read(pivec[0], xname, sizeof xname);
221 if (l < 0) {
222 if (errno == EINTR)
223 goto again;
224 perror("read");
225 close(pivec[0]);
226 return NULL;
228 close(pivec[0]);
229 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
230 if (!(fexpm & FEXP_SILENT))
231 fprintf(stderr, tr(81, "\"%s\": Expansion failed.\n"),
232 name);
233 return NULL;
235 if (l == 0) {
236 if (!(fexpm & FEXP_SILENT))
237 fprintf(stderr, tr(82, "\"%s\": No match.\n"), name);
238 return NULL;
240 if (l == sizeof xname) {
241 if (!(fexpm & FEXP_SILENT))
242 fprintf(stderr,
243 tr(83, "\"%s\": Expansion buffer overflow.\n"),
244 name);
245 return NULL;
247 xname[l] = 0;
248 for (cp = &xname[l - 1]; *cp == '\n' && cp > xname; --cp)
250 cp[1] = '\0';
251 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') &&
252 stat(xname, &sbuf) < 0) {
253 if (!(fexpm & FEXP_SILENT))
254 fprintf(stderr, tr(84, "\"%s\": Ambiguous.\n"), name);
255 return NULL;
257 return savestr(xname);
258 #endif /* !HAVE_WORDEXP */
262 * line is a buffer with the result of fgets(). Returns the first
263 * newline or the last character read.
265 static size_t
266 _length_of_line(const char *line, size_t linesize)
268 size_t i;
270 /* Last character is always '\0' and was added by fgets() */
271 for (--linesize, i = 0; i < linesize; i++)
272 if (line[i] == '\n')
273 break;
274 return (i < linesize) ? i + 1 : linesize;
277 static char *
278 _fgetline_byone(char **line, size_t *linesize, size_t *llen,
279 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS)
281 int c;
283 assert(*linesize == 0 || *line != NULL);
284 for (;;) {
285 if (*linesize <= LINESIZE || n >= *linesize - 128) {
286 *linesize += ((*line == NULL)
287 ? LINESIZE + n + 1 : 256);
288 *line = (srealloc)(*line, *linesize
289 SMALLOC_DEBUG_ARGSCALL);
291 c = getc(fp);
292 if (c != EOF) {
293 (*line)[n++] = c;
294 (*line)[n] = '\0';
295 if (c == '\n')
296 break;
297 } else {
298 if (n > 0) {
299 if (appendnl) {
300 (*line)[n++] = '\n';
301 (*line)[n] = '\0';
303 break;
304 } else
305 return NULL;
308 if (llen)
309 *llen = n;
310 return *line;
313 FL char *
314 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen,
315 FILE *fp, int appendnl SMALLOC_DEBUG_ARGS)
317 size_t i_llen, sz;
319 if (cnt == NULL)
321 * If we have no count, we cannot determine where the
322 * characters returned by fgets() end if there was no
323 * newline. We have to read one character at one.
325 return _fgetline_byone(line, linesize, llen, fp, appendnl, 0
326 SMALLOC_DEBUG_ARGSCALL);
327 if (*line == NULL || *linesize < LINESIZE)
328 *line = (srealloc)(*line, *linesize = LINESIZE
329 SMALLOC_DEBUG_ARGSCALL);
330 sz = *linesize <= *cnt ? *linesize : *cnt + 1;
331 if (sz <= 1 || fgets(*line, sz, fp) == NULL)
333 * Leave llen untouched; it is used to determine whether
334 * the last line was \n-terminated in some callers.
336 return NULL;
337 i_llen = _length_of_line(*line, sz);
338 *cnt -= i_llen;
339 while ((*line)[i_llen - 1] != '\n') {
340 *line = (srealloc)(*line, *linesize += 256
341 SMALLOC_DEBUG_ARGSCALL);
342 sz = *linesize - i_llen;
343 sz = (sz <= *cnt ? sz : *cnt + 1);
344 if (sz <= 1 || fgets(&(*line)[i_llen], sz, fp) == NULL) {
345 if (appendnl) {
346 (*line)[i_llen++] = '\n';
347 (*line)[i_llen] = '\0';
349 break;
351 sz = _length_of_line(&(*line)[i_llen], sz);
352 i_llen += sz;
353 *cnt -= sz;
355 if (llen)
356 *llen = i_llen;
357 return *line;
360 FL int
361 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
362 SMALLOC_DEBUG_ARGS)
364 /* TODO readline_restart(): always *appends* LF just to strip it again;
365 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
366 long sz;
368 clearerr(ibuf);
370 * Interrupts will cause trouble if we are inside a stdio call. As
371 * this is only relevant if input comes from a terminal, we can simply
372 * bypass it by read() then.
374 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
375 assert(*linesize == 0 || *linebuf != NULL);
376 for (;;) {
377 if (*linesize <= LINESIZE || n >= *linesize - 128) {
378 *linesize += ((*linebuf == NULL)
379 ? LINESIZE + n + 1 : 256);
380 *linebuf = (srealloc)(*linebuf, *linesize
381 SMALLOC_DEBUG_ARGSCALL);
383 again:
384 sz = read(0, *linebuf + n, *linesize - n - 1);
385 if (sz > 0) {
386 n += sz;
387 (*linebuf)[n] = '\0';
388 if (n > 0 && (*linebuf)[n - 1] == '\n')
389 break;
390 } else {
391 if (sz < 0 && errno == EINTR)
392 goto again;
393 if (n > 0) {
394 if ((*linebuf)[n - 1] != '\n') {
395 (*linebuf)[n++] = '\n';
396 (*linebuf)[n] = '\0';
398 break;
399 } else
400 return -1;
403 } else {
405 * Not reading from standard input or standard input not
406 * a terminal. We read one char at a time as it is the
407 * only way to get lines with embedded NUL characters in
408 * standard stdio.
410 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
411 SMALLOC_DEBUG_ARGSCALL) == NULL)
412 return -1;
414 if (n > 0 && (*linebuf)[n - 1] == '\n')
415 (*linebuf)[--n] = '\0';
416 return n;
419 FL int
420 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
421 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
423 /* TODO readline: linebuf pool! */
424 FILE *ifile = (_input != NULL) ? _input : stdin;
425 bool_t doprompt, dotty;
426 int n;
428 doprompt = (!sourcing && (options & OPT_INTERACTIVE));
429 dotty = (doprompt && !ok_blook(line_editor_disable));
430 if (!doprompt)
431 prompt = NULL;
432 else if (prompt == NULL)
433 prompt = getprompt();
435 for (n = 0;;) {
436 if (dotty) {
437 assert(ifile == stdin);
438 if (string != NULL && (n = (int)strlen(string)) > 0) {
439 if (*linesize > 0)
440 *linesize += n +1;
441 else
442 *linesize = (size_t)n + LINESIZE +1;
443 *linebuf = (srealloc)(*linebuf, *linesize
444 SMALLOC_DEBUG_ARGSCALL);
445 memcpy(*linebuf, string, (size_t)n +1);
447 string = NULL;
448 n = (tty_readline)(prompt, linebuf, linesize, n
449 SMALLOC_DEBUG_ARGSCALL);
450 } else {
451 if (prompt != NULL && *prompt != '\0') {
452 fputs(prompt, stdout);
453 fflush(stdout);
455 n = (readline_restart)(ifile, linebuf, linesize, n
456 SMALLOC_DEBUG_ARGSCALL);
458 if (n <= 0)
459 break;
460 /* POSIX says:
461 * An unquoted <backslash> at the end of a command line shall
462 * be discarded and the next line shall continue the command */
463 if (nl_escape && (*linebuf)[n - 1] == '\\') {
464 (*linebuf)[--n] = '\0';
465 if (prompt != NULL && *prompt != '\0')
466 prompt = ".. "; /* XXX PS2 .. */
467 continue;
469 break;
471 return n;
474 FL char *
475 readstr_input(char const *prompt, char const *string)
477 /* FIXME readstr_input: without linepool leaks on sigjmp */
478 size_t linesize = 0;
479 char *linebuf = NULL, *rv = NULL;
480 int n;
482 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
483 if (n > 0)
484 rv = savestrbuf(linebuf, (size_t)n + 1);
486 if (linebuf != NULL)
487 free(linebuf);
488 return rv;
491 * Set up the input pointers while copying the mail file into /tmp.
493 FL void
494 setptr(FILE *ibuf, off_t offset)
496 int c;
497 char *cp, *linebuf = NULL;
498 char const *cp2;
499 struct message this;
500 int maybe, inhead, thiscnt;
501 size_t linesize = 0, filesize, cnt;
503 maybe = 1;
504 inhead = 0;
505 thiscnt = 0;
506 memset(&this, 0, sizeof this);
507 this.m_flag = MUSED|MNEW|MNEWEST;
508 filesize = mailsize - offset;
509 offset = ftell(mb.mb_otf);
510 for (;;) {
511 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0)
512 == NULL) {
513 this.m_xsize = this.m_size;
514 this.m_xlines = this.m_lines;
515 this.m_have = HAVE_HEADER|HAVE_BODY;
516 if (thiscnt > 0)
517 _fio_append(&this);
518 makemessage();
519 if (linebuf)
520 free(linebuf);
521 return;
523 #ifdef notdef
524 if (linebuf[0] == '\0')
525 linebuf[0] = '.';
526 #endif
527 /* XXX Convert CRLF to LF; this should be rethought in that
528 * XXX CRLF input should possibly end as CRLF output? */
529 if (cnt >= 2 && linebuf[cnt - 1] == '\n' &&
530 linebuf[cnt - 2] == '\r')
531 linebuf[--cnt - 1] = '\n';
532 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
533 if (ferror(mb.mb_otf)) {
534 perror("/tmp");
535 exit(1);
537 if (linebuf[cnt - 1] == '\n')
538 linebuf[cnt - 1] = '\0';
539 if (maybe && linebuf[0] == 'F' && is_head(linebuf, cnt)) {
540 /* TODO
541 * TODO char date[FROM_DATEBUF];
542 * TODO extract_date_from_from_(linebuf, cnt, date);
543 * TODO this.m_time = 10000;
545 this.m_xsize = this.m_size;
546 this.m_xlines = this.m_lines;
547 this.m_have = HAVE_HEADER|HAVE_BODY;
548 if (thiscnt++ > 0)
549 _fio_append(&this);
550 msgCount++;
551 this.m_flag = MUSED|MNEW|MNEWEST;
552 this.m_size = 0;
553 this.m_lines = 0;
554 this.m_block = mailx_blockof(offset);
555 this.m_offset = mailx_offsetof(offset);
556 inhead = 1;
557 } else if (linebuf[0] == 0) {
558 inhead = 0;
559 } else if (inhead) {
560 for (cp = linebuf, cp2 = "status";; cp++) {
561 if ((c = *cp2++) == 0) {
562 while (c = *cp++, whitechar(c));
563 if (cp[-1] != ':')
564 break;
565 while ((c = *cp++) != '\0')
566 if (c == 'R')
567 this.m_flag |= MREAD;
568 else if (c == 'O')
569 this.m_flag &= ~MNEW;
570 break;
572 if (*cp != c && *cp != upperconv(c))
573 break;
575 for (cp = linebuf, cp2 = "x-status";; cp++) {
576 if ((c = *cp2++) == 0) {
577 while (c = *cp++, whitechar(c));
578 if (cp[-1] != ':')
579 break;
580 while ((c = *cp++) != '\0')
581 if (c == 'F')
582 this.m_flag |= MFLAGGED;
583 else if (c == 'A')
584 this.m_flag|=MANSWERED;
585 else if (c == 'T')
586 this.m_flag|=MDRAFTED;
587 break;
589 if (*cp != c && *cp != upperconv(c))
590 break;
593 offset += cnt;
594 this.m_size += cnt;
595 this.m_lines++;
596 maybe = linebuf[0] == 0;
598 /*NOTREACHED*/
602 * Drop the passed line onto the passed output buffer.
603 * If a write error occurs, return -1, else the count of
604 * characters written, including the newline.
606 FL int
607 putline(FILE *obuf, char *linebuf, size_t cnt)
609 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
610 putc('\n', obuf);
611 if (ferror(obuf))
612 return (-1);
613 return (cnt + 1);
617 * Return a file buffer all ready to read up the
618 * passed message pointer.
620 FL FILE *
621 setinput(struct mailbox *mp, struct message *m, enum needspec need)
623 enum okay ok = STOP;
625 switch (need) {
626 case NEED_HEADER:
627 if (m->m_have & HAVE_HEADER)
628 ok = OKAY;
629 else
630 ok = get_header(m);
631 break;
632 case NEED_BODY:
633 if (m->m_have & HAVE_BODY)
634 ok = OKAY;
635 else
636 ok = get_body(m);
637 break;
638 case NEED_UNSPEC:
639 ok = OKAY;
640 break;
642 if (ok != OKAY)
643 return NULL;
644 fflush(mp->mb_otf);
645 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
646 m->m_offset), SEEK_SET) < 0) {
647 perror("fseek");
648 panic(tr(77, "temporary file seek"));
650 return (mp->mb_itf);
653 FL struct message *
654 setdot(struct message *mp)
656 if (dot != mp) {
657 prevdot = dot;
658 did_print_dot = FAL0;
660 dot = mp;
661 uncollapse1(dot, 0);
662 return dot;
666 * Take the data out of the passed ghost file and toss it into
667 * a dynamically allocated message structure.
669 static void
670 makemessage(void)
672 if (msgCount == 0)
673 _fio_append(NULL);
674 setdot(message);
675 message[msgCount].m_size = 0;
676 message[msgCount].m_lines = 0;
680 * Append the passed message descriptor onto the message structure.
682 static void
683 _fio_append(struct message *mp)
685 if (msgCount + 1 >= msgspace)
686 message = srealloc(message, (msgspace += 64) * sizeof *message);
687 if (msgCount > 0)
688 message[msgCount - 1] = *mp;
692 * Delete a file, but only if the file is a plain file.
694 FL int
695 rm(char *name) /* TODO TOCTOU; but i'm out of ideas today */
697 struct stat sb;
698 int ret = -1;
700 if (stat(name, &sb) < 0)
702 else if (! S_ISREG(sb.st_mode))
703 errno = EISDIR;
704 else
705 ret = unlink(name);
706 return ret;
710 * Determine the size of the file possessed by
711 * the passed buffer.
713 FL off_t
714 fsize(FILE *iob)
716 struct stat sbuf;
718 if (fstat(fileno(iob), &sbuf) < 0)
719 return 0;
720 return sbuf.st_size;
723 FL char *
724 fexpand(char const *name, enum fexp_mode fexpm)
726 char cbuf[MAXPATHLEN], *res;
727 struct str s;
728 struct shortcut *sh;
729 bool_t dyn;
732 * The order of evaluation is "%" and "#" expand into constants.
733 * "&" can expand into "+". "+" can expand into shell meta characters.
734 * Shell meta characters expand into constants.
735 * This way, we make no recursive expansion.
737 res = UNCONST(name);
738 if (! (fexpm & FEXP_NSHORTCUT) && (sh = get_shortcut(res)) != NULL)
739 res = sh->sh_long;
741 if (fexpm & FEXP_SHELL) {
742 dyn = FAL0;
743 goto jshell;
745 jnext:
746 dyn = FAL0;
747 switch (*res) {
748 case '%':
749 if (res[1] == ':' && res[2] != '\0') {
750 res = &res[2];
751 goto jnext;
753 _findmail(cbuf, sizeof cbuf,
754 (res[1] != '\0') ? res + 1 : myname,
755 (res[1] != '\0' || (options & OPT_u_FLAG)));
756 res = cbuf;
757 goto jislocal;
758 case '#':
759 if (res[1] != '\0')
760 break;
761 if (prevfile[0] == '\0') {
762 fprintf(stderr, tr(80, "No previous file\n"));
763 res = NULL;
764 goto jleave;
766 res = prevfile;
767 goto jislocal;
768 case '&':
769 if (res[1] == '\0') {
770 if ((res = ok_vlook(MBOX)) == NULL)
771 res = UNCONST("~/mbox");
772 else if (res[0] != '&' || res[1] != '\0')
773 goto jnext;
775 break;
778 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
779 res = str_concat_csvl(&s,
780 protbase(mailname), "/", res + 1, NULL)->s;
781 dyn = TRU1;
784 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
785 size_t i = strlen(cbuf);
787 res = str_concat_csvl(&s, cbuf,
788 (i > 0 && cbuf[i - 1] == '/') ? "" : "/",
789 res + 1, NULL)->s;
790 dyn = TRU1;
792 if (res[0] == '%' && res[1] == ':') {
793 res += 2;
794 goto jnext;
798 /* Catch the most common shell meta character */
799 jshell:
800 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
801 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
802 dyn = TRU1;
805 if (anyof(res, "|&;<>~{}()[]*?$`'\"\\") &&
806 which_protocol(res) == PROTO_FILE) {
807 res = _globname(res, fexpm);
808 dyn = TRU1;
809 goto jleave;
812 jislocal:
813 if (fexpm & FEXP_LOCAL)
814 switch (which_protocol(res)) {
815 case PROTO_FILE:
816 case PROTO_MAILDIR: /* XXX Really? ok MAILDIR for local? */
817 break;
818 default:
819 fprintf(stderr, tr(280,
820 "`%s': only a local file or directory may "
821 "be used\n"), name);
822 res = NULL;
823 break;
825 jleave:
826 if (res && ! dyn)
827 res = savestr(res);
828 return res;
831 FL void
832 demail(void)
835 if (ok_blook(keep) || rm(mailname) < 0) {
836 int fd = open(mailname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
837 if (fd >= 0)
838 close(fd);
842 FL bool_t
843 var_folder_updated(char const *name, char **store)
845 char rv = TRU1;
846 char *folder, *unres = NULL, *res = NULL;
848 if ((folder = UNCONST(name)) == NULL)
849 goto jleave;
851 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
852 /* XXX This *only* works because we do NOT
853 * XXX update environment variables via the "set" mechanism */
854 if (folder[0] == '%' && folder[1] == ':')
855 folder += 2;
856 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
857 goto jleave;
859 switch (which_protocol(folder)) {
860 case PROTO_POP3:
861 /* Ooops. This won't work */
862 fprintf(stderr, tr(501, "`folder' cannot be set to a flat, "
863 "readonly POP3 account\n"));
864 rv = FAL0;
865 goto jleave;
866 case PROTO_IMAP:
867 /* Simply assign what we have, even including `%:' prefix */
868 if (folder != name)
869 goto jvcopy;
870 goto jleave;
871 default:
872 /* Further expansion desired */
873 break;
876 /* All non-absolute paths are relative to our home directory */
877 if (*folder != '/') {
878 size_t l1 = strlen(homedir), l2 = strlen(folder);
879 unres = ac_alloc(l1 + l2 + 2);
880 memcpy(unres, homedir, l1);
881 unres[l1] = '/';
882 memcpy(unres + l1 + 1, folder, l2);
883 unres[l1 + 1 + l2] = '\0';
884 folder = unres;
887 /* Since lex.c:_update_mailname() uses realpath(3) if available to
888 * avoid that we loose track of our currently open folder in case we
889 * chdir away, but still checks the leading path portion against
890 * getfold() to be able to abbreviate to the +FOLDER syntax if
891 * possible, we need to realpath(3) the folder, too */
892 #ifdef HAVE_REALPATH
893 res = ac_alloc(MAXPATHLEN);
894 if (realpath(folder, res) == NULL)
895 fprintf(stderr, tr(151, "Can't canonicalize `%s'\n"), folder);
896 else
897 folder = res;
898 #endif
900 jvcopy:
901 *store = sstrdup(folder);
903 if (res != NULL)
904 ac_free(res);
905 if (unres != NULL)
906 ac_free(unres);
907 jleave:
908 return rv;
911 FL bool_t
912 getfold(char *name, size_t size)
914 char const *folder;
916 if ((folder = ok_vlook(folder)) != NULL)
917 (void)n_strlcpy(name, folder, size);
918 return (folder != NULL);
922 * Return the name of the dead.letter file.
924 FL char const *
925 getdeadletter(void)
927 char const *cp;
929 if ((cp = ok_vlook(DEAD)) == NULL ||
930 (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
931 cp = fexpand("~/dead.letter", FEXP_LOCAL|FEXP_SHELL);
932 else if (*cp != '/') {
933 size_t sz = strlen(cp) + 3;
934 char *buf = ac_alloc(sz);
936 snprintf(buf, sz, "~/%s", cp);
937 cp = fexpand(buf, FEXP_LOCAL|FEXP_SHELL);
938 ac_free(buf);
940 if (cp == NULL)
941 cp = "dead.letter";
942 return cp;
945 static enum okay
946 get_header(struct message *mp)
948 (void)mp;
949 switch (mb.mb_type) {
950 case MB_FILE:
951 case MB_MAILDIR:
952 return (OKAY);
953 #ifdef HAVE_POP3
954 case MB_POP3:
955 return (pop3_header(mp));
956 #endif
957 #ifdef HAVE_IMAP
958 case MB_IMAP:
959 case MB_CACHE:
960 return imap_header(mp);
961 #endif
962 case MB_VOID:
963 default:
964 return (STOP);
968 FL enum okay
969 get_body(struct message *mp)
971 (void)mp;
972 switch (mb.mb_type) {
973 case MB_FILE:
974 case MB_MAILDIR:
975 return (OKAY);
976 #ifdef HAVE_POP3
977 case MB_POP3:
978 return (pop3_body(mp));
979 #endif
980 #ifdef HAVE_IMAP
981 case MB_IMAP:
982 case MB_CACHE:
983 return imap_body(mp);
984 #endif
985 case MB_VOID:
986 default:
987 return (STOP);
991 #ifdef HAVE_SOCKETS
992 static long xwrite(int fd, const char *data, size_t sz);
994 static long
995 xwrite(int fd, const char *data, size_t sz)
997 long wo;
998 size_t wt = 0;
1000 do {
1001 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
1002 if (errno == EINTR)
1003 continue;
1004 else
1005 return -1;
1007 wt += wo;
1008 } while (wt < sz);
1009 return sz;
1012 FL int
1013 sclose(struct sock *sp)
1015 int i;
1017 if (sp->s_fd > 0) {
1018 if (sp->s_onclose != NULL)
1019 (*sp->s_onclose)();
1020 #ifdef HAVE_OPENSSL
1021 if (sp->s_use_ssl) {
1022 sp->s_use_ssl = 0;
1023 SSL_shutdown(sp->s_ssl);
1024 SSL_free(sp->s_ssl);
1025 sp->s_ssl = NULL;
1026 SSL_CTX_free(sp->s_ctx);
1027 sp->s_ctx = NULL;
1029 #endif
1031 i = close(sp->s_fd);
1033 sp->s_fd = -1;
1034 return i;
1036 sp->s_fd = -1;
1037 return 0;
1040 FL enum okay
1041 swrite(struct sock *sp, const char *data)
1043 return swrite1(sp, data, strlen(data), 0);
1046 FL enum okay
1047 swrite1(struct sock *sp, const char *data, int sz, int use_buffer)
1049 int x;
1051 if (use_buffer > 0) {
1052 int di;
1053 enum okay ok;
1055 if (sp->s_wbuf == NULL) {
1056 sp->s_wbufsize = 4096;
1057 sp->s_wbuf = smalloc(sp->s_wbufsize);
1058 sp->s_wbufpos = 0;
1060 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1061 di = sp->s_wbufsize - sp->s_wbufpos;
1062 sz -= di;
1063 if (sp->s_wbufpos > 0) {
1064 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, di);
1065 ok = swrite1(sp, sp->s_wbuf,
1066 sp->s_wbufsize, -1);
1067 } else
1068 ok = swrite1(sp, data,
1069 sp->s_wbufsize, -1);
1070 if (ok != OKAY)
1071 return STOP;
1072 data += di;
1073 sp->s_wbufpos = 0;
1075 if (sz == sp->s_wbufsize) {
1076 ok = swrite1(sp, data, sp->s_wbufsize, -1);
1077 if (ok != OKAY)
1078 return STOP;
1079 } else if (sz) {
1080 memcpy(&sp->s_wbuf[sp->s_wbufpos], data, sz);
1081 sp->s_wbufpos += sz;
1083 return OKAY;
1084 } else if (use_buffer == 0 && sp->s_wbuf != NULL &&
1085 sp->s_wbufpos > 0) {
1086 x = sp->s_wbufpos;
1087 sp->s_wbufpos = 0;
1088 if (swrite1(sp, sp->s_wbuf, x, -1) != OKAY)
1089 return STOP;
1091 if (sz == 0)
1092 return OKAY;
1093 #ifdef HAVE_OPENSSL
1094 if (sp->s_use_ssl) {
1095 ssl_retry: x = SSL_write(sp->s_ssl, data, sz);
1096 if (x < 0) {
1097 switch (SSL_get_error(sp->s_ssl, x)) {
1098 case SSL_ERROR_WANT_READ:
1099 case SSL_ERROR_WANT_WRITE:
1100 goto ssl_retry;
1103 } else
1104 #endif
1106 x = xwrite(sp->s_fd, data, sz);
1108 if (x != sz) {
1109 char o[512];
1110 snprintf(o, sizeof o, "%s write error",
1111 sp->s_desc ? sp->s_desc : "socket");
1112 #ifdef HAVE_OPENSSL
1113 sp->s_use_ssl ? ssl_gen_err("%s", o) : perror(o);
1114 #else
1115 perror(o);
1116 #endif
1117 if (x < 0)
1118 sclose(sp);
1119 return STOP;
1121 return OKAY;
1124 FL enum okay
1125 sopen(const char *xserver, struct sock *sp, int use_ssl,
1126 const char *uhp, const char *portstr, int verbose)
1128 #ifdef HAVE_SO_SNDTIMEO
1129 struct timeval tv;
1130 #endif
1131 #ifdef HAVE_SO_LINGER
1132 struct linger li;
1133 #endif
1134 #ifdef HAVE_IPV6
1135 char hbuf[NI_MAXHOST];
1136 struct addrinfo hints, *res0, *res;
1137 #else
1138 struct sockaddr_in servaddr;
1139 struct in_addr **pptr;
1140 struct hostent *hp;
1141 struct servent *ep;
1142 unsigned short port = 0;
1143 #endif
1144 int sockfd;
1145 char *cp;
1146 char *server = UNCONST(xserver);
1147 (void)use_ssl;
1148 (void)uhp;
1150 if ((cp = strchr(server, ':')) != NULL) { /* TODO URI parse! IPv6! */
1151 portstr = &cp[1];
1152 #ifndef HAVE_IPV6
1153 port = strtol(portstr, NULL, 10);
1154 #endif
1155 server = salloc(cp - xserver + 1);
1156 memcpy(server, xserver, cp - xserver);
1157 server[cp - xserver] = '\0';
1160 /* Connect timeouts after 30 seconds */
1161 #ifdef HAVE_SO_SNDTIMEO
1162 tv.tv_sec = 30;
1163 tv.tv_usec = 0;
1164 #endif
1166 #ifdef HAVE_IPV6
1167 if (verbose)
1168 fprintf(stderr, "Resolving host %s . . .", server);
1169 memset(&hints, 0, sizeof hints);
1170 hints.ai_socktype = SOCK_STREAM;
1171 if (getaddrinfo(server, portstr, &hints, &res0) != 0) {
1172 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1173 return STOP;
1174 } else if (verbose)
1175 fprintf(stderr, tr(500, " done.\n"));
1177 sockfd = -1;
1178 for (res = res0; res != NULL && sockfd < 0; res = res->ai_next) {
1179 if (verbose) {
1180 if (getnameinfo(res->ai_addr, res->ai_addrlen,
1181 hbuf, sizeof hbuf, NULL, 0,
1182 NI_NUMERICHOST) != 0)
1183 strcpy(hbuf, "unknown host");
1184 fprintf(stderr, tr(192,
1185 "%sConnecting to %s:%s . . ."),
1186 (res == res0) ? "" : "\n",
1187 hbuf, portstr);
1189 if ((sockfd = socket(res->ai_family, res->ai_socktype,
1190 res->ai_protocol)) >= 0) {
1191 # ifdef HAVE_SO_SNDTIMEO
1192 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,
1193 &tv, sizeof tv);
1194 # endif
1195 if (connect(sockfd, res->ai_addr, res->ai_addrlen)!=0) {
1196 close(sockfd);
1197 sockfd = -1;
1201 if (sockfd < 0) {
1202 perror(tr(254, " could not connect"));
1203 freeaddrinfo(res0);
1204 return STOP;
1206 freeaddrinfo(res0);
1208 #else /* HAVE_IPV6 */
1209 if (port == 0) {
1210 if (strcmp(portstr, "smtp") == 0)
1211 port = htons(25);
1212 else if (strcmp(portstr, "smtps") == 0)
1213 port = htons(465);
1214 # ifdef HAVE_IMAP
1215 else if (strcmp(portstr, "imap") == 0)
1216 port = htons(143);
1217 else if (strcmp(portstr, "imaps") == 0)
1218 port = htons(993);
1219 # endif
1220 # ifdef HAVE_POP3
1221 else if (strcmp(portstr, "pop3") == 0)
1222 port = htons(110);
1223 else if (strcmp(portstr, "pop3s") == 0)
1224 port = htons(995);
1225 # endif
1226 else if ((ep = getservbyname(UNCONST(portstr), "tcp")) != NULL)
1227 port = ep->s_port;
1228 else {
1229 fprintf(stderr, tr(251, "Unknown service: %s\n"),
1230 portstr);
1231 return (STOP);
1233 } else
1234 port = htons(port);
1236 if (verbose)
1237 fprintf(stderr, "Resolving host %s . . .", server);
1238 if ((hp = gethostbyname(server)) == NULL) {
1239 fprintf(stderr, tr(252, " lookup of `%s' failed.\n"), server);
1240 return STOP;
1241 } else if (verbose)
1242 fprintf(stderr, tr(500, " done.\n"));
1244 pptr = (struct in_addr **)hp->h_addr_list;
1245 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
1246 perror(tr(253, "could not create socket"));
1247 return STOP;
1249 memset(&servaddr, 0, sizeof servaddr);
1250 servaddr.sin_family = AF_INET;
1251 servaddr.sin_port = port;
1252 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
1253 if (verbose)
1254 fprintf(stderr, tr(192, "%sConnecting to %s:%d . . ."),
1255 "", inet_ntoa(**pptr), ntohs(port));
1257 # ifdef HAVE_SO_SNDTIMEO
1258 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1259 # endif
1260 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof servaddr)
1261 != 0) {
1262 perror(tr(254, " could not connect"));
1263 return STOP;
1265 #endif /* HAVE_IPV6 */
1266 if (verbose)
1267 fputs(tr(193, " connected.\n"), stderr);
1269 /* And the regular timeouts */
1270 #ifdef HAVE_SO_SNDTIMEO
1271 tv.tv_sec = 42;
1272 tv.tv_usec = 0;
1273 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
1274 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
1275 #endif
1276 #ifdef HAVE_SO_LINGER
1277 li.l_onoff = 1;
1278 li.l_linger = 42;
1279 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
1280 #endif
1282 memset(sp, 0, sizeof *sp);
1283 sp->s_fd = sockfd;
1284 #ifdef HAVE_SSL
1285 if (use_ssl && ssl_open(server, sp, uhp) != OKAY) {
1286 sclose(sp);
1287 return STOP;
1289 #endif
1290 return OKAY;
1293 FL int
1294 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
1295 SMALLOC_DEBUG_ARGS)
1297 char *lp = *line;
1299 if (sp->s_rsz < 0) {
1300 sclose(sp);
1301 return sp->s_rsz;
1303 do {
1304 if (*line == NULL || lp > &(*line)[*linesize - 128]) {
1305 size_t diff = lp - *line;
1306 *line = (srealloc)(*line, *linesize += 256
1307 SMALLOC_DEBUG_ARGSCALL);
1308 lp = &(*line)[diff];
1310 if (sp->s_rbufptr == NULL ||
1311 sp->s_rbufptr >= &sp->s_rbuf[sp->s_rsz]) {
1312 #ifdef HAVE_OPENSSL
1313 if (sp->s_use_ssl) {
1314 ssl_retry: if ((sp->s_rsz = SSL_read(sp->s_ssl,
1315 sp->s_rbuf,
1316 sizeof sp->s_rbuf)) <= 0) {
1317 if (sp->s_rsz < 0) {
1318 char o[512];
1319 switch(SSL_get_error(sp->s_ssl,
1320 sp->s_rsz)) {
1321 case SSL_ERROR_WANT_READ:
1322 case SSL_ERROR_WANT_WRITE:
1323 goto ssl_retry;
1325 snprintf(o, sizeof o, "%s",
1326 sp->s_desc ?
1327 sp->s_desc :
1328 "socket");
1329 ssl_gen_err("%s", o);
1332 break;
1334 } else
1335 #endif
1337 again: if ((sp->s_rsz = read(sp->s_fd, sp->s_rbuf,
1338 sizeof sp->s_rbuf)) <= 0) {
1339 if (sp->s_rsz < 0) {
1340 char o[512];
1341 if (errno == EINTR)
1342 goto again;
1343 snprintf(o, sizeof o, "%s",
1344 sp->s_desc ?
1345 sp->s_desc :
1346 "socket");
1347 perror(o);
1349 break;
1352 sp->s_rbufptr = sp->s_rbuf;
1354 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
1355 *lp = '\0';
1356 if (linelen)
1357 *linelen = lp - *line;
1358 return lp - *line;
1360 #endif /* HAVE_SOCKETS */
1362 FL void
1363 load(char const *name)
1365 FILE *in, *oldin;
1367 if (name == NULL || (in = Fopen(name, "r")) == NULL)
1368 return;
1369 oldin = _input;
1370 _input = in;
1371 loading = TRU1;
1372 sourcing = TRU1;
1373 commands();
1374 loading = FAL0;
1375 sourcing = FAL0;
1376 _input = oldin;
1377 Fclose(in);
1380 FL int
1381 csource(void *v)
1383 int rv = 1;
1384 char **arglist = v;
1385 FILE *fi;
1386 char *cp;
1388 if ((cp = fexpand(*arglist, FEXP_LOCAL)) == NULL)
1389 goto jleave;
1390 if ((fi = Fopen(cp, "r")) == NULL) {
1391 perror(cp);
1392 goto jleave;
1394 if (_ssp >= SSTACK - 1) {
1395 fprintf(stderr, tr(3, "Too much \"sourcing\" going on.\n"));
1396 Fclose(fi);
1397 goto jleave;
1400 _sstack[_ssp].s_file = _input;
1401 _sstack[_ssp].s_cond = cond_state;
1402 _sstack[_ssp].s_loading = loading;
1403 ++_ssp;
1404 loading = FAL0;
1405 cond_state = COND_ANY;
1406 _input = fi;
1407 sourcing = TRU1;
1408 rv = 0;
1409 jleave:
1410 return rv;
1413 FL int
1414 unstack(void)
1416 int rv = 1;
1418 if (_ssp <= 0) {
1419 fprintf(stderr, tr(4, "\"Source\" stack over-pop.\n"));
1420 sourcing = FAL0;
1421 goto jleave;
1424 Fclose(_input);
1425 if (cond_state != COND_ANY)
1426 fprintf(stderr, tr(5, "Unmatched \"if\"\n"));
1427 --_ssp;
1428 cond_state = _sstack[_ssp].s_cond;
1429 loading = _sstack[_ssp].s_loading;
1430 _input = _sstack[_ssp].s_file;
1431 if (_ssp == 0)
1432 sourcing = loading;
1433 rv = 0;
1434 jleave:
1435 return rv;