예제 #1
0
파일: enqueue.c 프로젝트: jcarnat/OpenSMTPD
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(&timestamp);

	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);
}
예제 #2
0
파일: enqueue.c 프로젝트: lucasad/OpenSMTPD
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(&timestamp);

	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);
}