Example #1
0
File: lmtp.c Project: jinwon/dbmail
static void lmtp_rset(ClientSession_T *session, gboolean reset_state)
{
	int state = session->state;
	client_session_reset(session);
	if (reset_state)
		session->state = AUTH;
	else
		session->state = state;
}
Example #2
0
void client_session_bailout(ClientSession_t **session)
{
	ClientSession_t *c = *session;

	if (! c) return;
	TRACE(TRACE_DEBUG,"[%p]", c);

	// brute force:
	if (server_conf->no_daemonize == 1) _exit(0);

	client_session_reset(c);
	c->state = CLIENTSTATE_ANY;
	ci_close(c->ci);
	c->ci = NULL;
	g_free(c);
	c = NULL;
}
Example #3
0
File: lmtp.c Project: jinwon/dbmail
int lmtp(ClientSession_T * session)
{
	DbmailMessage *msg;
	ClientBase_T *ci = session->ci;
	int helpcmd;
	const char *class, *subject, *detail;
	size_t tmplen = 0, tmppos = 0;
	char *tmpaddr = NULL, *tmpbody = NULL, *arg;

	switch (session->command_type) {

	case LMTP_QUIT:
		ci_write(ci, "221 %s BYE\r\n", session->hostname);
		session->state = QUIT;
		return 1;

	case LMTP_NOOP:
		ci_write(ci, "250 OK\r\n");
		return 1;

	case LMTP_RSET:
		ci_write(ci, "250 OK\r\n");
		lmtp_rset(session,TRUE);
		return 1;

	case LMTP_LHLO:
		/* Reply wth our hostname and a list of features.
		 * The RFC requires a couple of SMTP extensions
		 * with a MUST statement, so just hardcode them.
		 * */
		ci_write(ci, "250-%s\r\n250-PIPELINING\r\n"
			"250-ENHANCEDSTATUSCODES\r\n250 SIZE\r\n", 
			session->hostname);
				/* This is a SHOULD implement:
				 * "250-8BITMIME\r\n"
				 * Might as well do these, too:
				 * "250-CHUNKING\r\n"
				 * "250-BINARYMIME\r\n"
				 * */
		client_session_reset(session);
		session->state = AUTH;
		client_session_set_timeout(session, server_conf->timeout);

		return 1;

	case LMTP_HELP:
	
		session->args = g_list_first(session->args);
		if (session->args && session->args->data)
			arg = (char *)session->args->data;
		else
			arg = NULL;

		if (arg == NULL)
			helpcmd = LMTP_END;
		else
			for (helpcmd = LMTP_LHLO; helpcmd < LMTP_END; helpcmd++)
				if (strcasecmp (arg, commands[helpcmd]) == 0)
					break;

		TRACE(TRACE_DEBUG, "LMTP_HELP requested for commandtype %d", helpcmd);

		if ((helpcmd == LMTP_LHLO) || (helpcmd == LMTP_DATA) || 
			(helpcmd == LMTP_RSET) || (helpcmd == LMTP_QUIT) || 
			(helpcmd == LMTP_NOOP) || (helpcmd == LMTP_HELP)) {
			ci_write(ci, "%s", LMTP_HELP_TEXT[helpcmd]);
		} else
			ci_write(ci, "%s", LMTP_HELP_TEXT[LMTP_END]);
		return 1;

	case LMTP_VRFY:
		/* RFC 2821 says this SHOULD be implemented...
		 * and the goal is to say if the given address
		 * is a valid delivery address at this server. */
		ci_write(ci, "502 Command not implemented\r\n");
		return 1;

	case LMTP_EXPN:
		/* RFC 2821 says this SHOULD be implemented...
		 * and the goal is to return the membership
		 * of the specified mailing list. */
		ci_write(ci, "502 Command not implemented\r\n");
		return 1;

	case LMTP_MAIL:
		/* We need to LHLO first because the client
		 * needs to know what extensions we support.
		 * */
		if (session->state != AUTH) {
			ci_write(ci, "550 Command out of sequence.\r\n");
			return 1;
		} 
		if (g_list_length(session->from) > 0) {
			ci_write(ci, "500 Sender already received. Use RSET to clear.\r\n");
			return 1;
		}
		/* First look for an email address.
		 * Don't bother verifying or whatever,
		 * just find something between angle brackets!
		 * */

		session->args = g_list_first(session->args);
		if (! (session->args && session->args->data))
			return 1;
		arg = (char *)session->args->data;

		if (find_bounded(arg, '<', '>', &tmpaddr, &tmplen, &tmppos) < 0) {
			ci_write(ci, "500 No address found. Missing <> boundries.\r\n");
			return 1;
		}

		/* Second look for a BODY keyword.
		 * See if it has an argument, and if we
		 * support that feature. Don't give an OK
		 * if we can't handle it yet, like 8BIT!
		 * */

		/* Find the '=' following the address
		 * then advance one character past it
		 * (but only if there's more string!)
		 * */
		if ((tmpbody = strstr(arg + tmppos, "=")) != NULL)
			if (strlen(tmpbody))
				tmpbody++;

		/* This is all a bit nested now... */
		if (tmpbody) {
			if (MATCH(tmpbody, "8BITMIME")) {   // RFC1652
				ci_write(ci, "500 Please use 7BIT MIME only.\r\n");
				return 1;
			}
			if (MATCH(tmpbody, "BINARYMIME")) { // RFC3030
				ci_write(ci, "500 Please use 7BIT MIME only.\r\n");
				return 1;
			}
		}

		session->from = g_list_prepend(session->from, g_strdup(tmpaddr));
		ci_write(ci, "250 Sender <%s> OK\r\n", (char *)(session->from->data));

		g_free(tmpaddr);

		return 1;

	case LMTP_RCPT:
		if (session->state != AUTH) {
			ci_write(ci, "550 Command out of sequence.\r\n");
			return 1;
		} 

		session->args = g_list_first(session->args);
		if (! (session->args && session->args->data))
			return 1;
		arg = (char *)session->args->data;

		if (find_bounded(arg, '<', '>', &tmpaddr, &tmplen, &tmppos) < 0 || tmplen < 1) {
			ci_write(ci, "500 No address found. Missing <> boundries or address is null.\r\n");
			return 1;
		}

		Delivery_T *dsnuser = g_new0(Delivery_T,1);

		dsnuser_init(dsnuser);

		/* find_bounded() allocated tmpaddr for us, and that's ok
		 * since dsnuser_free() will free it for us later on. */
		dsnuser->address = tmpaddr;

		if (dsnuser_resolve(dsnuser) != 0) {
			TRACE(TRACE_ERR, "dsnuser_resolve_list failed");
			ci_write(ci, "430 Temporary failure in recipient lookup\r\n");
			dsnuser_free(dsnuser);
			g_free(dsnuser);
			return 1;
		}

		/* Class 2 means the address was deliverable in some way. */
		switch (dsnuser->dsn.class) {
			case DSN_CLASS_OK:
				ci_write(ci, "250 Recipient <%s> OK\r\n", dsnuser->address);
				session->rcpt = g_list_prepend(session->rcpt, dsnuser);
				break;
			default:
				ci_write(ci, "550 Recipient <%s> FAIL\r\n", dsnuser->address);
				dsnuser_free(dsnuser);
				g_free(dsnuser);
				break;
		}
		return 1;

	/* Here's where it gets really exciting! */
	case LMTP_DATA:
		msg = dbmail_message_new();
		dbmail_message_init_with_string(msg, session->rbuff);
		dbmail_message_set_header(msg, "Return-Path", (char *)session->from->data);
		g_string_truncate(session->rbuff,0);
		g_string_maybe_shrink(session->rbuff);

		if (insert_messages(msg, session->rcpt) == -1) {
			ci_write(ci, "430 Message not received\r\n");
			dbmail_message_free(msg);
			return 1;
		}
		/* The DATA command itself it not given a reply except
		 * that of the status of each of the remaining recipients. */

		/* The replies MUST be in the order received */
		session->rcpt = g_list_reverse(session->rcpt);
		while (session->rcpt) {
			Delivery_T * dsnuser = (Delivery_T *)session->rcpt->data;
			dsn_tostring(dsnuser->dsn, &class, &subject, &detail);

			/* Give a simple OK, otherwise a detailed message. */
			switch (dsnuser->dsn.class) {
				case DSN_CLASS_OK:
					ci_write(ci, "%d%d%d Recipient <%s> OK\r\n",
							dsnuser->dsn.class, dsnuser->dsn.subject, dsnuser->dsn.detail,
							dsnuser->address);
					break;
				default:
					ci_write(ci, "%d%d%d Recipient <%s> %s %s %s\r\n",
							dsnuser->dsn.class, dsnuser->dsn.subject, dsnuser->dsn.detail,
							dsnuser->address, class, subject, detail);
			}

			if (! g_list_next(session->rcpt)) break;
			session->rcpt = g_list_next(session->rcpt);
		}
		dbmail_message_free(msg);
		/* Reset the session after a successful delivery;
		 * MTA's like Exim prefer to immediately begin the
		 * next delivery without an RSET or a reconnect. */
		lmtp_rset(session,TRUE);
		return 1;

	default:
		return lmtp_error(session, "500 What are you trying to say here?\r\n");

	}
	return 1;
}