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>.
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
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
38 #ifndef HAVE_AMALGAMATION
45 # include <sys/socket.h>
49 # include <netinet/in.h>
51 # ifdef HAVE_ARPA_INET_H
52 # include <arpa/inet.h>
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>
73 FILE *s_file
; /* File we were in. */
74 void *s_cond
; /* Saved state of conditional stack */
75 int s_loading
; /* Loading .mailrc, etc. */
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 */
90 static enum file_lock_type _dotlock_flt
;
91 static int _dotlock_fd
;
92 struct dotlock_info
* _dotlock_dip
;
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
,
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
);
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 */
131 static int _dotlock_main(void);
134 /* Write to socket fd, restarting on EINTR, unless anything is written */
136 static long xwrite(int fd
, char const *data
, size_t sz
);
139 /* `source' and `source_if' */
140 static bool_t
_source_file(char const *file
, bool_t silent_error
);
143 _findmail(char *buf
, size_t bufsize
, char const *user
, bool_t force
)
145 char const *ibox
, *cp
;
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*");
157 if (!force
&& ibox
!= NULL
) {
158 /* Folder extra introduced to avoid % recursion loops */
159 if ((cp
= fexpand(ibox
, FEXP_FOLDER
| FEXP_NSHELL
)) != NULL
)
161 n_err(_("Failed to expand *ibox*, using $MAIL or default: %s\n"), ibox
);
165 if (force
|| (((cp
= ibox
) == NULL
|| *cp
== '\0') &&
166 (cp
= ok_vlook(MAIL
)) == NULL
))
167 snprintf(buf
, bufsize
, "%s/%s", MAILSPOOL
, user
);
170 n_strlcpy(buf
, cp
, bufsize
);
175 _shvar_exp(char const *name
)
177 struct shvar_stack top
;
181 memset(&top
, 0, sizeof top
);
182 top
.shs_value
= name
;
183 rv
= __shvar_exp(&top
);
190 __shvar_exp(struct shvar_stack
*shsp
)
192 struct shvar_stack next
, *np
, *tmp
;
194 char lc
, c
, *cp
, *rv
;
198 if (*(vp
= shsp
->shs_value
) != '$') {
199 union {bool_t hadbs
; char c
;} u
= {FAL0
};
202 for (lc
= '\0', i
= 0; ((c
= *vp
) != '\0'); ++i
, ++vp
) {
203 if (c
== '$' && lc
!= '\\')
205 lc
= (lc
== '\\') ? (u
.hadbs
= TRU1
, '\0') : c
;
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
== '\\')
215 lc
= (lc
== '\\') ? '\0' : u
.c
;
219 shsp
->shs_len
= PTR2SIZE(rv
- shsp
->shs_dat
);
222 if ((lc
= (*++vp
== '{')))
226 for (i
= 0; (c
= *vp
) != '\0'; ++i
, ++vp
)
227 if (!alnumchar(c
) && c
!= '_')
232 n_err(_("Variable name misses closing \"}\": \"%s\"\n"),
234 shsp
->shs_len
= strlen(shsp
->shs_value
);
235 shsp
->shs_dat
= shsp
->shs_value
;
242 if ((cp
= vok_vlook(savestrbuf(shsp
->shs_dat
, i
))) != NULL
)
243 shsp
->shs_len
= strlen(shsp
->shs_dat
= cp
);
248 /* That level made the great and completed encoding. Build result */
250 for (i
= 0, np
= shsp
, shsp
= NULL
; np
!= NULL
;) {
258 cp
= rv
= salloc(i
+1);
259 while (shsp
!= NULL
) {
261 shsp
= shsp
->shs_next
;
262 memcpy(cp
, np
->shs_dat
, np
->shs_len
);
271 memset(&next
, 0, sizeof next
);
272 next
.shs_next
= shsp
;
274 rv
= __shvar_exp(&next
);
279 _globname(char const *name
, enum fexp_mode fexpm
)
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 */
295 sigaddset(&nset
, SIGCHLD
);
296 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
298 # define WRDE_NOCMD 0
300 i
= wordexp(name
, &we
, WRDE_NOCMD
);
301 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
308 if (!(fexpm
& FEXP_SILENT
))
309 n_err(_("\"%s\": Command substitution not allowed\n"), name
);
313 if (!(fexpm
& FEXP_SILENT
))
314 n_err(_("\"%s\": Expansion buffer overflow\n"), name
);
319 if (!(fexpm
& FEXP_SILENT
))
320 n_err(_("Syntax error in \"%s\"\n"), name
);
324 switch (we
.we_wordc
) {
326 cp
= savestr(we
.we_wordv
[0]);
329 if (!(fexpm
& FEXP_SILENT
))
330 n_err(_("\"%s\": No match\n"), name
);
333 if (fexpm
& FEXP_MULTIOK
) {
336 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
)
337 l
+= strlen(we
.we_wordv
[j
]) + 1;
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
);
347 } else if (!(fexpm
& FEXP_SILENT
))
348 n_err(_("\"%s\": Ambiguous\n"), name
);
356 #else /* HAVE_WORDEXP */
358 char xname
[PATH_MAX
+1], cmdbuf
[PATH_MAX
+1], /* also used for files */
360 int pivec
[2], pid
, l
, waits
;
363 if (pipe(pivec
) < 0) {
364 n_perr(_("pipe"), 0);
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
);
379 l
= read(pivec
[0], xname
, sizeof xname
);
383 n_perr(_("read"), 0);
388 if (!wait_child(pid
, &waits
) && WTERMSIG(waits
) != SIGPIPE
) {
389 if (!(fexpm
& FEXP_SILENT
))
390 n_err(_("\"%s\": Expansion failed\n"), name
);
394 if (!(fexpm
& FEXP_SILENT
))
395 n_err(_("\"%s\": No match\n"), name
);
398 if (l
== sizeof xname
) {
399 if (!(fexpm
& FEXP_SILENT
))
400 n_err(_("\"%s\": Expansion buffer overflow\n"), name
);
404 for (cp
= xname
+ l
- 1; *cp
== '\n' && cp
> xname
; --cp
)
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
);
418 #endif /* !HAVE_WORDEXP */
422 _length_of_line(char const *line
, size_t linesize
)
427 /* Last character is always '\0' and was added by fgets() */
428 for (--linesize
, i
= 0; i
< linesize
; i
++)
431 i
= (i
< linesize
) ? i
+ 1 : linesize
;
437 _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
, FILE *fp
,
438 int appendnl
, size_t n SMALLOC_DEBUG_ARGS
)
444 assert(*linesize
== 0 || *line
!= NULL
);
446 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
447 *linesize
+= ((rv
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
448 *line
= rv
= (srealloc
)(rv
, *linesize SMALLOC_DEBUG_ARGSCALL
);
481 message_append(NULL
);
483 message
[msgCount
].m_size
= 0;
484 message
[msgCount
].m_lines
= 0;
489 get_header(struct message
*mp
)
495 switch (mb
.mb_type
) {
502 rv
= pop3_header(mp
);
508 rv
= imap_header(mp
);
521 _file_lock(int fd
, enum file_lock_type flt
, off_t off
, off_t len
)
527 memset(&flp
, 0, sizeof flp
);
531 case FLT_READ
: rv
= F_RDLCK
; break;
532 case FLT_WRITE
: rv
= F_WRLCK
; break;
536 flp
.l_whence
= SEEK_SET
;
539 rv
= (fcntl(fd
, F_SETLK
, &flp
) != -1);
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
;
556 enum file_lock_type flt
;
559 /* Ignore SIGPIPE, we'll see EPIPE and "fall through" */
560 safe_signal(SIGPIPE
, SIG_IGN
);
562 /* Get the arguments "passed to us" */
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] == '/')
577 cp
= savestrbuf(di
.di_file_name
, PTR2SIZE(cp
- di
.di_file_name
));
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)
590 if (S_ISLNK(stb
.st_mode
)) {
591 /* Use salloc() and hope we stay in builtin buffer.. */
596 for (x
= NULL
, i
= PATH_MAX
;; i
+= PATH_MAX
) {
598 sr
= readlink(cp
, x
, i
);
600 dls
= DLS_FISHY
| DLS_ABANDON
;
603 if (UICMP(z
, sr
, <, i
)) {
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
)
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
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 */
634 dls
= DLS_NAMETOOLONG
| DLS_ABANDON
;
637 # ifdef HAVE_PATHCONF
639 if((pc
= pathconf(".", _PC_NAME_MAX
)) == -1){
640 /* errno unchanged: no limit */
643 }else if(pc
- 1 >= (long)i
)
648 if(UICMP(z
, NAME_MAX
- 1, <, (uiz_t
)i
))
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
;
676 if (cp
== NULL
|| stb
.st_uid
!= user_id
|| stb
.st_gid
!= group_id
) {
678 char const *args
[13];
680 snprintf(itoabuf
, sizeof itoabuf
, "%" PRIuZ
, di
.di_pollmsecs
);
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
;
689 execv(LIBEXECDIR
"/" UAGENT
"-privsep", UNCONST(args
));
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
);
708 dls
= _dotlock_create(&di
);
711 /* Finally: notify our parent about the actual lock state.. */
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
);
726 #endif /* HAVE_DOTLOCK */
730 xwrite(int fd
, char const *data
, size_t sz
)
737 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
750 #endif /* HAVE_SOCKETS */
753 _source_file(char const *file
, bool_t silent_error
)
759 if ((cp
= fexpand(file
, FEXP_LOCAL
)) == NULL
)
761 if ((fi
= Fopen(cp
, "r")) == NULL
) {
762 if (!silent_error
|| (options
& OPT_D_V
))
767 if (temporary_localopts_store
!= NULL
) {
768 n_err(_("Before v15 you cannot `source' from within macros, sorry\n"));
771 if (_fio_stack_size
>= NELEM(_fio_stack
)) {
772 n_err(_("Too many `source' recursions\n"));
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
);
783 pstate
&= ~PS_LOADING
;
784 pstate
|= PS_SOURCING
;
792 (fgetline
)(char **line
, size_t *linesize
, size_t *cnt
, size_t *llen
, FILE *fp
,
793 int appendnl SMALLOC_DEBUG_ARGS
)
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
);
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 */
817 i_llen
= _length_of_line(rv
, sz
);
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
) {
830 sz
= _length_of_line(rv
+ i_llen
, sz
);
842 (readline_restart
)(FILE *ibuf
, char **linebuf
, size_t *linesize
, size_t n
845 /* TODO readline_restart(): always *appends* LF just to strip it again;
846 * TODO should be configurable just as for fgetline(); ..or whatever.. */
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
);
858 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
859 *linesize
+= ((*linebuf
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
860 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
863 sz
= read(0, *linebuf
+ n
, *linesize
- n
- 1);
866 (*linebuf
)[n
] = '\0';
867 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
870 if (sz
< 0 && errno
== EINTR
)
873 if ((*linebuf
)[n
- 1] != '\n') {
874 (*linebuf
)[n
++] = '\n';
875 (*linebuf
)[n
] = '\0';
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
)
890 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
891 (*linebuf
)[--n
] = '\0';
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
;
908 doprompt
= (!(pstate
& PS_SOURCING
) && (options
& OPT_INTERACTIVE
));
909 dotty
= (doprompt
&& !ok_blook(line_editor_disable
));
912 else if (prompt
== NULL
)
913 prompt
= getprompt();
915 /* Ensure stdout is flushed first anyway */
916 if (!dotty
&& prompt
== NULL
)
919 for (nold
= n
= 0;;) {
921 assert(ifile
== stdin
);
922 if (string
!= NULL
&& (n
= (int)strlen(string
)) > 0) {
926 *linesize
= (size_t)n
+ LINESIZE
+1;
927 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
928 memcpy(*linebuf
, string
, (size_t)n
+1);
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
);
936 if (prompt
!= NULL
) {
938 fputs(prompt
, stdout
);
941 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
942 SMALLOC_DEBUG_ARGSCALL
);
944 if (n
> 0 && nold
> 0) {
946 char const *cp
= *linebuf
+ nold
;
948 while (blankspacechar(*cp
) && nold
+ i
< n
)
951 memmove(*linebuf
+ nold
, cp
, n
- nold
- i
);
953 (*linebuf
)[n
] = '\0';
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] != '\\')
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"),
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 */
984 char *linebuf
= NULL
, *rv
= NULL
;
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
);
1000 setptr(FILE *ibuf
, off_t offset
)
1002 struct message self
;
1003 char *cp
, *linebuf
= NULL
;
1006 bool_t maybe
, inhead
, rfc4155
;
1007 size_t linesize
= 0, filesize
, cnt
;
1010 memset(&self
, 0, sizeof self
);
1011 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
1012 filesize
= mailsize
- offset
;
1013 offset
= ftell(mb
.mb_otf
);
1015 rfc4155
= inhead
= FAL0
;
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
;
1023 message_append(&self
);
1031 if (linebuf
[0] == '\0')
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);
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"),
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
;
1062 message_append(&self
);
1064 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
1067 self
.m_block
= mailx_blockof(offset
);
1068 self
.m_offset
= mailx_offsetof(offset
);
1070 } else if (linebuf
[0] == 0) {
1072 } else if (inhead
) {
1073 for (cp
= linebuf
, cp2
= "status";; ++cp
) {
1074 if ((c
= *cp2
++) == 0) {
1075 while (c
= *cp
++, whitechar(c
))
1079 while ((c
= *cp
++) != '\0')
1081 self
.m_flag
|= MREAD
;
1083 self
.m_flag
&= ~MNEW
;
1086 if (*cp
!= c
&& *cp
!= upperconv(c
))
1089 for (cp
= linebuf
, cp2
= "x-status";; ++cp
) {
1090 if ((c
= *cp2
++) == 0) {
1091 while ((c
= *cp
++, whitechar(c
)))
1095 while ((c
= *cp
++) != '\0')
1097 self
.m_flag
|= MFLAGGED
;
1099 self
.m_flag
|= MANSWERED
;
1101 self
.m_flag
|= MDRAFTED
;
1104 if (*cp
!= c
&& *cp
!= upperconv(c
))
1111 maybe
= (linebuf
[0] == 0);
1117 putline(FILE *obuf
, char *linebuf
, size_t cnt
)
1122 fwrite(linebuf
, sizeof *linebuf
, cnt
, obuf
);
1125 rv
= (int)(cnt
+ 1);
1131 setinput(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1134 enum okay ok
= STOP
;
1139 ok
= (m
->m_have
& HAVE_HEADER
) ? OKAY
: get_header(m
);
1142 ok
= (m
->m_have
& HAVE_BODY
) ? OKAY
: get_body(m
);
1152 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
1154 n_perr(_("fseek"), 0);
1155 n_panic(_("temporary file seek"));
1167 if (message
!= NULL
) {
1177 message_append(struct message
*mp
)
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
);
1188 message
[msgCount
- 1] = *mp
;
1190 memset(message
+ msgCount
- 1, 0, sizeof *message
);
1196 message_match(struct message
*mp
, struct search_expr
const *sep
,
1197 bool_t with_headers
)
1200 size_t *linesize
, cnt
;
1205 if ((fp
= Ftmp(NULL
, "mpmatch", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
1209 if (sendmp(mp
, fp
, NULL
, NULL
, SEND_TOSRCH
, NULL
) < 0)
1214 line
= &termios_state
.ts_linebuf
; /* XXX line pool */
1215 linesize
= &termios_state
.ts_linesize
; /* XXX line pool */
1218 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0))
1222 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0)) {
1224 if (sep
->ss_sexpr
== NULL
) {
1225 if (regexec(&sep
->ss_regex
, *line
, 0,NULL
, 0) == REG_NOMATCH
)
1229 if (!substr(*line
, sep
->ss_sexpr
))
1243 setdot(struct message
*mp
)
1248 pstate
&= ~PS_DID_PRINT_DOT
;
1251 uncollapse1(dot
, 0);
1257 rm(char const *name
)
1263 if (stat(name
, &sb
) < 0)
1265 else if (!S_ISREG(sb
.st_mode
))
1280 rv
= (fstat(fileno(iob
), &sbuf
) == -1) ? 0 : sbuf
.st_size
;
1286 fexpand(char const *name
, enum fexp_mode fexpm
)
1288 char cbuf
[PATH_MAX
+1];
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
)) {
1303 if (fexpm
& FEXP_SHELL
)
1312 if (res
[1] == ':' && res
[2] != '\0') {
1316 _findmail(cbuf
, sizeof cbuf
, (res
[1] != '\0' ? res
+ 1 : myname
),
1317 (res
[1] != '\0' || (options
& OPT_u_FLAG
)));
1323 if (prevfile
[0] == '\0') {
1324 n_err(_("No previous file\n"));
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')
1340 if (res
[0] == '@' && which_protocol(mailname
) == PROTO_IMAP
) {
1341 res
= str_concat_csvl(&s
, protbase(mailname
), "/", res
+ 1, NULL
)->s
;
1346 if (res
[0] == '+' && getfold(cbuf
, sizeof cbuf
)) {
1347 res
= str_concat_csvl(&s
, cbuf
, &res
[1], NULL
)->s
;
1350 if (res
[0] == '%' && res
[1] == ':') {
1356 /* Catch the most common shell meta character */
1358 if (res
[0] == '~' && (res
[1] == '/' || res
[1] == '\0')) {
1359 res
= str_concat_csvl(&s
, homedir
, res
+ 1, NULL
)->s
;
1362 if (anyof(res
, n_SHEXP_MAGIC_PATH_CHARS
))
1363 switch (which_protocol(res
)) {
1366 res
= (fexpm
& FEXP_NSHELL
) ? _shvar_exp(res
) : _globname(res
, fexpm
);
1373 if (fexpm
& FEXP_LOCAL
)
1374 switch (which_protocol(res
)) {
1379 n_err(_("Not a local file or directory: \"%s\"\n"), name
);
1387 return UNCONST(res
);
1391 fexpand_nshell_quote(char const *name
)
1397 for (i
= j
= 0; (c
= name
[i
]) != '\0'; ++i
)
1402 rv
= savestrbuf(name
, i
);
1404 rv
= salloc(i
+ j
+1);
1405 for (i
= j
= 0; (c
= name
[i
]) != '\0'; ++i
) {
1417 var_folder_updated(char const *name
, char **store
)
1420 char *folder
, *unres
= NULL
, *res
= NULL
;
1423 if ((folder
= UNCONST(name
)) == NULL
)
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] == ':')
1431 if ((folder
= fexpand(folder
, FEXP_FULL
)) == NULL
) /* XXX error? */
1434 switch (which_protocol(folder
)) {
1436 n_err(_("*folder* cannot be set to a flat, readonly POP3 account\n"));
1440 /* Simply assign what we have, even including `%:' prefix */
1445 /* Further expansion desired */
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
);
1455 memcpy(unres
+ l1
+ 1, folder
, l2
);
1456 unres
[l1
+ 1 + l2
] = '\0';
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
);
1474 *store
= sstrdup(folder
);
1486 getfold(char *name
, size_t size
)
1491 if ((folder
= ok_vlook(folder
)) != NULL
){
1495 if(i
> 0 && folder
[i
- 1] != '/')
1496 folder
= savecat(folder
, "/");
1497 n_strlcpy(name
, folder
, size
);
1500 return (folder
!= NULL
);
1504 getdeadletter(void) /* XXX should that be in auxlily.c? */
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
);
1521 cp
= "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1527 get_body(struct message
*mp
)
1533 switch (mb
.mb_type
) {
1559 file_lock(int fd
, enum file_lock_type flt
, off_t off
, off_t len
,
1566 for (tries
= 0; tries
<= FILE_LOCK_TRIES
; ++tries
)
1567 if ((rv
= _file_lock(fd
, flt
, off
, len
)) || pollmsecs
== 0)
1570 sleep(1); /* TODO pollmsecs -> use finer grain */
1576 dot_lock(char const *fname
, int fd
, enum file_lock_type flt
,
1577 off_t off
, off_t len
, size_t pollmsecs
)
1581 # define _DOMSG() n_err(_("Creating dotlock for \"%s\" "), fname)
1583 # define _DOMSG() n_err(_("Trying to lock file \"%s\" "), fname)
1588 struct dotlock_info di
;
1589 enum dotlock_state dls
;
1590 char const *emsg
= NULL
;
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
;
1598 if (options
& OPT_D_VV
) {
1604 for (u
.tries
= 0; !_file_lock(fd
, flt
, off
, len
);)
1605 switch ((serrno
= errno
)) {
1609 if (pollmsecs
> 0 && ++u
.tries
< FILE_LOCK_TRIES
) {
1614 sleep(1); /* TODO pollmsecs -> use finer grain */
1623 #ifndef HAVE_DOTLOCK
1625 if (didmsg
== TRUM1
)
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) {
1639 emsg
= N_(" Can't create file lock control pipe\n");
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
1653 di
.di_hostname
= nodename(FAL0
);
1654 di
.di_randstr
= getrandstring(16);
1659 u
.ptf
= &_dotlock_main
;
1660 rv
= Popen((char*)-1, "W", u
.sh
, NULL
, cpipe
[1]);
1666 emsg
= N_(" Can't create file lock process\n");
1670 /* Let's check wether we were able to create the dotlock file */
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
;
1679 if (dls
== DLS_NONE
|| (dls
& DLS_ABANDON
))
1682 switch (dls
& ~DLS_ABANDON
) {
1685 case DLS_CANT_CHDIR
:
1686 if (options
& OPT_D_V
)
1687 emsg
= N_(" Can't change to directory! Please check permissions\n");
1690 case DLS_NAMETOOLONG
:
1691 emsg
= N_("Resulting dotlock filename would be too long\n");
1695 assert(dls
& DLS_ABANDON
);
1696 if (options
& OPT_D_V
)
1697 emsg
= N_(" Read-only filesystem, not creating lock file\n");
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");
1707 if (options
& OPT_D_V
)
1708 emsg
= N_(" Can't find privilege-separated file lock program\n");
1711 case DLS_PRIVFAILED
:
1712 emsg
= N_(" Privilege-separated file lock program can't change "
1717 emsg
= N_(" It seems there is a stale dotlock file?\n"
1718 " Please remove the lock file manually, then retry\n");
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 */
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
)
1746 if (didmsg
== TRUM1
)
1753 if (dls
& DLS_ABANDON
) {
1761 if (didmsg
== TRUM1
)
1764 if (flocked
&& (serrno
== EROFS
||
1765 (serrno
!= EAGAIN
&& serrno
!= EEXIST
&&
1766 ok_blook(dotlock_ignore_error
))))
1780 #endif /* HAVE_DOTLOCK */
1786 sclose(struct sock
*sp
)
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 */
1799 if (sp
->s_onclose
!= NULL
)
1801 if (sp
->s_wbuf
!= NULL
)
1803 # ifdef HAVE_OPENSSL
1804 if (sp
->s_use_ssl
) {
1805 void *s_ssl
= sp
->s_ssl
;
1809 while (!SSL_shutdown(s_ssl
)) /* XXX proper error handling;signals! */
1821 swrite(struct sock
*sp
, char const *data
)
1826 rv
= swrite1(sp
, data
, strlen(data
), 0);
1832 swrite1(struct sock
*sp
, char const *data
, int sz
, int use_buffer
)
1834 enum okay rv
= STOP
;
1838 if (use_buffer
> 0) {
1841 if (sp
->s_wbuf
== NULL
) {
1842 sp
->s_wbufsize
= 4096;
1843 sp
->s_wbuf
= smalloc(sp
->s_wbufsize
);
1846 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
1847 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
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);
1853 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1859 if (sz
== sp
->s_wbufsize
) {
1860 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1864 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, sz
);
1865 sp
->s_wbufpos
+= sz
;
1869 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&& sp
->s_wbufpos
> 0) {
1872 if ((rv
= swrite1(sp
, sp
->s_wbuf
, x
, -1)) != OKAY
)
1880 # ifdef HAVE_OPENSSL
1881 if (sp
->s_use_ssl
) {
1883 x
= SSL_write(sp
->s_ssl
, data
, sz
);
1885 switch (SSL_get_error(sp
->s_ssl
, x
)) {
1886 case SSL_ERROR_WANT_READ
:
1887 case SSL_ERROR_WANT_WRITE
:
1894 x
= xwrite(sp
->s_fd
, data
, sz
);
1898 snprintf(o
, sizeof o
, "%s write error",
1899 (sp
->s_desc
? sp
->s_desc
: "socket"));
1900 # ifdef HAVE_OPENSSL
1902 ssl_gen_err("%s", o
);
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 */
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 */
1927 if (__sopen_sig
== -1) {
1929 _("\nInterrupting it could turn the (GNU/Linux+) DNS resolver "
1931 " Wait until it's done, or do terminate the program\n"));
1933 } else if ((i
= j
= ABS(__sopen_sig
)) + 15 < scrnwidth
) {
1937 fputs("___( o)", stderr
);
1938 putc((i
& 1) ? '=' : '>', stderr
);
1946 siglongjmp(__sopen_actjmp
, 1);
1951 sopen(struct sock
*sp
, struct url
*urlp
) /* TODO sighandling; refactor */
1953 # ifdef HAVE_SO_SNDTIMEO
1956 # ifdef HAVE_SO_LINGER
1959 # ifdef HAVE_GETADDRINFO
1960 char hbuf
[NI_MAXHOST
];
1961 struct addrinfo hints
, *res0
= NULL
, *res
;
1963 struct sockaddr_in servaddr
;
1964 struct in_addr
**pptr
;
1968 sighandler_type
volatile ohup
, oint
;
1969 char const * volatile serv
;
1970 int volatile sofd
= -1, errval
;
1975 /* Connect timeouts after 30 seconds XXX configurable */
1976 # ifdef HAVE_SO_SNDTIMEO
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 */
1990 ohup
= safe_signal(SIGHUP
, &__sopen_onsig
);
1991 oint
= safe_signal(SIGINT
, &__sopen_onsig
);
1992 if (sigsetjmp(__sopen_actjmp
, 0)) {
1995 (__sopen_sig
== SIGHUP
? _("Hangup") : _("Interrupted")));
2004 # ifdef HAVE_GETADDRINFO
2006 memset(&hints
, 0, sizeof hints
);
2007 hints
.ai_socktype
= SOCK_STREAM
;
2009 errval
= getaddrinfo(urlp
->url_host
.s
, serv
, &hints
, &res0
);
2010 if (__sopen_sig
!= -1) {
2011 __sopen_sig
= SIGINT
;
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"),
2034 if (serv
!= urlp
->url_port
)
2035 n_err(_(" Including a port number in the URL may "
2036 "circumvent this problem\n"));
2042 if (options
& OPT_VERB
)
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
);
2056 # ifdef HAVE_SO_SNDTIMEO
2057 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
2059 if (connect(sofd
, res
->ai_addr
, res
->ai_addrlen
)) {
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
);
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
);
2086 n_err(_(" Unknown service: \"%s\"\n"
2087 " Including a port in the URL may circumvent this problem\n"),
2089 assert(sofd
== -1 && errval
== 0);
2096 hp
= gethostbyname(urlp
->url_host
.s
);
2097 if (__sopen_sig
!= -1) {
2098 __sopen_sig
= SIGINT
;
2106 if (options
& OPT_VERB
)
2107 n_err(_("failed\n"));
2109 case HOST_NOT_FOUND
: emsg
= N_("host not found"); break;
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
));
2118 } else if (options
& OPT_VERB
)
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);
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
);
2138 if (connect(sofd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
)) {
2144 # endif /* !HAVE_GETADDRINFO */
2147 safe_signal(SIGINT
, oint
);
2148 safe_signal(SIGHUP
, ohup
);
2154 n_perr(_("Could not connect"), 0);
2159 if (options
& OPT_VERB
)
2160 n_err(_("connected.\n"));
2162 /* And the regular timeouts XXX configurable */
2163 # ifdef HAVE_SO_SNDTIMEO
2166 (void)setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
2167 (void)setsockopt(sofd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
2169 # ifdef HAVE_SO_LINGER
2172 (void)setsockopt(sofd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
2175 memset(sp
, 0, sizeof *sp
);
2178 /* SSL/TLS upgrade? */
2180 if (urlp
->url_needs_tls
) {
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")));
2191 if (ssl_open(urlp
, sp
) != OKAY
) {
2198 safe_signal(SIGINT
, oint
);
2199 safe_signal(SIGHUP
, ohup
);
2202 # endif /* HAVE_SSL */
2205 /* May need to bounce the signal to the lex.c trampoline (or wherever) */
2206 if (__sopen_sig
!= 0) {
2209 sigaddset(&cset
, __sopen_sig
);
2210 sigprocmask(SIG_UNBLOCK
, &cset
, NULL
);
2211 n_raise(__sopen_sig
);
2218 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
2230 if (sp
->s_rsz
< 0) {
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
) {
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) {
2253 switch(SSL_get_error(sp
->s_ssl
, sp
->s_rsz
)) {
2254 case SSL_ERROR_WANT_READ
:
2255 case SSL_ERROR_WANT_WRITE
:
2258 snprintf(o
, sizeof o
, "%s",
2259 (sp
->s_desc
? sp
->s_desc
: "socket"));
2260 ssl_gen_err("%s", o
);
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) {
2274 snprintf(o
, sizeof o
, "%s",
2275 (sp
->s_desc
? sp
->s_desc
: "socket"));
2281 sp
->s_rbufptr
= sp
->s_rbuf
;
2283 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
2285 lsize
= PTR2SIZE(lp
- lp_base
);
2294 #endif /* HAVE_SOCKETS */
2297 load(char const *name
)
2304 if (name
== NULL
|| *name
== '\0' || (in
= Fopen(name
, "r")) == NULL
)
2309 pstate
|= PS_IN_LOAD
;
2310 /* commands() may sreset(), copy over file name */
2312 n
.s
= ac_alloc(n
.l
+1);
2313 memcpy(n
.s
, name
, n
.l
+1);
2315 cond
= condstack_release();
2317 n_err(_("Stopped loading \"%s\" due to errors "
2318 "(enable *debug* for trace)\n"), n
.s
);
2319 condstack_take(cond
);
2322 pstate
&= ~PS_IN_LOAD
;
2335 rv
= _source_file(*(char**)v
, FAL0
) ? 0 : 1;
2341 c_source_if(void *v
) /* XXX obsolete?, support file tests in `if' etc.! */
2346 rv
= _source_file(*(char**)v
, TRU1
) ? 0 : 1;
2358 if (_fio_stack_size
== 0) {
2359 n_err(_("`source' stack over-pop\n"));
2360 pstate
&= ~PS_SOURCING
;
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
;
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
;
2378 pstate
&= ~PS_SOURCING
;