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