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
53 # include <sys/socket.h>
55 # include <netinet/in.h>
56 # ifdef HAVE_ARPA_INET_H
57 # include <arpa/inet.h>
62 # include <openssl/err.h>
63 # include <openssl/rand.h>
64 # include <openssl/ssl.h>
65 # include <openssl/x509v3.h>
66 # include <openssl/x509.h>
72 FILE *s_file
; /* File we were in. */
73 enum condition s_cond
; /* Saved state of conditionals */
74 int s_loading
; /* Loading .mailrc, etc. */
77 static size_t _ssp
; /* Top of file stack */
80 /* Locate the user's mailbox file (where new, unread mail is queued) */
81 static void _findmail(char *buf
, size_t bufsize
, char const *user
,
84 /* Perform shell meta character expansion */
85 static char * _globname(char const *name
, enum fexp_mode fexpm
);
87 /* *line* is a buffer with the result of fgets().
88 * Returns the first newline or the last character read */
89 static size_t _length_of_line(const char *line
, size_t linesize
);
91 /* Read a line, one character at a time */
92 static char * _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
93 FILE *fp
, int appendnl
, size_t n SMALLOC_DEBUG_ARGS
);
95 static void makemessage(void);
96 static void append(struct message
*mp
);
97 static enum okay
get_header(struct message
*mp
);
100 _findmail(char *buf
, size_t bufsize
, char const *user
, bool_t force
)
104 if (strcmp(user
, myname
) == 0 && ! force
&&
105 (cp
= value("folder")) != NULL
) {
106 switch (which_protocol(cp
)) {
108 if (strcmp(cp
, protbase(cp
)) != 0)
110 snprintf(buf
, bufsize
, "%s/INBOX", cp
);
117 if (force
|| (cp
= value("MAIL")) == NULL
)
118 snprintf(buf
, bufsize
, "%s/%s", MAILSPOOL
, user
);
121 n_strlcpy(buf
, cp
, bufsize
);
128 _globname(char const *name
, enum fexp_mode fexpm
)
137 * Some systems (notably Open UNIX 8.0.0) fork a shell for
138 * wordexp() and wait for it; waiting will fail if our SIGCHLD
142 sigaddset(&nset
, SIGCHLD
);
143 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
144 /* Mac OS X Snow Leopard doesn't init fields on error, causing SIGSEGV
147 memset(&we
, 0, sizeof we
);
149 i
= wordexp(name
, &we
, 0);
150 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
156 if (! (fexpm
& FEXP_SILENT
))
158 tr(83, "\"%s\": Expansion buffer overflow.\n"),
164 if (! (fexpm
& FEXP_SILENT
))
165 fprintf(stderr
, tr(242, "Syntax error in \"%s\"\n"),
170 switch (we
.we_wordc
) {
172 cp
= savestr(we
.we_wordv
[0]);
175 if (! (fexpm
& FEXP_SILENT
))
176 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
179 if (fexpm
& FEXP_MULTIOK
) {
182 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
)
183 l
+= strlen(we
.we_wordv
[j
]) + 1;
186 for (l
= 0, j
= 0; j
< we
.we_wordc
; ++j
) {
187 size_t x
= strlen(we
.we_wordv
[j
]);
188 memcpy(cp
+ l
, we
.we_wordv
[j
], x
);
193 } else if (! (fexpm
& FEXP_SILENT
))
194 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
201 #else /* !HAVE_WORDEXP */
202 extern int wait_status
;
205 char xname
[MAXPATHLEN
], cmdbuf
[MAXPATHLEN
], /* also used for files */
207 int pid
, l
, pivec
[2];
209 if (pipe(pivec
) < 0) {
213 snprintf(cmdbuf
, sizeof cmdbuf
, "echo %s", name
);
214 if ((shell
= value("SHELL")) == NULL
)
215 shell
= UNCONST(SHELL
);
216 pid
= start_command(shell
, 0, -1, pivec
[1], "-c", cmdbuf
, NULL
);
225 l
= read(pivec
[0], xname
, sizeof xname
);
234 if (wait_child(pid
) < 0 && WTERMSIG(wait_status
) != SIGPIPE
) {
235 if (! (fexpm
& FEXP_SILENT
))
236 fprintf(stderr
, tr(81, "\"%s\": Expansion failed.\n"),
241 if (! (fexpm
& FEXP_SILENT
))
242 fprintf(stderr
, tr(82, "\"%s\": No match.\n"), name
);
245 if (l
== sizeof xname
) {
246 if (! (fexpm
& FEXP_SILENT
))
248 tr(83, "\"%s\": Expansion buffer overflow.\n"),
253 for (cp
= &xname
[l
-1]; *cp
== '\n' && cp
> xname
; cp
--)
256 if (! (fexpm
& FEXP_MULTIOK
) && strchr(xname
, ' ') &&
257 stat(xname
, &sbuf
) < 0) {
258 if (! (fexpm
& FEXP_SILENT
))
259 fprintf(stderr
, tr(84, "\"%s\": Ambiguous.\n"), name
);
262 return savestr(xname
);
263 #endif /* !HAVE_WORDEXP */
267 * line is a buffer with the result of fgets(). Returns the first
268 * newline or the last character read.
271 _length_of_line(const char *line
, size_t linesize
)
275 /* Last character is always '\0' and was added by fgets() */
276 for (--linesize
, i
= 0; i
< linesize
; i
++)
279 return (i
< linesize
) ? i
+ 1 : linesize
;
283 _fgetline_byone(char **line
, size_t *linesize
, size_t *llen
,
284 FILE *fp
, int appendnl
, size_t n SMALLOC_DEBUG_ARGS
)
288 if (*line
== NULL
|| *linesize
< LINESIZE
+ n
+ 1)
289 *line
= (srealloc
)(*line
, *linesize
= LINESIZE
+ n
+ 1
290 SMALLOC_DEBUG_ARGSCALL
);
292 if (n
>= *linesize
- 128)
293 *line
= (srealloc
)(*line
, *linesize
+= 256
294 SMALLOC_DEBUG_ARGSCALL
);
318 (fgetline
)(char **line
, size_t *linesize
, size_t *cnt
, size_t *llen
,
319 FILE *fp
, int appendnl SMALLOC_DEBUG_ARGS
)
325 * If we have no count, we cannot determine where the
326 * characters returned by fgets() end if there was no
327 * newline. We have to read one character at one.
329 return _fgetline_byone(line
, linesize
, llen
, fp
, appendnl
, 0
330 SMALLOC_DEBUG_ARGSCALL
);
331 if (*line
== NULL
|| *linesize
< LINESIZE
)
332 *line
= (srealloc
)(*line
, *linesize
= LINESIZE
333 SMALLOC_DEBUG_ARGSCALL
);
334 sz
= *linesize
<= *cnt
? *linesize
: *cnt
+ 1;
335 if (sz
<= 1 || fgets(*line
, sz
, fp
) == NULL
)
337 * Leave llen untouched; it is used to determine whether
338 * the last line was \n-terminated in some callers.
341 i_llen
= _length_of_line(*line
, sz
);
343 while ((*line
)[i_llen
- 1] != '\n') {
344 *line
= (srealloc
)(*line
, *linesize
+= 256
345 SMALLOC_DEBUG_ARGSCALL
);
346 sz
= *linesize
- i_llen
;
347 sz
= (sz
<= *cnt
? sz
: *cnt
+ 1);
348 if (sz
<= 1 || fgets(&(*line
)[i_llen
], sz
, fp
) == NULL
) {
350 (*line
)[i_llen
++] = '\n';
351 (*line
)[i_llen
] = '\0';
355 sz
= _length_of_line(&(*line
)[i_llen
], sz
);
365 (readline_restart
)(FILE *ibuf
, char **linebuf
, size_t *linesize
, size_t n
368 /* TODO readline_restart(): always *appends* LF just to strip it again;
369 * TODO should be configurable just as for fgetline(); ..or whatevr.. */
374 * Interrupts will cause trouble if we are inside a stdio call. As
375 * this is only relevant if input comes from a terminal, we can simply
376 * bypass it by read() then.
378 if (fileno(ibuf
) == 0 && (options
& OPT_TTYIN
)) {
379 if (*linebuf
== NULL
|| *linesize
< LINESIZE
+ n
+ 1)
380 *linebuf
= (srealloc
)(*linebuf
,
381 *linesize
= LINESIZE
+ n
+ 1
382 SMALLOC_DEBUG_ARGSCALL
);
384 if (n
>= *linesize
- 128)
385 *linebuf
= (srealloc
)(*linebuf
,
387 SMALLOC_DEBUG_ARGSCALL
);
389 sz
= read(0, *linebuf
+ n
, *linesize
- n
- 1);
392 (*linebuf
)[n
] = '\0';
393 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
396 if (sz
< 0 && errno
== EINTR
)
399 if ((*linebuf
)[n
- 1] != '\n') {
400 (*linebuf
)[n
++] = '\n';
401 (*linebuf
)[n
] = '\0';
410 * Not reading from standard input or standard input not
411 * a terminal. We read one char at a time as it is the
412 * only way to get lines with embedded NUL characters in
415 if (_fgetline_byone(linebuf
, linesize
, &n
, ibuf
, 1, n
416 SMALLOC_DEBUG_ARGSCALL
) == NULL
)
419 if (n
> 0 && (*linebuf
)[n
- 1] == '\n')
420 (*linebuf
)[--n
] = '\0';
425 (readline_input
)(enum lned_mode lned
, char const *prompt
, char **linebuf
,
426 size_t *linesize SMALLOC_DEBUG_ARGS
)
428 FILE *ifile
= (_input
!= NULL
) ? _input
: stdin
;
429 bool_t doprompt
, dotty
;
433 prompt
= getprompt();
434 doprompt
= (! sourcing
&& (options
& OPT_INTERACTIVE
));
435 dotty
= (doprompt
&& ! boption("line-editor-disable"));
439 assert(ifile
== stdin
);
440 n
= (tty_readline
)(prompt
, linebuf
, linesize
, n
441 SMALLOC_DEBUG_ARGSCALL
);
443 if (doprompt
&& *prompt
) {
444 fputs(prompt
, stdout
);
447 n
= (readline_restart
)(ifile
, linebuf
, linesize
, n
448 SMALLOC_DEBUG_ARGSCALL
);
454 * An unquoted <backslash> at the end of a command line
455 * shall be discarded and the next line shall continue the
458 if ((lned
& LNED_LF_ESC
) && (*linebuf
)[n
- 1] == '\\') {
459 (*linebuf
)[--n
] = '\0';
461 prompt
= ".. "; /* XXX PS2 .. */
464 if (dotty
&& (lned
& LNED_HIST_ADD
))
465 tty_addhist(*linebuf
);
472 readstr_input(char const *prompt
, char const *string
) /* FIXME SIGS<->leaks */
474 /* TODO readstr_input(): linebuf pool */
475 size_t linesize
= 0, slen
;
476 char *linebuf
= NULL
, *rv
= NULL
;
477 bool_t doprompt
, dotty
;
480 prompt
= getprompt();
481 doprompt
= (! sourcing
&& (options
& OPT_INTERACTIVE
));
482 dotty
= (doprompt
&& ! boption("line-editor-disable"));
484 /* If STDIN is not a terminal, simply read from it */
486 slen
= (string
!= NULL
) ? strlen(string
) : 0;
488 linesize
= slen
+ LINESIZE
+ 1;
489 linebuf
= smalloc(linesize
);
491 memcpy(linebuf
, string
, slen
+ 1);
493 if (tty_readline(prompt
, &linebuf
, &linesize
, slen
) >= 0)
496 if (doprompt
&& *prompt
) {
497 fputs(prompt
, stdout
);
502 if (readline_restart(stdin
, &linebuf
, &linesize
, slen
) >= 0)
507 rv
= (*rv
== '\0') ? NULL
: savestr(rv
);
513 * Set up the input pointers while copying the mail file into /tmp.
516 setptr(FILE *ibuf
, off_t offset
)
519 char *cp
, *linebuf
= NULL
;
522 int maybe
, inhead
, thiscnt
;
523 size_t linesize
= 0, filesize
, cnt
;
528 memset(&this, 0, sizeof this);
529 this.m_flag
= MUSED
|MNEW
|MNEWEST
;
530 filesize
= mailsize
- offset
;
531 offset
= ftell(mb
.mb_otf
);
533 if (fgetline(&linebuf
, &linesize
, &filesize
, &cnt
, ibuf
, 0)
535 this.m_xsize
= this.m_size
;
536 this.m_xlines
= this.m_lines
;
537 this.m_have
= HAVE_HEADER
|HAVE_BODY
;
546 if (linebuf
[0] == '\0')
549 /* XXX Convert CRLF to LF; this should be rethought in that
550 * XXX CRLF input should possibly end as CRLF output? */
551 if (cnt
>= 2 && linebuf
[cnt
- 1] == '\n' &&
552 linebuf
[cnt
- 2] == '\r')
553 linebuf
[--cnt
- 1] = '\n';
554 fwrite(linebuf
, sizeof *linebuf
, cnt
, mb
.mb_otf
);
555 if (ferror(mb
.mb_otf
)) {
559 if (linebuf
[cnt
- 1] == '\n')
560 linebuf
[cnt
- 1] = '\0';
561 if (maybe
&& linebuf
[0] == 'F' && is_head(linebuf
, cnt
)) {
563 * TODO char date[FROM_DATEBUF];
564 * TODO extract_date_from_from_(linebuf, cnt, date);
565 * TODO this.m_time = 10000;
567 this.m_xsize
= this.m_size
;
568 this.m_xlines
= this.m_lines
;
569 this.m_have
= HAVE_HEADER
|HAVE_BODY
;
573 this.m_flag
= MUSED
|MNEW
|MNEWEST
;
576 this.m_block
= mailx_blockof(offset
);
577 this.m_offset
= mailx_offsetof(offset
);
579 } else if (linebuf
[0] == 0) {
582 for (cp
= linebuf
, cp2
= "status";; cp
++) {
583 if ((c
= *cp2
++) == 0) {
584 while (c
= *cp
++, whitechar(c
));
587 while ((c
= *cp
++) != '\0')
589 this.m_flag
|= MREAD
;
591 this.m_flag
&= ~MNEW
;
594 if (*cp
!= c
&& *cp
!= upperconv(c
))
597 for (cp
= linebuf
, cp2
= "x-status";; cp
++) {
598 if ((c
= *cp2
++) == 0) {
599 while (c
= *cp
++, whitechar(c
));
602 while ((c
= *cp
++) != '\0')
604 this.m_flag
|= MFLAGGED
;
606 this.m_flag
|=MANSWERED
;
608 this.m_flag
|=MDRAFTED
;
611 if (*cp
!= c
&& *cp
!= upperconv(c
))
618 maybe
= linebuf
[0] == 0;
624 * Drop the passed line onto the passed output buffer.
625 * If a write error occurs, return -1, else the count of
626 * characters written, including the newline.
629 putline(FILE *obuf
, char *linebuf
, size_t cnt
)
631 fwrite(linebuf
, sizeof *linebuf
, cnt
, obuf
);
639 * Return a file buffer all ready to read up the
640 * passed message pointer.
643 setinput(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
649 if (m
->m_have
& HAVE_HEADER
)
655 if (m
->m_have
& HAVE_BODY
)
667 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
,
668 m
->m_offset
), SEEK_SET
) < 0) {
670 panic(catgets(catd
, CATSET
, 77, "temporary file seek"));
676 setdot(struct message
*mp
)
680 did_print_dot
= FAL0
;
688 * Take the data out of the passed ghost file and toss it into
689 * a dynamically allocated message structure.
697 message
[msgCount
].m_size
= 0;
698 message
[msgCount
].m_lines
= 0;
702 * Append the passed message descriptor onto the message structure.
705 append(struct message
*mp
)
707 if (msgCount
+ 1 >= msgspace
)
708 message
= srealloc(message
, (msgspace
+= 64) * sizeof *message
);
710 message
[msgCount
- 1] = *mp
;
714 * Delete a file, but only if the file is a plain file.
717 rm(char *name
) /* TODO TOCTOU; but i'm out of ideas today */
722 if (stat(name
, &sb
) < 0)
724 else if (! S_ISREG(sb
.st_mode
))
731 static int sigdepth
; /* depth of holdsigs() */
732 static sigset_t nset
, oset
;
734 * Hold signals SIGHUP, SIGINT, and SIGQUIT.
740 if (sigdepth
++ == 0) {
742 sigaddset(&nset
, SIGHUP
);
743 sigaddset(&nset
, SIGINT
);
744 sigaddset(&nset
, SIGQUIT
);
745 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
750 * Release signals SIGHUP, SIGINT, and SIGQUIT.
756 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
760 * Determine the size of the file possessed by
768 if (fstat(fileno(iob
), &sbuf
) < 0)
774 fexpand(char const *name
, enum fexp_mode fexpm
)
776 char cbuf
[MAXPATHLEN
], *res
;
782 * The order of evaluation is "%" and "#" expand into constants.
783 * "&" can expand into "+". "+" can expand into shell meta characters.
784 * Shell meta characters expand into constants.
785 * This way, we make no recursive expansion.
788 if (! (fexpm
& FEXP_NSHORTCUT
) && (sh
= get_shortcut(res
)) != NULL
)
791 if (fexpm
& FEXP_SHELL
) {
799 if (res
[1] == ':' && res
[2] != '\0') {
803 _findmail(cbuf
, sizeof cbuf
,
804 (res
[1] != '\0') ? res
+ 1 : myname
,
805 (res
[1] != '\0' || option_u_arg
!= NULL
));
811 if (prevfile
[0] == '\0') {
812 fprintf(stderr
, tr(80, "No previous file\n"));
819 if (res
[1] == '\0') {
820 if ((res
= value("MBOX")) == NULL
)
821 res
= UNCONST("~/mbox");
822 else if (res
[0] != '&' || res
[1] != '\0')
828 if (res
[0] == '@' && which_protocol(mailname
) == PROTO_IMAP
) {
829 res
= str_concat_csvl(&s
,
830 protbase(mailname
), "/", res
+ 1, NULL
)->s
;
834 if (res
[0] == '+' && getfold(cbuf
, sizeof cbuf
)) {
835 size_t i
= strlen(cbuf
);
837 res
= str_concat_csvl(&s
, cbuf
,
838 (i
> 0 && cbuf
[i
- 1] == '/') ? "" : "/",
842 if (res
[0] == '%' && res
[1] == ':') {
848 /* Catch the most common shell meta character */
850 if (res
[0] == '~' && (res
[1] == '/' || res
[1] == '\0')) {
851 res
= str_concat_csvl(&s
, homedir
, res
+ 1, NULL
)->s
;
855 if (anyof(res
, "|&;<>~{}()[]*?$`'\"\\") &&
856 which_protocol(res
) == PROTO_FILE
) {
857 res
= _globname(res
, fexpm
);
863 if (fexpm
& FEXP_LOCAL
)
864 switch (which_protocol(res
)) {
866 case PROTO_MAILDIR
: /* XXX Really? ok MAILDIR for local? */
869 fprintf(stderr
, tr(280,
870 "`%s': only a local file or directory may "
885 if (value("keep") != NULL
|| rm(mailname
) < 0) {
886 int fd
= open(mailname
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
893 var_folder_updated(char const *name
, char **store
)
896 char *folder
, *unres
= NULL
, *res
= NULL
;
898 if ((folder
= UNCONST(name
)) == NULL
)
901 /* Expand the *folder*; skip `%:' prefix for simplicity of use */
902 /* XXX This *only* works because we do NOT
903 * XXX update environment variables via the "set" mechanism */
904 if (folder
[0] == '%' && folder
[1] == ':')
906 if ((folder
= fexpand(folder
, FEXP_FULL
)) == NULL
) /* XXX error? */
909 switch (which_protocol(folder
)) {
911 /* Ooops. This won't work */
912 fprintf(stderr
, tr(501, "`folder' cannot be set to a flat, "
913 "readonly POP3 account\n"));
917 /* Simply assign what we have, even including `%:' prefix */
922 /* Further expansion desired */
926 /* All non-absolute paths are relative to our home directory */
927 if (*folder
!= '/') {
928 size_t l1
= strlen(homedir
), l2
= strlen(folder
);
929 unres
= ac_alloc(l1
+ l2
+ 2);
930 memcpy(unres
, homedir
, l1
);
932 memcpy(unres
+ l1
+ 1, folder
, l2
);
933 unres
[l1
+ 1 + l2
] = '\0';
937 /* Since lex.c:_update_mailname() uses realpath(3) if available to
938 * avoid that we loose track of our currently open folder in case we
939 * chdir away, but still checks the leading path portion against
940 * getfold() to be able to abbreviate to the +FOLDER syntax if
941 * possible, we need to realpath(3) the folder, too */
943 res
= ac_alloc(MAXPATHLEN
);
944 if (realpath(folder
, res
) == NULL
)
945 fprintf(stderr
, tr(151, "Can't canonicalize `%s'\n"), folder
);
951 *store
= sstrdup(folder
);
962 getfold(char *name
, size_t size
)
966 if ((folder
= value("folder")) != NULL
)
967 (void)n_strlcpy(name
, folder
, size
);
968 return (folder
!= NULL
);
972 * Return the name of the dead.letter file.
979 if ((cp
= value("DEAD")) == NULL
||
980 (cp
= fexpand(cp
, FEXP_LOCAL
)) == NULL
)
981 cp
= fexpand("~/dead.letter", FEXP_LOCAL
|FEXP_SHELL
);
982 else if (*cp
!= '/') {
983 size_t sz
= strlen(cp
) + 3;
984 char *buf
= ac_alloc(sz
);
986 snprintf(buf
, sz
, "~/%s", cp
);
987 cp
= fexpand(buf
, FEXP_LOCAL
|FEXP_SHELL
);
996 get_header(struct message
*mp
)
999 switch (mb
.mb_type
) {
1005 return (pop3_header(mp
));
1010 return imap_header(mp
);
1019 get_body(struct message
*mp
)
1022 switch (mb
.mb_type
) {
1028 return (pop3_body(mp
));
1033 return imap_body(mp
);
1042 static long xwrite(int fd
, const char *data
, size_t sz
);
1045 xwrite(int fd
, const char *data
, size_t sz
)
1051 if ((wo
= write(fd
, data
+ wt
, sz
- wt
)) < 0) {
1063 sclose(struct sock
*sp
)
1068 if (sp
->s_onclose
!= NULL
)
1071 if (sp
->s_use_ssl
) {
1073 SSL_shutdown(sp
->s_ssl
);
1074 SSL_free(sp
->s_ssl
);
1076 SSL_CTX_free(sp
->s_ctx
);
1081 i
= close(sp
->s_fd
);
1091 swrite(struct sock
*sp
, const char *data
)
1093 return swrite1(sp
, data
, strlen(data
), 0);
1097 swrite1(struct sock
*sp
, const char *data
, int sz
, int use_buffer
)
1101 if (use_buffer
> 0) {
1105 if (sp
->s_wbuf
== NULL
) {
1106 sp
->s_wbufsize
= 4096;
1107 sp
->s_wbuf
= smalloc(sp
->s_wbufsize
);
1110 while (sp
->s_wbufpos
+ sz
> sp
->s_wbufsize
) {
1111 di
= sp
->s_wbufsize
- sp
->s_wbufpos
;
1113 if (sp
->s_wbufpos
> 0) {
1114 memcpy(&sp
->s_wbuf
[sp
->s_wbufpos
], data
, di
);
1115 ok
= swrite1(sp
, sp
->s_wbuf
,
1116 sp
->s_wbufsize
, -1);
1118 ok
= swrite1(sp
, data
,
1119 sp
->s_wbufsize
, -1);
1125 if (sz
== sp
->s_wbufsize
) {
1126 ok
= swrite1(sp
, data
, sp
->s_wbufsize
, -1);
1130 memcpy(&sp
->s_wbuf
[sp
->s_wbufpos
], data
, sz
);
1131 sp
->s_wbufpos
+= sz
;
1134 } else if (use_buffer
== 0 && sp
->s_wbuf
!= NULL
&&
1135 sp
->s_wbufpos
> 0) {
1138 if (swrite1(sp
, sp
->s_wbuf
, x
, -1) != OKAY
)
1144 if (sp
->s_use_ssl
) {
1145 ssl_retry
: x
= SSL_write(sp
->s_ssl
, data
, sz
);
1147 switch (SSL_get_error(sp
->s_ssl
, x
)) {
1148 case SSL_ERROR_WANT_READ
:
1149 case SSL_ERROR_WANT_WRITE
:
1156 x
= xwrite(sp
->s_fd
, data
, sz
);
1160 snprintf(o
, sizeof o
, "%s write error",
1161 sp
->s_desc
? sp
->s_desc
: "socket");
1163 sp
->s_use_ssl
? ssl_gen_err("%s", o
) : perror(o
);
1175 sopen(const char *xserver
, struct sock
*sp
, int use_ssl
,
1176 const char *uhp
, const char *portstr
, int verbose
)
1178 #ifdef HAVE_SO_SNDTIMEO
1181 #ifdef HAVE_SO_LINGER
1185 char hbuf
[NI_MAXHOST
];
1186 struct addrinfo hints
, *res0
, *res
;
1188 struct sockaddr_in servaddr
;
1189 struct in_addr
**pptr
;
1192 unsigned short port
= 0;
1196 char *server
= UNCONST(xserver
);
1200 if ((cp
= strchr(server
, ':')) != NULL
) { /* TODO URI parse! IPv6! */
1203 port
= strtol(portstr
, NULL
, 10);
1205 server
= salloc(cp
- xserver
+ 1);
1206 memcpy(server
, xserver
, cp
- xserver
);
1207 server
[cp
- xserver
] = '\0';
1210 /* Connect timeouts after 30 seconds */
1211 #ifdef HAVE_SO_SNDTIMEO
1218 fprintf(stderr
, "Resolving host %s . . .", server
);
1219 memset(&hints
, 0, sizeof hints
);
1220 hints
.ai_socktype
= SOCK_STREAM
;
1221 if (getaddrinfo(server
, portstr
, &hints
, &res0
) != 0) {
1222 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1225 fprintf(stderr
, tr(500, " done.\n"));
1228 for (res
= res0
; res
!= NULL
&& sockfd
< 0; res
= res
->ai_next
) {
1230 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
,
1231 hbuf
, sizeof hbuf
, NULL
, 0,
1232 NI_NUMERICHOST
) != 0)
1233 strcpy(hbuf
, "unknown host");
1234 fprintf(stderr
, tr(192,
1235 "%sConnecting to %s:%s . . ."),
1236 (res
== res0
) ? "" : "\n",
1239 if ((sockfd
= socket(res
->ai_family
, res
->ai_socktype
,
1240 res
->ai_protocol
)) >= 0) {
1241 # ifdef HAVE_SO_SNDTIMEO
1242 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
,
1245 if (connect(sockfd
, res
->ai_addr
, res
->ai_addrlen
)!=0) {
1252 perror(tr(254, " could not connect"));
1258 #else /* HAVE_IPV6 */
1260 if (strcmp(portstr
, "smtp") == 0)
1262 else if (strcmp(portstr
, "smtps") == 0)
1265 else if (strcmp(portstr
, "imap") == 0)
1267 else if (strcmp(portstr
, "imaps") == 0)
1271 else if (strcmp(portstr
, "pop3") == 0)
1273 else if (strcmp(portstr
, "pop3s") == 0)
1276 else if ((ep
= getservbyname(UNCONST(portstr
), "tcp")) != NULL
)
1279 fprintf(stderr
, tr(251, "Unknown service: %s\n"),
1287 fprintf(stderr
, "Resolving host %s . . .", server
);
1288 if ((hp
= gethostbyname(server
)) == NULL
) {
1289 fprintf(stderr
, tr(252, " lookup of `%s' failed.\n"), server
);
1292 fprintf(stderr
, tr(500, " done.\n"));
1294 pptr
= (struct in_addr
**)hp
->h_addr_list
;
1295 if ((sockfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
1296 perror(tr(253, "could not create socket"));
1299 memset(&servaddr
, 0, sizeof servaddr
);
1300 servaddr
.sin_family
= AF_INET
;
1301 servaddr
.sin_port
= port
;
1302 memcpy(&servaddr
.sin_addr
, *pptr
, sizeof(struct in_addr
));
1304 fprintf(stderr
, tr(192, "%sConnecting to %s:%d . . ."),
1305 "", inet_ntoa(**pptr
), ntohs(port
));
1307 # ifdef HAVE_SO_SNDTIMEO
1308 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1310 if (connect(sockfd
, (struct sockaddr
*)&servaddr
, sizeof servaddr
)
1312 perror(tr(254, " could not connect"));
1315 #endif /* HAVE_IPV6 */
1317 fputs(tr(193, " connected.\n"), stderr
);
1319 /* And the regular timeouts */
1320 #ifdef HAVE_SO_SNDTIMEO
1323 setsockopt(sockfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof tv
);
1324 setsockopt(sockfd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof tv
);
1326 #ifdef HAVE_SO_LINGER
1329 setsockopt(sockfd
, SOL_SOCKET
, SO_LINGER
, &li
, sizeof li
);
1332 memset(sp
, 0, sizeof *sp
);
1335 if (use_ssl
&& ssl_open(server
, sp
, uhp
) != OKAY
) {
1344 (sgetline
)(char **line
, size_t *linesize
, size_t *linelen
, struct sock
*sp
1349 if (sp
->s_rsz
< 0) {
1354 if (*line
== NULL
|| lp
> &(*line
)[*linesize
- 128]) {
1355 size_t diff
= lp
- *line
;
1356 *line
= (srealloc
)(*line
, *linesize
+= 256
1357 SMALLOC_DEBUG_ARGSCALL
);
1358 lp
= &(*line
)[diff
];
1360 if (sp
->s_rbufptr
== NULL
||
1361 sp
->s_rbufptr
>= &sp
->s_rbuf
[sp
->s_rsz
]) {
1363 if (sp
->s_use_ssl
) {
1364 ssl_retry
: if ((sp
->s_rsz
= SSL_read(sp
->s_ssl
,
1366 sizeof sp
->s_rbuf
)) <= 0) {
1367 if (sp
->s_rsz
< 0) {
1369 switch(SSL_get_error(sp
->s_ssl
,
1371 case SSL_ERROR_WANT_READ
:
1372 case SSL_ERROR_WANT_WRITE
:
1375 snprintf(o
, sizeof o
, "%s",
1379 ssl_gen_err("%s", o
);
1387 again
: if ((sp
->s_rsz
= read(sp
->s_fd
, sp
->s_rbuf
,
1388 sizeof sp
->s_rbuf
)) <= 0) {
1389 if (sp
->s_rsz
< 0) {
1393 snprintf(o
, sizeof o
, "%s",
1402 sp
->s_rbufptr
= sp
->s_rbuf
;
1404 } while ((*lp
++ = *sp
->s_rbufptr
++) != '\n');
1407 *linelen
= lp
- *line
;
1410 #endif /* HAVE_SOCKETS */
1413 load(char const *name
)
1417 if (name
== NULL
|| (in
= Fopen(name
, "r")) == NULL
)
1438 if ((cp
= fexpand(*arglist
, FEXP_LOCAL
)) == NULL
)
1440 if ((fi
= Fopen(cp
, "r")) == NULL
) {
1444 if (_ssp
>= SSTACK
- 1) {
1445 fprintf(stderr
, tr(3, "Too much \"sourcing\" going on.\n"));
1450 _sstack
[_ssp
].s_file
= _input
;
1451 _sstack
[_ssp
].s_cond
= cond
;
1452 _sstack
[_ssp
].s_loading
= loading
;
1469 fprintf(stderr
, tr(4, "\"Source\" stack over-pop.\n"));
1476 fprintf(stderr
, tr(5, "Unmatched \"if\"\n"));
1478 cond
= _sstack
[_ssp
].s_cond
;
1479 loading
= _sstack
[_ssp
].s_loading
;
1480 _input
= _sstack
[_ssp
].s_file
;