1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #ifndef HAVE_AMALGAMATION
53 # include <sys/socket.h>
57 # include <netinet/in.h>
59 # ifdef HAVE_ARPA_INET_H
60 # include <arpa/inet.h>
65 # include <openssl/err.h>
66 # include <openssl/rand.h>
67 # include <openssl/ssl.h>
68 # include <openssl/x509v3.h>
69 # include <openssl/x509.h>
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 static size_t _message_space
; /* Slots in ::message */
79 static struct fio_stack _fio_stack
[FIO_STACK_SIZE
];
80 static size_t _fio_stack_size
;
81 static FILE * _fio_input
;
83 /* Locate the user's mailbox file (where new, unread mail is queued) */
84 static void _findmail(char *buf
, size_t bufsize
, char const *user
,
87 /* Perform shell meta character expansion */
88 static char * _globname(char const *name
, enum fexp_mode fexpm
);
90 /* line is a buffer with the result of fgets(). Returns the first newline or
91 * the last character read */
92 static size_t _length_of_line(char const *line
, size_t linesize
);
94 /* Read a line, one character at a time */
95 static char * _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
96 FILE *fp
, int appendnl
, size_t n SMALLOC_DEBUG_ARGS
);
98 /* Take the data out of the passed ghost file and toss it into a dynamically
99 * allocated message structure */
100 static void makemessage(void);
102 static enum okay
get_header(struct message
*mp
);
104 /* Write to socket fd, restarting on EINTR, unless anything is written */
106 static long xwrite(int fd
, char const *data
, size_t sz
);
110 _findmail(char *buf
, size_t bufsize
, char const *user
, bool_t force
)
115 if (!strcmp(user
, myname
) && !force
&& (cp
= ok_vlook(folder
)) != NULL
) {
116 switch (which_protocol(cp
)) {
118 if (strcmp(cp
, protbase(cp
)))
120 snprintf(buf
, bufsize
, "%s/INBOX", cp
);
127 if (force
|| (cp
= ok_vlook(MAIL
)) == NULL
)
128 snprintf(buf
, bufsize
, "%s/%s", MAILSPOOL
, user
);
131 n_strlcpy(buf
, cp
, bufsize
);
137 _globname(char const *name
, enum fexp_mode fexpm
)
146 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
147 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
148 memset(&we
, 0, sizeof we
);
150 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
151 * and wait, which will fail if our SIGCHLD handler is active */
153 sigaddset(&nset
, SIGCHLD
);
154 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
155 i
= wordexp(name
, &we
, 0);
156 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
162 if (!(fexpm
& FEXP_SILENT
))
163 fprintf(stderr
, tr(83, "\"%s\": Expansion buffer overflow.\n"), name
);
168 if (!(fexpm
& FEXP_SILENT
))
169 fprintf(stderr
, tr(242, "Syntax error in \"%s\"\n"), name
);
173 switch (we
.we_wordc
) {
175 cp
= savestr(we
.we_wordv
[0]);
178 if (!(fexpm
& FEXP_SILENT
))
179 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
182 if (fexpm
& FEXP_MULTIOK
) {
185 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
)
186 l
+= strlen(we
.we_wordv
[j
]) + 1;
189 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
) {
190 size_t x
= strlen(we
.we_wordv
[j
]);
191 memcpy(cp
+ l
, we
.we_wordv
[j
], x
);
196 } else if (!(fexpm
& FEXP_SILENT
))
197 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
205 #else /* HAVE_WORDEXP */
207 char xname
[PATH_MAX
], cmdbuf
[PATH_MAX
], /* also used for files */
209 int pivec
[2], pid
, l
, waits
;
212 if (pipe(pivec
) < 0) {
216 snprintf(cmdbuf
, sizeof cmdbuf
, "echo %s", name
);
217 if ((shellp
= ok_vlook(SHELL
)) == NULL
)
218 shellp
= UNCONST(XSHELL
);
219 pid
= start_command(shellp
, 0, -1, pivec
[1], "-c", cmdbuf
, NULL
);
228 l
= read(pivec
[0], xname
, sizeof xname
);
237 if (!wait_child(pid
, &waits
) && WTERMSIG(waits
) != SIGPIPE
) {
238 if (!(fexpm
& FEXP_SILENT
))
239 fprintf(stderr
, tr(81, "\"%s\": Expansion failed.\n"), name
);
243 if (!(fexpm
& FEXP_SILENT
))
244 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
247 if (l
== sizeof xname
) {
248 if (!(fexpm
& FEXP_SILENT
))
249 fprintf(stderr
, tr(83, "\"%s\": Expansion buffer overflow.\n"), name
);
253 for (cp
= xname
+ l
- 1; *cp
== '\n' && cp
> xname
; --cp
)
256 if (!(fexpm
& FEXP_MULTIOK
) && strchr(xname
, ' ') != NULL
&&
257 stat(xname
, &sbuf
) < 0) {
258 if (!(fexpm
& FEXP_SILENT
))
259 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
267 #endif /* !HAVE_WORDEXP */
271 _length_of_line(char const *line
, size_t linesize
)
276 /* Last character is always '\0' and was added by fgets() */
277 for (--linesize
, i
= 0; i
< linesize
; i
++)
280 i
= (i
< linesize
) ? i
+ 1 : linesize
;
286 _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
, FILE *fp
,
287 int appendnl
, size_t n SMALLOC_DEBUG_ARGS
)
293 assert(*linesize
== 0 || *line
!= NULL
);
295 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
296 *linesize
+= ((rv
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
297 *line
= rv
= (srealloc
)(rv
, *linesize SMALLOC_DEBUG_ARGSCALL
);
330 message_append(NULL
);
332 message
[msgCount
].m_size
= 0;
333 message
[msgCount
].m_lines
= 0;
338 get_header(struct message
*mp
)
344 switch (mb
.mb_type
) {
351 rv
= pop3_header(mp
);
357 rv
= imap_header(mp
);
371 xwrite(int fd
, char const *data
, size_t sz
)
378 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
391 #endif /* HAVE_SOCKETS */
394 (fgetline
)(char **line
, size_t *linesize
, size_t *cnt
, size_t *llen
, FILE *fp
,
395 int appendnl SMALLOC_DEBUG_ARGS
)
402 /* Without count, we can't determine where the chars returned by fgets()
403 * end if there's no newline. We have to read one character by one */
404 rv
= _fgetline_byone(line
, linesize
, llen
, fp
, appendnl
, 0
405 SMALLOC_DEBUG_ARGSCALL
);
409 if ((rv
= *line
) == NULL
|| *linesize
< LINESIZE
)
410 *line
= rv
= (srealloc
)(rv
, *linesize
= LINESIZE SMALLOC_DEBUG_ARGSCALL
);
411 sz
= (*linesize
<= *cnt
) ? *linesize
: *cnt
+ 1;
412 if (sz
<= 1 || fgets(rv
, sz
, fp
) == NULL
) {
413 /* Leave llen untouched; it is used to determine whether the last line
414 * was \n-terminated in some callers */
419 i_llen
= _length_of_line(rv
, sz
);
421 while (rv
[i_llen
- 1] != '\n') {
422 *line
= rv
= (srealloc
)(rv
, *linesize
+= 256 SMALLOC_DEBUG_ARGSCALL
);
423 sz
= *linesize
- i_llen
;
424 sz
= (sz
<= *cnt
) ? sz
: *cnt
+ 1;
425 if (sz
<= 1 || fgets(rv
+ i_llen
, sz
, fp
) == NULL
) {
432 sz
= _length_of_line(rv
+ i_llen
, sz
);
444 (readline_restart
)(FILE *ibuf
, char **linebuf
, size_t *linesize
, size_t n
447 /* TODO readline_restart(): always *appends* LF just to strip it again;
448 * TODO should be configurable just as for fgetline(); ..or whatever.. */
455 /* Interrupts will cause trouble if we are inside a stdio call. As this is
456 * only relevant if input is from tty, bypass it by read(), then */
457 if (fileno(ibuf
) == 0 && (options
& OPT_TTYIN
)) {
458 assert(*linesize
== 0 || *linebuf
!= NULL
);
460 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
461 *linesize
+= ((*linebuf
== NULL
) ? LINESIZE
+ n
+ 1 : 256);
462 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
465 sz
= read(0, *linebuf
+ n
, *linesize
- n
- 1);
468 (*linebuf
)[n
] = '\0';
469 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
472 if (sz
< 0 && errno
== EINTR
)
475 if ((*linebuf
)[n
- 1] != '\n') {
476 (*linebuf
)[n
++] = '\n';
477 (*linebuf
)[n
] = '\0';
485 /* Not reading from standard input or standard input not a terminal. We
486 * read one char at a time as it is the only way to get lines with
487 * embedded NUL characters in standard stdio */
488 if (_fgetline_byone(linebuf
, linesize
, &n
, ibuf
, 1, n
489 SMALLOC_DEBUG_ARGSCALL
) == NULL
)
492 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
493 (*linebuf
)[--n
] = '\0';
501 (readline_input
)(char const *prompt
, bool_t nl_escape
, char **linebuf
,
502 size_t *linesize
, char const *string SMALLOC_DEBUG_ARGS
)
504 /* TODO readline: linebuf pool! */
505 FILE *ifile
= (_fio_input
!= NULL
) ? _fio_input
: stdin
;
506 bool_t doprompt
, dotty
;
510 doprompt
= (!sourcing
&& (options
& OPT_INTERACTIVE
));
511 dotty
= (doprompt
&& !ok_blook(line_editor_disable
));
514 else if (prompt
== NULL
)
515 prompt
= getprompt();
519 assert(ifile
== stdin
);
520 if (string
!= NULL
&& (n
= (int)strlen(string
)) > 0) {
524 *linesize
= (size_t)n
+ LINESIZE
+1;
525 *linebuf
= (srealloc
)(*linebuf
, *linesize SMALLOC_DEBUG_ARGSCALL
);
526 memcpy(*linebuf
, string
, (size_t)n
+1);
529 n
= (tty_readline
)(prompt
, linebuf
, linesize
, n
530 SMALLOC_DEBUG_ARGSCALL
);
532 if (prompt
!= NULL
&& *prompt
!= '\0') {
533 fputs(prompt
, stdout
);
536 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
537 SMALLOC_DEBUG_ARGSCALL
);
542 * An unquoted <backslash> at the end of a command line shall
543 * be discarded and the next line shall continue the command */
544 if (nl_escape
&& (*linebuf
)[n
- 1] == '\\') {
545 (*linebuf
)[--n
] = '\0';
546 if (prompt
!= NULL
&& *prompt
!= '\0')
547 prompt
= ".. "; /* XXX PS2 .. */
557 readstr_input(char const *prompt
, char const *string
)
559 /* FIXME readstr_input: leaks on sigjmp without linepool */
561 char *linebuf
= NULL
, *rv
= NULL
;
565 n
= readline_input(prompt
, FAL0
, &linebuf
, &linesize
, string
);
567 rv
= savestrbuf(linebuf
, (size_t)n
+ 1);
576 setptr(FILE *ibuf
, off_t offset
)
579 char *cp
, *linebuf
= NULL
;
581 int c
, maybe
= 1, inhead
= 0, selfcnt
= 0;
582 size_t linesize
= 0, filesize
, cnt
;
585 memset(&self
, 0, sizeof self
);
586 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
587 filesize
= mailsize
- offset
;
588 offset
= ftell(mb
.mb_otf
);
591 if (fgetline(&linebuf
, &linesize
, &filesize
, &cnt
, ibuf
, 0) == NULL
) {
592 self
.m_xsize
= self
.m_size
;
593 self
.m_xlines
= self
.m_lines
;
594 self
.m_have
= HAVE_HEADER
| HAVE_BODY
;
596 message_append(&self
);
604 if (linebuf
[0] == '\0')
607 /* XXX Convert CRLF to LF; this should be rethought in that
608 * XXX CRLF input should possibly end as CRLF output? */
609 if (cnt
>= 2 && linebuf
[cnt
- 1] == '\n' && linebuf
[cnt
- 2] == '\r')
610 linebuf
[--cnt
- 1] = '\n';
611 fwrite(linebuf
, sizeof *linebuf
, cnt
, mb
.mb_otf
);
612 if (ferror(mb
.mb_otf
)) {
616 if (linebuf
[cnt
- 1] == '\n')
617 linebuf
[cnt
- 1] = '\0';
618 if (maybe
&& linebuf
[0] == 'F' && is_head(linebuf
, cnt
)) {
619 /* TODO char date[FROM_DATEBUF];
620 * TODO extract_date_from_from_(linebuf, cnt, date);
621 * TODO self.m_time = 10000; */
622 self
.m_xsize
= self
.m_size
;
623 self
.m_xlines
= self
.m_lines
;
624 self
.m_have
= HAVE_HEADER
| HAVE_BODY
;
626 message_append(&self
);
628 self
.m_flag
= MUSED
| MNEW
| MNEWEST
;
631 self
.m_block
= mailx_blockof(offset
);
632 self
.m_offset
= mailx_offsetof(offset
);
634 } else if (linebuf
[0] == 0) {
637 for (cp
= linebuf
, cp2
= "status";; ++cp
) {
638 if ((c
= *cp2
++) == 0) {
639 while (c
= *cp
++, whitechar(c
))
643 while ((c
= *cp
++) != '\0')
645 self
.m_flag
|= MREAD
;
647 self
.m_flag
&= ~MNEW
;
650 if (*cp
!= c
&& *cp
!= upperconv(c
))
653 for (cp
= linebuf
, cp2
= "x-status";; ++cp
) {
654 if ((c
= *cp2
++) == 0) {
655 while ((c
= *cp
++, whitechar(c
)))
659 while ((c
= *cp
++) != '\0')
661 self
.m_flag
|= MFLAGGED
;
663 self
.m_flag
|= MANSWERED
;
665 self
.m_flag
|= MDRAFTED
;
668 if (*cp
!= c
&& *cp
!= upperconv(c
))
675 maybe
= linebuf
[0] == 0;
681 putline(FILE *obuf
, char *linebuf
, size_t cnt
)
686 fwrite(linebuf
, sizeof *linebuf
, cnt
, obuf
);
695 setinput(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
703 ok
= (m
->m_have
& HAVE_HEADER
) ? OKAY
: get_header(m
);
706 ok
= (m
->m_have
& HAVE_BODY
) ? OKAY
: get_body(m
);
716 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
719 panic(tr(77, "temporary file seek"));
731 if (message
!= NULL
) {
741 message_append(struct message
*mp
)
744 if (UICMP(z
, msgCount
+ 1, >=, _message_space
)) {
745 /* XXX remove _message_space magics (or use s_Vector) */
746 _message_space
= (_message_space
>= 128 && _message_space
<= 1000000)
747 ? _message_space
<< 1 : _message_space
+ 64;
748 message
= srealloc(message
, _message_space
* sizeof *message
);
752 message
[msgCount
- 1] = *mp
;
754 memset(message
+ msgCount
- 1, 0, sizeof *message
);
760 message_match(struct message
*mp
, struct search_expr
const *sep
,
764 size_t *linesize
, cnt
;
769 if ((fp
= Ftmp(NULL
, "mpmatch", OF_RDWR
| OF_UNLINK
| OF_REGISTER
, 0600)) ==
773 if (sendmp(mp
, fp
, NULL
, NULL
, SEND_TOSRCH
, NULL
) < 0)
778 line
= &termios_state
.ts_linebuf
; /* XXX line pool */
779 linesize
= &termios_state
.ts_linesize
; /* XXX line pool */
782 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0))
786 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0)) {
788 if (sep
->ss_sexpr
== NULL
) {
789 if (regexec(&sep
->ss_reexpr
, *line
, 0,NULL
, 0) == REG_NOMATCH
)
793 if (!substr(*line
, sep
->ss_sexpr
))
807 setdot(struct message
*mp
)
812 did_print_dot
= FAL0
;
827 if (stat(name
, &sb
) < 0)
829 else if (!S_ISREG(sb
.st_mode
))
844 rv
= (fstat(fileno(iob
), &sbuf
) < 0) ? 0 : sbuf
.st_size
;
850 fexpand(char const *name
, enum fexp_mode fexpm
)
852 char cbuf
[PATH_MAX
], *res
;
858 /* The order of evaluation is "%" and "#" expand into constants.
859 * "&" can expand into "+". "+" can expand into shell meta characters.
860 * Shell meta characters expand into constants.
861 * This way, we make no recursive expansion */
863 if (!(fexpm
& FEXP_NSHORTCUT
) && (sh
= get_shortcut(res
)) != NULL
)
866 if (fexpm
& FEXP_SHELL
) {
874 if (res
[1] == ':' && res
[2] != '\0') {
878 _findmail(cbuf
, sizeof cbuf
, (res
[1] != '\0' ? res
+ 1 : myname
),
879 (res
[1] != '\0' || (options
& OPT_u_FLAG
)));
885 if (prevfile
[0] == '\0') {
886 fprintf(stderr
, tr(80, "No previous file\n"));
893 if (res
[1] == '\0') {
894 if ((res
= ok_vlook(MBOX
)) == NULL
)
895 res
= UNCONST("~/mbox"); /* XXX no magics (POSIX though) */
896 else if (res
[0] != '&' || res
[1] != '\0')
902 if (res
[0] == '@' && which_protocol(mailname
) == PROTO_IMAP
) {
903 res
= str_concat_csvl(&s
, protbase(mailname
), "/", res
+ 1, NULL
)->s
;
907 if (res
[0] == '+' && getfold(cbuf
, sizeof cbuf
)) {
908 size_t i
= strlen(cbuf
);
910 res
= str_concat_csvl(&s
, cbuf
,
911 ((i
> 0 && cbuf
[i
- 1] == '/') ? "" : "/"), res
+ 1, NULL
)->s
;
914 if (res
[0] == '%' && res
[1] == ':') {
920 /* Catch the most common shell meta character */
922 if (res
[0] == '~' && (res
[1] == '/' || res
[1] == '\0')) {
923 res
= str_concat_csvl(&s
, homedir
, res
+ 1, NULL
)->s
;
926 if (anyof(res
, "|&;<>~{}()[]*?$`'\"\\"))
927 switch (which_protocol(res
)) {
930 res
= _globname(res
, fexpm
);
937 if (fexpm
& FEXP_LOCAL
)
938 switch (which_protocol(res
)) {
943 fprintf(stderr
, tr(280,
944 "`%s': only a local file or directory may be used\n"), name
);
959 if (ok_blook(keep
) || rm(mailname
) < 0) {
960 int fd
= open(mailname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
968 var_folder_updated(char const *name
, char **store
)
971 char *folder
, *unres
= NULL
, *res
= NULL
;
974 if ((folder
= UNCONST(name
)) == NULL
)
977 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
978 /* XXX This *only* works because we do NOT
979 * XXX update environment variables via the "set" mechanism */
980 if (folder
[0] == '%' && folder
[1] == ':')
982 if ((folder
= fexpand(folder
, FEXP_FULL
)) == NULL
) /* XXX error? */
985 switch (which_protocol(folder
)) {
987 fprintf(stderr
, tr(501,
988 "`folder' cannot be set to a flat, readonly POP3 account\n"));
992 /* Simply assign what we have, even including `%:' prefix */
997 /* Further expansion desired */
1001 /* All non-absolute paths are relative to our home directory */
1002 if (*folder
!= '/') {
1003 size_t l1
= strlen(homedir
), l2
= strlen(folder
);
1004 unres
= ac_alloc(l1
+ l2
+ 1 +1);
1005 memcpy(unres
, homedir
, l1
);
1007 memcpy(unres
+ l1
+ 1, folder
, l2
);
1008 unres
[l1
+ 1 + l2
] = '\0';
1012 /* Since lex.c:_update_mailname() uses realpath(3) if available to
1013 * avoid that we loose track of our currently open folder in case we
1014 * chdir away, but still checks the leading path portion against
1015 * getfold() to be able to abbreviate to the +FOLDER syntax if
1016 * possible, we need to realpath(3) the folder, too */
1017 #ifdef HAVE_REALPATH
1018 res
= ac_alloc(PATH_MAX
+1);
1019 if (realpath(folder
, res
) == NULL
)
1020 fprintf(stderr
, tr(151, "Can't canonicalize `%s'\n"), folder
);
1026 *store
= sstrdup(folder
);
1038 getfold(char *name
, size_t size
)
1043 if ((folder
= ok_vlook(folder
)) != NULL
)
1044 n_strlcpy(name
, folder
, size
);
1046 return (folder
!= NULL
);
1050 getdeadletter(void) /* XXX should that be in auxlily.c? */
1055 if ((cp
= ok_vlook(DEAD
)) == NULL
|| (cp
= fexpand(cp
, FEXP_LOCAL
)) == NULL
)
1056 cp
= fexpand("~/dead.letter", FEXP_LOCAL
| FEXP_SHELL
);
1057 else if (*cp
!= '/') {
1058 size_t sz
= strlen(cp
) + 2 +1;
1059 char *buf
= ac_alloc(sz
);
1061 snprintf(buf
, sz
, "~/%s", cp
);
1062 cp
= fexpand(buf
, FEXP_LOCAL
| FEXP_SHELL
);
1067 cp
= "dead.letter"; /* XXX magic -> nail.h (POSIX thing though) */
1073 get_body(struct message
*mp
)
1079 switch (mb
.mb_type
) {
1106 sclose(struct sock
*sp
)
1113 /* TODO NOTE: we MUST NOT close the descriptor `0' here...
1114 * TODO of course this should be handled in a VMAILFS->open() .s_fd=-1,
1115 * TODO but unfortunately it isn't yet */
1119 if (sp
->s_onclose
!= NULL
)
1121 if (sp
->s_wbuf
!= NULL
)
1123 # ifdef HAVE_OPENSSL
1124 if (sp
->s_use_ssl
) {
1125 /* XXX Don't wonder: as of v14.6 there is a problem in the IMAP code in
1126 * XXX that if i connect via `file' to an IMAP account, that connection
1127 * XXX breaks, and i simply re-`file' to the same account, then it may
1128 * XXX happen that the OpenSSL library crashes, in SSL_CTX_free()?,
1129 * XXX but more checking is needed. That is true for 0* as well as for
1130 * XXX `OpenSSL 1.0.1f 6 Jan 2014'; i've reported that on @openssl-user
1131 * XXX somewhen in november 2013, i.e., the wrong list. What i still
1132 * XXX don't understand, and the reason for why all this NYD_X is here
1133 * XXX etc.: the socket is has not been closed, so these SSL_* funs
1134 * XXX below have not yet been called on the SSL objects */
1135 void *s_ssl
= sp
->s_ssl
, *s_ctx
= sp
->s_ctx
;
1136 sp
->s_ssl
= sp
->s_ctx
= NULL
;
1139 while (!SSL_shutdown(s_ssl
)) /* XXX proper error handling */
1144 SSL_CTX_free(s_ctx
);
1155 swrite(struct sock
*sp
, char const *data
)
1160 rv
= swrite1(sp
, data
, strlen(data
), 0);
1166 swrite1(struct sock
*sp
, char const *data
, int sz
, int use_buffer
)
1168 enum okay rv
= STOP
;
1172 if (use_buffer
> 0) {
1175 if (sp
->s_wbuf
== NULL
) {
1176 sp
->s_wbufsize
= 4096;
1177 sp
->s_wbuf
= smalloc(sp
->s_wbufsize
);
1180 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
1181 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
1183 if (sp
->s_wbufpos
> 0) {
1184 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, di
);
1185 rv
= swrite1(sp
, sp
->s_wbuf
, sp
->s_wbufsize
, -1);
1187 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1193 if (sz
== sp
->s_wbufsize
) {
1194 rv
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1198 memcpy(sp
->s_wbuf
+ sp
->s_wbufpos
, data
, sz
);
1199 sp
->s_wbufpos
+= sz
;
1203 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&& sp
->s_wbufpos
> 0) {
1206 if ((rv
= swrite1(sp
, sp
->s_wbuf
, x
, -1)) != OKAY
)
1214 # ifdef HAVE_OPENSSL
1215 if (sp
->s_use_ssl
) {
1217 x
= SSL_write(sp
->s_ssl
, data
, sz
);
1219 switch (SSL_get_error(sp
->s_ssl
, x
)) {
1220 case SSL_ERROR_WANT_READ
:
1221 case SSL_ERROR_WANT_WRITE
:
1228 x
= xwrite(sp
->s_fd
, data
, sz
);
1232 snprintf(o
, sizeof o
, "%s write error",
1233 (sp
->s_desc
? sp
->s_desc
: "socket"));
1234 # ifdef HAVE_OPENSSL
1235 sp
->s_use_ssl
? ssl_gen_err("%s", o
) : perror(o
);
1251 sopen(struct sock
*sp
, struct url
*urlp
)
1253 # ifdef HAVE_SO_SNDTIMEO
1256 # ifdef HAVE_SO_LINGER
1260 char hbuf
[NI_MAXHOST
];
1261 struct addrinfo hints
, *res0
, *res
;
1263 struct sockaddr_in servaddr
;
1264 struct in_addr
**pptr
;
1272 /* Connect timeouts after 30 seconds XXX configurable */
1273 # ifdef HAVE_SO_SNDTIMEO
1277 serv
= (urlp
->url_port
!= NULL
) ? urlp
->url_port
: urlp
->url_proto
;
1279 if (options
& OPT_VERBOSE
)
1280 fprintf(stderr
, tr(187, "Resolving host %s:%s ..."),
1281 urlp
->url_host
.s
, serv
);
1284 memset(&hints
, 0, sizeof hints
);
1285 hints
.ai_socktype
= SOCK_STREAM
;
1286 if (getaddrinfo(urlp
->url_host
.s
, serv
, &hints
, &res0
)) {
1287 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), urlp
->url_host
.s
);
1289 } else if (options
& OPT_VERBOSE
)
1290 fprintf(stderr
, tr(500, " done.\n"));
1292 for (res
= res0
; res
!= NULL
&& sofd
< 0; res
= res
->ai_next
) {
1293 if (options
& OPT_VERBOSE
) {
1294 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
, hbuf
, sizeof hbuf
,
1295 NULL
, 0, NI_NUMERICHOST
))
1296 strcpy(hbuf
, "unknown host");
1297 fprintf(stderr
, tr(192, "%sConnecting to %s:%s ..."),
1298 (res
== res0
? "" : "\n"), hbuf
, serv
);
1300 sofd
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
1302 # ifdef HAVE_SO_SNDTIMEO
1303 setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1305 if (connect(sofd
, res
->ai_addr
, res
->ai_addrlen
)) {
1313 perror(tr(254, " could not connect"));
1317 # else /* HAVE_IPV6 */
1318 if (urlp
->url_port
== NULL
&& urlp
->url_portno
== 0) {
1319 if ((ep
= getservbyname(UNCONST(urlp
->url_proto
), "tcp")) != NULL
)
1320 urlp
->url_portno
= ep
->s_port
;
1322 fprintf(stderr
, tr(251, "Unknown service: %s\n"), urlp
->url_proto
);
1327 if ((hp
= gethostbyname(urlp
->url_host
.s
)) == NULL
) {
1328 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), urlp
->url_host
.s
);
1330 } else if (options
& OPT_VERBOSE
)
1331 fprintf(stderr
, tr(500, " done.\n"));
1333 pptr
= (struct in_addr
**)hp
->h_addr_list
;
1334 if ((sofd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
1335 perror(tr(253, "could not create socket"));
1339 memset(&servaddr
, 0, sizeof servaddr
);
1340 servaddr
.sin_family
= AF_INET
;
1341 servaddr
.sin_port
= htons(urlp
->url_portno
);
1342 memcpy(&servaddr
.sin_addr
, *pptr
, sizeof(struct in_addr
));
1343 if (options
& OPT_VERBOSE
)
1344 fprintf(stderr
, tr(190, "%sConnecting to %s:%d ..."),
1345 "", inet_ntoa(**pptr
), (int)urlp
->url_portno
);
1346 # ifdef HAVE_SO_SNDTIMEO
1347 setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1349 if (connect(sofd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
)) {
1350 perror(tr(254, " could not connect"));
1355 # endif /* !HAVE_IPV6 */
1357 if (options
& OPT_VERBOSE
)
1358 fputs(tr(193, " connected.\n"), stderr
);
1360 /* And the regular timeouts XXX configurable */
1361 # ifdef HAVE_SO_SNDTIMEO
1364 setsockopt(sofd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1365 setsockopt(sofd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
1367 # ifdef HAVE_SO_LINGER
1370 setsockopt(sofd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
1373 memset(sp
, 0, sizeof *sp
);
1376 if (urlp
->url_needs_tls
&&
1377 ssl_open(urlp
->url_host
.s
, sp
, urlp
->url_uhp
.s
) != OKAY
) {
1388 sopen_old(char const *xserver
, struct sock
*sp
, int use_ssl
, char const *uhp
,
1389 char const *portstr
)
1391 # ifdef HAVE_SO_SNDTIMEO
1394 # ifdef HAVE_SO_LINGER
1398 char hbuf
[NI_MAXHOST
];
1399 struct addrinfo hints
, *res0
, *res
;
1401 struct sockaddr_in servaddr
;
1402 struct in_addr
**pptr
;
1405 unsigned short port
= 0;
1408 char *cp
, *server
= UNCONST(xserver
);
1409 enum okay rv
= STOP
;
1414 if ((cp
= strchr(server
, ':')) != NULL
) { /* TODO URI parse! IPv6! */
1417 port
= strtol(portstr
, NULL
, 10);
1419 server
= salloc(cp
- xserver
+1);
1420 memcpy(server
, xserver
, cp
- xserver
);
1421 server
[cp
- xserver
] = '\0';
1424 /* Connect timeouts after 30 seconds */
1425 # ifdef HAVE_SO_SNDTIMEO
1431 if (options
& OPT_VERBOSE
)
1432 fprintf(stderr
, "Resolving host %s ...", server
);
1433 memset(&hints
, 0, sizeof hints
);
1434 hints
.ai_socktype
= SOCK_STREAM
;
1435 if (getaddrinfo(server
, portstr
, &hints
, &res0
) != 0) {
1436 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1438 } else if (options
& OPT_VERBOSE
)
1439 fprintf(stderr
, tr(500, " done.\n"));
1442 for (res
= res0
; res
!= NULL
&& sockfd
< 0; res
= res
->ai_next
) {
1443 if (options
& OPT_VERBOSE
) {
1444 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
, hbuf
, sizeof hbuf
,
1445 NULL
, 0, NI_NUMERICHOST
) != 0)
1446 strcpy(hbuf
, "unknown host");
1447 fprintf(stderr
, tr(192, "%sConnecting to %s:%s ..."),
1448 (res
== res0
? "" : "\n"), hbuf
, portstr
);
1450 sockfd
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
1452 # ifdef HAVE_SO_SNDTIMEO
1453 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1455 if (connect(sockfd
, res
->ai_addr
, res
->ai_addrlen
) != 0) {
1462 perror(tr(254, " could not connect"));
1468 # else /* HAVE_IPV6 */
1471 if (!strcmp(portstr
, "smtp"))
1473 else if (!strcmp(portstr
, "smtps"))
1475 else if (!strcmp(portstr
, "submission"))
1479 else if (!strcmp(portstr
, "imap"))
1481 else if (!strcmp(portstr
, "imaps"))
1485 else if (!strcmp(portstr
, "pop3"))
1487 else if (!strcmp(portstr
, "pop3s"))
1490 else if ((ep
= getservbyname(UNCONST(portstr
), "tcp")) != NULL
)
1493 fprintf(stderr
, tr(251, "Unknown service: %s\n"), portstr
);
1500 if (options
& OPT_VERBOSE
)
1501 fprintf(stderr
, "Resolving host %s ...", server
);
1502 if ((hp
= gethostbyname(server
)) == NULL
) {
1503 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1505 } else if (options
& OPT_VERBOSE
)
1506 fprintf(stderr
, tr(500, " done.\n"));
1508 pptr
= (struct in_addr
**)hp
->h_addr_list
;
1509 if ((sockfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
1510 perror(tr(253, "could not create socket"));
1513 memset(&servaddr
, 0, sizeof servaddr
);
1514 servaddr
.sin_family
= AF_INET
;
1515 servaddr
.sin_port
= port
;
1516 memcpy(&servaddr
.sin_addr
, *pptr
, sizeof(struct in_addr
));
1517 if (options
& OPT_VERBOSE
)
1518 fprintf(stderr
, tr(190, "%sConnecting to %s:%d ..."),
1519 "", inet_ntoa(**pptr
), (int)ntohs(port
));
1521 # ifdef HAVE_SO_SNDTIMEO
1522 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1524 if (connect(sockfd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
) != 0) {
1525 perror(tr(254, " could not connect"));
1528 # endif /* !HAVE_IPV6 */
1529 if (options
& OPT_VERBOSE
)
1530 fputs(tr(193, " connected.\n"), stderr
);
1532 /* And the regular timeouts */
1533 # ifdef HAVE_SO_SNDTIMEO
1536 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1537 setsockopt(sockfd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
1539 # ifdef HAVE_SO_LINGER
1542 setsockopt(sockfd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
1545 memset(sp
, 0, sizeof *sp
);
1548 if (use_ssl
&& ssl_open(server
, sp
, uhp
) != OKAY
) {
1560 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
1572 if (sp
->s_rsz
< 0) {
1579 if (lp_base
== NULL
|| PTRCMP(lp
, >, lp_base
+ lsize
- 128)) {
1580 size_t diff
= PTR2SIZE(lp
- lp_base
);
1581 *linesize
= (lsize
+= 256); /* XXX magic */
1582 *line
= lp_base
= (srealloc
)(lp_base
, lsize SMALLOC_DEBUG_ARGSCALL
);
1583 lp
= lp_base
+ diff
;
1586 if (sp
->s_rbufptr
== NULL
||
1587 PTRCMP(sp
->s_rbufptr
, >=, sp
->s_rbuf
+ sp
->s_rsz
)) {
1588 # ifdef HAVE_OPENSSL
1589 if (sp
->s_use_ssl
) {
1591 sp
->s_rsz
= SSL_read(sp
->s_ssl
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
1592 if (sp
->s_rsz
<= 0) {
1593 if (sp
->s_rsz
< 0) {
1595 switch(SSL_get_error(sp
->s_ssl
, sp
->s_rsz
)) {
1596 case SSL_ERROR_WANT_READ
:
1597 case SSL_ERROR_WANT_WRITE
:
1600 snprintf(o
, sizeof o
, "%s",
1601 (sp
->s_desc
? sp
->s_desc
: "socket"));
1602 ssl_gen_err("%s", o
);
1610 sp
->s_rsz
= read(sp
->s_fd
, sp
->s_rbuf
, sizeof sp
->s_rbuf
);
1611 if (sp
->s_rsz
<= 0) {
1612 if (sp
->s_rsz
< 0) {
1616 snprintf(o
, sizeof o
, "%s",
1617 (sp
->s_desc
? sp
->s_desc
: "socket"));
1623 sp
->s_rbufptr
= sp
->s_rbuf
;
1625 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
1627 lsize
= PTR2SIZE(lp
- lp_base
);
1636 #endif /* HAVE_SOCKETS */
1639 load(char const *name
)
1644 if (name
== NULL
|| (in
= Fopen(name
, "r")) == NULL
)
1663 char **arglist
= v
, *cp
;
1667 if ((cp
= fexpand(*arglist
, FEXP_LOCAL
)) == NULL
)
1669 if ((fi
= Fopen(cp
, "r")) == NULL
) {
1674 if (_fio_stack_size
>= NELEM(_fio_stack
)) {
1675 fprintf(stderr
, tr(3, "Too much \"sourcing\" going on.\n"));
1680 _fio_stack
[_fio_stack_size
].s_file
= _fio_input
;
1681 _fio_stack
[_fio_stack_size
].s_cond
= condstack_release();
1682 _fio_stack
[_fio_stack_size
].s_loading
= loading
;
1699 if (_fio_stack_size
== 0) {
1700 fprintf(stderr
, tr(4, "\"Source\" stack over-pop.\n"));
1708 if (!condstack_take(_fio_stack
[_fio_stack_size
].s_cond
))
1709 fprintf(stderr
, tr(5, "Unmatched \"if\"\n"));
1710 loading
= _fio_stack
[_fio_stack_size
].s_loading
;
1711 _fio_input
= _fio_stack
[_fio_stack_size
].s_file
;
1712 if (_fio_stack_size
== 0)
1720 /* vim:set fenc=utf-8:s-it-mode */