FIX "," message specification (since [1c4b8c9], v14.8.4)..
[s-mailx.git] / fio.c
blob3480df777aebf8fd5a86ba1d5e095b67455a41c2
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ File I/O, including resource file loading etc.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE fio
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/wait.h>
44 #ifdef HAVE_SOCKETS
45 # include <sys/socket.h>
47 # include <netdb.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
53 # endif
54 #endif
56 #ifdef HAVE_WORDEXP
57 # include <wordexp.h>
58 #endif
60 #ifdef HAVE_OPENSSL
61 # include <openssl/err.h>
62 # include <openssl/rand.h>
63 # include <openssl/ssl.h>
64 # include <openssl/x509v3.h>
65 # include <openssl/x509.h>
66 #endif
68 #ifdef HAVE_DOTLOCK
69 # include "dotlock.h"
70 #endif
72 struct fio_stack {
73 FILE *s_file; /* File we were in. */
74 void *s_cond; /* Saved state of conditional stack */
75 int s_loading; /* Loading .mailrc, etc. */
78 struct shvar_stack {
79 struct shvar_stack *shs_next;
80 char const *shs_value; /* Remaining value to expand */
81 size_t shs_len; /* gth of .shs_dat this level */
82 char const *shs_dat; /* Result data of this level */
85 /* Slots in ::message */
86 static size_t _message_space;
88 /* XXX Our Popen() main() takes void, temporary global data store */
89 #ifdef HAVE_DOTLOCK
90 static enum file_lock_type _dotlock_flt;
91 static int _dotlock_fd;
92 struct dotlock_info * _dotlock_dip;
93 #endif
95 /* */
96 static struct fio_stack _fio_stack[FIO_STACK_SIZE];
97 static size_t _fio_stack_size;
98 static FILE * _fio_input;
100 /* Locate the user's mailbox file (where new, unread mail is queued) */
101 static void _findmail(char *buf, size_t bufsize, char const *user,
102 bool_t force);
104 /* Perform shell variable expansion */
105 static char * _shvar_exp(char const *name);
106 static char * __shvar_exp(struct shvar_stack *shsp);
108 /* Perform shell meta character expansion TODO obsolete (INSECURE!) */
109 static char * _globname(char const *name, enum fexp_mode fexpm);
111 /* line is a buffer with the result of fgets(). Returns the first newline or
112 * the last character read */
113 static size_t _length_of_line(char const *line, size_t linesize);
115 /* Read a line, one character at a time */
116 static char * _fgetline_byone(char **line, size_t *linesize, size_t *llen,
117 FILE *fp, int appendnl, size_t n SMALLOC_DEBUG_ARGS);
119 /* Take the data out of the passed ghost file and toss it into a dynamically
120 * allocated message structure */
121 static void makemessage(void);
123 static enum okay get_header(struct message *mp);
125 /* Workhorse */
126 static bool_t _file_lock(int fd, enum file_lock_type ft,
127 off_t off, off_t len);
129 /* main() of fork(2)ed dot file locker */
130 #ifdef HAVE_DOTLOCK
131 static int _dotlock_main(void);
132 #endif
134 /* Write to socket fd, restarting on EINTR, unless anything is written */
135 #ifdef HAVE_SOCKETS
136 static long xwrite(int fd, char const *data, size_t sz);
137 #endif
139 /* `source' and `source_if' */
140 static bool_t _source_file(char const *file, bool_t silent_error);
142 static void
143 _findmail(char *buf, size_t bufsize, char const *user, bool_t force)
145 char *cp;
146 NYD_ENTER;
148 if (!strcmp(user, myname) && !force && (cp = ok_vlook(folder)) != NULL) {
149 switch (which_protocol(cp)) {
150 case PROTO_IMAP:
151 if (strcmp(cp, protbase(cp)))
152 goto jcopy;
153 snprintf(buf, bufsize, "%s/INBOX", cp);
154 goto jleave;
155 default:
156 break;
160 if (force || (cp = ok_vlook(MAIL)) == NULL)
161 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
162 else
163 jcopy:
164 n_strlcpy(buf, cp, bufsize);
165 jleave:
166 NYD_LEAVE;
169 static char *
170 _shvar_exp(char const *name)
172 struct shvar_stack top;
173 char *rv;
174 NYD2_ENTER;
176 memset(&top, 0, sizeof top);
177 top.shs_value = name;
178 rv = __shvar_exp(&top);
180 NYD2_LEAVE;
181 return rv;
184 static char *
185 __shvar_exp(struct shvar_stack *shsp)
187 struct shvar_stack next, *np, *tmp;
188 char const *vp;
189 char lc, c, *cp, *rv;
190 size_t i;
191 NYD2_ENTER;
193 if (*(vp = shsp->shs_value) != '$') {
194 union {bool_t hadbs; char c;} u = {FAL0};
196 shsp->shs_dat = vp;
197 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
198 if (c == '$' && lc != '\\')
199 break;
200 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
202 shsp->shs_len = i;
204 if (u.hadbs) {
205 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
207 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
208 if (u.c != '\\' || lc == '\\')
209 *rv++ = u.c;
210 lc = (lc == '\\') ? '\0' : u.c;
212 *rv = '\0';
214 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
216 } else {
217 if ((lc = (*++vp == '{')))
218 ++vp;
220 shsp->shs_dat = vp;
221 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
222 if (!alnumchar(c) && c != '_')
223 break;
225 if (lc) {
226 if (c != '}') {
227 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
228 shsp->shs_value);
229 shsp->shs_len = strlen(shsp->shs_value);
230 shsp->shs_dat = shsp->shs_value;
231 goto junroll;
233 c = *++vp;
236 shsp->shs_len = i;
237 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
238 shsp->shs_len = strlen(shsp->shs_dat = cp);
240 if (c != '\0')
241 goto jrecurse;
243 /* That level made the great and completed encoding. Build result */
244 junroll:
245 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
246 i += np->shs_len;
247 tmp = np->shs_next;
248 np->shs_next = shsp;
249 shsp = np;
250 np = tmp;
253 cp = rv = salloc(i +1);
254 while (shsp != NULL) {
255 np = shsp;
256 shsp = shsp->shs_next;
257 memcpy(cp, np->shs_dat, np->shs_len);
258 cp += np->shs_len;
260 *cp = '\0';
262 jleave:
263 NYD2_LEAVE;
264 return rv;
265 jrecurse:
266 memset(&next, 0, sizeof next);
267 next.shs_next = shsp;
268 next.shs_value = vp;
269 rv = __shvar_exp(&next);
270 goto jleave;
273 static char *
274 _globname(char const *name, enum fexp_mode fexpm)
276 #ifdef HAVE_WORDEXP
277 wordexp_t we;
278 char *cp = NULL;
279 sigset_t nset;
280 int i;
281 NYD_ENTER;
283 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
284 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
285 memset(&we, 0, sizeof we);
287 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
288 * and wait, which will fail if our SIGCHLD handler is active */
289 sigemptyset(&nset);
290 sigaddset(&nset, SIGCHLD);
291 sigprocmask(SIG_BLOCK, &nset, NULL);
292 # ifndef WRDE_NOCMD
293 # define WRDE_NOCMD 0
294 # endif
295 i = wordexp(name, &we, WRDE_NOCMD);
296 sigprocmask(SIG_UNBLOCK, &nset, NULL);
298 switch (i) {
299 case 0:
300 break;
301 #ifdef WRDE_CMDSUB
302 case WRDE_CMDSUB:
303 if (!(fexpm & FEXP_SILENT))
304 n_err(_("\"%s\": Command substitution not allowed\n"), name);
305 goto jleave;
306 #endif
307 case WRDE_NOSPACE:
308 if (!(fexpm & FEXP_SILENT))
309 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
310 goto jleave;
311 case WRDE_BADCHAR:
312 case WRDE_SYNTAX:
313 default:
314 if (!(fexpm & FEXP_SILENT))
315 n_err(_("Syntax error in \"%s\"\n"), name);
316 goto jleave;
319 switch (we.we_wordc) {
320 case 1:
321 cp = savestr(we.we_wordv[0]);
322 break;
323 case 0:
324 if (!(fexpm & FEXP_SILENT))
325 n_err(_("\"%s\": No match\n"), name);
326 break;
327 default:
328 if (fexpm & FEXP_MULTIOK) {
329 size_t j, l;
331 for (l = 0, j = 0; j < we.we_wordc; ++j)
332 l += strlen(we.we_wordv[j]) + 1;
333 ++l;
334 cp = salloc(l);
335 for (l = 0, j = 0; j < we.we_wordc; ++j) {
336 size_t x = strlen(we.we_wordv[j]);
337 memcpy(cp + l, we.we_wordv[j], x);
338 l += x;
339 cp[l++] = ' ';
341 cp[l] = '\0';
342 } else if (!(fexpm & FEXP_SILENT))
343 n_err(_("\"%s\": Ambiguous\n"), name);
344 break;
346 jleave:
347 wordfree(&we);
348 NYD_LEAVE;
349 return cp;
351 #else /* HAVE_WORDEXP */
352 struct stat sbuf;
353 char xname[PATH_MAX +1], cmdbuf[PATH_MAX +1], /* also used for files */
354 *shellp, *cp = NULL;
355 int pivec[2], pid, l, waits;
356 NYD_ENTER;
358 if (pipe(pivec) < 0) {
359 n_perr(_("pipe"), 0);
360 goto jleave;
362 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
363 if ((shellp = ok_vlook(SHELL)) == NULL)
364 shellp = UNCONST(XSHELL);
365 pid = start_command(shellp, NULL, -1, pivec[1], "-c", cmdbuf, NULL, NULL);
366 if (pid < 0) {
367 close(pivec[0]);
368 close(pivec[1]);
369 goto jleave;
371 close(pivec[1]);
373 jagain:
374 l = read(pivec[0], xname, sizeof xname);
375 if (l < 0) {
376 if (errno == EINTR)
377 goto jagain;
378 n_perr(_("read"), 0);
379 close(pivec[0]);
380 goto jleave;
382 close(pivec[0]);
383 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
384 if (!(fexpm & FEXP_SILENT))
385 n_err(_("\"%s\": Expansion failed\n"), name);
386 goto jleave;
388 if (l == 0) {
389 if (!(fexpm & FEXP_SILENT))
390 n_err(_("\"%s\": No match\n"), name);
391 goto jleave;
393 if (l == sizeof xname) {
394 if (!(fexpm & FEXP_SILENT))
395 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
396 goto jleave;
398 xname[l] = 0;
399 for (cp = xname + l - 1; *cp == '\n' && cp > xname; --cp)
401 cp[1] = '\0';
402 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
403 stat(xname, &sbuf) < 0) {
404 if (!(fexpm & FEXP_SILENT))
405 n_err(_("\"%s\": Ambiguous\n"), name);
406 cp = NULL;
407 goto jleave;
409 cp = savestr(xname);
410 jleave:
411 NYD_LEAVE;
412 return cp;
413 #endif /* !HAVE_WORDEXP */
416 static size_t
417 _length_of_line(char const *line, size_t linesize)
419 size_t i;
420 NYD2_ENTER;
422 /* Last character is always '\0' and was added by fgets() */
423 for (--linesize, i = 0; i < linesize; i++)
424 if (line[i] == '\n')
425 break;
426 i = (i < linesize) ? i + 1 : linesize;
427 NYD2_LEAVE;
428 return i;
431 static char *
432 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
433 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
435 char *rv;
436 int c;
437 NYD2_ENTER;
439 assert(*linesize == 0 || *line != NULL);
440 for (rv = *line;;) {
441 if (*linesize <= LINESIZE || n >= *linesize - 128) {
442 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
443 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
445 c = getc(fp);
446 if (c != EOF) {
447 rv[n++] = c;
448 rv[n] = '\0';
449 if (c == '\n')
450 break;
451 } else {
452 if (n > 0) {
453 if (appendnl) {
454 rv[n++] = '\n';
455 rv[n] = '\0';
457 break;
458 } else {
459 rv = NULL;
460 goto jleave;
464 if (llen)
465 *llen = n;
466 jleave:
467 NYD2_LEAVE;
468 return rv;
471 static void
472 makemessage(void)
474 NYD_ENTER;
475 if (msgCount == 0)
476 message_append(NULL);
477 setdot(message);
478 message[msgCount].m_size = 0;
479 message[msgCount].m_lines = 0;
480 NYD_LEAVE;
483 static enum okay
484 get_header(struct message *mp)
486 enum okay rv;
487 NYD_ENTER;
488 UNUSED(mp);
490 switch (mb.mb_type) {
491 case MB_FILE:
492 case MB_MAILDIR:
493 rv = OKAY;
494 break;
495 #ifdef HAVE_POP3
496 case MB_POP3:
497 rv = pop3_header(mp);
498 break;
499 #endif
500 #ifdef HAVE_IMAP
501 case MB_IMAP:
502 case MB_CACHE:
503 rv = imap_header(mp);
504 break;
505 #endif
506 case MB_VOID:
507 default:
508 rv = STOP;
509 break;
511 NYD_LEAVE;
512 return rv;
515 static bool_t
516 _file_lock(int fd, enum file_lock_type flt, off_t off, off_t len)
518 struct flock flp;
519 bool_t rv;
520 NYD2_ENTER;
522 memset(&flp, 0, sizeof flp);
524 switch (flt) {
525 default:
526 case FLT_READ: rv = F_RDLCK; break;
527 case FLT_WRITE: rv = F_WRLCK; break;
529 flp.l_type = rv;
530 flp.l_start = off;
531 flp.l_whence = SEEK_SET;
532 flp.l_len = len;
534 rv = (fcntl(fd, F_SETLK, &flp) != -1);
535 NYD2_LEAVE;
536 return rv;
539 #ifdef HAVE_DOTLOCK
540 static int
541 _dotlock_main(void)
543 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
544 * problems (SunOS), since the pathconf(3) value comes too late! */
545 char name[PATH_MAX +1];
546 struct dotlock_info di;
547 struct stat stb, fdstb;
548 enum dotlock_state dls;
549 char const *cp;
550 int fd;
551 enum file_lock_type flt;
552 NYD_ENTER;
554 /* Ignore SIGPIPE, we'll see EPIPE and "fall through" */
555 safe_signal(SIGPIPE, SIG_IGN);
557 /* Get the arguments "passed to us" */
558 flt = _dotlock_flt;
559 fd = _dotlock_fd;
560 UNUSED(fd);
561 di = *_dotlock_dip;
563 /* chdir(2)? */
564 jislink:
565 dls = DLS_CANT_CHDIR | DLS_ABANDON;
567 if ((cp = strrchr(di.di_file_name, '/')) != NULL) {
568 char const *fname = cp + 1;
570 while (PTRCMP(cp - 1, >, di.di_file_name) && cp[-1] == '/')
571 --cp;
572 cp = savestrbuf(di.di_file_name, PTR2SIZE(cp - di.di_file_name));
573 if (chdir(cp))
574 goto jmsg;
576 di.di_file_name = fname;
579 /* So we're here, but then again the file can be a symbolic link!
580 * This is however only true if we do not have realpath(3) available since
581 * that'll have resolved the path already otherwise; nonetheless, let
582 * readlink(2) be a precondition for dotlocking and keep this code */
583 if (lstat(cp = di.di_file_name, &stb) == -1)
584 goto jmsg;
585 if (S_ISLNK(stb.st_mode)) {
586 /* Use salloc() and hope we stay in builtin buffer.. */
587 char *x;
588 size_t i;
589 ssize_t sr;
591 for (x = NULL, i = PATH_MAX;; i += PATH_MAX) {
592 x = salloc(i +1);
593 sr = readlink(cp, x, i);
594 if (sr <= 0) {
595 dls = DLS_FISHY | DLS_ABANDON;
596 goto jmsg;
598 if (UICMP(z, sr, <, i)) {
599 x[sr] = '\0';
600 i = (size_t)sr;
601 break;
604 di.di_file_name = x;
605 goto jislink;
608 dls = DLS_FISHY | DLS_ABANDON;
610 /* Bail out if the file has changed its identity in the meanwhile */
611 if (fstat(fd, &fdstb) == -1 ||
612 fdstb.st_dev != stb.st_dev || fdstb.st_ino != stb.st_ino ||
613 fdstb.st_uid != stb.st_uid || fdstb.st_gid != stb.st_gid ||
614 fdstb.st_mode != stb.st_mode)
615 goto jmsg;
617 /* Be aware, even if the error is false! Note the shared code in dotlock.h
618 * *requires* that it is possible to create a filename at least one byte
619 * longer than di_lock_name! */
620 do/* while(0) breaker */{
621 # ifdef HAVE_PATHCONF
622 long pc;
623 # endif
624 int i = snprintf(name, sizeof name, "%s.lock", di.di_file_name);
626 /* fd is a file, not portable to use for _PC_NAME_MAX */
627 if(i < 0){
628 jenametool:
629 dls = DLS_NAMETOOLONG | DLS_ABANDON;
630 goto jmsg;
632 # ifdef HAVE_PATHCONF
633 errno = 0;
634 if((pc = pathconf(".", _PC_NAME_MAX)) == -1){
635 /* errno unchanged: no limit */
636 if(errno == 0)
637 break;
638 }else if(pc - 1 >= (long)i)
639 break;
640 else
641 goto jenametool;
642 # endif
643 if(UICMP(z, NAME_MAX - 1, <, (uiz_t)i))
644 goto jenametool;
645 }while(0);
647 di.di_lock_name = name;
649 /* We are in the directory of the mailbox for which we have to create
650 * a dotlock file for. We don't know wether we have realpath(3) available,
651 * and manually resolving the path is due especially given that S-nail
652 * supports the special "%:" syntax to warp any file into a "system
653 * mailbox"; there may also be multiple system mailbox directories...
654 * So what we do is that we fstat(2) the mailbox and check its UID and
655 * GID against that of our own process: if any of those mismatch we must
656 * either assume a directory we are not allowed to write in, or that we run
657 * via -u/$USER/%USER as someone else, in which case we favour our
658 * privilege-separated dotlock process */
659 assert(cp != NULL); /* Ugly: avoid a useless var and reuse that one */
660 if (access(".", W_OK)) {
661 /* This may however also indicate a read-only filesystem, which is not
662 * really an error from our point of view since the mailbox will degrade
663 * to a readonly one for which no dotlock is needed, then, and errors
664 * may arise only due to actions which require box modifications */
665 if (errno == EROFS) {
666 dls = DLS_ROFS | DLS_ABANDON;
667 goto jmsg;
669 cp = NULL;
671 if (cp == NULL || stb.st_uid != user_id || stb.st_gid != group_id) {
672 char itoabuf[64];
673 char const *args[13];
675 snprintf(itoabuf, sizeof itoabuf, "%" PRIuZ, di.di_pollmsecs);
676 args[ 0] = PRIVSEP;
677 args[ 1] = (flt == FLT_READ ? "rdotlock" : "wdotlock");
678 args[ 2] = "mailbox"; args[ 3] = di.di_file_name;
679 args[ 4] = "name"; args[ 5] = di.di_lock_name;
680 args[ 6] = "hostname"; args[ 7] = di.di_hostname;
681 args[ 8] = "randstr"; args[ 9] = di.di_randstr;
682 args[10] = "pollmsecs"; args[11] = itoabuf;
683 args[12] = NULL;
684 execv(LIBEXECDIR "/" UAGENT "-privsep", UNCONST(args));
686 dls = DLS_NOEXEC;
687 write(STDOUT_FILENO, &dls, sizeof dls);
688 /* But fall through and try it with normal privileges! */
691 /* So let's try and call it ourselfs! Note that we don't block signals just
692 * like our privsep child does, the user will anyway be able to remove his
693 * file again, and if we're in -u/$USER mode then we are allowed to access
694 * the user's box: shall we leave behind a stale dotlock then at least we
695 * start a friendly human conversation. Since we cannot handle SIGKILL and
696 * SIGSTOP malicious things could happen whatever we do */
697 safe_signal(SIGHUP, SIG_IGN);
698 safe_signal(SIGINT, SIG_IGN);
699 safe_signal(SIGQUIT, SIG_IGN);
700 safe_signal(SIGTERM, SIG_IGN);
702 NYD;
703 dls = _dotlock_create(&di);
704 NYD;
706 /* Finally: notify our parent about the actual lock state.. */
707 jmsg:
708 write(STDOUT_FILENO, &dls, sizeof dls);
709 close(STDOUT_FILENO);
711 /* ..then eventually wait until we shall remove the lock again, which will
712 * be notified via the read returning */
713 if (dls == DLS_NONE) {
714 read(STDIN_FILENO, &dls, sizeof dls);
716 unlink(name);
718 NYD_LEAVE;
719 return EXIT_OK;
721 #endif /* HAVE_DOTLOCK */
723 #ifdef HAVE_SOCKETS
724 static long
725 xwrite(int fd, char const *data, size_t sz)
727 long rv = -1, wo;
728 size_t wt = 0;
729 NYD_ENTER;
731 do {
732 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
733 if (errno == EINTR)
734 continue;
735 else
736 goto jleave;
738 wt += wo;
739 } while (wt < sz);
740 rv = (long)sz;
741 jleave:
742 NYD_LEAVE;
743 return rv;
745 #endif /* HAVE_SOCKETS */
747 static bool_t
748 _source_file(char const *file, bool_t silent_error)
750 char *cp;
751 FILE *fi = NULL;
752 NYD_ENTER;
754 if ((cp = fexpand(file, FEXP_LOCAL)) == NULL)
755 goto jleave;
756 if ((fi = Fopen(cp, "r")) == NULL) {
757 if (!silent_error || (options & OPT_D_V))
758 n_perr(cp, 0);
759 goto jleave;
762 if (temporary_localopts_store != NULL) {
763 n_err(_("Before v15 you cannot `source' from within macros, sorry\n"));
764 goto jeclose;
766 if (_fio_stack_size >= NELEM(_fio_stack)) {
767 n_err(_("Too many `source' recursions\n"));
768 jeclose:
769 Fclose(fi);
770 fi = NULL;
771 goto jleave;
774 _fio_stack[_fio_stack_size].s_file = _fio_input;
775 _fio_stack[_fio_stack_size].s_cond = condstack_release();
776 _fio_stack[_fio_stack_size].s_loading = !!(pstate & PS_LOADING);
777 ++_fio_stack_size;
778 pstate &= ~PS_LOADING;
779 pstate |= PS_SOURCING;
780 _fio_input = fi;
781 jleave:
782 NYD_LEAVE;
783 return (fi != NULL);
786 FL char *
787 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
788 int appendnl SMALLOC_DEBUG_ARGS)
790 size_t i_llen, sz;
791 char *rv;
792 NYD2_ENTER;
794 if (cnt == NULL) {
795 /* Without count, we can't determine where the chars returned by fgets()
796 * end if there's no newline. We have to read one character by one */
797 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
798 SMALLOC_DEBUG_ARGSCALL);
799 goto jleave;
802 if ((rv = *line) == NULL || *linesize < LINESIZE)
803 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
804 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
805 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
806 /* Leave llen untouched; it is used to determine whether the last line
807 * was \n-terminated in some callers */
808 rv = NULL;
809 goto jleave;
812 i_llen = _length_of_line(rv, sz);
813 *cnt -= i_llen;
814 while (rv[i_llen - 1] != '\n') {
815 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
816 sz = *linesize - i_llen;
817 sz = (sz <= *cnt) ? sz : *cnt + 1;
818 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
819 if (appendnl) {
820 rv[i_llen++] = '\n';
821 rv[i_llen] = '\0';
823 break;
825 sz = _length_of_line(rv + i_llen, sz);
826 i_llen += sz;
827 *cnt -= sz;
829 if (llen)
830 *llen = i_llen;
831 jleave:
832 NYD2_LEAVE;
833 return rv;
836 FL int
837 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
838 SMALLOC_DEBUG_ARGS)
840 /* TODO readline_restart(): always *appends* LF just to strip it again;
841 * TODO should be configurable just as for fgetline(); ..or whatever.. */
842 int rv = -1;
843 long sz;
844 NYD2_ENTER;
846 clearerr(ibuf);
848 /* Interrupts will cause trouble if we are inside a stdio call. As this is
849 * only relevant if input is from tty, bypass it by read(), then */
850 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
851 assert(*linesize == 0 || *linebuf != NULL);
852 for (;;) {
853 if (*linesize <= LINESIZE || n >= *linesize - 128) {
854 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
855 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
857 jagain:
858 sz = read(0, *linebuf + n, *linesize - n - 1);
859 if (sz > 0) {
860 n += sz;
861 (*linebuf)[n] = '\0';
862 if (n > 0 && (*linebuf)[n - 1] == '\n')
863 break;
864 } else {
865 if (sz < 0 && errno == EINTR)
866 goto jagain;
867 if (n > 0) {
868 if ((*linebuf)[n - 1] != '\n') {
869 (*linebuf)[n++] = '\n';
870 (*linebuf)[n] = '\0';
872 break;
873 } else
874 goto jleave;
877 } else {
878 /* Not reading from standard input or standard input not a terminal. We
879 * read one char at a time as it is the only way to get lines with
880 * embedded NUL characters in standard stdio */
881 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
882 SMALLOC_DEBUG_ARGSCALL) == NULL)
883 goto jleave;
885 if (n > 0 && (*linebuf)[n - 1] == '\n')
886 (*linebuf)[--n] = '\0';
887 rv = (int)n;
888 jleave:
889 NYD2_LEAVE;
890 return rv;
893 FL int
894 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
895 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
897 /* TODO readline: linebuf pool! */
898 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
899 bool_t doprompt, dotty;
900 int n, nold;
901 NYD2_ENTER;
903 doprompt = (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE));
904 dotty = (doprompt && !ok_blook(line_editor_disable));
905 if (!doprompt)
906 prompt = NULL;
907 else if (prompt == NULL)
908 prompt = getprompt();
910 /* Ensure stdout is flushed first anyway */
911 if (!dotty && prompt == NULL)
912 fflush(stdout);
914 for (nold = n = 0;;) {
915 if (dotty) {
916 assert(ifile == stdin);
917 if (string != NULL && (n = (int)strlen(string)) > 0) {
918 if (*linesize > 0)
919 *linesize += n +1;
920 else
921 *linesize = (size_t)n + LINESIZE +1;
922 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
923 memcpy(*linebuf, string, (size_t)n +1);
925 string = NULL;
926 /* TODO if nold>0, don't redisplay the entire line!
927 * TODO needs complete redesign ... */
928 n = (tty_readline)(prompt, linebuf, linesize, n
929 SMALLOC_DEBUG_ARGSCALL);
930 } else {
931 if (prompt != NULL) {
932 if (*prompt != '\0')
933 fputs(prompt, stdout);
934 fflush(stdout);
936 n = (readline_restart)(ifile, linebuf, linesize, n
937 SMALLOC_DEBUG_ARGSCALL);
939 if (n > 0 && nold > 0) {
940 int i = 0;
941 char const *cp = *linebuf + nold;
943 while (blankspacechar(*cp) && nold + i < n)
944 ++cp, ++i;
945 if (i > 0) {
946 memmove(*linebuf + nold, cp, n - nold - i);
947 n -= i;
948 (*linebuf)[n] = '\0';
952 if (n <= 0)
953 break;
955 /* POSIX says:
956 * An unquoted <backslash> at the end of a command line shall
957 * be discarded and the next line shall continue the command */
958 if (!nl_escape || n == 0 || (*linebuf)[n - 1] != '\\')
959 break;
960 (*linebuf)[nold = --n] = '\0';
961 if (prompt != NULL && *prompt != '\0')
962 prompt = ".. "; /* XXX PS2 .. */
965 if (n >= 0 && (options & OPT_D_VV))
966 n_err(_("%s %d bytes <%.*s>\n"),
967 ((pstate & PS_LOADING) ? "LOAD"
968 : (pstate & PS_SOURCING) ? "SOURCE" : "READ"),
969 n, n, *linebuf);
970 NYD2_LEAVE;
971 return n;
974 FL char *
975 n_input_cp_addhist(char const *prompt, char const *string, bool_t isgabby)
977 /* FIXME n_input_cp_addhist(): leaks on sigjmp without linepool */
978 size_t linesize = 0;
979 char *linebuf = NULL, *rv = NULL;
980 int n;
981 NYD2_ENTER;
983 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
984 if (n > 0 && *(rv = savestrbuf(linebuf, (size_t)n)) != '\0' &&
985 (options & OPT_INTERACTIVE))
986 tty_addhist(rv, isgabby);
988 if (linebuf != NULL)
989 free(linebuf);
990 NYD2_LEAVE;
991 return rv;
994 FL void
995 setptr(FILE *ibuf, off_t offset)
997 struct message self;
998 char *cp, *linebuf = NULL;
999 char const *cp2;
1000 int c, selfcnt = 0;
1001 bool_t maybe, inhead, rfc4155;
1002 size_t linesize = 0, filesize, cnt;
1003 NYD_ENTER;
1005 memset(&self, 0, sizeof self);
1006 self.m_flag = MUSED | MNEW | MNEWEST;
1007 filesize = mailsize - offset;
1008 offset = ftell(mb.mb_otf);
1009 maybe = TRU1;
1010 rfc4155 = inhead = FAL0;
1012 for (;;) {
1013 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
1014 self.m_xsize = self.m_size;
1015 self.m_xlines = self.m_lines;
1016 self.m_have = HAVE_HEADER | HAVE_BODY;
1017 if (selfcnt > 0)
1018 message_append(&self);
1019 makemessage();
1020 if (linebuf)
1021 free(linebuf);
1022 break;
1025 #ifdef notdef
1026 if (linebuf[0] == '\0')
1027 linebuf[0] = '.';
1028 #endif
1029 /* XXX Convert CRLF to LF; this should be rethought in that
1030 * XXX CRLF input should possibly end as CRLF output? */
1031 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
1032 linebuf[--cnt - 1] = '\n';
1033 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
1034 if (ferror(mb.mb_otf)) {
1035 n_perr(_("/tmp"), 0);
1036 exit(EXIT_ERR);
1038 if (linebuf[cnt - 1] == '\n')
1039 linebuf[cnt - 1] = '\0';
1040 if (maybe && linebuf[0] == 'F' &&
1041 (rfc4155 = is_head(linebuf, cnt, TRU1))) {
1042 /* TODO char date[FROM_DATEBUF];
1043 * TODO extract_date_from_from_(linebuf, cnt, date);
1044 * TODO self.m_time = 10000; */
1045 if (rfc4155 == TRUM1) {
1046 if (options & OPT_D_V)
1047 n_err(_("Invalid MBOX \"From_ line\": %.*s\n"),
1048 (int)cnt, linebuf);
1049 else if (!(mb.mb_active & MB_FROM__WARNED))
1050 n_err(_("MBOX mailbox contains non-conforming From_ line(s)\n"));
1051 mb.mb_active |= MB_FROM__WARNED;
1053 self.m_xsize = self.m_size;
1054 self.m_xlines = self.m_lines;
1055 self.m_have = HAVE_HEADER | HAVE_BODY;
1056 if (selfcnt++ > 0)
1057 message_append(&self);
1058 msgCount++;
1059 self.m_flag = MUSED | MNEW | MNEWEST;
1060 self.m_size = 0;
1061 self.m_lines = 0;
1062 self.m_block = mailx_blockof(offset);
1063 self.m_offset = mailx_offsetof(offset);
1064 inhead = TRU1;
1065 } else if (linebuf[0] == 0) {
1066 inhead = FAL0;
1067 } else if (inhead) {
1068 for (cp = linebuf, cp2 = "status";; ++cp) {
1069 if ((c = *cp2++) == 0) {
1070 while (c = *cp++, whitechar(c))
1072 if (cp[-1] != ':')
1073 break;
1074 while ((c = *cp++) != '\0')
1075 if (c == 'R')
1076 self.m_flag |= MREAD;
1077 else if (c == 'O')
1078 self.m_flag &= ~MNEW;
1079 break;
1081 if (*cp != c && *cp != upperconv(c))
1082 break;
1084 for (cp = linebuf, cp2 = "x-status";; ++cp) {
1085 if ((c = *cp2++) == 0) {
1086 while ((c = *cp++, whitechar(c)))
1088 if (cp[-1] != ':')
1089 break;
1090 while ((c = *cp++) != '\0')
1091 if (c == 'F')
1092 self.m_flag |= MFLAGGED;
1093 else if (c == 'A')
1094 self.m_flag |= MANSWERED;
1095 else if (c == 'T')
1096 self.m_flag |= MDRAFTED;
1097 break;
1099 if (*cp != c && *cp != upperconv(c))
1100 break;
1103 offset += cnt;
1104 self.m_size += cnt;
1105 ++self.m_lines;
1106 maybe = (linebuf[0] == 0);
1108 NYD_LEAVE;
1111 FL int
1112 putline(FILE *obuf, char *linebuf, size_t cnt)
1114 int rv = -1;
1115 NYD_ENTER;
1117 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
1118 putc('\n', obuf);
1119 if (!ferror(obuf))
1120 rv = (int)(cnt + 1);
1121 NYD_LEAVE;
1122 return rv;
1125 FL FILE *
1126 setinput(struct mailbox *mp, struct message *m, enum needspec need)
1128 FILE *rv = NULL;
1129 enum okay ok = STOP;
1130 NYD_ENTER;
1132 switch (need) {
1133 case NEED_HEADER:
1134 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
1135 break;
1136 case NEED_BODY:
1137 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
1138 break;
1139 case NEED_UNSPEC:
1140 ok = OKAY;
1141 break;
1143 if (ok != OKAY)
1144 goto jleave;
1146 fflush(mp->mb_otf);
1147 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1148 SEEK_SET) == -1) {
1149 n_perr(_("fseek"), 0);
1150 n_panic(_("temporary file seek"));
1152 rv = mp->mb_itf;
1153 jleave:
1154 NYD_LEAVE;
1155 return rv;
1158 FL void
1159 message_reset(void)
1161 NYD_ENTER;
1162 if (message != NULL) {
1163 free(message);
1164 message = NULL;
1166 msgCount = 0;
1167 _message_space = 0;
1168 NYD_LEAVE;
1171 FL void
1172 message_append(struct message *mp)
1174 NYD_ENTER;
1175 if (UICMP(z, msgCount + 1, >=, _message_space)) {
1176 /* XXX remove _message_space magics (or use s_Vector) */
1177 _message_space = (_message_space >= 128 && _message_space <= 1000000)
1178 ? _message_space << 1 : _message_space + 64;
1179 message = srealloc(message, _message_space * sizeof *message);
1181 if (msgCount > 0) {
1182 if (mp != NULL)
1183 message[msgCount - 1] = *mp;
1184 else
1185 memset(message + msgCount - 1, 0, sizeof *message);
1187 NYD_LEAVE;
1190 FL bool_t
1191 message_match(struct message *mp, struct search_expr const *sep,
1192 bool_t with_headers)
1194 char **line;
1195 size_t *linesize, cnt;
1196 FILE *fp;
1197 bool_t rv = FAL0;
1198 NYD_ENTER;
1200 if ((fp = Ftmp(NULL, "mpmatch", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1201 NULL)
1202 goto j_leave;
1204 if (sendmp(mp, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
1205 goto jleave;
1206 fflush_rewind(fp);
1208 cnt = fsize(fp);
1209 line = &termios_state.ts_linebuf; /* XXX line pool */
1210 linesize = &termios_state.ts_linesize; /* XXX line pool */
1212 if (!with_headers)
1213 while (fgetline(line, linesize, &cnt, NULL, fp, 0))
1214 if (**line == '\n')
1215 break;
1217 while (fgetline(line, linesize, &cnt, NULL, fp, 0)) {
1218 #ifdef HAVE_REGEX
1219 if (sep->ss_sexpr == NULL) {
1220 if (regexec(&sep->ss_regex, *line, 0,NULL, 0) == REG_NOMATCH)
1221 continue;
1222 } else
1223 #endif
1224 if (!substr(*line, sep->ss_sexpr))
1225 continue;
1226 rv = TRU1;
1227 break;
1230 jleave:
1231 Fclose(fp);
1232 j_leave:
1233 NYD_LEAVE;
1234 return rv;
1237 FL struct message *
1238 setdot(struct message *mp)
1240 NYD_ENTER;
1241 if (dot != mp) {
1242 prevdot = dot;
1243 pstate &= ~PS_DID_PRINT_DOT;
1245 dot = mp;
1246 uncollapse1(dot, 0);
1247 NYD_LEAVE;
1248 return dot;
1251 FL int
1252 rm(char const *name)
1254 struct stat sb;
1255 int rv = -1;
1256 NYD_ENTER;
1258 if (stat(name, &sb) < 0)
1260 else if (!S_ISREG(sb.st_mode))
1261 errno = EISDIR;
1262 else
1263 rv = unlink(name);
1264 NYD_LEAVE;
1265 return rv;
1268 FL off_t
1269 fsize(FILE *iob)
1271 struct stat sbuf;
1272 off_t rv;
1273 NYD_ENTER;
1275 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
1276 NYD_LEAVE;
1277 return rv;
1280 FL char *
1281 fexpand(char const *name, enum fexp_mode fexpm)
1283 char cbuf[PATH_MAX +1];
1284 char const *res;
1285 struct str s;
1286 bool_t dyn;
1287 NYD_ENTER;
1289 /* The order of evaluation is "%" and "#" expand into constants.
1290 * "&" can expand into "+". "+" can expand into shell meta characters.
1291 * Shell meta characters expand into constants.
1292 * This way, we make no recursive expansion */
1293 if ((fexpm & FEXP_NSHORTCUT) || (res = shortcut_expand(name)) == NULL)
1294 res = UNCONST(name);
1296 if (fexpm & FEXP_SHELL) {
1297 dyn = FAL0;
1298 goto jshell;
1300 jnext:
1301 dyn = FAL0;
1302 switch (*res) {
1303 case '%':
1304 if (res[1] == ':' && res[2] != '\0') {
1305 res = &res[2];
1306 goto jnext;
1308 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
1309 (res[1] != '\0' || (options & OPT_u_FLAG)));
1310 res = cbuf;
1311 goto jislocal;
1312 case '#':
1313 if (res[1] != '\0')
1314 break;
1315 if (prevfile[0] == '\0') {
1316 n_err(_("No previous file\n"));
1317 res = NULL;
1318 goto jleave;
1320 res = prevfile;
1321 goto jislocal;
1322 case '&':
1323 if (res[1] == '\0') {
1324 if ((res = ok_vlook(MBOX)) == NULL)
1325 res = UNCONST("~/mbox"); /* XXX no magics (POSIX though) */
1326 else if (res[0] != '&' || res[1] != '\0')
1327 goto jnext;
1329 break;
1332 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
1333 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
1334 dyn = TRU1;
1337 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
1338 size_t i = strlen(cbuf);
1340 res = str_concat_csvl(&s, cbuf,
1341 ((i > 0 && cbuf[i - 1] == '/') ? "" : "/"), res + 1, NULL)->s;
1342 dyn = TRU1;
1344 if (res[0] == '%' && res[1] == ':') {
1345 res += 2;
1346 goto jnext;
1350 /* Catch the most common shell meta character */
1351 jshell:
1352 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
1353 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
1354 dyn = TRU1;
1356 if (anyof(res, "|&;<>{}()[]*?$`'\"\\"))
1357 switch (which_protocol(res)) {
1358 case PROTO_FILE:
1359 case PROTO_MAILDIR:
1360 res = (fexpm & FEXP_NSHELL) ? _shvar_exp(res) : _globname(res, fexpm);
1361 dyn = TRU1;
1362 goto jleave;
1363 default:
1364 break;
1366 jislocal:
1367 if (fexpm & FEXP_LOCAL)
1368 switch (which_protocol(res)) {
1369 case PROTO_FILE:
1370 case PROTO_MAILDIR:
1371 break;
1372 default:
1373 n_err(_("Not a local file or directory: \"%s\"\n"), name);
1374 res = NULL;
1375 break;
1377 jleave:
1378 if (res && !dyn)
1379 res = savestr(res);
1380 NYD_LEAVE;
1381 return UNCONST(res);
1384 FL char *
1385 fexpand_nshell_quote(char const *name)
1387 size_t i, j;
1388 char *rv, c;
1389 NYD_ENTER;
1391 for (i = j = 0; (c = name[i]) != '\0'; ++i)
1392 if (c == '\\')
1393 ++j;
1395 if (j == 0)
1396 rv = savestrbuf(name, i);
1397 else {
1398 rv = salloc(i + j +1);
1399 for (i = j = 0; (c = name[i]) != '\0'; ++i) {
1400 rv[j++] = c;
1401 if (c == '\\')
1402 rv[j++] = c;
1404 rv[j] = '\0';
1406 NYD_LEAVE;
1407 return rv;
1410 FL bool_t
1411 var_folder_updated(char const *name, char **store)
1413 char rv = TRU1;
1414 char *folder, *unres = NULL, *res = NULL;
1415 NYD_ENTER;
1417 if ((folder = UNCONST(name)) == NULL)
1418 goto jleave;
1420 /* Expand the *folder*; skip %: prefix for simplicity of use */
1421 /* XXX This *only* works because we do NOT
1422 * XXX update environment variables via the "set" mechanism */
1423 if (folder[0] == '%' && folder[1] == ':')
1424 folder += 2;
1425 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
1426 goto jleave;
1428 switch (which_protocol(folder)) {
1429 case PROTO_POP3:
1430 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
1431 rv = FAL0;
1432 goto jleave;
1433 case PROTO_IMAP:
1434 /* Simply assign what we have, even including `%:' prefix */
1435 if (folder != name)
1436 goto jvcopy;
1437 goto jleave;
1438 default:
1439 /* Further expansion desired */
1440 break;
1443 /* All non-absolute paths are relative to our home directory */
1444 if (*folder != '/') {
1445 size_t l1 = strlen(homedir), l2 = strlen(folder);
1446 unres = ac_alloc(l1 + l2 + 1 +1);
1447 memcpy(unres, homedir, l1);
1448 unres[l1] = '/';
1449 memcpy(unres + l1 + 1, folder, l2);
1450 unres[l1 + 1 + l2] = '\0';
1451 folder = unres;
1454 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1455 * avoid that we loose track of our currently open folder in case we
1456 * chdir away, but still checks the leading path portion against
1457 * getfold() to be able to abbreviate to the +FOLDER syntax if
1458 * possible, we need to realpath(3) the folder, too */
1459 #ifdef HAVE_REALPATH
1460 res = ac_alloc(PATH_MAX +1);
1461 if (realpath(folder, res) == NULL)
1462 n_err(_("Can't canonicalize \"%s\"\n"), folder);
1463 else
1464 folder = res;
1465 #endif
1467 jvcopy:
1468 *store = sstrdup(folder);
1470 if (res != NULL)
1471 ac_free(res);
1472 if (unres != NULL)
1473 ac_free(unres);
1474 jleave:
1475 NYD_LEAVE;
1476 return rv;
1479 FL bool_t
1480 getfold(char *name, size_t size)
1482 char const *folder;
1483 NYD_ENTER;
1485 if ((folder = ok_vlook(folder)) != NULL)
1486 n_strlcpy(name, folder, size);
1487 NYD_LEAVE;
1488 return (folder != NULL);
1491 FL char const *
1492 getdeadletter(void) /* XXX should that be in auxlily.c? */
1494 char const *cp;
1495 NYD_ENTER;
1497 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1498 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1499 else if (*cp != '/') {
1500 size_t sz = strlen(cp) + 2 +1;
1501 char *buf = ac_alloc(sz);
1503 snprintf(buf, sz, "~/%s", cp);
1504 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1505 ac_free(buf);
1508 if (cp == NULL)
1509 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1510 NYD_LEAVE;
1511 return cp;
1514 FL enum okay
1515 get_body(struct message *mp)
1517 enum okay rv;
1518 NYD_ENTER;
1519 UNUSED(mp);
1521 switch (mb.mb_type) {
1522 case MB_FILE:
1523 case MB_MAILDIR:
1524 rv = OKAY;
1525 break;
1526 #ifdef HAVE_POP3
1527 case MB_POP3:
1528 rv = pop3_body(mp);
1529 break;
1530 #endif
1531 #ifdef HAVE_IMAP
1532 case MB_IMAP:
1533 case MB_CACHE:
1534 rv = imap_body(mp);
1535 break;
1536 #endif
1537 case MB_VOID:
1538 default:
1539 rv = STOP;
1540 break;
1542 NYD_LEAVE;
1543 return rv;
1546 FL bool_t
1547 file_lock(int fd, enum file_lock_type flt, off_t off, off_t len,
1548 size_t pollmsecs)
1550 size_t tries;
1551 bool_t rv;
1552 NYD_ENTER;
1554 for (tries = 0; tries <= FILE_LOCK_TRIES; ++tries)
1555 if ((rv = _file_lock(fd, flt, off, len)) || pollmsecs == 0)
1556 break;
1557 else
1558 sleep(1); /* TODO pollmsecs -> use finer grain */
1559 NYD_LEAVE;
1560 return rv;
1563 FL FILE *
1564 dot_lock(char const *fname, int fd, enum file_lock_type flt,
1565 off_t off, off_t len, size_t pollmsecs)
1567 #undef _DOMSG
1568 #ifdef HAVE_DOTLOCK
1569 # define _DOMSG() n_err(_("Creating dotlock for \"%s\" "), fname)
1570 #else
1571 # define _DOMSG() n_err(_("Trying to lock file \"%s\" "), fname)
1572 #endif
1574 #ifdef HAVE_DOTLOCK
1575 int cpipe[2];
1576 struct dotlock_info di;
1577 enum dotlock_state dls;
1578 char const *emsg = NULL;
1579 #endif
1580 int UNINIT(serrno, 0);
1581 union {size_t tries; int (*ptf)(void); char const *sh; ssize_t r;} u;
1582 bool_t flocked, didmsg = FAL0;
1583 FILE *rv = NULL;
1584 NYD_ENTER;
1586 if (options & OPT_D_VV) {
1587 _DOMSG();
1588 didmsg = TRUM1;
1591 flocked = FAL0;
1592 for (u.tries = 0; !_file_lock(fd, flt, off, len);)
1593 switch ((serrno = errno)) {
1594 case EACCES:
1595 case EAGAIN:
1596 case ENOLCK:
1597 if (pollmsecs > 0 && ++u.tries < FILE_LOCK_TRIES) {
1598 if (!didmsg)
1599 _DOMSG();
1600 n_err(".");
1601 didmsg = TRUM1;
1602 sleep(1); /* TODO pollmsecs -> use finer grain */
1603 continue;
1605 /* FALLTHRU */
1606 default:
1607 goto jleave;
1609 flocked = TRU1;
1611 #ifndef HAVE_DOTLOCK
1612 jleave:
1613 if (didmsg == TRUM1)
1614 n_err("\n");
1615 if (flocked)
1616 rv = (FILE*)-1;
1617 else
1618 errno = serrno;
1619 NYD_LEAVE;
1620 return rv;
1622 #else
1623 /* Create control-pipe for our dot file locker process, which will remove
1624 * the lock and terminate once the pipe is closed, for whatever reason */
1625 if (pipe_cloexec(cpipe) == -1) {
1626 serrno = errno;
1627 emsg = N_(" Can't create file lock control pipe\n");
1628 goto jemsg;
1631 /* And the locker process itself; it'll be a (rather cheap) thread only
1632 * unless the lock has to be placed in the system spool and we have our
1633 * privilege-separated dotlock program available, in which case that will be
1634 * executed and do "it" with changed group-id */
1635 di.di_file_name = fname;
1636 di.di_pollmsecs = pollmsecs;
1637 /* Initialize some more stuff; query the two strings in the parent in order
1638 * to cache the result of the former and anyway minimalize child page-ins.
1639 * Especially uname(3) may hang for multiple seconds when it is called the
1640 * first time! */
1641 di.di_hostname = nodename(FAL0);
1642 di.di_randstr = getrandstring(16);
1643 _dotlock_flt = flt;
1644 _dotlock_fd = fd;
1645 _dotlock_dip = &di;
1647 u.ptf = &_dotlock_main;
1648 rv = Popen((char*)-1, "W", u.sh, NULL, cpipe[1]);
1649 serrno = errno;
1651 close(cpipe[1]);
1652 if (rv == NULL) {
1653 close(cpipe[0]);
1654 emsg = N_(" Can't create file lock process\n");
1655 goto jemsg;
1658 /* Let's check wether we were able to create the dotlock file */
1659 for (;;) {
1660 u.r = read(cpipe[0], &dls, sizeof dls);
1661 if (UICMP(z, u.r, !=, sizeof dls)) {
1662 serrno = (u.r != -1) ? EAGAIN : errno;
1663 dls = DLS_DUNNO | DLS_ABANDON;
1664 } else
1665 serrno = 0;
1667 if (dls == DLS_NONE || (dls & DLS_ABANDON))
1668 close(cpipe[0]);
1670 switch (dls & ~DLS_ABANDON) {
1671 case DLS_NONE:
1672 goto jleave;
1673 case DLS_CANT_CHDIR:
1674 if (options & OPT_D_V)
1675 emsg = N_(" Can't change to directory! Please check permissions\n");
1676 serrno = EACCES;
1677 break;
1678 case DLS_NAMETOOLONG:
1679 emsg = N_("Resulting dotlock filename would be too long\n");
1680 serrno = EACCES;
1681 break;
1682 case DLS_ROFS:
1683 assert(dls & DLS_ABANDON);
1684 if (options & OPT_D_V)
1685 emsg = N_(" Read-only filesystem, not creating lock file\n");
1686 serrno = EROFS;
1687 break;
1688 case DLS_NOPERM:
1689 if (options & OPT_D_V)
1690 emsg = N_(" Can't create a lock file! Please check permissions\n"
1691 " (Maybe setting *dotlock-ignore-error* variable helps.)\n");
1692 serrno = EACCES;
1693 break;
1694 case DLS_NOEXEC:
1695 if (options & OPT_D_V)
1696 emsg = N_(" Can't find privilege-separated file lock program\n");
1697 serrno = ENOENT;
1698 break;
1699 case DLS_PRIVFAILED:
1700 emsg = N_(" Privilege-separated file lock program can't change "
1701 "privileges\n");
1702 serrno = EPERM;
1703 break;
1704 case DLS_EXIST:
1705 emsg = N_(" It seems there is a stale dotlock file?\n"
1706 " Please remove the lock file manually, then retry\n");
1707 serrno = EEXIST;
1708 break;
1709 case DLS_FISHY:
1710 emsg = N_(" Fishy! Is someone trying to \"steal\" foreign files?\n"
1711 " Please check the mailbox file etc. manually, then retry\n");
1712 serrno = EAGAIN; /* ? Hack to ignore *dotlock-ignore-error* xxx */
1713 break;
1714 default:
1715 case DLS_DUNNO:
1716 emsg = N_(" Unspecified dotlock file control process error.\n"
1717 " Like broken I/O pipe; this one is unlikely to happen\n");
1718 if (serrno != EAGAIN)
1719 serrno = EINVAL;
1720 break;
1721 case DLS_PING:
1722 if (!didmsg)
1723 _DOMSG();
1724 n_err(".");
1725 didmsg = TRUM1;
1726 continue;
1729 if (emsg != NULL) {
1730 if (!didmsg) {
1731 _DOMSG();
1732 didmsg = TRUM1;
1734 if (didmsg == TRUM1)
1735 n_err("\n");
1736 didmsg = TRU1;
1737 n_err(V_(emsg));
1738 emsg = NULL;
1741 if (dls & DLS_ABANDON) {
1742 Pclose(rv, FAL0);
1743 rv = NULL;
1744 break;
1748 jleave:
1749 if (didmsg == TRUM1)
1750 n_err("\n");
1751 if (rv == NULL) {
1752 if (flocked && (serrno == EROFS ||
1753 (serrno != EAGAIN && serrno != EEXIST &&
1754 ok_blook(dotlock_ignore_error))))
1755 rv = (FILE*)-1;
1756 else
1757 errno = serrno;
1759 NYD_LEAVE;
1760 return rv;
1761 jemsg:
1762 if (!didmsg)
1763 _DOMSG();
1764 n_err("\n");
1765 didmsg = TRU1;
1766 n_err(V_(emsg));
1767 goto jleave;
1768 #endif /* HAVE_DOTLOCK */
1769 #undef _DOMSG
1772 #ifdef HAVE_SOCKETS
1773 FL int
1774 sclose(struct sock *sp)
1776 int i;
1777 NYD_ENTER;
1779 i = sp->s_fd;
1780 sp->s_fd = -1;
1781 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
1782 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1783 * TODO but unfortunately it isn't yet */
1784 if (i <= 0)
1785 i = 0;
1786 else {
1787 if (sp->s_onclose != NULL)
1788 (*sp->s_onclose)();
1789 if (sp->s_wbuf != NULL)
1790 free(sp->s_wbuf);
1791 # ifdef HAVE_OPENSSL
1792 if (sp->s_use_ssl) {
1793 void *s_ssl = sp->s_ssl;
1795 sp->s_ssl = NULL;
1796 sp->s_use_ssl = 0;
1797 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling;signals! */
1799 SSL_free(s_ssl);
1801 # endif
1802 i = close(i);
1804 NYD_LEAVE;
1805 return i;
1808 FL enum okay
1809 swrite(struct sock *sp, char const *data)
1811 enum okay rv;
1812 NYD2_ENTER;
1814 rv = swrite1(sp, data, strlen(data), 0);
1815 NYD2_LEAVE;
1816 return rv;
1819 FL enum okay
1820 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1822 enum okay rv = STOP;
1823 int x;
1824 NYD2_ENTER;
1826 if (use_buffer > 0) {
1827 int di;
1829 if (sp->s_wbuf == NULL) {
1830 sp->s_wbufsize = 4096;
1831 sp->s_wbuf = smalloc(sp->s_wbufsize);
1832 sp->s_wbufpos = 0;
1834 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1835 di = sp->s_wbufsize - sp->s_wbufpos;
1836 sz -= di;
1837 if (sp->s_wbufpos > 0) {
1838 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
1839 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1840 } else
1841 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1842 if (rv != OKAY)
1843 goto jleave;
1844 data += di;
1845 sp->s_wbufpos = 0;
1847 if (sz == sp->s_wbufsize) {
1848 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1849 if (rv != OKAY)
1850 goto jleave;
1851 } else if (sz) {
1852 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
1853 sp->s_wbufpos += sz;
1855 rv = OKAY;
1856 goto jleave;
1857 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1858 x = sp->s_wbufpos;
1859 sp->s_wbufpos = 0;
1860 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1861 goto jleave;
1863 if (sz == 0) {
1864 rv = OKAY;
1865 goto jleave;
1868 # ifdef HAVE_OPENSSL
1869 if (sp->s_use_ssl) {
1870 jssl_retry:
1871 x = SSL_write(sp->s_ssl, data, sz);
1872 if (x < 0) {
1873 switch (SSL_get_error(sp->s_ssl, x)) {
1874 case SSL_ERROR_WANT_READ:
1875 case SSL_ERROR_WANT_WRITE:
1876 goto jssl_retry;
1879 } else
1880 # endif
1882 x = xwrite(sp->s_fd, data, sz);
1884 if (x != sz) {
1885 char o[512];
1886 snprintf(o, sizeof o, "%s write error",
1887 (sp->s_desc ? sp->s_desc : "socket"));
1888 # ifdef HAVE_OPENSSL
1889 if (sp->s_use_ssl)
1890 ssl_gen_err("%s", o);
1891 else
1892 # endif
1893 n_perr(o, 0);
1894 if (x < 0)
1895 sclose(sp);
1896 rv = STOP;
1897 goto jleave;
1899 rv = OKAY;
1900 jleave:
1901 NYD2_LEAVE;
1902 return rv;
1905 static sigjmp_buf __sopen_actjmp; /* TODO someday, we won't need it no more */
1906 static int __sopen_sig; /* TODO someday, we won't need it no more */
1907 static void
1908 __sopen_onsig(int sig) /* TODO someday, we won't need it no more */
1910 NYD_X; /* Signal handler */
1911 if (__sopen_sig < 0) {
1912 /* Of course the following doesn't belong into a signal handler XXX */
1913 int i, j;
1915 if (__sopen_sig == -1) {
1916 fprintf(stderr,
1917 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
1918 "unusable.\n"
1919 " Wait until it's done, or do terminate the program\n"));
1920 __sopen_sig = -2;
1921 } else if ((i = j = ABS(__sopen_sig)) + 15 < scrnwidth) {
1922 putc('\r', stderr);
1923 for (; j > 0; --j)
1924 putc(' ', stderr);
1925 fputs("___( o)", stderr);
1926 putc((i & 1) ? '=' : '>', stderr);
1927 putc(' ', stderr);
1928 putc(' ', stderr);
1929 ++i;
1930 __sopen_sig = -i;
1932 } else {
1933 __sopen_sig = sig;
1934 siglongjmp(__sopen_actjmp, 1);
1938 FL bool_t
1939 sopen(struct sock *sp, struct url *urlp) /* TODO sighandling; refactor */
1941 # ifdef HAVE_SO_SNDTIMEO
1942 struct timeval tv;
1943 # endif
1944 # ifdef HAVE_SO_LINGER
1945 struct linger li;
1946 # endif
1947 # ifdef HAVE_GETADDRINFO
1948 char hbuf[NI_MAXHOST];
1949 struct addrinfo hints, *res0 = NULL, *res;
1950 # else
1951 struct sockaddr_in servaddr;
1952 struct in_addr **pptr;
1953 struct hostent *hp;
1954 struct servent *ep;
1955 # endif
1956 sighandler_type volatile ohup, oint;
1957 char const * volatile serv;
1958 int volatile sofd = -1, errval;
1959 NYD_ENTER;
1961 UNINIT(errval, 0);
1963 /* Connect timeouts after 30 seconds XXX configurable */
1964 # ifdef HAVE_SO_SNDTIMEO
1965 tv.tv_sec = 30;
1966 tv.tv_usec = 0;
1967 # endif
1968 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
1970 if (options & OPT_VERB)
1971 n_err(_("Resolving host \"%s:%s\" ... "),
1972 urlp->url_host.s, serv);
1974 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
1975 * healing until v15.0 and i want to end up with that functionality */
1976 hold_sigs();
1977 __sopen_sig = 0;
1978 ohup = safe_signal(SIGHUP, &__sopen_onsig);
1979 oint = safe_signal(SIGINT, &__sopen_onsig);
1980 if (sigsetjmp(__sopen_actjmp, 0)) {
1981 jpseudo_jump:
1982 n_err("%s\n",
1983 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
1984 if (sofd >= 0) {
1985 close(sofd);
1986 sofd = -1;
1988 goto jjumped;
1990 rele_sigs();
1992 # ifdef HAVE_GETADDRINFO
1993 for (;;) {
1994 memset(&hints, 0, sizeof hints);
1995 hints.ai_socktype = SOCK_STREAM;
1996 __sopen_sig = -1;
1997 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
1998 if (__sopen_sig != -1) {
1999 __sopen_sig = SIGINT;
2000 goto jpseudo_jump;
2002 __sopen_sig = 0;
2003 if (errval == 0)
2004 break;
2006 if (options & OPT_VERB)
2007 n_err(_("failed\n"));
2008 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
2009 urlp->url_host.s, serv, gai_strerror(errval));
2011 /* Error seems to depend on how "smart" the /etc/service code is: is it
2012 * "able" to state wether the service as such is NONAME or does it only
2013 * check for the given ai_socktype.. */
2014 if (errval == EAI_NONAME || errval == EAI_SERVICE) {
2015 if (serv == urlp->url_proto &&
2016 (serv = url_servbyname(urlp, NULL)) != NULL) {
2017 n_err(_(" Trying standard protocol port \"%s\"\n"
2018 " If that succeeds consider including the port in the URL!\n"),
2019 serv);
2020 continue;
2022 if (serv != urlp->url_port)
2023 n_err(_(" Including a port number in the URL may "
2024 "circumvent this problem\n"));
2026 assert(sofd == -1);
2027 errval = 0;
2028 goto jjumped;
2030 if (options & OPT_VERB)
2031 n_err(_("done\n"));
2033 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
2034 if (options & OPT_VERB) {
2035 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
2036 NULL, 0, NI_NUMERICHOST))
2037 memcpy(hbuf, "unknown host", sizeof("unknown host"));
2038 n_err(_("%sConnecting to \"%s:%s\" ..."),
2039 (res == res0 ? "" : "\n"), hbuf, serv);
2042 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2043 if (sofd >= 0) {
2044 # ifdef HAVE_SO_SNDTIMEO
2045 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2046 # endif
2047 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
2048 errval = errno;
2049 close(sofd);
2050 sofd = -1;
2055 jjumped:
2056 if (res0 != NULL) {
2057 freeaddrinfo(res0);
2058 res0 = NULL;
2061 # else /* HAVE_GETADDRINFO */
2062 if (serv == urlp->url_proto) {
2063 if ((ep = getservbyname(UNCONST(serv), "tcp")) != NULL)
2064 urlp->url_portno = ntohs(ep->s_port);
2065 else {
2066 if (options & OPT_VERB)
2067 n_err(_("failed\n"));
2068 if ((serv = url_servbyname(urlp, &urlp->url_portno)) != NULL)
2069 n_err(_(" Unknown service: \"%s\"\n"
2070 " Trying standard protocol port \"%s\"\n"
2071 " If that succeeds consider including the port in the URL!\n"),
2072 urlp->url_proto, serv);
2073 else {
2074 n_err(_(" Unknown service: \"%s\"\n"
2075 " Including a port in the URL may circumvent this problem\n"),
2076 urlp->url_proto);
2077 assert(sofd == -1 && errval == 0);
2078 goto jjumped;
2083 __sopen_sig = -1;
2084 hp = gethostbyname(urlp->url_host.s);
2085 if (__sopen_sig != -1) {
2086 __sopen_sig = SIGINT;
2087 goto jpseudo_jump;
2089 __sopen_sig = 0;
2091 if (hp == NULL) {
2092 char const *emsg;
2094 if (options & OPT_VERB)
2095 n_err(_("failed\n"));
2096 switch (h_errno) {
2097 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
2098 default:
2099 case TRY_AGAIN: emsg = N_("(maybe) try again later"); break;
2100 case NO_RECOVERY: emsg = N_("non-recoverable server error"); break;
2101 case NO_DATA: emsg = N_("valid name without IP address"); break;
2103 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
2104 urlp->url_host.s, serv, V_(emsg));
2105 goto jjumped;
2106 } else if (options & OPT_VERB)
2107 n_err(_("done\n"));
2109 pptr = (struct in_addr**)hp->h_addr_list;
2110 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
2111 n_perr(_("could not create socket"), 0);
2112 assert(sofd == -1 && errval == 0);
2113 goto jjumped;
2116 memset(&servaddr, 0, sizeof servaddr);
2117 servaddr.sin_family = AF_INET;
2118 servaddr.sin_port = htons(urlp->url_portno);
2119 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
2120 if (options & OPT_VERB)
2121 n_err(_("%sConnecting to \"%s:%d\" ... "),
2122 "", inet_ntoa(**pptr), (int)urlp->url_portno);
2123 # ifdef HAVE_SO_SNDTIMEO
2124 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2125 # endif
2126 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
2127 errval = errno;
2128 close(sofd);
2129 sofd = -1;
2131 jjumped:
2132 # endif /* !HAVE_GETADDRINFO */
2134 hold_sigs();
2135 safe_signal(SIGINT, oint);
2136 safe_signal(SIGHUP, ohup);
2137 rele_sigs();
2139 if (sofd < 0) {
2140 if (errval != 0) {
2141 errno = errval;
2142 n_perr(_("Could not connect"), 0);
2144 goto jleave;
2147 if (options & OPT_VERB)
2148 n_err(_("connected.\n"));
2150 /* And the regular timeouts XXX configurable */
2151 # ifdef HAVE_SO_SNDTIMEO
2152 tv.tv_sec = 42;
2153 tv.tv_usec = 0;
2154 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2155 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
2156 # endif
2157 # ifdef HAVE_SO_LINGER
2158 li.l_onoff = 1;
2159 li.l_linger = 42;
2160 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
2161 # endif
2163 memset(sp, 0, sizeof *sp);
2164 sp->s_fd = sofd;
2166 /* SSL/TLS upgrade? */
2167 # ifdef HAVE_SSL
2168 if (urlp->url_needs_tls) {
2169 hold_sigs();
2170 ohup = safe_signal(SIGHUP, &__sopen_onsig);
2171 oint = safe_signal(SIGINT, &__sopen_onsig);
2172 if (sigsetjmp(__sopen_actjmp, 0)) {
2173 n_err(_("%s during SSL/TLS handshake\n"),
2174 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
2175 goto jsclose;
2177 rele_sigs();
2179 if (ssl_open(urlp, sp) != OKAY) {
2180 jsclose:
2181 sclose(sp);
2182 sofd = -1;
2185 hold_sigs();
2186 safe_signal(SIGINT, oint);
2187 safe_signal(SIGHUP, ohup);
2188 rele_sigs();
2190 # endif /* HAVE_SSL */
2192 jleave:
2193 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
2194 if (__sopen_sig != 0) {
2195 sigset_t cset;
2196 sigemptyset(&cset);
2197 sigaddset(&cset, __sopen_sig);
2198 sigprocmask(SIG_UNBLOCK, &cset, NULL);
2199 n_raise(__sopen_sig);
2201 NYD_LEAVE;
2202 return (sofd >= 0);
2205 FL int
2206 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
2207 SMALLOC_DEBUG_ARGS)
2209 int rv;
2210 size_t lsize;
2211 char *lp_base, *lp;
2212 NYD2_ENTER;
2214 lsize = *linesize;
2215 lp_base = *line;
2216 lp = lp_base;
2218 if (sp->s_rsz < 0) {
2219 sclose(sp);
2220 rv = sp->s_rsz;
2221 goto jleave;
2224 do {
2225 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
2226 size_t diff = PTR2SIZE(lp - lp_base);
2227 *linesize = (lsize += 256); /* XXX magic */
2228 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
2229 lp = lp_base + diff;
2232 if (sp->s_rbufptr == NULL ||
2233 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
2234 # ifdef HAVE_OPENSSL
2235 if (sp->s_use_ssl) {
2236 jssl_retry:
2237 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
2238 if (sp->s_rsz <= 0) {
2239 if (sp->s_rsz < 0) {
2240 char o[512];
2241 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
2242 case SSL_ERROR_WANT_READ:
2243 case SSL_ERROR_WANT_WRITE:
2244 goto jssl_retry;
2246 snprintf(o, sizeof o, "%s",
2247 (sp->s_desc ? sp->s_desc : "socket"));
2248 ssl_gen_err("%s", o);
2250 break;
2252 } else
2253 # endif
2255 jagain:
2256 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
2257 if (sp->s_rsz <= 0) {
2258 if (sp->s_rsz < 0) {
2259 char o[512];
2260 if (errno == EINTR)
2261 goto jagain;
2262 snprintf(o, sizeof o, "%s",
2263 (sp->s_desc ? sp->s_desc : "socket"));
2264 n_perr(o, 0);
2266 break;
2269 sp->s_rbufptr = sp->s_rbuf;
2271 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
2272 *lp = '\0';
2273 lsize = PTR2SIZE(lp - lp_base);
2275 if (linelen)
2276 *linelen = lsize;
2277 rv = (int)lsize;
2278 jleave:
2279 NYD2_LEAVE;
2280 return rv;
2282 #endif /* HAVE_SOCKETS */
2284 FL void
2285 load(char const *name)
2287 struct str n;
2288 void *cond;
2289 FILE *in, *oldin;
2290 NYD_ENTER;
2292 if (name == NULL || *name == '\0' || (in = Fopen(name, "r")) == NULL)
2293 goto jleave;
2295 oldin = _fio_input;
2296 _fio_input = in;
2297 pstate |= PS_IN_LOAD;
2298 /* commands() may sreset(), copy over file name */
2299 n.l = strlen(name);
2300 n.s = ac_alloc(n.l +1);
2301 memcpy(n.s, name, n.l +1);
2303 cond = condstack_release();
2304 if (!commands())
2305 n_err(_("Stopped loading \"%s\" due to errors "
2306 "(enable *debug* for trace)\n"), n.s);
2307 condstack_take(cond);
2309 ac_free(n.s);
2310 pstate &= ~PS_IN_LOAD;
2311 _fio_input = oldin;
2312 Fclose(in);
2313 jleave:
2314 NYD_LEAVE;
2317 FL int
2318 c_source(void *v)
2320 int rv;
2321 NYD_ENTER;
2323 rv = _source_file(*(char**)v, FAL0) ? 0 : 1;
2324 NYD_LEAVE;
2325 return rv;
2328 FL int
2329 c_source_if(void *v) /* XXX obsolete?, support file tests in `if' etc.! */
2331 int rv;
2332 NYD_ENTER;
2334 rv = _source_file(*(char**)v, TRU1) ? 0 : 1;
2335 rv = 0;
2336 NYD_LEAVE;
2337 return rv;
2340 FL int
2341 unstack(void)
2343 int rv = 1;
2344 NYD_ENTER;
2346 if (_fio_stack_size == 0) {
2347 n_err(_("`source' stack over-pop\n"));
2348 pstate &= ~PS_SOURCING;
2349 goto jleave;
2352 Fclose(_fio_input);
2354 --_fio_stack_size;
2355 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
2356 n_err(_("Unmatched \"if\"\n"));
2357 if (_fio_stack[_fio_stack_size].s_loading)
2358 pstate |= PS_LOADING;
2359 else
2360 pstate &= ~PS_LOADING;
2361 _fio_input = _fio_stack[_fio_stack_size].s_file;
2362 if (_fio_stack_size == 0) {
2363 if (pstate & PS_LOADING)
2364 pstate |= PS_SOURCING;
2365 else
2366 pstate &= ~PS_SOURCING;
2368 rv = 0;
2369 jleave:
2370 NYD_LEAVE;
2371 return rv;
2374 /* s-it-mode */