#include "smtpqueue.h"
#include "smtp_clienthandlers.h"
+ConstStr SMTPStates[] = {
+ {HKEY("looking up mx - record")},
+ {HKEY("evaluating what to do next")},
+ {HKEY("looking up a - record")},
+ {HKEY("looking up aaaa - record")},
+ {HKEY("connecting remote")},
+ {HKEY("smtp conversation ongoing")},
+ {HKEY("smtp sending maildata")},
+ {HKEY("smtp sending done")},
+ {HKEY("smtp successfully finished")},
+ {HKEY("failed one attempt")},
+ {HKEY("failed temporarily")},
+ {HKEY("failed permanently")}
+};
+
+void SetSMTPState(AsyncIO *IO, smtpstate State)
+{
+ CitContext* CCC = IO->CitContext;
+ if (CCC != NULL)
+ memcpy(CCC->cs_clientname, SMTPStates[State].Key, SMTPStates[State].len + 1);
+}
+
int SMTPClientDebugEnabled = 0;
-const unsigned short DefaultMXPort = 25;
void DeleteSmtpOutMsg(void *v)
{
SmtpOutMsg *Msg = v;
AsyncIO *IO = &Msg->IO;
- EVS_syslog(LOG_DEBUG, "%s Exit\n", __FUNCTION__);
+ EV_syslog(LOG_DEBUG, "%s Exit\n", __FUNCTION__);
/* these are kept in our own space and free'd below */
Msg->IO.ConnectMe = NULL;
eNextState mx_connect_ip(AsyncIO *IO);
eNextState get_one_mx_host_ip(AsyncIO *IO);
-eNextState FinalizeMessageSendDB(AsyncIO *IO);
-eNextState FinalizeMessageSend_DB1(AsyncIO *IO);
-eNextState FinalizeMessageSend_DB2(AsyncIO *IO);
-eNextState FinalizeMessageSend_DB3(AsyncIO *IO);
-eNextState FinalizeMessageSend_DB4(AsyncIO *IO);
/******************************************************************************
* So, we're finished with sending (regardless of success or failure) *
* we need to free the memory and send bounce messages (on terminal failure) *
* else we just free our SMTP-Message struct. *
******************************************************************************/
-inline void FinalizeMessageSend_1(AsyncIO *IO)
+eNextState FinalizeMessageSend_DB(AsyncIO *IO)
{
const char *Status;
SmtpOutMsg *Msg = IO->Data;
-
- if (Msg->MyQEntry->Status == 2)
+ StrBuf *StatusMessage;
+
+ if (Msg->MyQEntry->AllStatusMessages != NULL)
+ StatusMessage = Msg->MyQEntry->AllStatusMessages;
+ else
+ StatusMessage = Msg->MyQEntry->StatusMessage;
+
+
+ if (Msg->MyQEntry->Status == 2) {
+ SetSMTPState(IO, eSTMPfinished);
Status = "Delivery successful.";
- else if (Msg->MyQEntry->Status == 5)
+ }
+ else if (Msg->MyQEntry->Status == 5) {
+ SetSMTPState(IO, eSMTPFailTotal);
Status = "Delivery failed permanently; giving up.";
- else
+ }
+ else {
+ SetSMTPState(IO, eSMTPFailTemporary);
Status = "Delivery failed temporarily; will retry later.";
+ }
EVS_syslog(LOG_INFO,
"%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n",
Msg->user,
Msg->node,
Msg->name,
- ChrPtr(Msg->MyQEntry->StatusMessage));
+ ChrPtr(StatusMessage));
Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem);
- Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem);
+ Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem, 0);
if (Msg->MyQEntry->Active &&
+ !Msg->MyQEntry->StillActive &&
CheckQEntryIsBounce(Msg->MyQEntry))
{
/* are we casue for a bounce mail? */
Msg->QMsgData = SerializeQueueItem(Msg->MyQItem);
else
Msg->QMsgData = NULL;
-}
-eNextState FinalizeMessageSend(SmtpOutMsg *Msg)
-{
- return QueueDBOperation(&Msg->IO, FinalizeMessageSend_DB1);
-}
-
-inline void FinalizeMessageSend_DB_1(AsyncIO *IO)
-{
- SmtpOutMsg *Msg = IO->Data;
/*
* Uncompleted delivery instructions remain, so delete the old
*/
EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
-}
-eNextState FinalizeMessageSend_DB1(AsyncIO *IO)
-{
- FinalizeMessageSend_1(IO);
- FinalizeMessageSend_DB_1(IO);
- return NextDBOperation(IO, FinalizeMessageSend_DB2);
-}
-
-
-inline void FinalizeMessageSend_DB_2(AsyncIO *IO)
-{
- SmtpOutMsg *Msg = IO->Data;
+ Msg->MyQItem->QueMsgID = -1;
if (Msg->IDestructQueItem)
- smtpq_do_bounce(Msg->MyQItem, Msg->msgtext);
-}
-eNextState FinalizeMessageSend_DB2(AsyncIO *IO)
-{
- FinalizeMessageSend_DB_2(IO);
-
- return NextDBOperation(IO, FinalizeMessageSend_DB3);
-}
-
-
-inline void FinalizeMessageSend_DB_3(AsyncIO *IO)
-{
- SmtpOutMsg *Msg = IO->Data;
+ smtpq_do_bounce(Msg->MyQItem, Msg->msgtext, Msg->pCurrRelay);
if (Msg->nRemain > 0)
{
"");
FreeStrBuf(&Msg->QMsgData);
}
- DecreaseShutdownDeliveries(Msg->MyQItem);
-}
-eNextState FinalizeMessageSend_DB3(AsyncIO *IO)
-{
- SmtpOutMsg *Msg = IO->Data;
- FinalizeMessageSend_DB_3(IO);
- if (!Msg->IDestructQueItem)
- return eAbort;
- return NextDBOperation(IO, FinalizeMessageSend_DB4);
-}
-eNextState FinalizeMessageSend_DB4(AsyncIO *IO)
-{
- int Done;
- SmtpOutMsg *Msg = IO->Data;
-
- Done = GetShutdownDeliveries(Msg->MyQItem);
- if (!Done)
- return NextDBOperation(IO, FinalizeMessageSend_DB4);
- else
- return eAbort;
+ RemoveContext(Msg->IO.CitContext);
+ return eAbort;
}
-eNextState FinalizeMessageSend_DB(AsyncIO *IO)
+eNextState Terminate(AsyncIO *IO)
{
SmtpOutMsg *Msg = IO->Data;
- RemoveContext(Msg->IO.CitContext);
if (Msg->IDestructQueItem)
RemoveQItem(Msg->MyQItem);
+
DeleteSmtpOutMsg(Msg);
return eAbort;
}
+eNextState FinalizeMessageSend(SmtpOutMsg *Msg)
+{
+ /* hand over to DB Queue */
+ return EventQueueDBOperation(&Msg->IO, FinalizeMessageSend_DB);
+}
eNextState FailOneAttempt(AsyncIO *IO)
{
SmtpOutMsg *Msg = IO->Data;
+ SetSMTPState(IO, eSTMPfailOne);
if (Msg->MyQEntry->Status == 2)
return eAbort;
* - connection timeout
* - dns lookup failed
*/
- StopClientWatchers(IO);
+ StopClientWatchers(IO, 1);
+
+ Msg->MyQEntry->nAttempt ++;
+ if (Msg->MyQEntry->AllStatusMessages == NULL)
+ Msg->MyQEntry->AllStatusMessages = NewStrBuf();
+
+ StrBufAppendPrintf(Msg->MyQEntry->AllStatusMessages, "%ld) ", Msg->MyQEntry->nAttempt);
+ StrBufAppendBuf(Msg->MyQEntry->AllStatusMessages, Msg->MyQEntry->StatusMessage, 0);
+ StrBufAppendBufPlain(Msg->MyQEntry->AllStatusMessages, HKEY("; "), 0);
if (Msg->pCurrRelay != NULL)
Msg->pCurrRelay = Msg->pCurrRelay->Next;
+ if ((Msg->pCurrRelay != NULL) &&
+ !Msg->pCurrRelay->IsRelay &&
+ Msg->MyQItem->HaveRelay)
+ {
+ EVS_syslog(LOG_DEBUG, "%s Aborting; last relay failed.\n", __FUNCTION__);
+ return eAbort;
+ }
if (Msg->pCurrRelay == NULL) {
EVS_syslog(LOG_DEBUG, "%s Aborting\n", __FUNCTION__);
buf,
Msg->IO.ConnectMe->Port);
- Msg->MyQEntry->Status = 5;
+ Msg->MyQEntry->Status = 4;
StrBufPrintf(Msg->MyQEntry->StatusMessage,
"Timeout while connecting %s [%s]:%d ",
Msg->mx_host,
eNextState mx_connect_ip(AsyncIO *IO)
{
SmtpOutMsg *Msg = IO->Data;
+ SetSMTPState(IO, eSTMPconnecting);
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
SmtpOutMsg *Msg = IO->Data;
struct hostent *hostent;
+ IO->ConnectMe = Msg->pCurrRelay;
+
QueryCbDone(IO);
EVS_syslog(LOG_DEBUG, "%s Time[%fs]\n",
__FUNCTION__,
Msg->pCurrRelay->Addr.sin6_family =
hostent->h_addrtype;
Msg->pCurrRelay->Addr.sin6_port =
- htons(DefaultMXPort);
+ htons(Msg->IO.ConnectMe->Port);
}
else {
struct sockaddr_in *addr;
sizeof(uint32_t));
addr->sin_family = hostent->h_addrtype;
- addr->sin_port = htons(DefaultMXPort);
+ addr->sin_port = htons(Msg->IO.ConnectMe->Port);
}
Msg->mx_host = Msg->pCurrRelay->Host;
if (Msg->HostLookup.VParsedDNSReply != NULL) {
return mx_connect_ip(IO);
}
else {
+ SetSMTPState(IO, eSTMPfailOne);
if (Msg->HostLookup.VParsedDNSReply != NULL) {
Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
Msg->HostLookup.VParsedDNSReply = NULL;
* - the direct hostname if there was no mx record
* - one of the mx'es
*/
+ SetSMTPState(IO, (Msg->pCurrRelay->IPv6)?eSTMPalookup:eSTMPaaaalookup);
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
{
SmtpOutMsg * Msg = IO->Data;
+ SetSMTPState(IO, eSTMPmxlookup);
+
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
/* start resolving MX records here. */
if (!QueueQuery(ns_t_mx,
SmtpOutMsg * Msg;
Msg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg));
+ if (Msg == NULL)
+ return NULL;
memset(Msg, 0, sizeof(SmtpOutMsg));
Msg->n = MsgCount;
SMTPC_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
Msg = new_smtp_outmsg(MyQItem, MyQEntry, MsgCount);
+ if (Msg == NULL) {
+ SMTPC_syslog(LOG_DEBUG, "%s Failed to alocate message context.\n", __FUNCTION__);
+ if (KeepMsgText)
+ FreeStrBuf (&MsgText);
+ return;
+ }
if (KeepMsgText) Msg->msgtext = MsgText;
else Msg->msgtext = NewStrBufDup(MsgText);
- if (smtp_resolve_recipients(Msg)) {
-
+ if (smtp_resolve_recipients(Msg) &&
+ (!MyQItem->HaveRelay ||
+ (MyQItem->URL != NULL)))
+ {
safestrncpy(
((CitContext *)Msg->IO.CitContext)->cs_host,
Msg->node,
Msg->MyQItem->MessageID,
ChrPtr(Msg->MyQEntry->Recipient),
((CitContext*)Msg->IO.CitContext)->cs_pid);
- if (Msg->pCurrRelay == NULL)
+ if (Msg->pCurrRelay == NULL) {
+ SetSMTPState(&Msg->IO, eSTMPmxlookup);
QueueEventContext(&Msg->IO,
resolve_mx_records);
+ }
else { /* oh... via relay host */
if (Msg->pCurrRelay->IsIP) {
+ SetSMTPState(&Msg->IO, eSTMPconnecting);
QueueEventContext(&Msg->IO,
mx_connect_ip);
}
else {
+ SetSMTPState(&Msg->IO, eSTMPalookup);
/* uneducated admin has chosen to
add DNS to the equation... */
QueueEventContext(&Msg->IO,
}
}
else {
+ SetSMTPState(&Msg->IO, eSMTPFailTotal);
/* No recipients? well fail then. */
- if ((Msg==NULL) ||
- (Msg->MyQEntry == NULL)) {
+ if (Msg->MyQEntry != NULL) {
Msg->MyQEntry->Status = 5;
- StrBufPlain(Msg->MyQEntry->StatusMessage,
- HKEY("Invalid Recipient!"));
+ if (StrLength(Msg->MyQEntry->StatusMessage) == 0)
+ StrBufPlain(Msg->MyQEntry->StatusMessage,
+ HKEY("Invalid Recipient!"));
}
- FinalizeMessageSend_1(&Msg->IO);
- FinalizeMessageSend_DB_1(&Msg->IO);
- FinalizeMessageSend_DB_2(&Msg->IO);
- FinalizeMessageSend_DB_3(&Msg->IO);
FinalizeMessageSend_DB(&Msg->IO);
+ DeleteSmtpOutMsg(Msg);
}
}
eNextState SMTP_C_TerminateDB(AsyncIO *IO)
{
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
- return FinalizeMessageSend_DB(IO);
+ return Terminate(IO);
}
eNextState SMTP_C_Timeout(AsyncIO *IO)
{
Msg->MyQEntry->Status = 4;
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
- StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
+ StrBufPrintf(IO->ErrMsg, "Timeout: %s while talking to %s",
+ ReadErrors[Msg->State].Key,
+ Msg->mx_host);
if (Msg->State > eRCPT)
return eAbort;
else
Msg->MyQEntry->Status = 4;
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
- StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
+ StrBufPrintf(IO->ErrMsg, "Connection failure: %s while talking to %s",
+ ReadErrors[Msg->State].Key,
+ Msg->mx_host);
+
return FailOneAttempt(IO);
}
eNextState SMTP_C_DNSFail(AsyncIO *IO)
EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
SmtpOutMsg *Msg = IO->Data;
+ switch (IO->NextState) {
+ case eSendDNSQuery:
+ case eReadDNSReply:
+
+ /* todo: abort c-ares */
+ case eConnect:
+ case eSendReply:
+ case eSendMore:
+ case eSendFile:
+ case eReadMessage:
+ case eReadMore:
+ case eReadPayload:
+ case eReadFile:
+ StopClientWatchers(IO, 1);
+ break;
+ case eDBQuery:
+
+ break;
+ case eTerminateConnection:
+ case eAbort:
+ break;
+ }
Msg->MyQEntry->Status = 3;
StrBufPlain(Msg->MyQEntry->StatusMessage,
HKEY("server shutdown during message submit."));