Example #1
0
int
main(int argc, char *argv[])
{
	struct sockaddr_un	sun;
	struct parse_result	*res = NULL;
	struct imsg		imsg;
	struct smtpd		smtpd;
	int			ctl_sock;
	int			done = 0;
	int			n, verbose = 0;

	/* parse options */
	if (strcmp(__progname, "sendmail") == 0 || strcmp(__progname, "send-mail") == 0)
		sendmail = 1;
	else if (strcmp(__progname, "mailq") == 0) {
		if (geteuid())
			errx(1, "need root privileges");
		setup_env(&smtpd);
		show_queue(0);
		return 0;
	} else if (strcmp(__progname, "smtpctl") == 0) {

		/* check for root privileges */
		if (geteuid())
			errx(1, "need root privileges");

		setup_env(&smtpd);

		if ((res = parse(argc - 1, argv + 1)) == NULL)
			exit(1);

		/* handle "disconnected" commands */
		switch (res->action) {
		case SHOW_QUEUE:
			show_queue(0);
			break;
		case SHOW_RUNQUEUE:
			break;
		default:
			goto connected;
		}
		return 0;
	} else
		errx(1, "unsupported mode");

connected:
	/* connect to smtpd control socket */
	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
		err(1, "socket");

	bzero(&sun, sizeof(sun));
	sun.sun_family = AF_UNIX;
	strlcpy(sun.sun_path, SMTPD_SOCKET, sizeof(sun.sun_path));
	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
		if (sendmail)
			return enqueue_offline(argc, argv);
		err(1, "connect: %s", SMTPD_SOCKET);
	}

	if ((ibuf = calloc(1, sizeof(struct imsgbuf))) == NULL)
		err(1, NULL);
	imsg_init(ibuf, ctl_sock);

	if (sendmail)
		return enqueue(argc, argv);

	/* process user request */
	switch (res->action) {
	case NONE:
		usage();
		/* not reached */

	case SCHEDULE:
	case REMOVE: {
		u_int64_t ulval;
		char *ep;

		errno = 0;
		ulval = strtoull(res->data, &ep, 16);
		if (res->data[0] == '\0' || *ep != '\0')
			errx(1, "invalid msgid/evpid");
		if (errno == ERANGE && ulval == ULLONG_MAX)
			errx(1, "invalid msgid/evpid");
		if (ulval == 0)
			errx(1, "invalid msgid/evpid");

		if (res->action == SCHEDULE)
			imsg_compose(ibuf, IMSG_SCHEDULER_SCHEDULE, 0, 0, -1, &ulval,
			    sizeof(ulval));
		if (res->action == REMOVE)
			imsg_compose(ibuf, IMSG_SCHEDULER_REMOVE, 0, 0, -1, &ulval,
			    sizeof(ulval));
		break;
	}

	case SCHEDULE_ALL: {
		u_int64_t ulval = 0;

		imsg_compose(ibuf, IMSG_SCHEDULER_SCHEDULE, 0, 0, -1, &ulval,
		    sizeof(ulval));
		break;
	}

	case SHUTDOWN:
		imsg_compose(ibuf, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
		break;
	case PAUSE_MDA:
		imsg_compose(ibuf, IMSG_QUEUE_PAUSE_MDA, 0, 0, -1, NULL, 0);
		break;
	case PAUSE_MTA:
		imsg_compose(ibuf, IMSG_QUEUE_PAUSE_MTA, 0, 0, -1, NULL, 0);
		break;
	case PAUSE_SMTP:
		imsg_compose(ibuf, IMSG_SMTP_PAUSE, 0, 0, -1, NULL, 0);
		break;
	case RESUME_MDA:
		imsg_compose(ibuf, IMSG_QUEUE_RESUME_MDA, 0, 0, -1, NULL, 0);
		break;
	case RESUME_MTA:
		imsg_compose(ibuf, IMSG_QUEUE_RESUME_MTA, 0, 0, -1, NULL, 0);
		break;
	case RESUME_SMTP:
		imsg_compose(ibuf, IMSG_SMTP_RESUME, 0, 0, -1, NULL, 0);
		break;
	case SHOW_STATS:
		imsg_compose(ibuf, IMSG_STATS, 0, 0, -1, NULL, 0);
		break;
	case MONITOR:
		/* XXX */
		break;
	case LOG_VERBOSE:
		verbose = 1;
		/* FALLTHROUGH */
	case LOG_BRIEF:
		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, &verbose,
		    sizeof(verbose));
		printf("logging request sent.\n");
		done = 1;
		break;
	default:
		errx(1, "unknown request (%d)", res->action);
	}

	while (ibuf->w.queued)
		if (msgbuf_write(&ibuf->w) < 0)
			err(1, "write error");

	while (!done) {
		if ((n = imsg_read(ibuf)) == -1)
			errx(1, "imsg_read error");
		if (n == 0)
			errx(1, "pipe closed");

		while (!done) {
			if ((n = imsg_get(ibuf, &imsg)) == -1)
				errx(1, "imsg_get error");
			if (n == 0)
				break;
			switch(res->action) {
			case REMOVE:
			case SCHEDULE:
			case SCHEDULE_ALL:
			case SHUTDOWN:
			case PAUSE_MDA:
			case PAUSE_MTA:
			case PAUSE_SMTP:
			case RESUME_MDA:
			case RESUME_MTA:
			case RESUME_SMTP:
			case LOG_VERBOSE:
			case LOG_BRIEF:
				done = show_command_output(&imsg);
				break;
			case SHOW_STATS:
				done = show_stats_output(&imsg);
				break;
			case NONE:
				break;
			case MONITOR:
				break;
			default:
				err(1, "unexpected reply (%d)", res->action);
			}
			/* insert imsg replies switch here */

			imsg_free(&imsg);
		}
	}
	close(ctl_sock);
	free(ibuf);

	return (0);
}
Example #2
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(&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);
}