int enqueue(int argc, char *argv[]) { int i, ch, tflag = 0, noheader; char *fake_from = NULL, *buf; struct passwd *pw; FILE *fp, *fout; size_t len; char *line; int dotted; int inheaders = 0; bzero(&msg, sizeof(msg)); time(×tamp); while ((ch = getopt(argc, argv, "A:B:b:E::e:F:f:iJ::L:mo:p:qtvx")) != -1) { switch (ch) { case 'f': fake_from = optarg; break; case 'F': msg.fromname = optarg; break; case 't': tflag = 1; break; case 'v': verbose = 1; 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 (0); default: usage(); } } argc -= optind; argv += optind; if (gethostname(host, sizeof(host)) == -1) err(1, "gethostname"); if ((pw = getpwuid(getuid())) == NULL) user = "******"; if (pw != NULL && (user = strdup(pw->pw_name)) == NULL) err(1, "strdup"); build_from(fake_from, pw); while(argc > 0) { rcpt_add(argv[0]); argv++; argc--; } signal(SIGALRM, sighdlr); alarm(300); fp = tmpfile(); if (fp == NULL) err(1, "tmpfile"); noheader = parse_message(stdin, fake_from == NULL, tflag, fp); if (msg.rcpt_cnt == 0) errx(1, "no recipients"); /* init session */ rewind(fp); if ((msg.fd = open_connection()) == -1) errx(1, "server too busy"); fout = fdopen(msg.fd, "a+"); if (fout == NULL) err(1, "fdopen"); /* * We need to call get_responses after every command because we don't * support PIPELINING on the server-side yet. */ /* banner */ get_responses(fout, 1); fprintf(fout, "EHLO localhost\n"); get_responses(fout, 1); fprintf(fout, "MAIL FROM: <%s>\n", msg.from); get_responses(fout, 1); for (i = 0; i < msg.rcpt_cnt; i++) { fprintf(fout, "RCPT TO: <%s>\n", msg.rcpts[i]); get_responses(fout, 1); } fprintf(fout, "DATA\n"); get_responses(fout, 1); /* add From */ if (!msg.saw_from) fprintf(fout, "From: %s%s<%s>\n", msg.fromname ? msg.fromname : "", msg.fromname ? " " : "", msg.from); /* add Date */ if (!msg.saw_date) fprintf(fout, "Date: %s\n", time_to_text(timestamp)); /* add Message-Id */ if (!msg.saw_msgid) fprintf(fout, "Message-Id: <%"PRIu64".enqueue@%s>\n", generate_uid(), host); if (msg.need_linesplit) { /* we will always need to mime encode for long lines */ if (!msg.saw_mime_version) fprintf(fout, "MIME-Version: 1.0\n"); if (!msg.saw_content_type) fprintf(fout, "Content-Type: text/plain; charset=unknown-8bit\n"); if (!msg.saw_content_disposition) fprintf(fout, "Content-Disposition: inline\n"); if (!msg.saw_content_transfer_encoding) fprintf(fout, "Content-Transfer-Encoding: quoted-printable\n"); } if (!msg.saw_user_agent) fprintf(fout, "User-Agent: OpenSMTPD enqueuer (Demoosh)\n"); /* add separating newline */ if (noheader) fprintf(fout, "\n"); else inheaders = 1; for (;;) { buf = fgetln(fp, &len); if (buf == NULL && ferror(fp)) err(1, "fgetln"); if (buf == NULL && feof(fp)) break; /* newlines have been normalized on first parsing */ if (buf[len-1] != '\n') errx(1, "expect EOL"); dotted = 0; if (buf[0] == '.') { fputc('.', fout); dotted = 1; } line = buf; if (msg.saw_content_transfer_encoding || noheader || inheaders || !msg.need_linesplit) { fprintf(fout, "%.*s", (int)len, line); if (inheaders && buf[0] == '\n') inheaders = 0; continue; } /* we don't have a content transfer encoding, use our default */ do { if (len < LINESPLIT) { qp_encoded_write(fout, line, len); break; } else { qp_encoded_write(fout, line, LINESPLIT - 2 - dotted); fprintf(fout, "=\n"); line += LINESPLIT - 2 - dotted; len -= LINESPLIT - 2 - dotted; } } while (len); } fprintf(fout, ".\n"); get_responses(fout, 1); fprintf(fout, "QUIT\n"); get_responses(fout, 1); fclose(fp); fclose(fout); exit(0); }
int enqueue(int argc, char *argv[]) { int i, ch, tflag = 0, noheader; char *fake_from = NULL, *buf; struct passwd *pw; FILE *fp, *fout; size_t len, envid_sz = 0; int fd; char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; char *line; int dotted; int inheaders = 0; int save_argc; char **save_argv; 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:tvV: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': msg.dsn_ret = optarg; 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) err(EX_NOHOST, "getmailname"); if ((user = getlogin()) != NULL && *user != '\0') pw = getpwnam(user); else if ((pw = getpwuid(getuid())) == NULL) user = "******"; user = xstrdup(pw ? pw->pw_name : user, "enqueue"); build_from(fake_from, pw); while (argc > 0) { rcpt_add(argv[0]); argv++; argc--; } if ((fd = mkstemp(sfn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { int saved_errno = errno; if (fd != -1) { unlink(sfn); close(fd); } errc(EX_UNAVAILABLE, saved_errno, "mkstemp"); } unlink(sfn); noheader = parse_message(stdin, fake_from == NULL, tflag, fp); if (msg.rcpt_cnt == 0) errx(EX_SOFTWARE, "no recipients"); /* init session */ rewind(fp); /* try to connect */ /* If the server is not running, enqueue the message offline */ if (!srv_connect()) return (enqueue_offline(save_argc, save_argv, fp)); if ((msg.fd = open_connection()) == -1) errx(EX_UNAVAILABLE, "server too busy"); 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 */ get_responses(fout, 1); send_line(fout, verbose, "EHLO localhost\n"); get_responses(fout, 1); if (msg.dsn_envid != NULL) envid_sz = strlen(msg.dsn_envid); send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n", msg.from, msg.dsn_ret ? "RET=" : "", msg.dsn_ret ? msg.dsn_ret : "", envid_sz ? "ENVID=" : "", envid_sz ? msg.dsn_envid : ""); get_responses(fout, 1); for (i = 0; i < msg.rcpt_cnt; i++) { send_line(fout, verbose, "RCPT TO:<%s> %s%s\n", msg.rcpts[i], msg.dsn_notify ? "NOTIFY=" : "", msg.dsn_notify ? msg.dsn_notify : ""); get_responses(fout, 1); } send_line(fout, verbose, "DATA\n"); get_responses(fout, 1); /* add From */ if (!msg.saw_from) send_line(fout, 0, "From: %s%s<%s>\n", msg.fromname ? msg.fromname : "", msg.fromname ? " " : "", msg.from); /* add Date */ if (!msg.saw_date) send_line(fout, 0, "Date: %s\n", time_to_text(timestamp)); /* add Message-Id */ if (!msg.saw_msgid) send_line(fout, 0, "Message-Id: <%"PRIu64".enqueue@%s>\n", generate_uid(), host); 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\n"); if (!msg.saw_content_type) send_line(fout, 0, "Content-Type: text/plain; " "charset=unknown-8bit\n"); if (!msg.saw_content_disposition) send_line(fout, 0, "Content-Disposition: inline\n"); if (!msg.saw_content_transfer_encoding) send_line(fout, 0, "Content-Transfer-Encoding: " "quoted-printable\n"); } /* add separating newline */ if (noheader) send_line(fout, 0, "\n"); else inheaders = 1; for (;;) { buf = fgetln(fp, &len); if (buf == NULL && ferror(fp)) err(EX_UNAVAILABLE, "fgetln"); if (buf == NULL && feof(fp)) break; /* newlines have been normalized on first parsing */ if (buf[len-1] != '\n') errx(EX_SOFTWARE, "expect EOL"); dotted = 0; if (buf[0] == '.') { fputc('.', fout); dotted = 1; } line = buf; if (msg.saw_content_transfer_encoding || noheader || inheaders || !msg.need_linesplit) { if (inheaders) send_header(fout, line, len); else send_line(fout, 0, "%.*s", (int)len, line); if (inheaders && buf[0] == '\n') inheaders = 0; continue; } /* we don't have a content transfer encoding, use our default */ do { if (len < LINESPLIT) { qp_encoded_write(fout, line, len); break; } else { qp_encoded_write(fout, line, LINESPLIT - 2 - dotted); send_line(fout, 0, "=\n"); line += LINESPLIT - 2 - dotted; len -= LINESPLIT - 2 - dotted; } } while (len); } send_line(fout, verbose, ".\n"); get_responses(fout, 1); send_line(fout, verbose, "QUIT\n"); get_responses(fout, 1); fclose(fp); fclose(fout); exit(EX_OK); }