#include "smtpqueue.h"
#include "smtp_clienthandlers.h"
-const unsigned short DefaultMXPort = 25;
+int SMTPClientDebugEnabled = 0;
void DeleteSmtpOutMsg(void *v)
{
SmtpOutMsg *Msg = v;
AsyncIO *IO = &Msg->IO;
- EV_syslog(LOG_DEBUG, "SMTP: %s Aborting\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;
FreeURL(&Msg->Relay);
FreeStrBuf(&Msg->msgtext);
FreeAsyncIOContents(&Msg->IO);
-/// memset (Msg, 0, sizeof(SmtpOutMsg)); /* just to be shure... */
+ memset (Msg, 0, sizeof(SmtpOutMsg)); /* just to be shure... */
free(Msg);
}
eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO);
eNextState SMTP_C_DNSFail(AsyncIO *IO);
eNextState SMTP_C_Terminate(AsyncIO *IO);
+eNextState SMTP_C_TerminateDB(AsyncIO *IO);
eReadState SMTP_C_ReadServerStatus(AsyncIO *IO);
eNextState mx_connect_ip(AsyncIO *IO);
* we need to free the memory and send bounce messages (on terminal failure) *
* else we just free our SMTP-Message struct. *
******************************************************************************/
-void FinalizeMessageSend(SmtpOutMsg *Msg)
+eNextState FinalizeMessageSend_DB(AsyncIO *IO)
{
- int IDestructQueItem;
- int nRemain;
- StrBuf *MsgData;
- AsyncIO *IO = &Msg->IO;
+ const char *Status;
+ SmtpOutMsg *Msg = IO->Data;
+
+ if (Msg->MyQEntry->Status == 2)
+ Status = "Delivery successful.";
+ else if (Msg->MyQEntry->Status == 5)
+ Status = "Delivery failed permanently; giving up.";
+ else
+ Status = "Delivery failed temporarily; will retry later.";
+
+ EVS_syslog(LOG_INFO,
+ "%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n",
+ Status,
+ Msg->IO.Now - Msg->IO.StartIO,
+ Msg->user,
+ Msg->node,
+ Msg->name,
+ ChrPtr(Msg->MyQEntry->StatusMessage));
- IDestructQueItem = DecreaseQReference(Msg->MyQItem);
- nRemain = CountActiveQueueEntries(Msg->MyQItem);
+ Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem);
- if ((nRemain > 0) || IDestructQueItem)
- MsgData = SerializeQueueItem(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->MyQItem->SendBounceMail |= (1<<Msg->MyQEntry->Status);
+ }
+
+ if ((Msg->nRemain > 0) || Msg->IDestructQueItem)
+ Msg->QMsgData = SerializeQueueItem(Msg->MyQItem);
else
- MsgData = NULL;
+ Msg->QMsgData = NULL;
/*
* Uncompleted delivery instructions remain, so delete the old
* instructions and replace with the updated ones.
*/
- EVS_syslog(LOG_DEBUG, "SMTPQD: %ld", Msg->MyQItem->QueMsgID);
+ EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
+ Msg->MyQItem->QueMsgID = -1;
- if (IDestructQueItem)
- smtpq_do_bounce(Msg->MyQItem,Msg->msgtext);
+ if (Msg->IDestructQueItem)
+ smtpq_do_bounce(Msg->MyQItem, Msg->msgtext, Msg->pCurrRelay);
- if (nRemain > 0)
+ if (Msg->nRemain > 0)
{
struct CtdlMessage *msg;
msg = malloc(sizeof(struct CtdlMessage));
msg->cm_magic = CTDLMESSAGE_MAGIC;
msg->cm_anon_type = MES_NORMAL;
msg->cm_format_type = FMT_RFC822;
- msg->cm_fields['M'] = SmashStrBuf(&MsgData);
+ msg->cm_fields['M'] = SmashStrBuf(&Msg->QMsgData);
+ msg->cm_fields['U'] = strdup("QMSG");
Msg->MyQItem->QueMsgID =
CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
- EVS_syslog(LOG_DEBUG, "SMTPQ: %ld", Msg->MyQItem->QueMsgID);
+ EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
CtdlFreeMessage(msg);
}
else {
&Msg->MyQItem->MessageID,
1,
"");
- FreeStrBuf(&MsgData);
+ FreeStrBuf(&Msg->QMsgData);
}
+
RemoveContext(Msg->IO.CitContext);
- if (IDestructQueItem)
+ return eAbort;
+}
+
+eNextState Terminate(AsyncIO *IO)
+{
+ SmtpOutMsg *Msg = IO->Data;
+
+ if (Msg->IDestructQueItem)
RemoveQItem(Msg->MyQItem);
+
DeleteSmtpOutMsg(Msg);
+ return eAbort;
+}
+eNextState FinalizeMessageSend(SmtpOutMsg *Msg)
+{
+ /* hand over to DB Queue */
+ return QueueDBOperation(&Msg->IO, FinalizeMessageSend_DB);
}
eNextState FailOneAttempt(AsyncIO *IO)
* - connection timeout
* - dns lookup failed
*/
- StopClientWatchers(IO);
+ StopClientWatchers(IO, 1);
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, "SMTP: %s Aborting\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s Aborting\n", __FUNCTION__);
return eAbort;
}
if (Msg->pCurrRelay->IsIP) {
- EVS_syslog(LOG_DEBUG, "SMTP: %s connecting IP\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s connecting IP\n", __FUNCTION__);
return mx_connect_ip(IO);
}
else {
EVS_syslog(LOG_DEBUG,
- "SMTP: %s resolving next MX Record\n",
+ "%s resolving next MX Record\n",
__FUNCTION__);
return get_one_mx_host_ip(IO);
}
if (Msg->mx_host == NULL)
Msg->mx_host = "<no MX-Record>";
- EVS_syslog(LOG_DEBUG,
- "SMTP client[%ld]: connecting to %s [%s]:%d ...\n",
- Msg->n,
+ EVS_syslog(LOG_INFO,
+ "connecting to %s [%s]:%d ...\n",
Msg->mx_host,
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,
{
SmtpOutMsg *Msg = IO->Data;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
IO->ConnectMe = Msg->pCurrRelay;
Msg->State = eConnectMX;
SmtpOutMsg *Msg = IO->Data;
struct hostent *hostent;
+ IO->ConnectMe = Msg->pCurrRelay;
+
QueryCbDone(IO);
+ EVS_syslog(LOG_DEBUG, "%s Time[%fs]\n",
+ __FUNCTION__,
+ IO->Now - IO->DNS.Start);
hostent = Msg->HostLookup.VParsedDNSReply;
if ((Msg->HostLookup.DNSStatus == ARES_SUCCESS) &&
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) {
+ Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
+ Msg->HostLookup.VParsedDNSReply = NULL;
+ }
return mx_connect_ip(IO);
}
- else
+ else {
+ if (Msg->HostLookup.VParsedDNSReply != NULL) {
+ Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
+ Msg->HostLookup.VParsedDNSReply = NULL;
+ }
return FailOneAttempt(IO);
+ }
}
eNextState get_one_mx_host_ip(AsyncIO *IO)
* - one of the mx'es
*/
- InitC_ares_dns(IO);
-
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
EVS_syslog(LOG_DEBUG,
- "SMTP client[%ld]: looking up %s-Record %s : %d ...\n",
- Msg->n,
+ "looking up %s-Record %s : %d ...\n",
(Msg->pCurrRelay->IPv6)? "aaaa": "a",
Msg->pCurrRelay->Host,
Msg->pCurrRelay->Port);
QueryCbDone(IO);
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s Time[%fs]\n",
+ __FUNCTION__,
+ IO->Now - IO->DNS.Start);
+
pp = &Msg->Relay;
while ((pp != NULL) && (*pp != NULL) && ((*pp)->Next != NULL))
pp = &(*pp)->Next;
if ((IO->DNS.Query->DNSStatus == ARES_SUCCESS) &&
(IO->DNS.Query->VParsedDNSReply != NULL))
{ /* ok, we found mx records. */
- Msg->IO.ErrMsg = Msg->MyQEntry->StatusMessage;
Msg->CurrMX
= Msg->AllMX
p = (ParsedURL*) malloc(sizeof(ParsedURL));
memset(p, 0, sizeof(ParsedURL));
+ p->Priority = Msg->CurrMX->priority;
p->IsIP = 0;
p->Port = DefaultMXPort;
p->IPv6 = i == 1;
p->Host = Msg->CurrMX->host;
-
- *pp = p;
- pp = &p->Next;
+ if (*pp == NULL)
+ *pp = p;
+ else {
+ ParsedURL *ppp = *pp;
+
+ while ((ppp->Next != NULL) &&
+ (ppp->Next->Priority <= p->Priority))
+ ppp = ppp->Next;
+ if ((ppp == *pp) &&
+ (ppp->Priority > p->Priority)) {
+ p->Next = *pp;
+ *pp = p;
+ }
+ else {
+ p->Next = ppp->Next;
+ ppp->Next = p;
+ }
+ }
}
Msg->CurrMX = Msg->CurrMX->next;
}
}
Msg->CXFlags = Msg->CXFlags & F_DIRECT;
}
- *pp = Msg->MyQItem->FallBackHost;
+ if (Msg->MyQItem->FallBackHost != NULL)
+ {
+ Msg->MyQItem->FallBackHost->Next = *pp;
+ *pp = Msg->MyQItem->FallBackHost;
+ }
Msg->pCurrRelay = Msg->Relay;
return get_one_mx_host_ip(IO);
}
{
SmtpOutMsg * Msg = IO->Data;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
/* start resolving MX records here. */
if (!QueueQuery(ns_t_mx,
Msg->node,
SmtpOutMsg * Msg;
Msg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg));
+ if (Msg == NULL)
+ return NULL;
memset(Msg, 0, sizeof(SmtpOutMsg));
Msg->n = MsgCount;
SMTP_C_DispatchWriteDone,
SMTP_C_DispatchReadDone,
SMTP_C_Terminate,
+ SMTP_C_TerminateDB,
SMTP_C_ConnFail,
SMTP_C_Timeout,
SMTP_C_Shutdown);
+ Msg->IO.ErrMsg = Msg->MyQEntry->StatusMessage;
+
return Msg;
}
{
SmtpOutMsg *Msg;
- syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ 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,
sizeof(((CitContext *)
Msg->IO.CitContext)->cs_host));
- syslog(LOG_DEBUG, "SMTP Starting: [%ld] <%s> CC <%d> \n",
- Msg->MyQItem->MessageID,
- ChrPtr(Msg->MyQEntry->Recipient),
- ((CitContext*)Msg->IO.CitContext)->cs_pid);
+ SMTPC_syslog(LOG_DEBUG, "Starting: [%ld] <%s> CC <%d> \n",
+ Msg->MyQItem->MessageID,
+ ChrPtr(Msg->MyQEntry->Recipient),
+ ((CitContext*)Msg->IO.CitContext)->cs_pid);
if (Msg->pCurrRelay == NULL)
QueueEventContext(&Msg->IO,
resolve_mx_records);
}
else {
/* 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(Msg);
+ FinalizeMessageSend_DB(&Msg->IO);
+ DeleteSmtpOutMsg(Msg);
}
}
double Timeout = 0.0;
AsyncIO *IO = &Msg->IO;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
switch (NextTCPState) {
case eSendFile:
/* if we're sending a huge message,
* we need more time.
*/
- Timeout += StrLength(Msg->msgtext) / 1024;
+ Timeout += StrLength(Msg->msgtext) / 512;
}
break;
case eReadMessage:
* some mailservers take a nap before accepting
* the message content inspection and such.
*/
- Timeout += StrLength(Msg->msgtext) / 1024;
+ Timeout += StrLength(Msg->msgtext) / 512;
}
break;
case eSendDNSQuery:
}
eNextState SMTP_C_DispatchReadDone(AsyncIO *IO)
{
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
SmtpOutMsg *Msg = IO->Data;
eNextState rc;
}
eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO)
{
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
SmtpOutMsg *Msg = IO->Data;
eNextState rc;
{
SmtpOutMsg *Msg = IO->Data;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
- FinalizeMessageSend(Msg);
- return eAbort;
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+ return FinalizeMessageSend(Msg);
+}
+eNextState SMTP_C_TerminateDB(AsyncIO *IO)
+{
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
+ return Terminate(IO);
}
eNextState SMTP_C_Timeout(AsyncIO *IO)
{
SmtpOutMsg *Msg = IO->Data;
Msg->MyQEntry->Status = 4;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
- return FailOneAttempt(IO);
+ if (Msg->State > eRCPT)
+ return eAbort;
+ else
+ return FailOneAttempt(IO);
}
eNextState SMTP_C_ConnFail(AsyncIO *IO)
{
SmtpOutMsg *Msg = IO->Data;
Msg->MyQEntry->Status = 4;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
return FailOneAttempt(IO);
}
{
SmtpOutMsg *Msg = IO->Data;
Msg->MyQEntry->Status = 4;
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
return FailOneAttempt(IO);
}
eNextState SMTP_C_Shutdown(AsyncIO *IO)
{
- EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+ 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."));
- FinalizeMessageSend(Msg);
- return eAbort;
+ return FinalizeMessageSend(Msg);
}
return Finished;
}
+void LogDebugEnableSMTPClient(const int n)
+{
+ SMTPClientDebugEnabled = n;
+}
+
CTDL_MODULE_INIT(smtp_eventclient)
{
+ if (!threading)
+ CtdlRegisterDebugFlagHook(HKEY("smtpeventclient"), LogDebugEnableSMTPClient, &SMTPClientDebugEnabled);
return "smtpeventclient";
}