diff options
Diffstat (limited to 'enqueue.c')
-rw-r--r-- | enqueue.c | 927 |
1 files changed, 0 insertions, 927 deletions
diff --git a/enqueue.c b/enqueue.c deleted file mode 100644 index b92f8ca2..00000000 --- a/enqueue.c +++ /dev/null @@ -1,927 +0,0 @@ -/* $OpenBSD: enqueue.c,v 1.118 2020/03/18 20:17:14 eric Exp $ */ - -/* - * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> - * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> - * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <grp.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" - -extern struct imsgbuf *ibuf; - -void usage(void); -static void build_from(char *, struct passwd *); -static int parse_message(FILE *, int, int, FILE *); -static void parse_addr(char *, size_t, int); -static void parse_addr_terminal(int); -static char *qualify_addr(char *); -static void rcpt_add(char *); -static int open_connection(void); -static int get_responses(FILE *, int); -static int send_line(FILE *, int, char *, ...); -static int enqueue_offline(int, char *[], FILE *, FILE *); -static int savedeadletter(struct passwd *, FILE *); - -extern int srv_connected(void); - -enum headerfields { - HDR_NONE, - HDR_FROM, - HDR_TO, - HDR_CC, - HDR_BCC, - HDR_SUBJECT, - HDR_DATE, - HDR_MSGID, - HDR_MIME_VERSION, - HDR_CONTENT_TYPE, - HDR_CONTENT_DISPOSITION, - HDR_CONTENT_TRANSFER_ENCODING, - HDR_USER_AGENT -}; - -struct { - char *word; - enum headerfields type; -} keywords[] = { - { "From:", HDR_FROM }, - { "To:", HDR_TO }, - { "Cc:", HDR_CC }, - { "Bcc:", HDR_BCC }, - { "Subject:", HDR_SUBJECT }, - { "Date:", HDR_DATE }, - { "Message-Id:", HDR_MSGID }, - { "MIME-Version:", HDR_MIME_VERSION }, - { "Content-Type:", HDR_CONTENT_TYPE }, - { "Content-Disposition:", HDR_CONTENT_DISPOSITION }, - { "Content-Transfer-Encoding:", HDR_CONTENT_TRANSFER_ENCODING }, - { "User-Agent:", HDR_USER_AGENT }, -}; - -#define LINESPLIT 990 -#define SMTP_LINELEN 1000 -#define TIMEOUTMSG "Timeout\n" - -#define WSP(c) (c == ' ' || c == '\t') - -int verbose = 0; -static char host[HOST_NAME_MAX+1]; -char *user = NULL; -time_t timestamp; - -struct { - int fd; - char *from; - char *fromname; - char **rcpts; - char *dsn_notify; - char *dsn_ret; - char *dsn_envid; - int rcpt_cnt; - int need_linesplit; - int saw_date; - int saw_msgid; - int saw_from; - int saw_mime_version; - int saw_content_type; - int saw_content_disposition; - int saw_content_transfer_encoding; - int saw_user_agent; - int noheader; -} msg; - -struct { - uint quote; - uint comment; - uint esc; - uint brackets; - size_t wpos; - char buf[SMTP_LINELEN]; -} pstate; - -#define QP_TEST_WRAP(fp, buf, linelen, size) do { \ - if (((linelen) += (size)) + 1 > 76) { \ - fprintf((fp), "=\r\n"); \ - if (buf[0] == '.') \ - fprintf((fp), "."); \ - (linelen) = (size); \ - } \ -} while (0) - -/* RFC 2045 section 6.7 */ -static void -qp_encoded_write(FILE *fp, char *buf) -{ - size_t linelen = 0; - - for (;buf[0] != '\0' && buf[0] != '\n'; buf++) { - /* - * Point 3: Any TAB (HT) or SPACE characters on an encoded line - * MUST thus be followed on that line by a printable character. - * - * Ergo, only encode if the next character is EOL. - */ - if (buf[0] == ' ' || buf[0] == '\t') { - if (buf[1] == '\n') { - QP_TEST_WRAP(fp, buf, linelen, 3); - fprintf(fp, "=%2X", *buf & 0xff); - } else { - QP_TEST_WRAP(fp, buf, linelen, 1); - fprintf(fp, "%c", *buf & 0xff); - } - /* - * Point 1, with exclusion of point 2, skip EBCDIC NOTE. - * Do this after whitespace check, else they would match here. - */ - } else if (!((buf[0] >= 33 && buf[0] <= 60) || - (buf[0] >= 62 && buf[0] <= 126))) { - QP_TEST_WRAP(fp, buf, linelen, 3); - fprintf(fp, "=%2X", *buf & 0xff); - /* Point 2: 33 through 60 inclusive, and 62 through 126 */ - } else { - QP_TEST_WRAP(fp, buf, linelen, 1); - fprintf(fp, "%c", *buf); - } - } - fprintf(fp, "\r\n"); -} - -int -enqueue(int argc, char *argv[], FILE *ofp) -{ - int i, ch, tflag = 0; - char *fake_from = NULL, *buf = NULL; - struct passwd *pw; - FILE *fp = NULL, *fout; - size_t sz = 0, envid_sz = 0; - ssize_t len; - char *line; - int inheaders = 1; - int save_argc; - char **save_argv; - int no_getlogin = 0; - - memset(&msg, 0, sizeof(msg)); - time(×tamp); - - save_argc = argc; - save_argv = argv; - - while ((ch = getopt(argc, argv, - "A:B:b:E::e:F:f:iJ::L:mN:o:p:qr:R:StvV:x")) != -1) { - switch (ch) { - case 'f': - fake_from = optarg; - break; - case 'F': - msg.fromname = optarg; - break; - case 'N': - msg.dsn_notify = optarg; - break; - case 'r': - fake_from = optarg; - break; - case 'R': - msg.dsn_ret = optarg; - break; - case 'S': - no_getlogin = 1; - break; - case 't': - tflag = 1; - break; - case 'v': - verbose = 1; - break; - case 'V': - msg.dsn_envid = optarg; - break; - /* all remaining: ignored, sendmail compat */ - case 'A': - case 'B': - case 'b': - case 'E': - case 'e': - case 'i': - case 'L': - case 'm': - case 'o': - case 'p': - case 'x': - break; - case 'q': - /* XXX: implement "process all now" */ - return (EX_SOFTWARE); - default: - usage(); - } - } - - argc -= optind; - argv += optind; - - if (getmailname(host, sizeof(host)) == -1) - errx(EX_NOHOST, "getmailname"); - if (no_getlogin) { - if ((pw = getpwuid(getuid())) == NULL) - user = "anonymous"; - if (pw != NULL) - user = xstrdup(pw->pw_name); - } - else { - uid_t ruid = getuid(); - - if ((user = getlogin()) != NULL && *user != '\0') { - if ((pw = getpwnam(user)) == NULL || - (ruid != 0 && ruid != pw->pw_uid)) - pw = getpwuid(ruid); - } else if ((pw = getpwuid(ruid)) == NULL) { - user = "anonymous"; - } - user = xstrdup(pw ? pw->pw_name : user); - } - - build_from(fake_from, pw); - - while (argc > 0) { - rcpt_add(argv[0]); - argv++; - argc--; - } - - if ((fp = tmpfile()) == NULL) - err(EX_UNAVAILABLE, "tmpfile"); - - msg.noheader = parse_message(stdin, fake_from == NULL, tflag, fp); - - if (msg.rcpt_cnt == 0) - errx(EX_SOFTWARE, "no recipients"); - - /* init session */ - rewind(fp); - - /* check if working in offline mode */ - /* If the server is not running, enqueue the message offline */ - - if (!srv_connected()) { - if (pledge("stdio", NULL) == -1) - err(1, "pledge"); - - return (enqueue_offline(save_argc, save_argv, fp, ofp)); - } - - if ((msg.fd = open_connection()) == -1) - errx(EX_UNAVAILABLE, "server too busy"); - - if (pledge("stdio wpath cpath", NULL) == -1) - err(1, "pledge"); - - fout = fdopen(msg.fd, "a+"); - if (fout == NULL) - err(EX_UNAVAILABLE, "fdopen"); - - /* - * We need to call get_responses after every command because we don't - * support PIPELINING on the server-side yet. - */ - - /* banner */ - if (!get_responses(fout, 1)) - goto fail; - - if (!send_line(fout, verbose, "EHLO localhost\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - if (msg.dsn_envid != NULL) - envid_sz = strlen(msg.dsn_envid); - - if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\r\n", - msg.from, - msg.dsn_ret ? "RET=" : "", - msg.dsn_ret ? msg.dsn_ret : "", - envid_sz ? "ENVID=" : "", - envid_sz ? msg.dsn_envid : "")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - for (i = 0; i < msg.rcpt_cnt; i++) { - if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\r\n", - msg.rcpts[i], - msg.dsn_notify ? "NOTIFY=" : "", - msg.dsn_notify ? msg.dsn_notify : "")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - } - - if (!send_line(fout, verbose, "DATA\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - /* add From */ - if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\r\n", - msg.fromname ? msg.fromname : "", msg.fromname ? " " : "", - msg.from)) - goto fail; - - /* add Date */ - if (!msg.saw_date && !send_line(fout, 0, "Date: %s\r\n", - time_to_text(timestamp))) - goto fail; - - if (msg.need_linesplit) { - /* we will always need to mime encode for long lines */ - if (!msg.saw_mime_version && !send_line(fout, 0, - "MIME-Version: 1.0\r\n")) - goto fail; - if (!msg.saw_content_type && !send_line(fout, 0, - "Content-Type: text/plain; charset=unknown-8bit\r\n")) - goto fail; - if (!msg.saw_content_disposition && !send_line(fout, 0, - "Content-Disposition: inline\r\n")) - goto fail; - if (!msg.saw_content_transfer_encoding && !send_line(fout, 0, - "Content-Transfer-Encoding: quoted-printable\r\n")) - goto fail; - } - - /* add separating newline */ - if (msg.noheader) { - if (!send_line(fout, 0, "\r\n")) - goto fail; - inheaders = 0; - } - - for (;;) { - if ((len = getline(&buf, &sz, fp)) == -1) { - if (feof(fp)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* newlines have been normalized on first parsing */ - if (buf[len-1] != '\n') - errx(EX_SOFTWARE, "expect EOL"); - len--; - - if (buf[0] == '.') { - if (fputc('.', fout) == EOF) - goto fail; - } - - line = buf; - - if (inheaders) { - if (strncasecmp("from ", line, 5) == 0) - continue; - if (strncasecmp("return-path: ", line, 13) == 0) - continue; - } - - if (msg.saw_content_transfer_encoding || msg.noheader || - inheaders || !msg.need_linesplit) { - if (!send_line(fout, 0, "%.*s\r\n", (int)len, line)) - goto fail; - if (inheaders && buf[0] == '\n') - inheaders = 0; - continue; - } - - /* we don't have a content transfer encoding, use our default */ - qp_encoded_write(fout, line); - } - free(buf); - if (!send_line(fout, verbose, ".\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - if (!send_line(fout, verbose, "QUIT\r\n")) - goto fail; - if (!get_responses(fout, 1)) - goto fail; - - fclose(fp); - fclose(fout); - - exit(EX_OK); - -fail: - if (pw) - savedeadletter(pw, fp); - exit(EX_SOFTWARE); -} - -static int -get_responses(FILE *fin, int n) -{ - char *buf = NULL; - size_t sz = 0; - ssize_t len; - int e, ret = 0; - - fflush(fin); - if ((e = ferror(fin))) { - warnx("ferror: %d", e); - goto err; - } - - while (n) { - if ((len = getline(&buf, &sz, fin)) == -1) { - if (ferror(fin)) { - warn("getline"); - goto err; - } else if (feof(fin)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* account for \r\n linebreaks */ - if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') - buf[--len - 1] = '\n'; - - if (len < 4) { - warnx("bad response"); - goto err; - } - - if (verbose) - printf("<<< %.*s", (int)len, buf); - - if (buf[3] == '-') - continue; - if (buf[0] != '2' && buf[0] != '3') { - warnx("command failed: %.*s", (int)len, buf); - goto err; - } - n--; - } - - ret = 1; -err: - free(buf); - return ret; -} - -static int -send_line(FILE *fp, int v, char *fmt, ...) -{ - int ret = 0; - va_list ap; - - va_start(ap, fmt); - if (vfprintf(fp, fmt, ap) >= 0) - ret = 1; - va_end(ap); - - if (ret && v) { - printf(">>> "); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - } - - return (ret); -} - -static void -build_from(char *fake_from, struct passwd *pw) -{ - char *p; - - if (fake_from == NULL) - msg.from = qualify_addr(user); - else { - if (fake_from[0] == '<') { - if (fake_from[strlen(fake_from) - 1] != '>') - errx(1, "leading < but no trailing >"); - fake_from[strlen(fake_from) - 1] = 0; - p = xstrdup(fake_from + 1); - - msg.from = qualify_addr(p); - free(p); - } else - msg.from = qualify_addr(fake_from); - } - - if (msg.fromname == NULL && fake_from == NULL && pw != NULL) { - int len, apos; - - len = strcspn(pw->pw_gecos, ","); - if ((p = memchr(pw->pw_gecos, '&', len))) { - apos = p - pw->pw_gecos; - if (asprintf(&msg.fromname, "%.*s%s%.*s", - apos, pw->pw_gecos, - pw->pw_name, - len - apos - 1, p + 1) == -1) - err(1, NULL); - msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]); - } else { - if (asprintf(&msg.fromname, "%.*s", len, - pw->pw_gecos) == -1) - err(1, NULL); - } - } -} - -static int -parse_message(FILE *fin, int get_from, int tflag, FILE *fout) -{ - char *buf = NULL; - size_t sz = 0; - ssize_t len; - uint i, cur = HDR_NONE; - uint header_seen = 0, header_done = 0; - - memset(&pstate, 0, sizeof(pstate)); - for (;;) { - if ((len = getline(&buf, &sz, fin)) == -1) { - if (feof(fin)) - break; - else - err(EX_UNAVAILABLE, "getline"); - } - - /* account for \r\n linebreaks */ - if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') - buf[--len - 1] = '\n'; - - if (len == 1 && buf[0] == '\n') /* end of header */ - header_done = 1; - - if (!WSP(buf[0])) { /* whitespace -> continuation */ - if (cur == HDR_FROM) - parse_addr_terminal(1); - if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC) - parse_addr_terminal(0); - cur = HDR_NONE; - } - - /* not really exact, if we are still in headers */ - if (len + (buf[len - 1] == '\n' ? 0 : 1) >= LINESPLIT) - msg.need_linesplit = 1; - - for (i = 0; !header_done && cur == HDR_NONE && - i < nitems(keywords); i++) - if ((size_t)len > strlen(keywords[i].word) && - !strncasecmp(buf, keywords[i].word, - strlen(keywords[i].word))) - cur = keywords[i].type; - - if (cur != HDR_NONE) - header_seen = 1; - - if (cur != HDR_BCC) { - if (!send_line(fout, 0, "%.*s", (int)len, buf)) - err(1, "write error"); - if (buf[len - 1] != '\n') { - if (fputc('\n', fout) == EOF) - err(1, "write error"); - } - } - - /* - * using From: as envelope sender is not sendmail compatible, - * but I really want it that way - maybe needs a knob - */ - if (cur == HDR_FROM) { - msg.saw_from++; - if (get_from) - parse_addr(buf, len, 1); - } - - if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)) - parse_addr(buf, len, 0); - - if (cur == HDR_DATE) - msg.saw_date++; - if (cur == HDR_MSGID) - msg.saw_msgid++; - if (cur == HDR_MIME_VERSION) - msg.saw_mime_version = 1; - if (cur == HDR_CONTENT_TYPE) - msg.saw_content_type = 1; - if (cur == HDR_CONTENT_DISPOSITION) - msg.saw_content_disposition = 1; - if (cur == HDR_CONTENT_TRANSFER_ENCODING) - msg.saw_content_transfer_encoding = 1; - if (cur == HDR_USER_AGENT) - msg.saw_user_agent = 1; - } - - free(buf); - return (!header_seen); -} - -static void -parse_addr(char *s, size_t len, int is_from) -{ - size_t pos = 0; - int terminal = 0; - - /* unless this is a continuation... */ - if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') { - /* ... skip over everything before the ':' */ - for (; pos < len && s[pos] != ':'; pos++) - ; /* nothing */ - /* ... and check & reset parser state */ - parse_addr_terminal(is_from); - } - - /* skip over ':' ',' ';' and whitespace */ - for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' || - s[pos] == ',' || s[pos] == ';'); pos++) - ; /* nothing */ - - for (; pos < len; pos++) { - if (!pstate.esc && !pstate.quote && s[pos] == '(') - pstate.comment++; - if (!pstate.comment && !pstate.esc && s[pos] == '"') - pstate.quote = !pstate.quote; - - if (!pstate.comment && !pstate.quote && !pstate.esc) { - if (s[pos] == ':') { /* group */ - for (pos++; pos < len && WSP(s[pos]); pos++) - ; /* nothing */ - pstate.wpos = 0; - } - if (s[pos] == '\n' || s[pos] == '\r') - break; - if (s[pos] == ',' || s[pos] == ';') { - terminal = 1; - break; - } - if (s[pos] == '<') { - pstate.brackets = 1; - pstate.wpos = 0; - } - if (pstate.brackets && s[pos] == '>') - terminal = 1; - } - - if (!pstate.comment && !terminal && (!(!(pstate.quote || - pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) { - if (pstate.wpos >= sizeof(pstate.buf)) - errx(1, "address exceeds buffer size"); - pstate.buf[pstate.wpos++] = s[pos]; - } - - if (!pstate.quote && pstate.comment && s[pos] == ')') - pstate.comment--; - - if (!pstate.esc && !pstate.comment && s[pos] == '\\') - pstate.esc = 1; - else - pstate.esc = 0; - } - - if (terminal) - parse_addr_terminal(is_from); - - for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++) - ; /* nothing */ - - if (pos < len) - parse_addr(s + pos, len - pos, is_from); -} - -static void -parse_addr_terminal(int is_from) -{ - if (pstate.comment || pstate.quote || pstate.esc) - errx(1, "syntax error in address"); - if (pstate.wpos) { - if (pstate.wpos >= sizeof(pstate.buf)) - errx(1, "address exceeds buffer size"); - pstate.buf[pstate.wpos] = '\0'; - if (is_from) - msg.from = qualify_addr(pstate.buf); - else - rcpt_add(pstate.buf); - pstate.wpos = 0; - } -} - -static char * -qualify_addr(char *in) -{ - char *out; - - if (strlen(in) > 0 && strchr(in, '@') == NULL) { - if (asprintf(&out, "%s@%s", in, host) == -1) - err(1, "qualify asprintf"); - } else - out = xstrdup(in); - - return (out); -} - -static void -rcpt_add(char *addr) -{ - void *nrcpts; - char *p; - int n; - - n = 1; - p = addr; - while ((p = strchr(p, ',')) != NULL) { - n++; - p++; - } - - if ((nrcpts = reallocarray(msg.rcpts, - msg.rcpt_cnt + n, sizeof(char *))) == NULL) - err(1, "rcpt_add realloc"); - msg.rcpts = nrcpts; - - while (n--) { - if ((p = strchr(addr, ',')) != NULL) - *p++ = '\0'; - msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr); - if (p == NULL) - break; - addr = p; - } -} - -static int -open_connection(void) -{ - struct imsg imsg; - int fd; - int n; - - imsg_compose(ibuf, IMSG_CTL_SMTP_SESSION, IMSG_VERSION, 0, -1, NULL, 0); - - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) - err(1, "write error"); - - while (1) { - if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) - errx(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); - - if ((n = imsg_get(ibuf, &imsg)) == -1) - errx(1, "imsg_get error"); - if (n == 0) - continue; - - switch (imsg.hdr.type) { - case IMSG_CTL_OK: - break; - case IMSG_CTL_FAIL: - errx(1, "server disallowed submission request"); - default: - errx(1, "unexpected imsg reply type"); - } - - fd = imsg.fd; - imsg_free(&imsg); - - break; - } - - return fd; -} - -static int -enqueue_offline(int argc, char *argv[], FILE *ifile, FILE *ofile) -{ - int i, ch; - - for (i = 1; i < argc; i++) { - if (strchr(argv[i], '|') != NULL) { - warnx("%s contains illegal character", argv[i]); - ftruncate(fileno(ofile), 0); - exit(EX_SOFTWARE); - } - if (fprintf(ofile, "%s%s", i == 1 ? "" : "|", argv[i]) < 0) - goto write_error; - } - - if (fputc('\n', ofile) == EOF) - goto write_error; - - while ((ch = fgetc(ifile)) != EOF) { - if (fputc(ch, ofile) == EOF) - goto write_error; - } - - if (ferror(ifile)) { - warn("read error"); - ftruncate(fileno(ofile), 0); - exit(EX_UNAVAILABLE); - } - - if (fclose(ofile) == EOF) - goto write_error; - - return (EX_TEMPFAIL); -write_error: - warn("write error"); - ftruncate(fileno(ofile), 0); - exit(EX_UNAVAILABLE); -} - -static int -savedeadletter(struct passwd *pw, FILE *in) -{ - char buffer[PATH_MAX]; - FILE *fp; - char *buf = NULL; - size_t sz = 0; - ssize_t len; - - (void)snprintf(buffer, sizeof buffer, "%s/dead.letter", pw->pw_dir); - - if (fseek(in, 0, SEEK_SET) != 0) - return 0; - - if ((fp = fopen(buffer, "w")) == NULL) - return 0; - - /* add From */ - if (!msg.saw_from) - fprintf(fp, "From: %s%s<%s>\n", - msg.fromname ? msg.fromname : "", - msg.fromname ? " " : "", - msg.from); - - /* add Date */ - if (!msg.saw_date) - fprintf(fp, "Date: %s\n", time_to_text(timestamp)); - - if (msg.need_linesplit) { - /* we will always need to mime encode for long lines */ - if (!msg.saw_mime_version) - fprintf(fp, "MIME-Version: 1.0\n"); - if (!msg.saw_content_type) - fprintf(fp, "Content-Type: text/plain; " - "charset=unknown-8bit\n"); - if (!msg.saw_content_disposition) - fprintf(fp, "Content-Disposition: inline\n"); - if (!msg.saw_content_transfer_encoding) - fprintf(fp, "Content-Transfer-Encoding: " - "quoted-printable\n"); - } - - /* add separating newline */ - if (msg.noheader) - fprintf(fp, "\n"); - - while ((len = getline(&buf, &sz, in)) != -1) { - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - fprintf(fp, "%s\n", buf); - } - - free(buf); - fprintf(fp, "\n"); - fclose(fp); - return 1; -} |