*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / fio.c
blobea64dc8bdca0fde9c4ff58d3c9237ec17ef7d5c9
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 const *ibox, *cp;
146 NYD_ENTER;
148 ibox = ok_vlook(inbox);
150 /* Heirloom compatibility: an IMAP *folder* becomes "%" */
151 if (!force && ibox == NULL && !strcmp(user, myname) &&
152 (cp = ok_vlook(folder)) != NULL && which_protocol(cp) == CPROTO_IMAP) {
153 OBSOLETE("no more expansion of *folder* in \"%\": please set *inbox*");
154 goto jcopy;
157 if (!force && ibox != NULL) {
158 /* Folder extra introduced to avoid % recursion loops */
159 if ((cp = fexpand(ibox, FEXP_FOLDER | FEXP_NSHELL)) != NULL)
160 goto jcopy;
161 n_err(_("Failed to expand *ibox*, using $MAIL or default: %s\n"), ibox);
162 ibox = NULL;
165 if (force || (((cp = ibox) == NULL || *cp == '\0') &&
166 (cp = ok_vlook(MAIL)) == NULL))
167 snprintf(buf, bufsize, "%s/%s", MAILSPOOL, user);
168 else
169 jcopy:
170 n_strlcpy(buf, cp, bufsize);
171 NYD_LEAVE;
174 static char *
175 _shvar_exp(char const *name)
177 struct shvar_stack top;
178 char *rv;
179 NYD2_ENTER;
181 memset(&top, 0, sizeof top);
182 top.shs_value = name;
183 rv = __shvar_exp(&top);
185 NYD2_LEAVE;
186 return rv;
189 static char *
190 __shvar_exp(struct shvar_stack *shsp)
192 struct shvar_stack next, *np, *tmp;
193 char const *vp;
194 char lc, c, *cp, *rv;
195 size_t i;
196 NYD2_ENTER;
198 if (*(vp = shsp->shs_value) != '$') {
199 union {bool_t hadbs; char c;} u = {FAL0};
201 shsp->shs_dat = vp;
202 for (lc = '\0', i = 0; ((c = *vp) != '\0'); ++i, ++vp) {
203 if (c == '$' && lc != '\\')
204 break;
205 lc = (lc == '\\') ? (u.hadbs = TRU1, '\0') : c;
207 shsp->shs_len = i;
209 if (u.hadbs) {
210 shsp->shs_dat = cp = savestrbuf(shsp->shs_dat, i);
212 for (lc = '\0', rv = cp; (u.c = *cp++) != '\0';) {
213 if (u.c != '\\' || lc == '\\')
214 *rv++ = u.c;
215 lc = (lc == '\\') ? '\0' : u.c;
217 *rv = '\0';
219 shsp->shs_len = PTR2SIZE(rv - shsp->shs_dat);
221 } else {
222 if ((lc = (*++vp == '{')))
223 ++vp;
225 shsp->shs_dat = vp;
226 for (i = 0; (c = *vp) != '\0'; ++i, ++vp)
227 if (!alnumchar(c) && c != '_')
228 break;
230 if (lc) {
231 if (c != '}') {
232 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
233 shsp->shs_value);
234 shsp->shs_len = strlen(shsp->shs_value);
235 shsp->shs_dat = shsp->shs_value;
236 goto junroll;
238 c = *++vp;
241 shsp->shs_len = i;
242 if ((cp = vok_vlook(savestrbuf(shsp->shs_dat, i))) != NULL)
243 shsp->shs_len = strlen(shsp->shs_dat = cp);
245 if (c != '\0')
246 goto jrecurse;
248 /* That level made the great and completed encoding. Build result */
249 junroll:
250 for (i = 0, np = shsp, shsp = NULL; np != NULL;) {
251 i += np->shs_len;
252 tmp = np->shs_next;
253 np->shs_next = shsp;
254 shsp = np;
255 np = tmp;
258 cp = rv = salloc(i +1);
259 while (shsp != NULL) {
260 np = shsp;
261 shsp = shsp->shs_next;
262 memcpy(cp, np->shs_dat, np->shs_len);
263 cp += np->shs_len;
265 *cp = '\0';
267 jleave:
268 NYD2_LEAVE;
269 return rv;
270 jrecurse:
271 memset(&next, 0, sizeof next);
272 next.shs_next = shsp;
273 next.shs_value = vp;
274 rv = __shvar_exp(&next);
275 goto jleave;
278 static char *
279 _globname(char const *name, enum fexp_mode fexpm)
281 #ifdef HAVE_WORDEXP
282 wordexp_t we;
283 char *cp = NULL;
284 sigset_t nset;
285 int i;
286 NYD_ENTER;
288 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
289 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
290 memset(&we, 0, sizeof we);
292 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
293 * and wait, which will fail if our SIGCHLD handler is active */
294 sigemptyset(&nset);
295 sigaddset(&nset, SIGCHLD);
296 sigprocmask(SIG_BLOCK, &nset, NULL);
297 # ifndef WRDE_NOCMD
298 # define WRDE_NOCMD 0
299 # endif
300 i = wordexp(name, &we, WRDE_NOCMD);
301 sigprocmask(SIG_UNBLOCK, &nset, NULL);
303 switch (i) {
304 case 0:
305 break;
306 #ifdef WRDE_CMDSUB
307 case WRDE_CMDSUB:
308 if (!(fexpm & FEXP_SILENT))
309 n_err(_("\"%s\": Command substitution not allowed\n"), name);
310 goto jleave;
311 #endif
312 case WRDE_NOSPACE:
313 if (!(fexpm & FEXP_SILENT))
314 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
315 goto jleave;
316 case WRDE_BADCHAR:
317 case WRDE_SYNTAX:
318 default:
319 if (!(fexpm & FEXP_SILENT))
320 n_err(_("Syntax error in \"%s\"\n"), name);
321 goto jleave;
324 switch (we.we_wordc) {
325 case 1:
326 cp = savestr(we.we_wordv[0]);
327 break;
328 case 0:
329 if (!(fexpm & FEXP_SILENT))
330 n_err(_("\"%s\": No match\n"), name);
331 break;
332 default:
333 if (fexpm & FEXP_MULTIOK) {
334 size_t j, l;
336 for (l = 0, j = 0; j < we.we_wordc; ++j)
337 l += strlen(we.we_wordv[j]) + 1;
338 ++l;
339 cp = salloc(l);
340 for (l = 0, j = 0; j < we.we_wordc; ++j) {
341 size_t x = strlen(we.we_wordv[j]);
342 memcpy(cp + l, we.we_wordv[j], x);
343 l += x;
344 cp[l++] = ' ';
346 cp[l] = '\0';
347 } else if (!(fexpm & FEXP_SILENT))
348 n_err(_("\"%s\": Ambiguous\n"), name);
349 break;
351 jleave:
352 wordfree(&we);
353 NYD_LEAVE;
354 return cp;
356 #else /* HAVE_WORDEXP */
357 struct stat sbuf;
358 char xname[PATH_MAX +1], cmdbuf[PATH_MAX +1], /* also used for files */
359 *shellp, *cp = NULL;
360 int pivec[2], pid, l, waits;
361 NYD_ENTER;
363 if (pipe(pivec) < 0) {
364 n_perr(_("pipe"), 0);
365 goto jleave;
367 snprintf(cmdbuf, sizeof cmdbuf, "echo %s", name);
368 if ((shellp = ok_vlook(SHELL)) == NULL)
369 shellp = UNCONST(XSHELL);
370 pid = start_command(shellp, NULL, -1, pivec[1], "-c", cmdbuf, NULL, NULL);
371 if (pid < 0) {
372 close(pivec[0]);
373 close(pivec[1]);
374 goto jleave;
376 close(pivec[1]);
378 jagain:
379 l = read(pivec[0], xname, sizeof xname);
380 if (l < 0) {
381 if (errno == EINTR)
382 goto jagain;
383 n_perr(_("read"), 0);
384 close(pivec[0]);
385 goto jleave;
387 close(pivec[0]);
388 if (!wait_child(pid, &waits) && WTERMSIG(waits) != SIGPIPE) {
389 if (!(fexpm & FEXP_SILENT))
390 n_err(_("\"%s\": Expansion failed\n"), name);
391 goto jleave;
393 if (l == 0) {
394 if (!(fexpm & FEXP_SILENT))
395 n_err(_("\"%s\": No match\n"), name);
396 goto jleave;
398 if (l == sizeof xname) {
399 if (!(fexpm & FEXP_SILENT))
400 n_err(_("\"%s\": Expansion buffer overflow\n"), name);
401 goto jleave;
403 xname[l] = 0;
404 for (cp = xname + l - 1; *cp == '\n' && cp > xname; --cp)
406 cp[1] = '\0';
407 if (!(fexpm & FEXP_MULTIOK) && strchr(xname, ' ') != NULL &&
408 stat(xname, &sbuf) < 0) {
409 if (!(fexpm & FEXP_SILENT))
410 n_err(_("\"%s\": Ambiguous\n"), name);
411 cp = NULL;
412 goto jleave;
414 cp = savestr(xname);
415 jleave:
416 NYD_LEAVE;
417 return cp;
418 #endif /* !HAVE_WORDEXP */
421 static size_t
422 _length_of_line(char const *line, size_t linesize)
424 size_t i;
425 NYD2_ENTER;
427 /* Last character is always '\0' and was added by fgets() */
428 for (--linesize, i = 0; i < linesize; i++)
429 if (line[i] == '\n')
430 break;
431 i = (i < linesize) ? i + 1 : linesize;
432 NYD2_LEAVE;
433 return i;
436 static char *
437 _fgetline_byone(char **line, size_t *linesize, size_t *llen, FILE *fp,
438 int appendnl, size_t n SMALLOC_DEBUG_ARGS)
440 char *rv;
441 int c;
442 NYD2_ENTER;
444 assert(*linesize == 0 || *line != NULL);
445 for (rv = *line;;) {
446 if (*linesize <= LINESIZE || n >= *linesize - 128) {
447 *linesize += ((rv == NULL) ? LINESIZE + n + 1 : 256);
448 *line = rv = (srealloc)(rv, *linesize SMALLOC_DEBUG_ARGSCALL);
450 c = getc(fp);
451 if (c != EOF) {
452 rv[n++] = c;
453 rv[n] = '\0';
454 if (c == '\n')
455 break;
456 } else {
457 if (n > 0) {
458 if (appendnl) {
459 rv[n++] = '\n';
460 rv[n] = '\0';
462 break;
463 } else {
464 rv = NULL;
465 goto jleave;
469 if (llen)
470 *llen = n;
471 jleave:
472 NYD2_LEAVE;
473 return rv;
476 static void
477 makemessage(void)
479 NYD_ENTER;
480 if (msgCount == 0)
481 message_append(NULL);
482 setdot(message);
483 message[msgCount].m_size = 0;
484 message[msgCount].m_lines = 0;
485 NYD_LEAVE;
488 static enum okay
489 get_header(struct message *mp)
491 enum okay rv;
492 NYD_ENTER;
493 UNUSED(mp);
495 switch (mb.mb_type) {
496 case MB_FILE:
497 case MB_MAILDIR:
498 rv = OKAY;
499 break;
500 #ifdef HAVE_POP3
501 case MB_POP3:
502 rv = pop3_header(mp);
503 break;
504 #endif
505 #ifdef HAVE_IMAP
506 case MB_IMAP:
507 case MB_CACHE:
508 rv = imap_header(mp);
509 break;
510 #endif
511 case MB_VOID:
512 default:
513 rv = STOP;
514 break;
516 NYD_LEAVE;
517 return rv;
520 static bool_t
521 _file_lock(int fd, enum file_lock_type flt, off_t off, off_t len)
523 struct flock flp;
524 bool_t rv;
525 NYD2_ENTER;
527 memset(&flp, 0, sizeof flp);
529 switch (flt) {
530 default:
531 case FLT_READ: rv = F_RDLCK; break;
532 case FLT_WRITE: rv = F_WRLCK; break;
534 flp.l_type = rv;
535 flp.l_start = off;
536 flp.l_whence = SEEK_SET;
537 flp.l_len = len;
539 rv = (fcntl(fd, F_SETLK, &flp) != -1);
540 NYD2_LEAVE;
541 return rv;
544 #ifdef HAVE_DOTLOCK
545 static int
546 _dotlock_main(void)
548 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
549 * problems (SunOS), since the pathconf(3) value comes too late! */
550 char name[PATH_MAX +1];
551 struct dotlock_info di;
552 struct stat stb, fdstb;
553 enum dotlock_state dls;
554 char const *cp;
555 int fd;
556 enum file_lock_type flt;
557 NYD_ENTER;
559 /* Ignore SIGPIPE, we'll see EPIPE and "fall through" */
560 safe_signal(SIGPIPE, SIG_IGN);
562 /* Get the arguments "passed to us" */
563 flt = _dotlock_flt;
564 fd = _dotlock_fd;
565 UNUSED(fd);
566 di = *_dotlock_dip;
568 /* chdir(2)? */
569 jislink:
570 dls = DLS_CANT_CHDIR | DLS_ABANDON;
572 if ((cp = strrchr(di.di_file_name, '/')) != NULL) {
573 char const *fname = cp + 1;
575 while (PTRCMP(cp - 1, >, di.di_file_name) && cp[-1] == '/')
576 --cp;
577 cp = savestrbuf(di.di_file_name, PTR2SIZE(cp - di.di_file_name));
578 if (chdir(cp))
579 goto jmsg;
581 di.di_file_name = fname;
584 /* So we're here, but then again the file can be a symbolic link!
585 * This is however only true if we do not have realpath(3) available since
586 * that'll have resolved the path already otherwise; nonetheless, let
587 * readlink(2) be a precondition for dotlocking and keep this code */
588 if (lstat(cp = di.di_file_name, &stb) == -1)
589 goto jmsg;
590 if (S_ISLNK(stb.st_mode)) {
591 /* Use salloc() and hope we stay in builtin buffer.. */
592 char *x;
593 size_t i;
594 ssize_t sr;
596 for (x = NULL, i = PATH_MAX;; i += PATH_MAX) {
597 x = salloc(i +1);
598 sr = readlink(cp, x, i);
599 if (sr <= 0) {
600 dls = DLS_FISHY | DLS_ABANDON;
601 goto jmsg;
603 if (UICMP(z, sr, <, i)) {
604 x[sr] = '\0';
605 i = (size_t)sr;
606 break;
609 di.di_file_name = x;
610 goto jislink;
613 dls = DLS_FISHY | DLS_ABANDON;
615 /* Bail out if the file has changed its identity in the meanwhile */
616 if (fstat(fd, &fdstb) == -1 ||
617 fdstb.st_dev != stb.st_dev || fdstb.st_ino != stb.st_ino ||
618 fdstb.st_uid != stb.st_uid || fdstb.st_gid != stb.st_gid ||
619 fdstb.st_mode != stb.st_mode)
620 goto jmsg;
622 /* Be aware, even if the error is false! Note the shared code in dotlock.h
623 * *requires* that it is possible to create a filename at least one byte
624 * longer than di_lock_name! */
625 do/* while(0) breaker */{
626 # ifdef HAVE_PATHCONF
627 long pc;
628 # endif
629 int i = snprintf(name, sizeof name, "%s.lock", di.di_file_name);
631 /* fd is a file, not portable to use for _PC_NAME_MAX */
632 if(i < 0){
633 jenametool:
634 dls = DLS_NAMETOOLONG | DLS_ABANDON;
635 goto jmsg;
637 # ifdef HAVE_PATHCONF
638 errno = 0;
639 if((pc = pathconf(".", _PC_NAME_MAX)) == -1){
640 /* errno unchanged: no limit */
641 if(errno == 0)
642 break;
643 }else if(pc - 1 >= (long)i)
644 break;
645 else
646 goto jenametool;
647 # endif
648 if(UICMP(z, NAME_MAX - 1, <, (uiz_t)i))
649 goto jenametool;
650 }while(0);
652 di.di_lock_name = name;
654 /* We are in the directory of the mailbox for which we have to create
655 * a dotlock file for. We don't know wether we have realpath(3) available,
656 * and manually resolving the path is due especially given that S-nail
657 * supports the special "%:" syntax to warp any file into a "system
658 * mailbox"; there may also be multiple system mailbox directories...
659 * So what we do is that we fstat(2) the mailbox and check its UID and
660 * GID against that of our own process: if any of those mismatch we must
661 * either assume a directory we are not allowed to write in, or that we run
662 * via -u/$USER/%USER as someone else, in which case we favour our
663 * privilege-separated dotlock process */
664 assert(cp != NULL); /* Ugly: avoid a useless var and reuse that one */
665 if (access(".", W_OK)) {
666 /* This may however also indicate a read-only filesystem, which is not
667 * really an error from our point of view since the mailbox will degrade
668 * to a readonly one for which no dotlock is needed, then, and errors
669 * may arise only due to actions which require box modifications */
670 if (errno == EROFS) {
671 dls = DLS_ROFS | DLS_ABANDON;
672 goto jmsg;
674 cp = NULL;
676 if (cp == NULL || stb.st_uid != user_id || stb.st_gid != group_id) {
677 char itoabuf[64];
678 char const *args[13];
680 snprintf(itoabuf, sizeof itoabuf, "%" PRIuZ, di.di_pollmsecs);
681 args[ 0] = PRIVSEP;
682 args[ 1] = (flt == FLT_READ ? "rdotlock" : "wdotlock");
683 args[ 2] = "mailbox"; args[ 3] = di.di_file_name;
684 args[ 4] = "name"; args[ 5] = di.di_lock_name;
685 args[ 6] = "hostname"; args[ 7] = di.di_hostname;
686 args[ 8] = "randstr"; args[ 9] = di.di_randstr;
687 args[10] = "pollmsecs"; args[11] = itoabuf;
688 args[12] = NULL;
689 execv(LIBEXECDIR "/" UAGENT "-privsep", UNCONST(args));
691 dls = DLS_NOEXEC;
692 write(STDOUT_FILENO, &dls, sizeof dls);
693 /* But fall through and try it with normal privileges! */
696 /* So let's try and call it ourselfs! Note that we don't block signals just
697 * like our privsep child does, the user will anyway be able to remove his
698 * file again, and if we're in -u/$USER mode then we are allowed to access
699 * the user's box: shall we leave behind a stale dotlock then at least we
700 * start a friendly human conversation. Since we cannot handle SIGKILL and
701 * SIGSTOP malicious things could happen whatever we do */
702 safe_signal(SIGHUP, SIG_IGN);
703 safe_signal(SIGINT, SIG_IGN);
704 safe_signal(SIGQUIT, SIG_IGN);
705 safe_signal(SIGTERM, SIG_IGN);
707 NYD;
708 dls = _dotlock_create(&di);
709 NYD;
711 /* Finally: notify our parent about the actual lock state.. */
712 jmsg:
713 write(STDOUT_FILENO, &dls, sizeof dls);
714 close(STDOUT_FILENO);
716 /* ..then eventually wait until we shall remove the lock again, which will
717 * be notified via the read returning */
718 if (dls == DLS_NONE) {
719 read(STDIN_FILENO, &dls, sizeof dls);
721 unlink(name);
723 NYD_LEAVE;
724 return EXIT_OK;
726 #endif /* HAVE_DOTLOCK */
728 #ifdef HAVE_SOCKETS
729 static long
730 xwrite(int fd, char const *data, size_t sz)
732 long rv = -1, wo;
733 size_t wt = 0;
734 NYD_ENTER;
736 do {
737 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
738 if (errno == EINTR)
739 continue;
740 else
741 goto jleave;
743 wt += wo;
744 } while (wt < sz);
745 rv = (long)sz;
746 jleave:
747 NYD_LEAVE;
748 return rv;
750 #endif /* HAVE_SOCKETS */
752 static bool_t
753 _source_file(char const *file, bool_t silent_error)
755 char *cp;
756 FILE *fi = NULL;
757 NYD_ENTER;
759 if ((cp = fexpand(file, FEXP_LOCAL)) == NULL)
760 goto jleave;
761 if ((fi = Fopen(cp, "r")) == NULL) {
762 if (!silent_error || (options & OPT_D_V))
763 n_perr(cp, 0);
764 goto jleave;
767 if (temporary_localopts_store != NULL) {
768 n_err(_("Before v15 you cannot `source' from within macros, sorry\n"));
769 goto jeclose;
771 if (_fio_stack_size >= NELEM(_fio_stack)) {
772 n_err(_("Too many `source' recursions\n"));
773 jeclose:
774 Fclose(fi);
775 fi = NULL;
776 goto jleave;
779 _fio_stack[_fio_stack_size].s_file = _fio_input;
780 _fio_stack[_fio_stack_size].s_cond = condstack_release();
781 _fio_stack[_fio_stack_size].s_loading = !!(pstate & PS_LOADING);
782 ++_fio_stack_size;
783 pstate &= ~PS_LOADING;
784 pstate |= PS_SOURCING;
785 _fio_input = fi;
786 jleave:
787 NYD_LEAVE;
788 return (fi != NULL);
791 FL char *
792 (fgetline)(char **line, size_t *linesize, size_t *cnt, size_t *llen, FILE *fp,
793 int appendnl SMALLOC_DEBUG_ARGS)
795 size_t i_llen, sz;
796 char *rv;
797 NYD2_ENTER;
799 if (cnt == NULL) {
800 /* Without count, we can't determine where the chars returned by fgets()
801 * end if there's no newline. We have to read one character by one */
802 rv = _fgetline_byone(line, linesize, llen, fp, appendnl, 0
803 SMALLOC_DEBUG_ARGSCALL);
804 goto jleave;
807 if ((rv = *line) == NULL || *linesize < LINESIZE)
808 *line = rv = (srealloc)(rv, *linesize = LINESIZE SMALLOC_DEBUG_ARGSCALL);
809 sz = (*linesize <= *cnt) ? *linesize : *cnt + 1;
810 if (sz <= 1 || fgets(rv, sz, fp) == NULL) {
811 /* Leave llen untouched; it is used to determine whether the last line
812 * was \n-terminated in some callers */
813 rv = NULL;
814 goto jleave;
817 i_llen = _length_of_line(rv, sz);
818 *cnt -= i_llen;
819 while (rv[i_llen - 1] != '\n') {
820 *line = rv = (srealloc)(rv, *linesize += 256 SMALLOC_DEBUG_ARGSCALL);
821 sz = *linesize - i_llen;
822 sz = (sz <= *cnt) ? sz : *cnt + 1;
823 if (sz <= 1 || fgets(rv + i_llen, sz, fp) == NULL) {
824 if (appendnl) {
825 rv[i_llen++] = '\n';
826 rv[i_llen] = '\0';
828 break;
830 sz = _length_of_line(rv + i_llen, sz);
831 i_llen += sz;
832 *cnt -= sz;
834 if (llen)
835 *llen = i_llen;
836 jleave:
837 NYD2_LEAVE;
838 return rv;
841 FL int
842 (readline_restart)(FILE *ibuf, char **linebuf, size_t *linesize, size_t n
843 SMALLOC_DEBUG_ARGS)
845 /* TODO readline_restart(): always *appends* LF just to strip it again;
846 * TODO should be configurable just as for fgetline(); ..or whatever.. */
847 int rv = -1;
848 long sz;
849 NYD2_ENTER;
851 clearerr(ibuf);
853 /* Interrupts will cause trouble if we are inside a stdio call. As this is
854 * only relevant if input is from tty, bypass it by read(), then */
855 if (fileno(ibuf) == 0 && (options & OPT_TTYIN)) {
856 assert(*linesize == 0 || *linebuf != NULL);
857 for (;;) {
858 if (*linesize <= LINESIZE || n >= *linesize - 128) {
859 *linesize += ((*linebuf == NULL) ? LINESIZE + n + 1 : 256);
860 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
862 jagain:
863 sz = read(0, *linebuf + n, *linesize - n - 1);
864 if (sz > 0) {
865 n += sz;
866 (*linebuf)[n] = '\0';
867 if (n > 0 && (*linebuf)[n - 1] == '\n')
868 break;
869 } else {
870 if (sz < 0 && errno == EINTR)
871 goto jagain;
872 if (n > 0) {
873 if ((*linebuf)[n - 1] != '\n') {
874 (*linebuf)[n++] = '\n';
875 (*linebuf)[n] = '\0';
877 break;
878 } else
879 goto jleave;
882 } else {
883 /* Not reading from standard input or standard input not a terminal. We
884 * read one char at a time as it is the only way to get lines with
885 * embedded NUL characters in standard stdio */
886 if (_fgetline_byone(linebuf, linesize, &n, ibuf, 1, n
887 SMALLOC_DEBUG_ARGSCALL) == NULL)
888 goto jleave;
890 if (n > 0 && (*linebuf)[n - 1] == '\n')
891 (*linebuf)[--n] = '\0';
892 rv = (int)n;
893 jleave:
894 NYD2_LEAVE;
895 return rv;
898 FL int
899 (readline_input)(char const *prompt, bool_t nl_escape, char **linebuf,
900 size_t *linesize, char const *string SMALLOC_DEBUG_ARGS)
902 /* TODO readline: linebuf pool! */
903 FILE *ifile = (_fio_input != NULL) ? _fio_input : stdin;
904 bool_t doprompt, dotty;
905 int n, nold;
906 NYD2_ENTER;
908 doprompt = (!(pstate & PS_SOURCING) && (options & OPT_INTERACTIVE));
909 dotty = (doprompt && !ok_blook(line_editor_disable));
910 if (!doprompt)
911 prompt = NULL;
912 else if (prompt == NULL)
913 prompt = getprompt();
915 /* Ensure stdout is flushed first anyway */
916 if (!dotty && prompt == NULL)
917 fflush(stdout);
919 for (nold = n = 0;;) {
920 if (dotty) {
921 assert(ifile == stdin);
922 if (string != NULL && (n = (int)strlen(string)) > 0) {
923 if (*linesize > 0)
924 *linesize += n +1;
925 else
926 *linesize = (size_t)n + LINESIZE +1;
927 *linebuf = (srealloc)(*linebuf, *linesize SMALLOC_DEBUG_ARGSCALL);
928 memcpy(*linebuf, string, (size_t)n +1);
930 string = NULL;
931 /* TODO if nold>0, don't redisplay the entire line!
932 * TODO needs complete redesign ... */
933 n = (tty_readline)(prompt, linebuf, linesize, n
934 SMALLOC_DEBUG_ARGSCALL);
935 } else {
936 if (prompt != NULL) {
937 if (*prompt != '\0')
938 fputs(prompt, stdout);
939 fflush(stdout);
941 n = (readline_restart)(ifile, linebuf, linesize, n
942 SMALLOC_DEBUG_ARGSCALL);
944 if (n > 0 && nold > 0) {
945 int i = 0;
946 char const *cp = *linebuf + nold;
948 while (blankspacechar(*cp) && nold + i < n)
949 ++cp, ++i;
950 if (i > 0) {
951 memmove(*linebuf + nold, cp, n - nold - i);
952 n -= i;
953 (*linebuf)[n] = '\0';
957 if (n <= 0)
958 break;
960 /* POSIX says:
961 * An unquoted <backslash> at the end of a command line shall
962 * be discarded and the next line shall continue the command */
963 if (!nl_escape || n == 0 || (*linebuf)[n - 1] != '\\')
964 break;
965 (*linebuf)[nold = --n] = '\0';
966 if (prompt != NULL && *prompt != '\0')
967 prompt = ".. "; /* XXX PS2 .. */
970 if (n >= 0 && (options & OPT_D_VV))
971 n_err(_("%s %d bytes <%.*s>\n"),
972 ((pstate & PS_LOADING) ? "LOAD"
973 : (pstate & PS_SOURCING) ? "SOURCE" : "READ"),
974 n, n, *linebuf);
975 NYD2_LEAVE;
976 return n;
979 FL char *
980 n_input_cp_addhist(char const *prompt, char const *string, bool_t isgabby)
982 /* FIXME n_input_cp_addhist(): leaks on sigjmp without linepool */
983 size_t linesize = 0;
984 char *linebuf = NULL, *rv = NULL;
985 int n;
986 NYD2_ENTER;
988 n = readline_input(prompt, FAL0, &linebuf, &linesize, string);
989 if (n > 0 && *(rv = savestrbuf(linebuf, (size_t)n)) != '\0' &&
990 (options & OPT_INTERACTIVE))
991 tty_addhist(rv, isgabby);
993 if (linebuf != NULL)
994 free(linebuf);
995 NYD2_LEAVE;
996 return rv;
999 FL void
1000 setptr(FILE *ibuf, off_t offset)
1002 struct message self;
1003 char *cp, *linebuf = NULL;
1004 char const *cp2;
1005 int c, selfcnt = 0;
1006 bool_t maybe, inhead, rfc4155;
1007 size_t linesize = 0, filesize, cnt;
1008 NYD_ENTER;
1010 memset(&self, 0, sizeof self);
1011 self.m_flag = MUSED | MNEW | MNEWEST;
1012 filesize = mailsize - offset;
1013 offset = ftell(mb.mb_otf);
1014 maybe = TRU1;
1015 rfc4155 = inhead = FAL0;
1017 for (;;) {
1018 if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
1019 self.m_xsize = self.m_size;
1020 self.m_xlines = self.m_lines;
1021 self.m_have = HAVE_HEADER | HAVE_BODY;
1022 if (selfcnt > 0)
1023 message_append(&self);
1024 makemessage();
1025 if (linebuf)
1026 free(linebuf);
1027 break;
1030 #ifdef notdef
1031 if (linebuf[0] == '\0')
1032 linebuf[0] = '.';
1033 #endif
1034 /* XXX Convert CRLF to LF; this should be rethought in that
1035 * XXX CRLF input should possibly end as CRLF output? */
1036 if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
1037 linebuf[--cnt - 1] = '\n';
1038 fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
1039 if (ferror(mb.mb_otf)) {
1040 n_perr(_("/tmp"), 0);
1041 exit(EXIT_ERR);
1043 if (linebuf[cnt - 1] == '\n')
1044 linebuf[cnt - 1] = '\0';
1045 if (maybe && linebuf[0] == 'F' &&
1046 (rfc4155 = is_head(linebuf, cnt, TRU1))) {
1047 /* TODO char date[FROM_DATEBUF];
1048 * TODO extract_date_from_from_(linebuf, cnt, date);
1049 * TODO self.m_time = 10000; */
1050 if (rfc4155 == TRUM1) {
1051 if (options & OPT_D_V)
1052 n_err(_("Invalid MBOX \"From_ line\": %.*s\n"),
1053 (int)cnt, linebuf);
1054 else if (!(mb.mb_active & MB_FROM__WARNED))
1055 n_err(_("MBOX mailbox contains non-conforming From_ line(s)\n"));
1056 mb.mb_active |= MB_FROM__WARNED;
1058 self.m_xsize = self.m_size;
1059 self.m_xlines = self.m_lines;
1060 self.m_have = HAVE_HEADER | HAVE_BODY;
1061 if (selfcnt++ > 0)
1062 message_append(&self);
1063 msgCount++;
1064 self.m_flag = MUSED | MNEW | MNEWEST;
1065 self.m_size = 0;
1066 self.m_lines = 0;
1067 self.m_block = mailx_blockof(offset);
1068 self.m_offset = mailx_offsetof(offset);
1069 inhead = TRU1;
1070 } else if (linebuf[0] == 0) {
1071 inhead = FAL0;
1072 } else if (inhead) {
1073 for (cp = linebuf, cp2 = "status";; ++cp) {
1074 if ((c = *cp2++) == 0) {
1075 while (c = *cp++, whitechar(c))
1077 if (cp[-1] != ':')
1078 break;
1079 while ((c = *cp++) != '\0')
1080 if (c == 'R')
1081 self.m_flag |= MREAD;
1082 else if (c == 'O')
1083 self.m_flag &= ~MNEW;
1084 break;
1086 if (*cp != c && *cp != upperconv(c))
1087 break;
1089 for (cp = linebuf, cp2 = "x-status";; ++cp) {
1090 if ((c = *cp2++) == 0) {
1091 while ((c = *cp++, whitechar(c)))
1093 if (cp[-1] != ':')
1094 break;
1095 while ((c = *cp++) != '\0')
1096 if (c == 'F')
1097 self.m_flag |= MFLAGGED;
1098 else if (c == 'A')
1099 self.m_flag |= MANSWERED;
1100 else if (c == 'T')
1101 self.m_flag |= MDRAFTED;
1102 break;
1104 if (*cp != c && *cp != upperconv(c))
1105 break;
1108 offset += cnt;
1109 self.m_size += cnt;
1110 ++self.m_lines;
1111 maybe = (linebuf[0] == 0);
1113 NYD_LEAVE;
1116 FL int
1117 putline(FILE *obuf, char *linebuf, size_t cnt)
1119 int rv = -1;
1120 NYD_ENTER;
1122 fwrite(linebuf, sizeof *linebuf, cnt, obuf);
1123 putc('\n', obuf);
1124 if (!ferror(obuf))
1125 rv = (int)(cnt + 1);
1126 NYD_LEAVE;
1127 return rv;
1130 FL FILE *
1131 setinput(struct mailbox *mp, struct message *m, enum needspec need)
1133 FILE *rv = NULL;
1134 enum okay ok = STOP;
1135 NYD_ENTER;
1137 switch (need) {
1138 case NEED_HEADER:
1139 ok = (m->m_have & HAVE_HEADER) ? OKAY : get_header(m);
1140 break;
1141 case NEED_BODY:
1142 ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m);
1143 break;
1144 case NEED_UNSPEC:
1145 ok = OKAY;
1146 break;
1148 if (ok != OKAY)
1149 goto jleave;
1151 fflush(mp->mb_otf);
1152 if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block, m->m_offset),
1153 SEEK_SET) == -1) {
1154 n_perr(_("fseek"), 0);
1155 n_panic(_("temporary file seek"));
1157 rv = mp->mb_itf;
1158 jleave:
1159 NYD_LEAVE;
1160 return rv;
1163 FL void
1164 message_reset(void)
1166 NYD_ENTER;
1167 if (message != NULL) {
1168 free(message);
1169 message = NULL;
1171 msgCount = 0;
1172 _message_space = 0;
1173 NYD_LEAVE;
1176 FL void
1177 message_append(struct message *mp)
1179 NYD_ENTER;
1180 if (UICMP(z, msgCount + 1, >=, _message_space)) {
1181 /* XXX remove _message_space magics (or use s_Vector) */
1182 _message_space = (_message_space >= 128 && _message_space <= 1000000)
1183 ? _message_space << 1 : _message_space + 64;
1184 message = srealloc(message, _message_space * sizeof *message);
1186 if (msgCount > 0) {
1187 if (mp != NULL)
1188 message[msgCount - 1] = *mp;
1189 else
1190 memset(message + msgCount - 1, 0, sizeof *message);
1192 NYD_LEAVE;
1195 FL bool_t
1196 message_match(struct message *mp, struct search_expr const *sep,
1197 bool_t with_headers)
1199 char **line;
1200 size_t *linesize, cnt;
1201 FILE *fp;
1202 bool_t rv = FAL0;
1203 NYD_ENTER;
1205 if ((fp = Ftmp(NULL, "mpmatch", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1206 NULL)
1207 goto j_leave;
1209 if (sendmp(mp, fp, NULL, NULL, SEND_TOSRCH, NULL) < 0)
1210 goto jleave;
1211 fflush_rewind(fp);
1213 cnt = fsize(fp);
1214 line = &termios_state.ts_linebuf; /* XXX line pool */
1215 linesize = &termios_state.ts_linesize; /* XXX line pool */
1217 if (!with_headers)
1218 while (fgetline(line, linesize, &cnt, NULL, fp, 0))
1219 if (**line == '\n')
1220 break;
1222 while (fgetline(line, linesize, &cnt, NULL, fp, 0)) {
1223 #ifdef HAVE_REGEX
1224 if (sep->ss_sexpr == NULL) {
1225 if (regexec(&sep->ss_regex, *line, 0,NULL, 0) == REG_NOMATCH)
1226 continue;
1227 } else
1228 #endif
1229 if (!substr(*line, sep->ss_sexpr))
1230 continue;
1231 rv = TRU1;
1232 break;
1235 jleave:
1236 Fclose(fp);
1237 j_leave:
1238 NYD_LEAVE;
1239 return rv;
1242 FL struct message *
1243 setdot(struct message *mp)
1245 NYD_ENTER;
1246 if (dot != mp) {
1247 prevdot = dot;
1248 pstate &= ~PS_DID_PRINT_DOT;
1250 dot = mp;
1251 uncollapse1(dot, 0);
1252 NYD_LEAVE;
1253 return dot;
1256 FL int
1257 rm(char const *name)
1259 struct stat sb;
1260 int rv = -1;
1261 NYD_ENTER;
1263 if (stat(name, &sb) < 0)
1265 else if (!S_ISREG(sb.st_mode))
1266 errno = EISDIR;
1267 else
1268 rv = unlink(name);
1269 NYD_LEAVE;
1270 return rv;
1273 FL off_t
1274 fsize(FILE *iob)
1276 struct stat sbuf;
1277 off_t rv;
1278 NYD_ENTER;
1280 rv = (fstat(fileno(iob), &sbuf) == -1) ? 0 : sbuf.st_size;
1281 NYD_LEAVE;
1282 return rv;
1285 FL char *
1286 fexpand(char const *name, enum fexp_mode fexpm)
1288 char cbuf[PATH_MAX +1];
1289 char const *res;
1290 struct str s;
1291 bool_t dyn;
1292 NYD_ENTER;
1294 /* The order of evaluation is "%" and "#" expand into constants.
1295 * "&" can expand into "+". "+" can expand into shell meta characters.
1296 * Shell meta characters expand into constants.
1297 * This way, we make no recursive expansion */
1298 if ((fexpm & FEXP_NSHORTCUT) || (res = shortcut_expand(name)) == NULL)
1299 res = UNCONST(name);
1301 if (fexpm & (FEXP_SHELL | FEXP_FOLDER)) {
1302 dyn = FAL0;
1303 if (fexpm & FEXP_SHELL)
1304 goto jshell;
1305 goto jfolder;
1308 jnext:
1309 dyn = FAL0;
1310 switch (*res) {
1311 case '%':
1312 if (res[1] == ':' && res[2] != '\0') {
1313 res = &res[2];
1314 goto jnext;
1316 _findmail(cbuf, sizeof cbuf, (res[1] != '\0' ? res + 1 : myname),
1317 (res[1] != '\0' || (options & OPT_u_FLAG)));
1318 res = cbuf;
1319 goto jislocal;
1320 case '#':
1321 if (res[1] != '\0')
1322 break;
1323 if (prevfile[0] == '\0') {
1324 n_err(_("No previous file\n"));
1325 res = NULL;
1326 goto jleave;
1328 res = prevfile;
1329 goto jislocal;
1330 case '&':
1331 if (res[1] == '\0') {
1332 if ((res = ok_vlook(MBOX)) == NULL)
1333 res = UNCONST("~/mbox"); /* XXX no magics (POSIX though) */
1334 else if (res[0] != '&' || res[1] != '\0')
1335 goto jnext;
1337 break;
1340 if (res[0] == '@' && which_protocol(mailname) == PROTO_IMAP) {
1341 res = str_concat_csvl(&s, protbase(mailname), "/", res + 1, NULL)->s;
1342 dyn = TRU1;
1345 jfolder:
1346 if (res[0] == '+' && getfold(cbuf, sizeof cbuf)) {
1347 res = str_concat_csvl(&s, cbuf, &res[1], NULL)->s;
1348 dyn = TRU1;
1350 if (res[0] == '%' && res[1] == ':') {
1351 res += 2;
1352 goto jnext;
1356 /* Catch the most common shell meta character */
1357 jshell:
1358 if (res[0] == '~' && (res[1] == '/' || res[1] == '\0')) {
1359 res = str_concat_csvl(&s, homedir, res + 1, NULL)->s;
1360 dyn = TRU1;
1362 if (anyof(res, n_SHEXP_MAGIC_PATH_CHARS))
1363 switch (which_protocol(res)) {
1364 case PROTO_FILE:
1365 case PROTO_MAILDIR:
1366 res = (fexpm & FEXP_NSHELL) ? _shvar_exp(res) : _globname(res, fexpm);
1367 dyn = TRU1;
1368 goto jleave;
1369 default:
1370 break;
1372 jislocal:
1373 if (fexpm & FEXP_LOCAL)
1374 switch (which_protocol(res)) {
1375 case PROTO_FILE:
1376 case PROTO_MAILDIR:
1377 break;
1378 default:
1379 n_err(_("Not a local file or directory: \"%s\"\n"), name);
1380 res = NULL;
1381 break;
1383 jleave:
1384 if (res && !dyn)
1385 res = savestr(res);
1386 NYD_LEAVE;
1387 return UNCONST(res);
1390 FL char *
1391 fexpand_nshell_quote(char const *name)
1393 size_t i, j;
1394 char *rv, c;
1395 NYD_ENTER;
1397 for (i = j = 0; (c = name[i]) != '\0'; ++i)
1398 if (c == '\\')
1399 ++j;
1401 if (j == 0)
1402 rv = savestrbuf(name, i);
1403 else {
1404 rv = salloc(i + j +1);
1405 for (i = j = 0; (c = name[i]) != '\0'; ++i) {
1406 rv[j++] = c;
1407 if (c == '\\')
1408 rv[j++] = c;
1410 rv[j] = '\0';
1412 NYD_LEAVE;
1413 return rv;
1416 FL bool_t
1417 var_folder_updated(char const *name, char **store)
1419 char rv = TRU1;
1420 char *folder, *unres = NULL, *res = NULL;
1421 NYD_ENTER;
1423 if ((folder = UNCONST(name)) == NULL)
1424 goto jleave;
1426 /* Expand the *folder*; skip %: prefix for simplicity of use */
1427 /* XXX This *only* works because we do NOT
1428 * XXX update environment variables via the "set" mechanism */
1429 if (folder[0] == '%' && folder[1] == ':')
1430 folder += 2;
1431 if ((folder = fexpand(folder, FEXP_FULL)) == NULL) /* XXX error? */
1432 goto jleave;
1434 switch (which_protocol(folder)) {
1435 case PROTO_POP3:
1436 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
1437 rv = FAL0;
1438 goto jleave;
1439 case PROTO_IMAP:
1440 /* Simply assign what we have, even including `%:' prefix */
1441 if (folder != name)
1442 goto jvcopy;
1443 goto jleave;
1444 default:
1445 /* Further expansion desired */
1446 break;
1449 /* All non-absolute paths are relative to our home directory */
1450 if (*folder != '/') {
1451 size_t l1 = strlen(homedir), l2 = strlen(folder);
1452 unres = ac_alloc(l1 + l2 + 1 +1);
1453 memcpy(unres, homedir, l1);
1454 unres[l1] = '/';
1455 memcpy(unres + l1 + 1, folder, l2);
1456 unres[l1 + 1 + l2] = '\0';
1457 folder = unres;
1460 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1461 * avoid that we loose track of our currently open folder in case we
1462 * chdir away, but still checks the leading path portion against
1463 * getfold() to be able to abbreviate to the +FOLDER syntax if
1464 * possible, we need to realpath(3) the folder, too */
1465 #ifdef HAVE_REALPATH
1466 res = ac_alloc(PATH_MAX +1);
1467 if (realpath(folder, res) == NULL)
1468 n_err(_("Can't canonicalize \"%s\"\n"), folder);
1469 else
1470 folder = res;
1471 #endif
1473 jvcopy:
1474 *store = sstrdup(folder);
1476 if (res != NULL)
1477 ac_free(res);
1478 if (unres != NULL)
1479 ac_free(unres);
1480 jleave:
1481 NYD_LEAVE;
1482 return rv;
1485 FL bool_t
1486 getfold(char *name, size_t size)
1488 char const *folder;
1489 NYD_ENTER;
1491 if ((folder = ok_vlook(folder)) != NULL){
1492 size_t i;
1494 i = strlen(folder);
1495 if(i > 0 && folder[i - 1] != '/')
1496 folder = savecat(folder, "/");
1497 n_strlcpy(name, folder, size);
1499 NYD_LEAVE;
1500 return (folder != NULL);
1503 FL char const *
1504 getdeadletter(void) /* XXX should that be in auxlily.c? */
1506 char const *cp;
1507 NYD_ENTER;
1509 if ((cp = ok_vlook(DEAD)) == NULL || (cp = fexpand(cp, FEXP_LOCAL)) == NULL)
1510 cp = fexpand("~/dead.letter", FEXP_LOCAL | FEXP_SHELL);
1511 else if (*cp != '/') {
1512 size_t sz = strlen(cp) + 2 +1;
1513 char *buf = ac_alloc(sz);
1515 snprintf(buf, sz, "~/%s", cp);
1516 cp = fexpand(buf, FEXP_LOCAL | FEXP_SHELL);
1517 ac_free(buf);
1520 if (cp == NULL)
1521 cp = "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1522 NYD_LEAVE;
1523 return cp;
1526 FL enum okay
1527 get_body(struct message *mp)
1529 enum okay rv;
1530 NYD_ENTER;
1531 UNUSED(mp);
1533 switch (mb.mb_type) {
1534 case MB_FILE:
1535 case MB_MAILDIR:
1536 rv = OKAY;
1537 break;
1538 #ifdef HAVE_POP3
1539 case MB_POP3:
1540 rv = pop3_body(mp);
1541 break;
1542 #endif
1543 #ifdef HAVE_IMAP
1544 case MB_IMAP:
1545 case MB_CACHE:
1546 rv = imap_body(mp);
1547 break;
1548 #endif
1549 case MB_VOID:
1550 default:
1551 rv = STOP;
1552 break;
1554 NYD_LEAVE;
1555 return rv;
1558 FL bool_t
1559 file_lock(int fd, enum file_lock_type flt, off_t off, off_t len,
1560 size_t pollmsecs)
1562 size_t tries;
1563 bool_t rv;
1564 NYD_ENTER;
1566 for (tries = 0; tries <= FILE_LOCK_TRIES; ++tries)
1567 if ((rv = _file_lock(fd, flt, off, len)) || pollmsecs == 0)
1568 break;
1569 else
1570 sleep(1); /* TODO pollmsecs -> use finer grain */
1571 NYD_LEAVE;
1572 return rv;
1575 FL FILE *
1576 dot_lock(char const *fname, int fd, enum file_lock_type flt,
1577 off_t off, off_t len, size_t pollmsecs)
1579 #undef _DOMSG
1580 #ifdef HAVE_DOTLOCK
1581 # define _DOMSG() n_err(_("Creating dotlock for \"%s\" "), fname)
1582 #else
1583 # define _DOMSG() n_err(_("Trying to lock file \"%s\" "), fname)
1584 #endif
1586 #ifdef HAVE_DOTLOCK
1587 int cpipe[2];
1588 struct dotlock_info di;
1589 enum dotlock_state dls;
1590 char const *emsg = NULL;
1591 #endif
1592 int UNINIT(serrno, 0);
1593 union {size_t tries; int (*ptf)(void); char const *sh; ssize_t r;} u;
1594 bool_t flocked, didmsg = FAL0;
1595 FILE *rv = NULL;
1596 NYD_ENTER;
1598 if (options & OPT_D_VV) {
1599 _DOMSG();
1600 didmsg = TRUM1;
1603 flocked = FAL0;
1604 for (u.tries = 0; !_file_lock(fd, flt, off, len);)
1605 switch ((serrno = errno)) {
1606 case EACCES:
1607 case EAGAIN:
1608 case ENOLCK:
1609 if (pollmsecs > 0 && ++u.tries < FILE_LOCK_TRIES) {
1610 if (!didmsg)
1611 _DOMSG();
1612 n_err(".");
1613 didmsg = TRUM1;
1614 sleep(1); /* TODO pollmsecs -> use finer grain */
1615 continue;
1617 /* FALLTHRU */
1618 default:
1619 goto jleave;
1621 flocked = TRU1;
1623 #ifndef HAVE_DOTLOCK
1624 jleave:
1625 if (didmsg == TRUM1)
1626 n_err("\n");
1627 if (flocked)
1628 rv = (FILE*)-1;
1629 else
1630 errno = serrno;
1631 NYD_LEAVE;
1632 return rv;
1634 #else
1635 /* Create control-pipe for our dot file locker process, which will remove
1636 * the lock and terminate once the pipe is closed, for whatever reason */
1637 if (pipe_cloexec(cpipe) == -1) {
1638 serrno = errno;
1639 emsg = N_(" Can't create file lock control pipe\n");
1640 goto jemsg;
1643 /* And the locker process itself; it'll be a (rather cheap) thread only
1644 * unless the lock has to be placed in the system spool and we have our
1645 * privilege-separated dotlock program available, in which case that will be
1646 * executed and do "it" with changed group-id */
1647 di.di_file_name = fname;
1648 di.di_pollmsecs = pollmsecs;
1649 /* Initialize some more stuff; query the two strings in the parent in order
1650 * to cache the result of the former and anyway minimalize child page-ins.
1651 * Especially uname(3) may hang for multiple seconds when it is called the
1652 * first time! */
1653 di.di_hostname = nodename(FAL0);
1654 di.di_randstr = getrandstring(16);
1655 _dotlock_flt = flt;
1656 _dotlock_fd = fd;
1657 _dotlock_dip = &di;
1659 u.ptf = &_dotlock_main;
1660 rv = Popen((char*)-1, "W", u.sh, NULL, cpipe[1]);
1661 serrno = errno;
1663 close(cpipe[1]);
1664 if (rv == NULL) {
1665 close(cpipe[0]);
1666 emsg = N_(" Can't create file lock process\n");
1667 goto jemsg;
1670 /* Let's check wether we were able to create the dotlock file */
1671 for (;;) {
1672 u.r = read(cpipe[0], &dls, sizeof dls);
1673 if (UICMP(z, u.r, !=, sizeof dls)) {
1674 serrno = (u.r != -1) ? EAGAIN : errno;
1675 dls = DLS_DUNNO | DLS_ABANDON;
1676 } else
1677 serrno = 0;
1679 if (dls == DLS_NONE || (dls & DLS_ABANDON))
1680 close(cpipe[0]);
1682 switch (dls & ~DLS_ABANDON) {
1683 case DLS_NONE:
1684 goto jleave;
1685 case DLS_CANT_CHDIR:
1686 if (options & OPT_D_V)
1687 emsg = N_(" Can't change to directory! Please check permissions\n");
1688 serrno = EACCES;
1689 break;
1690 case DLS_NAMETOOLONG:
1691 emsg = N_("Resulting dotlock filename would be too long\n");
1692 serrno = EACCES;
1693 break;
1694 case DLS_ROFS:
1695 assert(dls & DLS_ABANDON);
1696 if (options & OPT_D_V)
1697 emsg = N_(" Read-only filesystem, not creating lock file\n");
1698 serrno = EROFS;
1699 break;
1700 case DLS_NOPERM:
1701 if (options & OPT_D_V)
1702 emsg = N_(" Can't create a lock file! Please check permissions\n"
1703 " (Maybe setting *dotlock-ignore-error* variable helps.)\n");
1704 serrno = EACCES;
1705 break;
1706 case DLS_NOEXEC:
1707 if (options & OPT_D_V)
1708 emsg = N_(" Can't find privilege-separated file lock program\n");
1709 serrno = ENOENT;
1710 break;
1711 case DLS_PRIVFAILED:
1712 emsg = N_(" Privilege-separated file lock program can't change "
1713 "privileges\n");
1714 serrno = EPERM;
1715 break;
1716 case DLS_EXIST:
1717 emsg = N_(" It seems there is a stale dotlock file?\n"
1718 " Please remove the lock file manually, then retry\n");
1719 serrno = EEXIST;
1720 break;
1721 case DLS_FISHY:
1722 emsg = N_(" Fishy! Is someone trying to \"steal\" foreign files?\n"
1723 " Please check the mailbox file etc. manually, then retry\n");
1724 serrno = EAGAIN; /* ? Hack to ignore *dotlock-ignore-error* xxx */
1725 break;
1726 default:
1727 case DLS_DUNNO:
1728 emsg = N_(" Unspecified dotlock file control process error.\n"
1729 " Like broken I/O pipe; this one is unlikely to happen\n");
1730 if (serrno != EAGAIN)
1731 serrno = EINVAL;
1732 break;
1733 case DLS_PING:
1734 if (!didmsg)
1735 _DOMSG();
1736 n_err(".");
1737 didmsg = TRUM1;
1738 continue;
1741 if (emsg != NULL) {
1742 if (!didmsg) {
1743 _DOMSG();
1744 didmsg = TRUM1;
1746 if (didmsg == TRUM1)
1747 n_err("\n");
1748 didmsg = TRU1;
1749 n_err(V_(emsg));
1750 emsg = NULL;
1753 if (dls & DLS_ABANDON) {
1754 Pclose(rv, FAL0);
1755 rv = NULL;
1756 break;
1760 jleave:
1761 if (didmsg == TRUM1)
1762 n_err("\n");
1763 if (rv == NULL) {
1764 if (flocked && (serrno == EROFS ||
1765 (serrno != EAGAIN && serrno != EEXIST &&
1766 ok_blook(dotlock_ignore_error))))
1767 rv = (FILE*)-1;
1768 else
1769 errno = serrno;
1771 NYD_LEAVE;
1772 return rv;
1773 jemsg:
1774 if (!didmsg)
1775 _DOMSG();
1776 n_err("\n");
1777 didmsg = TRU1;
1778 n_err(V_(emsg));
1779 goto jleave;
1780 #endif /* HAVE_DOTLOCK */
1781 #undef _DOMSG
1784 #ifdef HAVE_SOCKETS
1785 FL int
1786 sclose(struct sock *sp)
1788 int i;
1789 NYD_ENTER;
1791 i = sp->s_fd;
1792 sp->s_fd = -1;
1793 /* TODO NOTE: we MUST NOT close the descriptor 0 here...
1794 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1795 * TODO but unfortunately it isn't yet */
1796 if (i <= 0)
1797 i = 0;
1798 else {
1799 if (sp->s_onclose != NULL)
1800 (*sp->s_onclose)();
1801 if (sp->s_wbuf != NULL)
1802 free(sp->s_wbuf);
1803 # ifdef HAVE_OPENSSL
1804 if (sp->s_use_ssl) {
1805 void *s_ssl = sp->s_ssl;
1807 sp->s_ssl = NULL;
1808 sp->s_use_ssl = 0;
1809 while (!SSL_shutdown(s_ssl)) /* XXX proper error handling;signals! */
1811 SSL_free(s_ssl);
1813 # endif
1814 i = close(i);
1816 NYD_LEAVE;
1817 return i;
1820 FL enum okay
1821 swrite(struct sock *sp, char const *data)
1823 enum okay rv;
1824 NYD2_ENTER;
1826 rv = swrite1(sp, data, strlen(data), 0);
1827 NYD2_LEAVE;
1828 return rv;
1831 FL enum okay
1832 swrite1(struct sock *sp, char const *data, int sz, int use_buffer)
1834 enum okay rv = STOP;
1835 int x;
1836 NYD2_ENTER;
1838 if (use_buffer > 0) {
1839 int di;
1841 if (sp->s_wbuf == NULL) {
1842 sp->s_wbufsize = 4096;
1843 sp->s_wbuf = smalloc(sp->s_wbufsize);
1844 sp->s_wbufpos = 0;
1846 while (sp->s_wbufpos + sz > sp->s_wbufsize) {
1847 di = sp->s_wbufsize - sp->s_wbufpos;
1848 sz -= di;
1849 if (sp->s_wbufpos > 0) {
1850 memcpy(sp->s_wbuf + sp->s_wbufpos, data, di);
1851 rv = swrite1(sp, sp->s_wbuf, sp->s_wbufsize, -1);
1852 } else
1853 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1854 if (rv != OKAY)
1855 goto jleave;
1856 data += di;
1857 sp->s_wbufpos = 0;
1859 if (sz == sp->s_wbufsize) {
1860 rv = swrite1(sp, data, sp->s_wbufsize, -1);
1861 if (rv != OKAY)
1862 goto jleave;
1863 } else if (sz) {
1864 memcpy(sp->s_wbuf+ sp->s_wbufpos, data, sz);
1865 sp->s_wbufpos += sz;
1867 rv = OKAY;
1868 goto jleave;
1869 } else if (use_buffer == 0 && sp->s_wbuf != NULL && sp->s_wbufpos > 0) {
1870 x = sp->s_wbufpos;
1871 sp->s_wbufpos = 0;
1872 if ((rv = swrite1(sp, sp->s_wbuf, x, -1)) != OKAY)
1873 goto jleave;
1875 if (sz == 0) {
1876 rv = OKAY;
1877 goto jleave;
1880 # ifdef HAVE_OPENSSL
1881 if (sp->s_use_ssl) {
1882 jssl_retry:
1883 x = SSL_write(sp->s_ssl, data, sz);
1884 if (x < 0) {
1885 switch (SSL_get_error(sp->s_ssl, x)) {
1886 case SSL_ERROR_WANT_READ:
1887 case SSL_ERROR_WANT_WRITE:
1888 goto jssl_retry;
1891 } else
1892 # endif
1894 x = xwrite(sp->s_fd, data, sz);
1896 if (x != sz) {
1897 char o[512];
1898 snprintf(o, sizeof o, "%s write error",
1899 (sp->s_desc ? sp->s_desc : "socket"));
1900 # ifdef HAVE_OPENSSL
1901 if (sp->s_use_ssl)
1902 ssl_gen_err("%s", o);
1903 else
1904 # endif
1905 n_perr(o, 0);
1906 if (x < 0)
1907 sclose(sp);
1908 rv = STOP;
1909 goto jleave;
1911 rv = OKAY;
1912 jleave:
1913 NYD2_LEAVE;
1914 return rv;
1917 static sigjmp_buf __sopen_actjmp; /* TODO someday, we won't need it no more */
1918 static int __sopen_sig; /* TODO someday, we won't need it no more */
1919 static void
1920 __sopen_onsig(int sig) /* TODO someday, we won't need it no more */
1922 NYD_X; /* Signal handler */
1923 if (__sopen_sig < 0) {
1924 /* Of course the following doesn't belong into a signal handler XXX */
1925 int i, j;
1927 if (__sopen_sig == -1) {
1928 fprintf(stderr,
1929 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
1930 "unusable.\n"
1931 " Wait until it's done, or do terminate the program\n"));
1932 __sopen_sig = -2;
1933 } else if ((i = j = ABS(__sopen_sig)) + 15 < scrnwidth) {
1934 putc('\r', stderr);
1935 for (; j > 0; --j)
1936 putc(' ', stderr);
1937 fputs("___( o)", stderr);
1938 putc((i & 1) ? '=' : '>', stderr);
1939 putc(' ', stderr);
1940 putc(' ', stderr);
1941 ++i;
1942 __sopen_sig = -i;
1944 } else {
1945 __sopen_sig = sig;
1946 siglongjmp(__sopen_actjmp, 1);
1950 FL bool_t
1951 sopen(struct sock *sp, struct url *urlp) /* TODO sighandling; refactor */
1953 # ifdef HAVE_SO_SNDTIMEO
1954 struct timeval tv;
1955 # endif
1956 # ifdef HAVE_SO_LINGER
1957 struct linger li;
1958 # endif
1959 # ifdef HAVE_GETADDRINFO
1960 char hbuf[NI_MAXHOST];
1961 struct addrinfo hints, *res0 = NULL, *res;
1962 # else
1963 struct sockaddr_in servaddr;
1964 struct in_addr **pptr;
1965 struct hostent *hp;
1966 struct servent *ep;
1967 # endif
1968 sighandler_type volatile ohup, oint;
1969 char const * volatile serv;
1970 int volatile sofd = -1, errval;
1971 NYD_ENTER;
1973 UNINIT(errval, 0);
1975 /* Connect timeouts after 30 seconds XXX configurable */
1976 # ifdef HAVE_SO_SNDTIMEO
1977 tv.tv_sec = 30;
1978 tv.tv_usec = 0;
1979 # endif
1980 serv = (urlp->url_port != NULL) ? urlp->url_port : urlp->url_proto;
1982 if (options & OPT_VERB)
1983 n_err(_("Resolving host \"%s:%s\" ... "),
1984 urlp->url_host.s, serv);
1986 /* Signal handling (in respect to __sopen_sig dealing) is heavy, but no
1987 * healing until v15.0 and i want to end up with that functionality */
1988 hold_sigs();
1989 __sopen_sig = 0;
1990 ohup = safe_signal(SIGHUP, &__sopen_onsig);
1991 oint = safe_signal(SIGINT, &__sopen_onsig);
1992 if (sigsetjmp(__sopen_actjmp, 0)) {
1993 jpseudo_jump:
1994 n_err("%s\n",
1995 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
1996 if (sofd >= 0) {
1997 close(sofd);
1998 sofd = -1;
2000 goto jjumped;
2002 rele_sigs();
2004 # ifdef HAVE_GETADDRINFO
2005 for (;;) {
2006 memset(&hints, 0, sizeof hints);
2007 hints.ai_socktype = SOCK_STREAM;
2008 __sopen_sig = -1;
2009 errval = getaddrinfo(urlp->url_host.s, serv, &hints, &res0);
2010 if (__sopen_sig != -1) {
2011 __sopen_sig = SIGINT;
2012 goto jpseudo_jump;
2014 __sopen_sig = 0;
2015 if (errval == 0)
2016 break;
2018 if (options & OPT_VERB)
2019 n_err(_("failed\n"));
2020 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
2021 urlp->url_host.s, serv, gai_strerror(errval));
2023 /* Error seems to depend on how "smart" the /etc/service code is: is it
2024 * "able" to state wether the service as such is NONAME or does it only
2025 * check for the given ai_socktype.. */
2026 if (errval == EAI_NONAME || errval == EAI_SERVICE) {
2027 if (serv == urlp->url_proto &&
2028 (serv = url_servbyname(urlp, NULL)) != NULL) {
2029 n_err(_(" Trying standard protocol port \"%s\"\n"
2030 " If that succeeds consider including the port in the URL!\n"),
2031 serv);
2032 continue;
2034 if (serv != urlp->url_port)
2035 n_err(_(" Including a port number in the URL may "
2036 "circumvent this problem\n"));
2038 assert(sofd == -1);
2039 errval = 0;
2040 goto jjumped;
2042 if (options & OPT_VERB)
2043 n_err(_("done\n"));
2045 for (res = res0; res != NULL && sofd < 0; res = res->ai_next) {
2046 if (options & OPT_VERB) {
2047 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof hbuf,
2048 NULL, 0, NI_NUMERICHOST))
2049 memcpy(hbuf, "unknown host", sizeof("unknown host"));
2050 n_err(_("%sConnecting to \"%s:%s\" ..."),
2051 (res == res0 ? "" : "\n"), hbuf, serv);
2054 sofd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2055 if (sofd >= 0) {
2056 # ifdef HAVE_SO_SNDTIMEO
2057 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2058 # endif
2059 if (connect(sofd, res->ai_addr, res->ai_addrlen)) {
2060 errval = errno;
2061 close(sofd);
2062 sofd = -1;
2067 jjumped:
2068 if (res0 != NULL) {
2069 freeaddrinfo(res0);
2070 res0 = NULL;
2073 # else /* HAVE_GETADDRINFO */
2074 if (serv == urlp->url_proto) {
2075 if ((ep = getservbyname(UNCONST(serv), "tcp")) != NULL)
2076 urlp->url_portno = ntohs(ep->s_port);
2077 else {
2078 if (options & OPT_VERB)
2079 n_err(_("failed\n"));
2080 if ((serv = url_servbyname(urlp, &urlp->url_portno)) != NULL)
2081 n_err(_(" Unknown service: \"%s\"\n"
2082 " Trying standard protocol port \"%s\"\n"
2083 " If that succeeds consider including the port in the URL!\n"),
2084 urlp->url_proto, serv);
2085 else {
2086 n_err(_(" Unknown service: \"%s\"\n"
2087 " Including a port in the URL may circumvent this problem\n"),
2088 urlp->url_proto);
2089 assert(sofd == -1 && errval == 0);
2090 goto jjumped;
2095 __sopen_sig = -1;
2096 hp = gethostbyname(urlp->url_host.s);
2097 if (__sopen_sig != -1) {
2098 __sopen_sig = SIGINT;
2099 goto jpseudo_jump;
2101 __sopen_sig = 0;
2103 if (hp == NULL) {
2104 char const *emsg;
2106 if (options & OPT_VERB)
2107 n_err(_("failed\n"));
2108 switch (h_errno) {
2109 case HOST_NOT_FOUND: emsg = N_("host not found"); break;
2110 default:
2111 case TRY_AGAIN: emsg = N_("(maybe) try again later"); break;
2112 case NO_RECOVERY: emsg = N_("non-recoverable server error"); break;
2113 case NO_DATA: emsg = N_("valid name without IP address"); break;
2115 n_err(_("Lookup of \"%s:%s\" failed: %s\n"),
2116 urlp->url_host.s, serv, V_(emsg));
2117 goto jjumped;
2118 } else if (options & OPT_VERB)
2119 n_err(_("done\n"));
2121 pptr = (struct in_addr**)hp->h_addr_list;
2122 if ((sofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
2123 n_perr(_("could not create socket"), 0);
2124 assert(sofd == -1 && errval == 0);
2125 goto jjumped;
2128 memset(&servaddr, 0, sizeof servaddr);
2129 servaddr.sin_family = AF_INET;
2130 servaddr.sin_port = htons(urlp->url_portno);
2131 memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
2132 if (options & OPT_VERB)
2133 n_err(_("%sConnecting to \"%s:%d\" ... "),
2134 "", inet_ntoa(**pptr), (int)urlp->url_portno);
2135 # ifdef HAVE_SO_SNDTIMEO
2136 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2137 # endif
2138 if (connect(sofd, (struct sockaddr*)&servaddr, sizeof servaddr)) {
2139 errval = errno;
2140 close(sofd);
2141 sofd = -1;
2143 jjumped:
2144 # endif /* !HAVE_GETADDRINFO */
2146 hold_sigs();
2147 safe_signal(SIGINT, oint);
2148 safe_signal(SIGHUP, ohup);
2149 rele_sigs();
2151 if (sofd < 0) {
2152 if (errval != 0) {
2153 errno = errval;
2154 n_perr(_("Could not connect"), 0);
2156 goto jleave;
2159 if (options & OPT_VERB)
2160 n_err(_("connected.\n"));
2162 /* And the regular timeouts XXX configurable */
2163 # ifdef HAVE_SO_SNDTIMEO
2164 tv.tv_sec = 42;
2165 tv.tv_usec = 0;
2166 (void)setsockopt(sofd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
2167 (void)setsockopt(sofd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
2168 # endif
2169 # ifdef HAVE_SO_LINGER
2170 li.l_onoff = 1;
2171 li.l_linger = 42;
2172 (void)setsockopt(sofd, SOL_SOCKET, SO_LINGER, &li, sizeof li);
2173 # endif
2175 memset(sp, 0, sizeof *sp);
2176 sp->s_fd = sofd;
2178 /* SSL/TLS upgrade? */
2179 # ifdef HAVE_SSL
2180 if (urlp->url_needs_tls) {
2181 hold_sigs();
2182 ohup = safe_signal(SIGHUP, &__sopen_onsig);
2183 oint = safe_signal(SIGINT, &__sopen_onsig);
2184 if (sigsetjmp(__sopen_actjmp, 0)) {
2185 n_err(_("%s during SSL/TLS handshake\n"),
2186 (__sopen_sig == SIGHUP ? _("Hangup") : _("Interrupted")));
2187 goto jsclose;
2189 rele_sigs();
2191 if (ssl_open(urlp, sp) != OKAY) {
2192 jsclose:
2193 sclose(sp);
2194 sofd = -1;
2197 hold_sigs();
2198 safe_signal(SIGINT, oint);
2199 safe_signal(SIGHUP, ohup);
2200 rele_sigs();
2202 # endif /* HAVE_SSL */
2204 jleave:
2205 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
2206 if (__sopen_sig != 0) {
2207 sigset_t cset;
2208 sigemptyset(&cset);
2209 sigaddset(&cset, __sopen_sig);
2210 sigprocmask(SIG_UNBLOCK, &cset, NULL);
2211 n_raise(__sopen_sig);
2213 NYD_LEAVE;
2214 return (sofd >= 0);
2217 FL int
2218 (sgetline)(char **line, size_t *linesize, size_t *linelen, struct sock *sp
2219 SMALLOC_DEBUG_ARGS)
2221 int rv;
2222 size_t lsize;
2223 char *lp_base, *lp;
2224 NYD2_ENTER;
2226 lsize = *linesize;
2227 lp_base = *line;
2228 lp = lp_base;
2230 if (sp->s_rsz < 0) {
2231 sclose(sp);
2232 rv = sp->s_rsz;
2233 goto jleave;
2236 do {
2237 if (lp_base == NULL || PTRCMP(lp, >, lp_base + lsize - 128)) {
2238 size_t diff = PTR2SIZE(lp - lp_base);
2239 *linesize = (lsize += 256); /* XXX magic */
2240 *line = lp_base = (srealloc)(lp_base, lsize SMALLOC_DEBUG_ARGSCALL);
2241 lp = lp_base + diff;
2244 if (sp->s_rbufptr == NULL ||
2245 PTRCMP(sp->s_rbufptr, >=, sp->s_rbuf + sp->s_rsz)) {
2246 # ifdef HAVE_OPENSSL
2247 if (sp->s_use_ssl) {
2248 jssl_retry:
2249 sp->s_rsz = SSL_read(sp->s_ssl, sp->s_rbuf, sizeof sp->s_rbuf);
2250 if (sp->s_rsz <= 0) {
2251 if (sp->s_rsz < 0) {
2252 char o[512];
2253 switch(SSL_get_error(sp->s_ssl, sp->s_rsz)) {
2254 case SSL_ERROR_WANT_READ:
2255 case SSL_ERROR_WANT_WRITE:
2256 goto jssl_retry;
2258 snprintf(o, sizeof o, "%s",
2259 (sp->s_desc ? sp->s_desc : "socket"));
2260 ssl_gen_err("%s", o);
2262 break;
2264 } else
2265 # endif
2267 jagain:
2268 sp->s_rsz = read(sp->s_fd, sp->s_rbuf, sizeof sp->s_rbuf);
2269 if (sp->s_rsz <= 0) {
2270 if (sp->s_rsz < 0) {
2271 char o[512];
2272 if (errno == EINTR)
2273 goto jagain;
2274 snprintf(o, sizeof o, "%s",
2275 (sp->s_desc ? sp->s_desc : "socket"));
2276 n_perr(o, 0);
2278 break;
2281 sp->s_rbufptr = sp->s_rbuf;
2283 } while ((*lp++ = *sp->s_rbufptr++) != '\n');
2284 *lp = '\0';
2285 lsize = PTR2SIZE(lp - lp_base);
2287 if (linelen)
2288 *linelen = lsize;
2289 rv = (int)lsize;
2290 jleave:
2291 NYD2_LEAVE;
2292 return rv;
2294 #endif /* HAVE_SOCKETS */
2296 FL void
2297 load(char const *name)
2299 struct str n;
2300 void *cond;
2301 FILE *in, *oldin;
2302 NYD_ENTER;
2304 if (name == NULL || *name == '\0' || (in = Fopen(name, "r")) == NULL)
2305 goto jleave;
2307 oldin = _fio_input;
2308 _fio_input = in;
2309 pstate |= PS_IN_LOAD;
2310 /* commands() may sreset(), copy over file name */
2311 n.l = strlen(name);
2312 n.s = ac_alloc(n.l +1);
2313 memcpy(n.s, name, n.l +1);
2315 cond = condstack_release();
2316 if (!commands())
2317 n_err(_("Stopped loading \"%s\" due to errors "
2318 "(enable *debug* for trace)\n"), n.s);
2319 condstack_take(cond);
2321 ac_free(n.s);
2322 pstate &= ~PS_IN_LOAD;
2323 _fio_input = oldin;
2324 Fclose(in);
2325 jleave:
2326 NYD_LEAVE;
2329 FL int
2330 c_source(void *v)
2332 int rv;
2333 NYD_ENTER;
2335 rv = _source_file(*(char**)v, FAL0) ? 0 : 1;
2336 NYD_LEAVE;
2337 return rv;
2340 FL int
2341 c_source_if(void *v) /* XXX obsolete?, support file tests in `if' etc.! */
2343 int rv;
2344 NYD_ENTER;
2346 rv = _source_file(*(char**)v, TRU1) ? 0 : 1;
2347 rv = 0;
2348 NYD_LEAVE;
2349 return rv;
2352 FL int
2353 unstack(void)
2355 int rv = 1;
2356 NYD_ENTER;
2358 if (_fio_stack_size == 0) {
2359 n_err(_("`source' stack over-pop\n"));
2360 pstate &= ~PS_SOURCING;
2361 goto jleave;
2364 Fclose(_fio_input);
2366 --_fio_stack_size;
2367 if (!condstack_take(_fio_stack[_fio_stack_size].s_cond))
2368 n_err(_("Unmatched \"if\"\n"));
2369 if (_fio_stack[_fio_stack_size].s_loading)
2370 pstate |= PS_LOADING;
2371 else
2372 pstate &= ~PS_LOADING;
2373 _fio_input = _fio_stack[_fio_stack_size].s_file;
2374 if (_fio_stack_size == 0) {
2375 if (pstate & PS_LOADING)
2376 pstate |= PS_SOURCING;
2377 else
2378 pstate &= ~PS_SOURCING;
2380 rv = 0;
2381 jleave:
2382 NYD_LEAVE;
2383 return rv;
2386 /* s-it-mode */