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 - 2013 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 enum condition s_cond
; /* Saved state of conditionals */
75 int s_loading
; /* Loading .mailrc, etc. */
78 static size_t _ssp
; /* Top of file stack */
81 /* Locate the user's mailbox file (where new, unread mail is queued) */
82 static void _findmail(char *buf
, size_t bufsize
, char const *user
,
85 /* Perform shell meta character expansion */
86 static char * _globname(char const *name
, enum fexp_mode fexpm
);
88 /* *line* is a buffer with the result of fgets().
89 * Returns the first newline or the last character read */
90 static size_t _length_of_line(const char *line
, size_t linesize
);
92 /* Read a line, one character at a time */
93 static char * _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
94 FILE *fp
, int appendnl
, size_t n SMALLOC_DEBUG_ARGS
);
96 static void makemessage(void);
97 static void _fio_append(struct message
*mp
);
98 static enum okay
get_header(struct message
*mp
);
101 _findmail(char *buf
, size_t bufsize
, char const *user
, bool_t force
)
105 if (strcmp(user
, myname
) == 0 && ! force
&&
106 (cp
= value("folder")) != NULL
) {
107 switch (which_protocol(cp
)) {
109 if (strcmp(cp
, protbase(cp
)) != 0)
111 snprintf(buf
, bufsize
, "%s/INBOX", cp
);
118 if (force
|| (cp
= value("MAIL")) == NULL
)
119 snprintf(buf
, bufsize
, "%s/%s", MAILSPOOL
, user
);
122 n_strlcpy(buf
, cp
, bufsize
);
129 _globname(char const *name
, enum fexp_mode fexpm
)
137 /* Some systems (notably Open UNIX 8.0.0) fork a shell for wordexp()
138 * and wait, which will fail if our SIGCHLD handler is active */
140 sigaddset(&nset
, SIGCHLD
);
141 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
143 /* Mac OS X Snow Leopard and Linux don't init fields on error, causing
144 * SIGSEGV in wordfree(3); so let's just always zero it ourselfs */
145 memset(&we
, 0, sizeof we
);
146 i
= wordexp(name
, &we
, 0);
147 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
153 if (! (fexpm
& FEXP_SILENT
))
155 tr(83, "\"%s\": Expansion buffer overflow.\n"),
161 if (! (fexpm
& FEXP_SILENT
))
162 fprintf(stderr
, tr(242, "Syntax error in \"%s\"\n"),
167 switch (we
.we_wordc
) {
169 cp
= savestr(we
.we_wordv
[0]);
172 if (! (fexpm
& FEXP_SILENT
))
173 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
176 if (fexpm
& FEXP_MULTIOK
) {
179 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
)
180 l
+= strlen(we
.we_wordv
[j
]) + 1;
183 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
) {
184 size_t x
= strlen(we
.we_wordv
[j
]);
185 memcpy(cp
+ l
, we
.we_wordv
[j
], x
);
190 } else if (! (fexpm
& FEXP_SILENT
))
191 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
198 #else /* !HAVE_WORDEXP */
199 extern int wait_status
;
202 char xname
[MAXPATHLEN
], cmdbuf
[MAXPATHLEN
], /* also used for files */
204 int pid
, l
, pivec
[2];
206 if (pipe(pivec
) < 0) {
210 snprintf(cmdbuf
, sizeof cmdbuf
, "echo %s", name
);
211 if ((shellp
= value("SHELL")) == NULL
)
212 shellp
= UNCONST(SHELL
);
213 pid
= start_command(shellp
, 0, -1, pivec
[1], "-c", cmdbuf
, NULL
);
222 l
= read(pivec
[0], xname
, sizeof xname
);
231 if (wait_child(pid
) < 0 && WTERMSIG(wait_status
) != SIGPIPE
) {
232 if (! (fexpm
& FEXP_SILENT
))
233 fprintf(stderr
, tr(81, "\"%s\": Expansion failed.\n"),
238 if (! (fexpm
& FEXP_SILENT
))
239 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
242 if (l
== sizeof xname
) {
243 if (! (fexpm
& FEXP_SILENT
))
245 tr(83, "\"%s\": Expansion buffer overflow.\n"),
250 for (cp
= &xname
[l
-1]; *cp
== '\n' && cp
> xname
; cp
--)
253 if (! (fexpm
& FEXP_MULTIOK
) && strchr(xname
, ' ') &&
254 stat(xname
, &sbuf
) < 0) {
255 if (! (fexpm
& FEXP_SILENT
))
256 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
259 return savestr(xname
);
260 #endif /* !HAVE_WORDEXP */
264 * line is a buffer with the result of fgets(). Returns the first
265 * newline or the last character read.
268 _length_of_line(const char *line
, size_t linesize
)
272 /* Last character is always '\0' and was added by fgets() */
273 for (--linesize
, i
= 0; i
< linesize
; i
++)
276 return (i
< linesize
) ? i
+ 1 : linesize
;
280 _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
281 FILE *fp
, int appendnl
, size_t n SMALLOC_DEBUG_ARGS
)
285 assert(*linesize
== 0 || *line
!= NULL
);
287 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
288 *linesize
+= ((*line
== NULL
)
289 ? LINESIZE
+ n
+ 1 : 256);
290 *line
= (srealloc_safe
)(*line
, *linesize
291 SMALLOC_DEBUG_ARGSCALL
);
316 (fgetline
)(char **line
, size_t *linesize
, size_t *cnt
, size_t *llen
,
317 FILE *fp
, int appendnl SMALLOC_DEBUG_ARGS
)
323 * If we have no count, we cannot determine where the
324 * characters returned by fgets() end if there was no
325 * newline. We have to read one character at one.
327 return _fgetline_byone(line
, linesize
, llen
, fp
, appendnl
, 0
328 SMALLOC_DEBUG_ARGSCALL
);
329 if (*line
== NULL
|| *linesize
< LINESIZE
)
330 *line
= (srealloc_safe
)(*line
, *linesize
= LINESIZE
331 SMALLOC_DEBUG_ARGSCALL
);
332 sz
= *linesize
<= *cnt
? *linesize
: *cnt
+ 1;
333 if (sz
<= 1 || fgets(*line
, sz
, fp
) == NULL
)
335 * Leave llen untouched; it is used to determine whether
336 * the last line was \n-terminated in some callers.
339 i_llen
= _length_of_line(*line
, sz
);
341 while ((*line
)[i_llen
- 1] != '\n') {
342 *line
= (srealloc_safe
)(*line
, *linesize
+= 256
343 SMALLOC_DEBUG_ARGSCALL
);
344 sz
= *linesize
- i_llen
;
345 sz
= (sz
<= *cnt
? sz
: *cnt
+ 1);
346 if (sz
<= 1 || fgets(&(*line
)[i_llen
], sz
, fp
) == NULL
) {
348 (*line
)[i_llen
++] = '\n';
349 (*line
)[i_llen
] = '\0';
353 sz
= _length_of_line(&(*line
)[i_llen
], sz
);
363 (readline_restart
)(FILE *ibuf
, char **linebuf
, size_t *linesize
, size_t n
366 /* TODO readline_restart(): always *appends* LF just to strip it again;
367 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
372 * Interrupts will cause trouble if we are inside a stdio call. As
373 * this is only relevant if input comes from a terminal, we can simply
374 * bypass it by read() then.
376 if (fileno(ibuf
) == 0 && (options
& OPT_TTYIN
)) {
377 assert(*linesize
== 0 || *linebuf
!= NULL
);
379 if (*linesize
<= LINESIZE
|| n
>= *linesize
- 128) {
380 *linesize
+= ((*linebuf
== NULL
)
381 ? LINESIZE
+ n
+ 1 : 256);
382 *linebuf
= (srealloc_safe
)(*linebuf
, *linesize
383 SMALLOC_DEBUG_ARGSCALL
);
386 sz
= read(0, *linebuf
+ n
, *linesize
- n
- 1);
389 (*linebuf
)[n
] = '\0';
390 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
393 if (sz
< 0 && errno
== EINTR
)
396 if ((*linebuf
)[n
- 1] != '\n') {
397 (*linebuf
)[n
++] = '\n';
398 (*linebuf
)[n
] = '\0';
407 * Not reading from standard input or standard input not
408 * a terminal. We read one char at a time as it is the
409 * only way to get lines with embedded NUL characters in
412 if (_fgetline_byone(linebuf
, linesize
, &n
, ibuf
, 1, n
413 SMALLOC_DEBUG_ARGSCALL
) == NULL
)
416 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
417 (*linebuf
)[--n
] = '\0';
422 (readline_input
)(enum lned_mode lned
, char const *prompt
, char **linebuf
,
423 size_t *linesize SMALLOC_DEBUG_ARGS
)
425 FILE *ifile
= (_input
!= NULL
) ? _input
: stdin
;
426 bool_t doprompt
, dotty
;
429 doprompt
= (!sourcing
&& (options
& OPT_INTERACTIVE
));
430 dotty
= (doprompt
&& !boption("line-editor-disable"));
433 else if (prompt
== NULL
)
434 prompt
= getprompt();
438 assert(ifile
== stdin
);
439 n
= (tty_readline
)(prompt
, linebuf
, linesize
, n
440 SMALLOC_DEBUG_ARGSCALL
);
442 if (prompt
!= NULL
&& *prompt
!= '\0') {
443 fputs(prompt
, stdout
);
446 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
447 SMALLOC_DEBUG_ARGSCALL
);
453 * An unquoted <backslash> at the end of a command line
454 * shall be discarded and the next line shall continue the
457 if ((lned
& LNED_LF_ESC
) && (*linebuf
)[n
- 1] == '\\') {
458 (*linebuf
)[--n
] = '\0';
459 if (prompt
!= NULL
&& *prompt
!= '\0')
460 prompt
= ".. "; /* XXX PS2 .. */
463 if (dotty
&& (lned
& LNED_HIST_ADD
))
464 tty_addhist(*linebuf
);
471 readstr_input(char const *prompt
, char const *string
) /* FIXME SIGS<->leaks */
473 /* TODO readstr_input(): linebuf pool */
474 size_t linesize
= 0, slen
;
475 char *linebuf
= NULL
, *rv
= NULL
;
476 bool_t doprompt
, dotty
;
478 doprompt
= (!sourcing
&& (options
& OPT_INTERACTIVE
));
479 dotty
= (doprompt
&& !boption("line-editor-disable"));
482 else if (prompt
== NULL
)
483 prompt
= getprompt();
485 /* If STDIN is not a terminal, simply read from it */
487 slen
= (string
!= NULL
) ? strlen(string
) : 0;
489 linesize
= slen
+ LINESIZE
+ 1;
490 linebuf
= smalloc_safe(linesize
);
492 memcpy(linebuf
, string
, slen
+ 1);
494 if (tty_readline(prompt
, &linebuf
, &linesize
, slen
) >= 0)
497 if (prompt
!= NULL
&& *prompt
!= '\0') {
498 fputs(prompt
, stdout
);
503 if (readline_restart(stdin
, &linebuf
, &linesize
, slen
) >= 0)
508 rv
= (*rv
== '\0') ? NULL
: savestr(rv
);
514 * Set up the input pointers while copying the mail file into /tmp.
517 setptr(FILE *ibuf
, off_t offset
)
520 char *cp
, *linebuf
= NULL
;
523 int maybe
, inhead
, thiscnt
;
524 size_t linesize
= 0, filesize
, cnt
;
529 memset(&this, 0, sizeof this);
530 this.m_flag
= MUSED
|MNEW
|MNEWEST
;
531 filesize
= mailsize
- offset
;
532 offset
= ftell(mb
.mb_otf
);
534 if (fgetline(&linebuf
, &linesize
, &filesize
, &cnt
, ibuf
, 0)
536 this.m_xsize
= this.m_size
;
537 this.m_xlines
= this.m_lines
;
538 this.m_have
= HAVE_HEADER
|HAVE_BODY
;
547 if (linebuf
[0] == '\0')
550 /* XXX Convert CRLF to LF; this should be rethought in that
551 * XXX CRLF input should possibly end as CRLF output? */
552 if (cnt
>= 2 && linebuf
[cnt
- 1] == '\n' &&
553 linebuf
[cnt
- 2] == '\r')
554 linebuf
[--cnt
- 1] = '\n';
555 fwrite(linebuf
, sizeof *linebuf
, cnt
, mb
.mb_otf
);
556 if (ferror(mb
.mb_otf
)) {
560 if (linebuf
[cnt
- 1] == '\n')
561 linebuf
[cnt
- 1] = '\0';
562 if (maybe
&& linebuf
[0] == 'F' && is_head(linebuf
, cnt
)) {
564 * TODO char date[FROM_DATEBUF];
565 * TODO extract_date_from_from_(linebuf, cnt, date);
566 * TODO this.m_time = 10000;
568 this.m_xsize
= this.m_size
;
569 this.m_xlines
= this.m_lines
;
570 this.m_have
= HAVE_HEADER
|HAVE_BODY
;
574 this.m_flag
= MUSED
|MNEW
|MNEWEST
;
577 this.m_block
= mailx_blockof(offset
);
578 this.m_offset
= mailx_offsetof(offset
);
580 } else if (linebuf
[0] == 0) {
583 for (cp
= linebuf
, cp2
= "status";; cp
++) {
584 if ((c
= *cp2
++) == 0) {
585 while (c
= *cp
++, whitechar(c
));
588 while ((c
= *cp
++) != '\0')
590 this.m_flag
|= MREAD
;
592 this.m_flag
&= ~MNEW
;
595 if (*cp
!= c
&& *cp
!= upperconv(c
))
598 for (cp
= linebuf
, cp2
= "x-status";; cp
++) {
599 if ((c
= *cp2
++) == 0) {
600 while (c
= *cp
++, whitechar(c
));
603 while ((c
= *cp
++) != '\0')
605 this.m_flag
|= MFLAGGED
;
607 this.m_flag
|=MANSWERED
;
609 this.m_flag
|=MDRAFTED
;
612 if (*cp
!= c
&& *cp
!= upperconv(c
))
619 maybe
= linebuf
[0] == 0;
625 * Drop the passed line onto the passed output buffer.
626 * If a write error occurs, return -1, else the count of
627 * characters written, including the newline.
630 putline(FILE *obuf
, char *linebuf
, size_t cnt
)
632 fwrite(linebuf
, sizeof *linebuf
, cnt
, obuf
);
640 * Return a file buffer all ready to read up the
641 * passed message pointer.
644 setinput(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
650 if (m
->m_have
& HAVE_HEADER
)
656 if (m
->m_have
& HAVE_BODY
)
668 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
669 m
->m_offset
), SEEK_SET
) < 0) {
671 panic(tr(77, "temporary file seek"));
677 setdot(struct message
*mp
)
681 did_print_dot
= FAL0
;
689 * Take the data out of the passed ghost file and toss it into
690 * a dynamically allocated message structure.
698 message
[msgCount
].m_size
= 0;
699 message
[msgCount
].m_lines
= 0;
703 * Append the passed message descriptor onto the message structure.
706 _fio_append(struct message
*mp
)
708 if (msgCount
+ 1 >= msgspace
)
709 message
= srealloc(message
, (msgspace
+= 64) * sizeof *message
);
711 message
[msgCount
- 1] = *mp
;
715 * Delete a file, but only if the file is a plain file.
718 rm(char *name
) /* TODO TOCTOU; but i'm out of ideas today */
723 if (stat(name
, &sb
) < 0)
725 else if (! S_ISREG(sb
.st_mode
))
733 * Determine the size of the file possessed by
741 if (fstat(fileno(iob
), &sbuf
) < 0)
747 fexpand(char const *name
, enum fexp_mode fexpm
)
749 char cbuf
[MAXPATHLEN
], *res
;
755 * The order of evaluation is "%" and "#" expand into constants.
756 * "&" can expand into "+". "+" can expand into shell meta characters.
757 * Shell meta characters expand into constants.
758 * This way, we make no recursive expansion.
761 if (! (fexpm
& FEXP_NSHORTCUT
) && (sh
= get_shortcut(res
)) != NULL
)
764 if (fexpm
& FEXP_SHELL
) {
772 if (res
[1] == ':' && res
[2] != '\0') {
776 _findmail(cbuf
, sizeof cbuf
,
777 (res
[1] != '\0') ? res
+ 1 : myname
,
778 (res
[1] != '\0' || (options
& OPT_u_FLAG
)));
784 if (prevfile
[0] == '\0') {
785 fprintf(stderr
, tr(80, "No previous file\n"));
792 if (res
[1] == '\0') {
793 if ((res
= value("MBOX")) == NULL
)
794 res
= UNCONST("~/mbox");
795 else if (res
[0] != '&' || res
[1] != '\0')
801 if (res
[0] == '@' && which_protocol(mailname
) == PROTO_IMAP
) {
802 res
= str_concat_csvl(&s
,
803 protbase(mailname
), "/", res
+ 1, NULL
)->s
;
807 if (res
[0] == '+' && getfold(cbuf
, sizeof cbuf
)) {
808 size_t i
= strlen(cbuf
);
810 res
= str_concat_csvl(&s
, cbuf
,
811 (i
> 0 && cbuf
[i
- 1] == '/') ? "" : "/",
815 if (res
[0] == '%' && res
[1] == ':') {
821 /* Catch the most common shell meta character */
823 if (res
[0] == '~' && (res
[1] == '/' || res
[1] == '\0')) {
824 res
= str_concat_csvl(&s
, homedir
, res
+ 1, NULL
)->s
;
828 if (anyof(res
, "|&;<>~{}()[]*?$`'\"\\") &&
829 which_protocol(res
) == PROTO_FILE
) {
830 res
= _globname(res
, fexpm
);
836 if (fexpm
& FEXP_LOCAL
)
837 switch (which_protocol(res
)) {
839 case PROTO_MAILDIR
: /* XXX Really? ok MAILDIR for local? */
842 fprintf(stderr
, tr(280,
843 "`%s': only a local file or directory may "
858 if (value("keep") != NULL
|| rm(mailname
) < 0) {
859 int fd
= open(mailname
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
866 var_folder_updated(char const *name
, char **store
)
869 char *folder
, *unres
= NULL
, *res
= NULL
;
871 if ((folder
= UNCONST(name
)) == NULL
)
874 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
875 /* XXX This *only* works because we do NOT
876 * XXX update environment variables via the "set" mechanism */
877 if (folder
[0] == '%' && folder
[1] == ':')
879 if ((folder
= fexpand(folder
, FEXP_FULL
)) == NULL
) /* XXX error? */
882 switch (which_protocol(folder
)) {
884 /* Ooops. This won't work */
885 fprintf(stderr
, tr(501, "`folder' cannot be set to a flat, "
886 "readonly POP3 account\n"));
890 /* Simply assign what we have, even including `%:' prefix */
895 /* Further expansion desired */
899 /* All non-absolute paths are relative to our home directory */
900 if (*folder
!= '/') {
901 size_t l1
= strlen(homedir
), l2
= strlen(folder
);
902 unres
= ac_alloc(l1
+ l2
+ 2);
903 memcpy(unres
, homedir
, l1
);
905 memcpy(unres
+ l1
+ 1, folder
, l2
);
906 unres
[l1
+ 1 + l2
] = '\0';
910 /* Since lex.c:_update_mailname() uses realpath(3) if available to
911 * avoid that we loose track of our currently open folder in case we
912 * chdir away, but still checks the leading path portion against
913 * getfold() to be able to abbreviate to the +FOLDER syntax if
914 * possible, we need to realpath(3) the folder, too */
916 res
= ac_alloc(MAXPATHLEN
);
917 if (realpath(folder
, res
) == NULL
)
918 fprintf(stderr
, tr(151, "Can't canonicalize `%s'\n"), folder
);
924 *store
= sstrdup(folder
);
935 getfold(char *name
, size_t size
)
939 if ((folder
= value("folder")) != NULL
)
940 (void)n_strlcpy(name
, folder
, size
);
941 return (folder
!= NULL
);
945 * Return the name of the dead.letter file.
952 if ((cp
= value("DEAD")) == NULL
||
953 (cp
= fexpand(cp
, FEXP_LOCAL
)) == NULL
)
954 cp
= fexpand("~/dead.letter", FEXP_LOCAL
|FEXP_SHELL
);
955 else if (*cp
!= '/') {
956 size_t sz
= strlen(cp
) + 3;
957 char *buf
= ac_alloc(sz
);
959 snprintf(buf
, sz
, "~/%s", cp
);
960 cp
= fexpand(buf
, FEXP_LOCAL
|FEXP_SHELL
);
969 get_header(struct message
*mp
)
972 switch (mb
.mb_type
) {
978 return (pop3_header(mp
));
983 return imap_header(mp
);
992 get_body(struct message
*mp
)
995 switch (mb
.mb_type
) {
1001 return (pop3_body(mp
));
1006 return imap_body(mp
);
1015 static long xwrite(int fd
, const char *data
, size_t sz
);
1018 xwrite(int fd
, const char *data
, size_t sz
)
1024 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
1036 sclose(struct sock
*sp
)
1041 if (sp
->s_onclose
!= NULL
)
1044 if (sp
->s_use_ssl
) {
1046 SSL_shutdown(sp
->s_ssl
);
1047 SSL_free(sp
->s_ssl
);
1049 SSL_CTX_free(sp
->s_ctx
);
1054 i
= close(sp
->s_fd
);
1064 swrite(struct sock
*sp
, const char *data
)
1066 return swrite1(sp
, data
, strlen(data
), 0);
1070 swrite1(struct sock
*sp
, const char *data
, int sz
, int use_buffer
)
1074 if (use_buffer
> 0) {
1078 if (sp
->s_wbuf
== NULL
) {
1079 sp
->s_wbufsize
= 4096;
1080 sp
->s_wbuf
= smalloc(sp
->s_wbufsize
);
1083 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
1084 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
1086 if (sp
->s_wbufpos
> 0) {
1087 memcpy(&sp
->s_wbuf
[sp
->s_wbufpos
], data
, di
);
1088 ok
= swrite1(sp
, sp
->s_wbuf
,
1089 sp
->s_wbufsize
, -1);
1091 ok
= swrite1(sp
, data
,
1092 sp
->s_wbufsize
, -1);
1098 if (sz
== sp
->s_wbufsize
) {
1099 ok
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1103 memcpy(&sp
->s_wbuf
[sp
->s_wbufpos
], data
, sz
);
1104 sp
->s_wbufpos
+= sz
;
1107 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&&
1108 sp
->s_wbufpos
> 0) {
1111 if (swrite1(sp
, sp
->s_wbuf
, x
, -1) != OKAY
)
1117 if (sp
->s_use_ssl
) {
1118 ssl_retry
: x
= SSL_write(sp
->s_ssl
, data
, sz
);
1120 switch (SSL_get_error(sp
->s_ssl
, x
)) {
1121 case SSL_ERROR_WANT_READ
:
1122 case SSL_ERROR_WANT_WRITE
:
1129 x
= xwrite(sp
->s_fd
, data
, sz
);
1133 snprintf(o
, sizeof o
, "%s write error",
1134 sp
->s_desc
? sp
->s_desc
: "socket");
1136 sp
->s_use_ssl
? ssl_gen_err("%s", o
) : perror(o
);
1148 sopen(const char *xserver
, struct sock
*sp
, int use_ssl
,
1149 const char *uhp
, const char *portstr
, int verbose
)
1151 #ifdef HAVE_SO_SNDTIMEO
1154 #ifdef HAVE_SO_LINGER
1158 char hbuf
[NI_MAXHOST
];
1159 struct addrinfo hints
, *res0
, *res
;
1161 struct sockaddr_in servaddr
;
1162 struct in_addr
**pptr
;
1165 unsigned short port
= 0;
1169 char *server
= UNCONST(xserver
);
1173 if ((cp
= strchr(server
, ':')) != NULL
) { /* TODO URI parse! IPv6! */
1176 port
= strtol(portstr
, NULL
, 10);
1178 server
= salloc(cp
- xserver
+ 1);
1179 memcpy(server
, xserver
, cp
- xserver
);
1180 server
[cp
- xserver
] = '\0';
1183 /* Connect timeouts after 30 seconds */
1184 #ifdef HAVE_SO_SNDTIMEO
1191 fprintf(stderr
, "Resolving host %s . . .", server
);
1192 memset(&hints
, 0, sizeof hints
);
1193 hints
.ai_socktype
= SOCK_STREAM
;
1194 if (getaddrinfo(server
, portstr
, &hints
, &res0
) != 0) {
1195 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1198 fprintf(stderr
, tr(500, " done.\n"));
1201 for (res
= res0
; res
!= NULL
&& sockfd
< 0; res
= res
->ai_next
) {
1203 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
,
1204 hbuf
, sizeof hbuf
, NULL
, 0,
1205 NI_NUMERICHOST
) != 0)
1206 strcpy(hbuf
, "unknown host");
1207 fprintf(stderr
, tr(192,
1208 "%sConnecting to %s:%s . . ."),
1209 (res
== res0
) ? "" : "\n",
1212 if ((sockfd
= socket(res
->ai_family
, res
->ai_socktype
,
1213 res
->ai_protocol
)) >= 0) {
1214 # ifdef HAVE_SO_SNDTIMEO
1215 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
,
1218 if (connect(sockfd
, res
->ai_addr
, res
->ai_addrlen
)!=0) {
1225 perror(tr(254, " could not connect"));
1231 #else /* HAVE_IPV6 */
1233 if (strcmp(portstr
, "smtp") == 0)
1235 else if (strcmp(portstr
, "smtps") == 0)
1238 else if (strcmp(portstr
, "imap") == 0)
1240 else if (strcmp(portstr
, "imaps") == 0)
1244 else if (strcmp(portstr
, "pop3") == 0)
1246 else if (strcmp(portstr
, "pop3s") == 0)
1249 else if ((ep
= getservbyname(UNCONST(portstr
), "tcp")) != NULL
)
1252 fprintf(stderr
, tr(251, "Unknown service: %s\n"),
1260 fprintf(stderr
, "Resolving host %s . . .", server
);
1261 if ((hp
= gethostbyname(server
)) == NULL
) {
1262 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1265 fprintf(stderr
, tr(500, " done.\n"));
1267 pptr
= (struct in_addr
**)hp
->h_addr_list
;
1268 if ((sockfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
1269 perror(tr(253, "could not create socket"));
1272 memset(&servaddr
, 0, sizeof servaddr
);
1273 servaddr
.sin_family
= AF_INET
;
1274 servaddr
.sin_port
= port
;
1275 memcpy(&servaddr
.sin_addr
, *pptr
, sizeof(struct in_addr
));
1277 fprintf(stderr
, tr(192, "%sConnecting to %s:%d . . ."),
1278 "", inet_ntoa(**pptr
), ntohs(port
));
1280 # ifdef HAVE_SO_SNDTIMEO
1281 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1283 if (connect(sockfd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
)
1285 perror(tr(254, " could not connect"));
1288 #endif /* HAVE_IPV6 */
1290 fputs(tr(193, " connected.\n"), stderr
);
1292 /* And the regular timeouts */
1293 #ifdef HAVE_SO_SNDTIMEO
1296 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1297 setsockopt(sockfd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
1299 #ifdef HAVE_SO_LINGER
1302 setsockopt(sockfd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
1305 memset(sp
, 0, sizeof *sp
);
1308 if (use_ssl
&& ssl_open(server
, sp
, uhp
) != OKAY
) {
1317 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
1322 if (sp
->s_rsz
< 0) {
1327 if (*line
== NULL
|| lp
> &(*line
)[*linesize
- 128]) {
1328 size_t diff
= lp
- *line
;
1329 *line
= (srealloc
)(*line
, *linesize
+= 256
1330 SMALLOC_DEBUG_ARGSCALL
);
1331 lp
= &(*line
)[diff
];
1333 if (sp
->s_rbufptr
== NULL
||
1334 sp
->s_rbufptr
>= &sp
->s_rbuf
[sp
->s_rsz
]) {
1336 if (sp
->s_use_ssl
) {
1337 ssl_retry
: if ((sp
->s_rsz
= SSL_read(sp
->s_ssl
,
1339 sizeof sp
->s_rbuf
)) <= 0) {
1340 if (sp
->s_rsz
< 0) {
1342 switch(SSL_get_error(sp
->s_ssl
,
1344 case SSL_ERROR_WANT_READ
:
1345 case SSL_ERROR_WANT_WRITE
:
1348 snprintf(o
, sizeof o
, "%s",
1352 ssl_gen_err("%s", o
);
1360 again
: if ((sp
->s_rsz
= read(sp
->s_fd
, sp
->s_rbuf
,
1361 sizeof sp
->s_rbuf
)) <= 0) {
1362 if (sp
->s_rsz
< 0) {
1366 snprintf(o
, sizeof o
, "%s",
1375 sp
->s_rbufptr
= sp
->s_rbuf
;
1377 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
1380 *linelen
= lp
- *line
;
1383 #endif /* HAVE_SOCKETS */
1386 load(char const *name
)
1390 if (name
== NULL
|| (in
= Fopen(name
, "r")) == NULL
)
1411 if ((cp
= fexpand(*arglist
, FEXP_LOCAL
)) == NULL
)
1413 if ((fi
= Fopen(cp
, "r")) == NULL
) {
1417 if (_ssp
>= SSTACK
- 1) {
1418 fprintf(stderr
, tr(3, "Too much \"sourcing\" going on.\n"));
1423 _sstack
[_ssp
].s_file
= _input
;
1424 _sstack
[_ssp
].s_cond
= cond
;
1425 _sstack
[_ssp
].s_loading
= loading
;
1442 fprintf(stderr
, tr(4, "\"Source\" stack over-pop.\n"));
1449 fprintf(stderr
, tr(5, "Unmatched \"if\"\n"));
1451 cond
= _sstack
[_ssp
].s_cond
;
1452 loading
= _sstack
[_ssp
].s_loading
;
1453 _input
= _sstack
[_ssp
].s_file
;