/* milter-callback - a sendmail milter to perform variuos sender verification
 Copyright (C) 2008  Eugene M. Zheganin, milter-callback@norma.perm.ru

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
#include <libmilter/mfapi.h>

#ifdef WITH_PGSQL
#include "libpq-fe.h"
#include <time.h>
#include <pthread.h>
#endif

#ifndef bool
# define bool   int
# define TRUE   1
# define FALSE  0
#endif /* ! bool */

#define NS_TYPE_ELT                     0x40 /* EDNS0 extended label type */
#define DNS_LABELTYPE_BITSTRING         0x41
#define CB_PASS				0
#define CB_REJECT			1
#define CB_TEMPFAIL			2
#define CB_SKIP				3
#define CB_GRACEFUL			4
#define MAXEMAIL			324
#define MAXIDENT			274
#define MAXLOGLINE			1023
#define MAXHOSTNAME			256
#define MAXPATH				2048

char *mcVersion = "1.3.26";
char *mcName = "milter-callback";
char *mcCR = "(c) eugene zheganin, 2008-2009";

int mcDebug = 0;

struct mlfiPriv {
    char *mcfi_connectfrom;
    char *mcfi_helofrom;
    int	skipDirect;
    uint32_t relayIP;
    char relayPresIP[INET_ADDRSTRLEN];
    int MXesThatReject;
    int MXesThatAccept;
    int MXesOutOfOrder;
    int MXesThatTempfail;
    int MXesNum;
    int whitelisted;
    int gracefulDNS;
    int killMeOnData;
    char resolvedRelayIP[MAXHOSTNAME];
#ifdef WITH_PGSQL
    int SQLlogged;
#endif
};

uint32_t whiteNetMask;
struct netList {
    struct in_addr *whiteNet;
    struct in_addr *whiteMask;
    int netCount;
};
struct netList whiteNets;
char callbackMode[256];
char fakeEmail[MAXEMAIL];
char milterEmail[MAXEMAIL] = "milter-callback@norma.perm.ru";
char socketPath[2048];
char milterUser[256];
#ifdef WITH_PGSQL
char milterDBHost[256];
char milterDBName[256];
char milterDBUser[256];
char milterDBPass[256];
struct tm *curTime;
char myHostName[MAXHOSTNAME];
int hostNameVal;
char action[32];
char *pgConnInfo;
int connPoolLen = 5;
struct connList {
    PGconn **pgConnArr;
    PGresult **pgResArr;
    pthread_mutex_t **pgMutexArr;
};
struct connList connPool;
#endif
char replyURL[1024];
int logLevel = 0;
int gracefulDNS = 0;

char *logString;

/* Error variables */
extern int h_errno;  /* for resolver errors */
extern int errno;    /* general system errors */

void
logNow(char *stringToLog, int logWhenPrio) {
    if (logWhenPrio <= logLevel && mcDebug == 0) {
	/* log only when appropriate logLevel is set */
	switch(logWhenPrio) {
	    case 0:
		syslog(LOG_ERR, "%s", stringToLog);
		break;
	    case 1:
		syslog(LOG_WARNING, "%s", stringToLog);
		break;
	    case 2:
		syslog(LOG_NOTICE, "%s", stringToLog);
		break;
	    case 3:
		syslog(LOG_INFO, "%s", stringToLog);
		break;
	}
    } else {
	if (mcDebug == 1) {
	    fprintf(stdout, "%s\n", stringToLog);
	}
    };
}

#ifdef WITH_PGSQL
int
getPgConn (){
    int res, i, rndConn;
    
    for (i = 0; i < connPoolLen; i++) {
	snprintf(logString, MAXLOGLINE, "Trying to aquire a lock on connection %d.", i);
	logNow(logString, 3);
	res = pthread_mutex_trylock(connPool.pgMutexArr[i]);
	if (res == 0) {
	    snprintf(logString, MAXLOGLINE, "Connection %d successfully aquired.", i);
	    logNow(logString, 3);
	    return i;
	}
    }
    /* all connection busy */
    /* reporting that */
    snprintf(logString, MAXLOGLINE, "All connections busy, consider increasing pool length from %d.", connPoolLen);
    logNow(logString, 0);
    /*getting random connection and breaking until it's available */
    /* rndConn = random(connPoolLen - 1); */
    /* random seems to be a complete rocket science so let's just take last connection */
    rndConn = connPoolLen - 1;
    snprintf(logString, MAXLOGLINE, "All connections busy, trying to aquire not so random connection %d.", rndConn);
    logNow(logString, 0);
    res = pthread_mutex_lock(connPool.pgMutexArr[rndConn]);
    if (res == 0) {
	snprintf(logString, MAXLOGLINE, "Random connection %d successfully aquired.", rndConn);
	logNow(logString, 0);
	return rndConn;
    } else {
	snprintf(logString, MAXLOGLINE, "Failed to aquire random connection %d, this is just wrong, aborting.", rndConn);
	logNow(logString, 0);
	exit(EXIT_FAILURE);
    }
}

void
unlockPgConn(int pgConnId) {
    snprintf(logString, MAXLOGLINE, "Trying to unlock mutex on pool element %d.", pgConnId);
    logNow(logString, 3);
    if (pthread_mutex_unlock(connPool.pgMutexArr[pgConnId]) == 0) {
	snprintf(logString, MAXLOGLINE, "Successfully unlocked mutex on pool element %d.", pgConnId);
	logNow(logString, 0);
    } else {
	snprintf(logString, MAXLOGLINE, "Failure unlocking mutex on pool element %d, this is wrong, aborting.", pgConnId);
	logNow(logString, 0);
    }
}
#endif

int
parseAddressString (char *addressString, struct in_addr *network, struct in_addr *mask) {
/* 0 - success
   1 - failure
*/
    char *cidrMask;
    const char *cidrDelimiters = "/";
    uint32_t netMask;

    /* striping cidr mask */
    cidrMask = strdup(addressString);
    addressString = strsep(&addressString, cidrDelimiters);
    strsep(&cidrMask, cidrDelimiters);
    
    snprintf(logString, MAXLOGLINE, "Got new network mask /%s.", cidrMask);
    logNow(logString, 3);
    /* getting new cidr mask to proper format */
    if (cidrMask == NULL) {
        netMask = 32;
    } else {
        netMask = strtol(cidrMask, NULL, 0);
    }
    if (netMask == 0) {
	snprintf(logString, MAXLOGLINE, "Cannot convert %s to netmask, aborting.", cidrMask);
	logNow(logString, 0);
	return 1;
    }
    if (netMask < 0 || netMask > 32) {
	snprintf(logString, MAXLOGLINE, "%d isnt't a valid netmask.", netMask);
	logNow(logString, 0);
	return 1;
    }
    netMask = 0xffffffff << (32 - netMask);
    netMask = htonl(netMask);
    snprintf(logString, MAXLOGLINE, "After conversion of mask /%s to network format got 0x%x in host order, 0x%x natively.", cidrMask, ntohl(netMask), netMask);
    logNow(logString, 3);
    mask->s_addr = netMask;
    
    if (inet_pton(AF_INET, addressString, network) == 1) {
	snprintf(logString, MAXLOGLINE, "After conversion of net %s to network format got 0x%x in host order, 0x%x natively.", 
	addressString, ntohl(network->s_addr), network->s_addr);
	logNow(logString, 3);	
	return 0;
    } else {
	snprintf(logString, MAXLOGLINE, "Cannot convert %s to network format, aborting.", addressString);
	logNow(logString, 3);
	return 1;
    }
}

int
hostIsWhitelisted(struct mlfiPriv *privData) {
/*
    0 - no error
    1 - some critical error happened, need to reject message
*/

    char presNet[INET_ADDRSTRLEN], presMask[INET_ADDRSTRLEN];
    int i = 0;
    int whiteListed = 0;
#ifdef WITH_PGSQL
    char *whiteListEntry;
    int whiteListEntryLen, retVal, pgConnId;
    struct in_addr *netToTest, *maskToTest;
#endif

    snprintf(logString, MAXLOGLINE, "Entered hostIsWhitelisted().");
    logNow(logString, 3);
    bzero(presNet, sizeof(presNet));
    bzero(presMask, sizeof(presMask));
    while (i < whiteNets.netCount && whiteListed == 0) {
	if (inet_ntop(AF_INET, &whiteNets.whiteNet[i], presNet, INET_ADDRSTRLEN) == NULL) {
    	    snprintf(logString, MAXLOGLINE, "Cannot convert %d to presentation format.", whiteNets.whiteNet[i].s_addr);
	    logNow(logString, 0);
	    return 1;
	}
	if (inet_ntop(AF_INET, &whiteNets.whiteMask[i], presMask, INET_ADDRSTRLEN) == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot convert %d to presentation format.", whiteNets.whiteMask[i].s_addr);
	    logNow(logString, 0);
	    return 1;
	}
	if ((privData->relayIP & whiteNets.whiteMask[i].s_addr) == whiteNets.whiteNet[i].s_addr) {
	    whiteListed = 1;
	    snprintf(logString, MAXLOGLINE, "Found host %s in whitelisted network %s, mask %s, passing message", privData->relayPresIP, presNet, presMask);
	    logNow(logString, 0);
	    privData->whitelisted = 1;
	    return 0;
	} else {
	    snprintf(logString, MAXLOGLINE, "Host %s doesn't belong to network %s, mask %s.", privData->relayPresIP, presNet, presMask);
	    logNow(logString, 2);
	}
	i++;
    }
#ifdef WITH_PGSQL
    pgConnId = getPgConn();
    connPool.pgResArr[pgConnId] = PQexec(connPool.pgConnArr[pgConnId], "select address from whitelist");
    if (PQresultStatus(connPool.pgResArr[pgConnId]) != PGRES_TUPLES_OK){
        snprintf(logString, MAXLOGLINE, "SELECT command failed: %s", PQerrorMessage(connPool.pgConnArr[pgConnId]));
	logNow(logString, 0);
	snprintf(logString, MAXLOGLINE, "The result status is: %s",PQresStatus(PQresultStatus(connPool.pgResArr[pgConnId])));
	logNow(logString, 0);
        PQclear(connPool.pgResArr[pgConnId]);
	return 1;
    }
    snprintf(logString, MAXLOGLINE, "Successfully queried database, got %d rows.", PQntuples(connPool.pgResArr[pgConnId]));
    logNow(logString, 0);
    
    /* here the main check starts */
    i = 0;
    while (i < PQntuples(connPool.pgResArr[pgConnId]) && privData->whitelisted == 0){
	whiteListEntryLen = strlen(PQgetvalue(connPool.pgResArr[pgConnId], i, 0));
	whiteListEntry = malloc(whiteListEntryLen + 1);
	if (whiteListEntry == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	    PQclear(connPool.pgResArr[pgConnId]);
	    return 1;
	}
	bzero(whiteListEntry, whiteListEntryLen + 1);
	strncpy(whiteListEntry, PQgetvalue(connPool.pgResArr[pgConnId], i, 0), whiteListEntryLen);
    
	snprintf(logString, MAXLOGLINE, "Got white network %s from database.", whiteListEntry);
	logNow(logString, 3);
	
	netToTest = malloc(sizeof(struct in_addr));
	maskToTest = malloc(sizeof(struct in_addr));
	if (netToTest == NULL  || maskToTest == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	    PQclear(connPool.pgResArr[pgConnId]);
	    return 1;
	}
	
	retVal = parseAddressString(whiteListEntry, netToTest, maskToTest);
	if (retVal == 0) {
	    /* the main check itself */
	    if ((privData->relayIP & maskToTest->s_addr) == netToTest->s_addr) {
		snprintf(logString, MAXLOGLINE, "Found host %s in whitelisted network %s, passing message.", privData->relayPresIP, whiteListEntry);
		logNow(logString, 0);
		privData->whitelisted = 1;		
	    } else {
		snprintf(logString, MAXLOGLINE, "Host %s doesn't belong to network %s.", privData->relayPresIP, whiteListEntry);
		logNow(logString, 2);
	    }
	} else {
	    snprintf(logString, MAXLOGLINE, "parseAddressString returned error, aborting.");
	    logNow(logString, 0);
	    PQclear(connPool.pgResArr[pgConnId]);
	    return 1;
	}
	
	snprintf(logString, MAXLOGLINE, "Trying to free() netToTest in hostIsWhitelisted().");
	logNow(logString, 3);
	free(netToTest);
	snprintf(logString, MAXLOGLINE, "Trying to free() maskToTest in hostIsWhitelisted().");
	logNow(logString, 3);
	free(maskToTest);
	i++;
    }
    /* flush out the rest */
    snprintf(logString, MAXLOGLINE, "Trying to PQClear() pgRes in hostIsWhitelisted().");
    logNow(logString, 3);
    PQclear(connPool.pgResArr[pgConnId]);
    /* freeing lock */
    unlockPgConn(pgConnId);
#endif
    return 0;
}


int
checkServerResponse (char *emailAddress, char *serverName, struct in_addr *serverIP, int resolved) {
/*
    return codes:
    0 - email accepped
    1 - email not accepted
    2 - email tempfailed
    3 - socket error
    4 - DNS error
    5 - unknown answer code
    6 - memory error
*/

    const int smtpPort = 25;
    char myHostname[MAXHOSTNAME];
    char *myHostnameP;
    int sockfd, socketResCode;
    struct sockaddr_in serverAddress;
    struct hostent *server;
    
    char *smtpCommand;
    char smtpAnswer[2048], smtpString[2048];
    char *smtpAnswerPointer;
    char *smtpAnswerCode;
    char serverPresIP[INET_ADDRSTRLEN];
    
    const char *eolDelimiter = "\r\n";
    
    int connectResCode, resCode;
    int need2FreeSN = 0;
    
    struct timeval sockTimeout;
    
    char
    *readSMTPCode (char *answerString) {
	
	char *smtpBuf;
	char *smtpAnswerCode;
	const char *smtpCodeDelimiter = " -";
	
	snprintf(logString, MAXLOGLINE, "Entered readSMTPCode().");
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "answerString is %s.", answerString);
	logNow(logString, 3);
				
	smtpAnswerCode = malloc(strlen(answerString) + 1);
	if (smtpAnswerCode == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	    return(NULL);
        }
	bzero(smtpAnswerCode, strlen(answerString) + 1);
        /* checking SMTP answer code */
	/* we just reserved enough memory, so "wrong buffer length" it's not a mistake */
        strncpy(smtpAnswerCode, answerString, strlen(answerString));
	smtpBuf = smtpAnswerCode;
        strsep(&smtpBuf, smtpCodeDelimiter);
        return smtpAnswerCode;
    }

    void
    closeSocket (int socketId, int exitNicely) {
	/* nice ppl say QUIT on exit */
	snprintf(logString, MAXLOGLINE, "Entered closeSocket().");
	logNow(logString, 3);
	
	if (exitNicely == 1){
	    smtpCommand = "QUIT";
	
	    bzero(smtpString, sizeof(smtpString));
	    strcat(smtpString, smtpCommand);
	    snprintf(logString, MAXLOGLINE, "Saying \"%s\" to SMTP server.", smtpString);
	    logNow(logString, 3);
	    strcat(smtpString, eolDelimiter);
	
	    socketResCode = write(socketId, smtpString, strlen(smtpString));
	    if (socketResCode < 0){
		snprintf(logString, MAXLOGLINE, "ERROR writing to socket.");
		logNow(logString, 0);
	    }
	    if (smtpAnswerCode != NULL) {
		snprintf(logString, MAXLOGLINE, "Trying to free smtpAnswerCode in closeSocket().");
		logNow(logString, 3);
		free(smtpAnswerCode);
	    }
	}
	/* now closing the socket */
	close(socketId);
    }
    
    snprintf(logString, MAXLOGLINE, "Entered checkServerResponse().");
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Got emailAddress, %s.", emailAddress);
    logNow(logString, 3);
    
    strncpy(&myHostname[0], &milterEmail[0], MAXHOSTNAME);
    myHostnameP = &myHostname[0];
    strsep(&myHostnameP, "@");

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
	snprintf(logString, MAXLOGLINE, "Error opening socket.");
	logNow(logString, 0);
        return(3);
    };
    
    /* setting the sending-receiving timeout to 30 */
    sockTimeout.tv_sec = 90;
    sockTimeout.tv_usec = 0;
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &sockTimeout, sizeof(struct timeval)) != 0){
	snprintf(logString, MAXLOGLINE, "Failed to set socket sending timeout.");
	logNow(logString, 0);
    }
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &sockTimeout, sizeof(struct timeval)) != 0){
	snprintf(logString, MAXLOGLINE, "Failed to set socket receiving timeout.");
	logNow(logString, 0);
    }
    
    if (resolved == 0) {
	server = gethostbyname(serverName);
	/* TODO: error handling */
	if (server == NULL) {
    	    snprintf(logString, MAXLOGLINE, "ERROR, no such host - %s.", serverName);
	    logNow(logString, 0);
    	    return(4);
	} else {
	    snprintf(logString, MAXLOGLINE, "Successfully resolved %s.", serverName);
	    logNow(logString, 3);
	}
	bzero(&serverAddress, sizeof(serverAddress));
	bcopy(server->h_addr, &serverAddress.sin_addr.s_addr, server->h_length);
	bzero(serverPresIP, sizeof(serverPresIP));
        inet_ntop(AF_INET, server->h_addr, serverPresIP, INET_ADDRSTRLEN);
        snprintf(logString, MAXLOGLINE, "Address of %s is %s.", serverName, serverPresIP);
        logNow(logString, 3);
	/* TODO: network addresses cycling */
    } else {
	/* we already got resolved address in desired order */
	serverAddress.sin_addr.s_addr = serverIP->s_addr;
	server = gethostbyaddr(&serverIP->s_addr, sizeof(struct in_addr), AF_INET);
	if (server == NULL) {
	    serverName = malloc(INET_ADDRSTRLEN);
            if (serverName == NULL) {
                snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
                logNow(logString, 0);
                return(6);
            } else {
		need2FreeSN = 1;
	    }
            inet_ntop(AF_INET, &serverAddress.sin_addr.s_addr, serverName, INET_ADDRSTRLEN);
	    snprintf(logString, MAXLOGLINE, "Failed to resolve IP of %s.", serverName);
	    logNow(logString, 0);
        } else {
            serverName = server->h_name;
        }
    }
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(smtpPort);
    snprintf(logString, MAXLOGLINE, "Got server name, %s.", serverName);
    logNow(logString, 3);
    inet_ntop(AF_INET, &serverAddress.sin_addr.s_addr, serverPresIP, sizeof(serverPresIP));
    snprintf(logString, MAXLOGLINE, "Got server IP, %s.", serverPresIP);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Checking %s on server %s.", emailAddress, serverName);
    logNow(logString, 3);
    
    snprintf(logString, MAXLOGLINE, "Trying to connect to %x, port 25.", serverAddress.sin_addr.s_addr);
    logNow(logString, 3);
    connectResCode = connect(sockfd, (const struct sockaddr *)&serverAddress, sizeof(serverAddress));
    if (connectResCode < 0) {
        snprintf(logString, MAXLOGLINE, "Cannot connect to port %d on server %s, got \"%s\".", smtpPort, serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    } else {
	snprintf(logString, MAXLOGLINE, "Successfully connected to port %d on server %s.", smtpPort, serverName);
	logNow(logString, 2);
    }
    
    /* reading greeting */
    bzero(smtpAnswer, sizeof(smtpAnswer));
    socketResCode = read(sockfd,smtpAnswer,sizeof(smtpAnswer) - 1);
    if (socketResCode < 0) {
         snprintf(logString, MAXLOGLINE, "ERROR reading from socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	 logNow(logString, 0);
	 closeSocket(sockfd, 0);
	 return(3);
    } else {
	snprintf(logString, MAXLOGLINE, "Successfully read greeting on server %s, greeting is %s.", serverName, smtpAnswer);
	logNow(logString, 3);
    }
    smtpAnswerPointer = &smtpAnswer[0];
    strsep(&smtpAnswerPointer, eolDelimiter);
    snprintf(logString, MAXLOGLINE, "Calling readSMTPCode with string %s.", smtpAnswer);
    logNow(logString, 3);
    smtpAnswerCode = readSMTPCode(&smtpAnswer[0]);
    snprintf(logString, MAXLOGLINE, "The answer was: \"%s\".", smtpAnswer);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "The SMTP answer code is: \"%s\".", smtpAnswerCode);
    logNow(logString, 2);
    
    /* if the code isn't 220 - tempfail message */
    if (strcmp(smtpAnswerCode, "220") != 0) {
	snprintf(logString, MAXLOGLINE, "Returning tempfailing relay, %s received in greeting.", smtpAnswerCode);
	logNow(logString, 2);
	closeSocket(sockfd, 1);
	return(2);
    }
    if (smtpAnswerCode != NULL) {
	snprintf(logString, MAXLOGLINE, "Trying to free smtpAnswerCode in checkServerResponse().");
	logNow(logString, 3);
	free(smtpAnswerCode);
    }
    
    /* nice ppl say HELO */
    smtpCommand = "HELO ";
    bzero(smtpString, sizeof(smtpString));
    
    strcat(smtpString, smtpCommand);
    strcat(smtpString, myHostnameP);
    snprintf(logString, MAXLOGLINE, "Saying \"%s\" to SMTP server.", smtpString);
    logNow(logString, 3);
    strcat(smtpString, eolDelimiter);
    
    socketResCode = write(sockfd,smtpString,strlen(smtpString));    
    if (socketResCode < 0){
        snprintf(logString, MAXLOGLINE, "ERROR writing to socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    }
    
    /* reading answer */
    bzero(smtpAnswer,sizeof(smtpAnswer));
    socketResCode = read(sockfd,smtpAnswer,sizeof(smtpAnswer) - 1);
    if (socketResCode < 0) {
         snprintf(logString, MAXLOGLINE, "ERROR reading from socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	 logNow(logString, 0);
	 closeSocket(sockfd, 0);
	 return(3);
    };
    smtpAnswerPointer = &smtpAnswer[0];
    strsep(&smtpAnswerPointer, eolDelimiter);
    smtpAnswerCode = readSMTPCode(&smtpAnswer[0]);
    snprintf(logString, MAXLOGLINE, "The answer was: \"%s\".", smtpAnswer);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "The SMTP answer code is: \"%s\".", smtpAnswerCode);
    logNow(logString, 2);
    
    /* if the code isn't 250 - tempfail message */
    if (strcmp(smtpAnswerCode, "250") != 0) {
	snprintf(logString, MAXLOGLINE, "Returning tempfailing relay, %s received after HELO.", smtpAnswerCode);
	logNow(logString, 2);
	closeSocket(sockfd, 1);
	return(2);
    }
    if (smtpAnswerCode != NULL) {
	snprintf(logString, MAXLOGLINE, "Trying to free smtpAnswerCode in checkServerResponse().");
	logNow(logString, 3);
	free(smtpAnswerCode);
    }
    
    /* beginning main procedure */
    smtpCommand = "MAIL FROM: ";
    bzero(smtpString, sizeof(smtpString));
    strcat(smtpString, smtpCommand);
    strcat(smtpString, "<");
    strcat(smtpString, milterEmail);
    strcat(smtpString, ">");
    snprintf(logString, MAXLOGLINE, "Saying \"%s\" to SMTP server.", smtpString);
    logNow(logString, 3);
    strcat(smtpString, eolDelimiter);
    
    socketResCode = write(sockfd,smtpString,strlen(smtpString));    
    if (socketResCode < 0){
        snprintf(logString, MAXLOGLINE, "ERROR writing to socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    };
    
    /* reading answer */
    bzero(smtpAnswer, sizeof(smtpAnswer));
    socketResCode = read(sockfd,smtpAnswer,sizeof(smtpAnswer) - 1);
    if (socketResCode < 0) {
        snprintf(logString, MAXLOGLINE, "ERROR reading from socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    };
    smtpAnswerPointer = &smtpAnswer[0];
    strsep(&smtpAnswerPointer, eolDelimiter);
    smtpAnswerCode = readSMTPCode(&smtpAnswer[0]);
    snprintf(logString, MAXLOGLINE, "The answer was: \"%s\".", smtpAnswer);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "The SMTP answer code is: \"%s\".", smtpAnswerCode);
    logNow(logString, 2);
    
    /* if the code isn't 250 - tempfail message */
    if (strcmp(smtpAnswerCode, "250") != 0) {
	snprintf(logString, MAXLOGLINE, "Returning tempfailing relay, %s received after MAIL FROM.", smtpAnswerCode);
	logNow(logString, 2);
	closeSocket(sockfd, 1);
	return(2);
    }
    if (smtpAnswerCode != NULL) {
	snprintf(logString, MAXLOGLINE, "Trying to free smtpAnswerCode in checkServerResponse().");
	logNow(logString, 3);
	free(smtpAnswerCode);
    }
    
    /* Telling the recipient address */
    smtpCommand = "RCPT TO: ";
    bzero(smtpString, sizeof(smtpString));
    strcat(smtpString, smtpCommand);
    strcat(smtpString, "<");
    strcat(smtpString, emailAddress);
    strcat(smtpString, ">");
    snprintf(logString, MAXLOGLINE, "Saying \"%s\" to SMTP server.", smtpString);
    logNow(logString, 3);
    strcat(smtpString, eolDelimiter);
    
    socketResCode = write(sockfd,smtpString,strlen(smtpString));    
    if (socketResCode < 0){
        snprintf(logString, MAXLOGLINE, "ERROR writing to socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    };
    
    /* reading answer */
    bzero(smtpAnswer,sizeof(smtpAnswer));
    socketResCode = read(sockfd,smtpAnswer,sizeof(smtpAnswer) - 1);
    if (socketResCode < 0) {
        snprintf(logString, MAXLOGLINE, "ERROR reading from socket while in session with %s, got \"%s\".", serverName, strerror(errno));
	logNow(logString, 0);
	closeSocket(sockfd, 0);
	return(3);
    };
    smtpAnswerPointer = &smtpAnswer[0];
    strsep(&smtpAnswerPointer, eolDelimiter);
    smtpAnswerCode = readSMTPCode(&smtpAnswer[0]);
    snprintf(logString, MAXLOGLINE, "The answer was: \"%s\".", smtpAnswer);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "The SMTP answer code is: \"%s\".", smtpAnswerCode);
    logNow(logString, 2);
    
    resCode = 0;
    resCode = strtol(smtpAnswerCode, NULL, 10);
    if (serverName != NULL && need2FreeSN == 1) {
	snprintf(logString, MAXLOGLINE, "Trying to free serverName in checkServerResponse().");
	logNow(logString, 3);
	free(serverName);
    }
    if (resCode == 0) {
        snprintf(logString, MAXLOGLINE, "Cannot parse SMTP answer code %s.", smtpAnswerCode);
        logNow(logString, 0);
        return(5);
    } else {
        if (resCode >= 400 && resCode <= 499) {
    	    snprintf(logString, MAXLOGLINE, "Returning tempfailing relay, received %d after RCPT TO.", resCode);
	    logNow(logString, 2);
	    closeSocket(sockfd, 1);
	    return(2);
	} else {
	    if (resCode >= 500 && resCode <= 599) {
	        snprintf(logString, MAXLOGLINE, "Returning rejecting relay, received %d after RCPT TO.", resCode);
	        logNow(logString, 2);
	        closeSocket(sockfd, 1);
	        return(1);
	    } else {
	        if (resCode >= 250 && resCode <= 251) {
	    	    snprintf(logString, MAXLOGLINE, "Returning passing relay, received %d after RCPT TO.", resCode);
	    	    logNow(logString, 2);
	    	    closeSocket(sockfd, 1);
	    	    return(0);
	        } else {
	    	    snprintf(logString, MAXLOGLINE, "Returning tempfailing relay, received unknow code %d after RCPT TO.", resCode);
	    	    logNow(logString, 2);
		    closeSocket(sockfd, 1);
	    	    return(5);
		}
	    }
	}
    }
}

int
dealWithMXes (char *emailaddress, char *hostPart, struct mlfiPriv *privData) {

/*
    Return Codes:
    0 - all of the MXes accepted emailAddress.
    1 - some of the MXes accepted emailAddress.
    2 - no MX for emailAddress found.
    3 - some software/memory error happened.
    4 - all MXes reject
    5 - some of the MXes tempfails
*/

    char emailBuf[MAXEMAIL];
    int i, dup, msgcount, returnCode;
    
    ns_msg handle;  /* handle for response packet */
    int rrnum;  /* resource record number */
    ns_rr rr;   /* expanded resource record */
    ns_sect section;
    
    #define MAX_MX 16
    
    /* MX array */
    char *mxList[MAX_MX];
    /* MX array length */
    int  mxNum = 0;
    
    union {
        HEADER hdr;              /* defined in resolv.h */
        u_char buf[NS_PACKETSZ]; /* defined in arpa/nameser.h */
    } response;                  /* response buffers */
    
    int responseLen;             /* buffer length */
    
    struct addrinfo hints, *res, *res0;
    int resError;
    int bufLen;
    
    char *relayVect;
    char *relayVectBeginsAt;
    char afterDog[MAXEMAIL];
    char *afterDogPointer;
	
    int relayRes2DogPart, domLevel;
    
    bzero(emailBuf, sizeof(emailBuf));
    
    snprintf(logString, MAXLOGLINE, "Entered dealWithMXes().");
    logNow(logString, 3);
    
    snprintf(logString, MAXLOGLINE, "Got an e-mail, %s.", emailaddress);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Got a host: '%s', %d symbols.", hostPart, strlen(hostPart));
    logNow(logString, 3);
    
    if ((responseLen = res_query(hostPart, ns_c_in, ns_t_mx, (u_char *) &response, sizeof(response))) < 0) {
	snprintf(logString, MAXLOGLINE, "Error while resolving %s, got \"%s\".", hostPart, hstrerror(h_errno));
	logNow(logString, 0);
	if (h_errno == 4) {
	    /* no MX found */
	    privData->MXesNum = 0;
	    privData->MXesThatAccept = 0;
	    privData->MXesThatReject = 0;
	    privData->MXesThatTempfail = 0;
	    privData->MXesOutOfOrder = 0;
	    snprintf(logString, MAXLOGLINE, "No MXes for the domain, %s.", hostPart);
    	    logNow(logString, 0);
	    return(2);
	}
    } else {
	snprintf(logString, MAXLOGLINE, "Successfully queried DNS.");
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Response length is %d, or %d bytes.", responseLen, sizeof(response));
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Size of response.buf was %d.", sizeof(response.buf));
	logNow(logString, 3);
	
	if (ns_initparse(response.buf, responseLen, &handle) < 0) {
	    snprintf(logString, MAXLOGLINE, "Error parsing response: %s.", strerror(errno));
	    logNow(logString, 0);
	    return(3);
	} else {	
	    section = ns_s_an;
	    msgcount = ns_msg_count(handle, section);
	    snprintf(logString, MAXLOGLINE, "Got %d records in answer section.", msgcount);
	    logNow(logString, 3);
	    for(rrnum = 0; rrnum < msgcount; rrnum++) {
		/*
	        * Expand the resource record number rrnum into rr.
		*/
		snprintf(logString, MAXLOGLINE, "Parsing record no %d.", rrnum);
		logNow(logString, 3);
		
		if (ns_parserr(&handle, section, rrnum, &rr) < 0) {
		    snprintf(logString, MAXLOGLINE, "ns_parserr: %s.", strerror(errno));
		    logNow(logString, 0);
		    return(3);
		} else {
		    /*
		    * If the record type is MX, save the name of the
		    * name server.
		    */
		    if (ns_rr_type(rr) == ns_t_mx) {
			snprintf(logString, MAXLOGLINE, "We got an MX record.");
			logNow(logString, 3);
			
			mxList[mxNum] = (char *) malloc(MAXDNAME);
			if(mxList[mxNum] == NULL){
			    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
			    logNow(logString, 0);
			    return(3);
			}
			/* Expand the MX name */
			if (ns_name_uncompress(
			    ns_msg_base(handle),/* Start of the packet   */
			    ns_msg_end(handle), /* End of the packet     */
		    	    ns_rr_rdata(rr) + 2,    /* Position in the packet*/
			    mxList[mxNum],     /* Result                */
			    MAXDNAME)           /* Size of nsList buffer */
			    	    < 0) {    /* Negative: error       */
			    snprintf(logString, MAXLOGLINE, "ns_name_uncompress failed.");
			    logNow(logString, 0);
			    return(3);
			}
			/*
		        * Check the name we've just unpacked and add it to
		        * the list of MXes if it is not a duplicate.
		        * If it is a duplicate, just ignore it.
		        */
			for(i = 0, dup = 0; (i < mxNum) && !dup; i++) {
			    dup = !strcasecmp(mxList[i], mxList[mxNum]);
			}
			if(dup) {
			    snprintf(logString, MAXLOGLINE, "We got a duplicate.");
			    logNow(logString, 3);
			    if (mxList[mxNum] != NULL) {
				free(mxList[mxNum]);
			    }
			} else {
			    mxNum++;
			}
		    } else {
			snprintf(logString, MAXLOGLINE, "We got a RR of type %d, skipping in wait for %d.", ns_rr_type(rr), ns_t_mx);
			logNow(logString, 3);
		    }
		}
	    }
	    snprintf(logString, MAXLOGLINE, "Got %d MXes:", mxNum);
	    logNow(logString, 2);
	    for (i = 0; i < mxNum; i++) {
		snprintf(logString, MAXLOGLINE, "%d: %s", i, mxList[i]);
		logNow(logString, 2);
	    }
	}
    }
    /* here we already got the list of MXes */
    snprintf(logString, MAXLOGLINE, "Commencing the mxlist parsing.");
    logNow(logString, 2);
    
    /* determining if relay is one on the MXes */
    for(i = 0; i < mxNum && privData->skipDirect == 0; i++) {
	snprintf(logString, MAXLOGLINE, "Checking MX %d of %d, %s against the relay IP, %s.", i + 1, mxNum, mxList[i], privData->relayPresIP);
	logNow(logString, 2);
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	resError = getaddrinfo(mxList[i], NULL, &hints, &res0);
	if (resError) {
	    snprintf(logString, MAXLOGLINE, "Cannot resolve host %s: %s", mxList[i], gai_strerror(resError));
	    logNow(logString, 2);
	} else {
	    for (res = res0; res; res = res->ai_next) {
		if (((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr == privData->relayIP) {
		    snprintf(logString, MAXLOGLINE, "Setting skipDirect flag for relay %s - it's IP is identical to MX IP %s", privData->relayPresIP, mxList[i]);
		    logNow(logString, 2);
		    privData->skipDirect = 1;
		}
	    }
	    freeaddrinfo(res0);
	}
    }
    
    if (privData->skipDirect == 0 && gracefulDNS == 1) {
	/* determinig if the relay IP resolves to the name containing full hostpart */
	relayRes2DogPart = 0 ;
	domLevel = 1;
	/* pushing email and relay*/
	bufLen = strlen(privData->resolvedRelayIP) + 1;
	relayVect = malloc(bufLen);
	bzero(relayVect, bufLen);
	strncpy(relayVect, privData->resolvedRelayIP, bufLen);	
	bzero(afterDog, sizeof(afterDog));
	strncpy(afterDog, emailaddress, sizeof(afterDog));
	if (relayVect == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	} else {
	    relayVectBeginsAt = relayVect;
	    afterDogPointer = &afterDog[0];
	    strsep(&afterDogPointer, "@");
	    snprintf(logString, MAXLOGLINE, "afterDogPointer is %s.", afterDogPointer);
	    logNow(logString, 3);
	    while (relayVect != NULL && relayRes2DogPart == 0) {
		snprintf(logString, MAXLOGLINE, "relayVect is %s.", relayVect);
		logNow(logString, 3);
    		if (relayVect != NULL && strcmp(relayVect, afterDogPointer) == 0) {
		    snprintf(logString, MAXLOGLINE, "%s is equal to %s.", relayVect, afterDogPointer);
		    logNow(logString, 3);
        	    relayRes2DogPart = 1;
    		}
		strsep(&relayVect, ".");
	    }
	    if (relayRes2DogPart == 1) {
		/* finding the level of the domain */
		for (i = 0; i < strlen(afterDogPointer); i++) {
    		    if (afterDogPointer[i] == '.') {
    			domLevel++;
    	    	    }
		}
		snprintf(logString, MAXLOGLINE, "Domain level of e-mail hostpart %s is %d.", afterDogPointer, domLevel);
		logNow(logString, 3);
		if (domLevel >= 2) {
		    snprintf(logString, MAXLOGLINE, "Setting graceful DNS bit, [%s] resolves to %s, which contains hostpart %s.", privData->relayPresIP, privData->resolvedRelayIP, afterDogPointer);
		    logNow(logString, 3);
		    privData->gracefulDNS = 1;
		}
	    }
	    if (relayVectBeginsAt != NULL) {
		snprintf(logString, MAXLOGLINE, "Trying to free relayVectBeginsAt in dealWithMXes().");
		logNow(logString, 3);
		free(relayVectBeginsAt);
	    }
	}
    } else {
	if (privData->skipDirect == 1) {
	    snprintf(logString, MAXLOGLINE, "Skipped graceful DNS check, skipDirect flag already set.");
	    logNow(logString, 3);
	}
    }
    
    privData->MXesNum = mxNum;
    privData->MXesThatAccept = 0;
    privData->MXesThatReject = 0;
    privData->MXesThatTempfail = 0;
    privData->MXesOutOfOrder = 0;
    
    for(i = 0; i < mxNum; i++) {
	snprintf(logString, MAXLOGLINE, "Parsing relay %d of %d, %s.", i + 1, mxNum, mxList[i]);
	logNow(logString, 2);
	returnCode = checkServerResponse(emailaddress, mxList[i], NULL, 0);
	snprintf(logString, MAXLOGLINE, "checkServerResponse() returned %d.", returnCode);
	logNow(logString, 3);
	switch (returnCode) {
	    case 0:
		/* 0 - email accepped */
		snprintf(logString, MAXLOGLINE, "Got one of the accepting MXes.");
		logNow(logString, 2);
		privData->MXesThatAccept++;
		break;
	    case 1:
		/* 1 - email not accepted */
		snprintf(logString, MAXLOGLINE, "Got one of the rejecting MXes.");
		logNow(logString, 2);
		privData->MXesThatReject++;
		break;
	    case 2:
		/* 2 - email tempfailed */
		snprintf(logString, MAXLOGLINE, "Got one of the tempfailing MXes.");
		logNow(logString, 2);
		privData->MXesThatTempfail++;
		break;
	    case 3:
		/* 3 - socket error */
		snprintf(logString, MAXLOGLINE, "Got a socket error.");
		logNow(logString, 2);
		privData->MXesOutOfOrder++;
		break;
	    case 4:
		/* 4 - DNS error */
		snprintf(logString, MAXLOGLINE, "Got a DNS error.");
		privData->MXesOutOfOrder++;
		logNow(logString, 2);
		break;
	    case 5:
		/* 5 - unknown answer code */
		snprintf(logString, MAXLOGLINE, "Got an unknown SMTP answer code.");
		logNow(logString, 0);
		privData->MXesOutOfOrder++;
		break;
	    case 6:
	    default:
		/* 6 - memory error */
		snprintf(logString, MAXLOGLINE, "Got a memory error.");
		logNow(logString, 0);
		privData->MXesOutOfOrder++;
		break;
	}
    }
    /* freeing mxList */
    snprintf(logString, MAXLOGLINE, "Trying to free mxList in dealWithMXes().");
    logNow(logString, 3);
    for (i = 0; i < mxNum; i++) {
	if (mxList[i] != NULL) {
	    snprintf(logString, MAXLOGLINE, "freeing MX %d in dealWithMXes().", i);
	    logNow(logString, 3);
	    free(mxList[i]);
	}
    }
    
    if (privData->MXesThatAccept == mxNum) {
	return(0);
    } else {
	if (privData->MXesThatAccept > 0){
	    return(1);
	} else {
	    if (privData->MXesThatTempfail > 0) {
		return(5);
	    } else {
		if (privData->MXesOutOfOrder > 0) {
		    return(3);
		} else {
		    return(4);
		}
	    }
	}
    }
}

sfsistat
mcfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) {
    struct mlfiPriv *priv;
    char ident[MAXIDENT];
    char presIP[INET_ADDRSTRLEN];
    
    snprintf(logString, MAXLOGLINE, "Entered mcfi_connect().");
    logNow(logString, 3);
    
    /* allocate some private memory */
    priv = malloc(sizeof(struct mlfiPriv));
    if (priv == NULL) {
        /* can't accept this message right now */
	snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	logNow(logString, 0);
	return SMFIS_TEMPFAIL;
    }
    memset(priv, '\0', sizeof(struct mlfiPriv));
    /* setting the skipDirect flag to off */
    priv->skipDirect = 0;
    /* resetting relayIP */
    priv->relayIP = 0;
    /* save the private data */
    /* dropping MXes flags */
    priv->MXesNum = -1;
    priv->MXesThatAccept = -1;
    priv->MXesThatReject = -1;
    priv->MXesOutOfOrder = -1;
    priv->MXesThatTempfail = -1;
    priv->whitelisted = 0;
    priv->gracefulDNS = 0;
    priv->killMeOnData = 0;
    strncpy(priv->resolvedRelayIP, hostname, MAXHOSTNAME);
#ifdef WITH_PGSQL
    /* connection was never open */
    priv->SQLlogged = 0;
#endif
    smfi_setpriv(ctx, priv);
    
    snprintf(logString, MAXLOGLINE, "Getting ident from socket address.");
    logNow(logString, 3);
    if (inet_ntop(AF_INET, &((struct sockaddr_in *)hostaddr)->sin_addr, &presIP[0], sizeof(presIP)) == NULL) {
        snprintf(logString, MAXLOGLINE, "Cannot parse IP address of connecting host from socket address.");
        logNow(logString, 0);
        return SMFIS_TEMPFAIL;
    }
    snprintf(logString, MAXLOGLINE, "Connect from host %s [%s].", hostname, presIP);
    logNow(logString, 3);
    priv->relayIP = ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr;
    strncpy(priv->relayPresIP, presIP, INET_ADDRSTRLEN);
    snprintf(ident, sizeof(ident), "%s [%s]", hostname, presIP);
    
    if ((priv->mcfi_connectfrom = strdup(ident)) == NULL) {
	snprintf(logString, MAXLOGLINE, "Cannot process ident, memory failure.");
	logNow(logString, 0);
	return SMFIS_TEMPFAIL;
    }
    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mcfi_helo(SMFICTX *ctx, char *helohost) {
    size_t len;
    char *tls;
    char *buf;
    struct mlfiPriv *priv; 
    
    priv = ((struct mlfiPriv *) smfi_getpriv(ctx));
    
    snprintf(logString, MAXLOGLINE, "Entered mcfi_helo().");
    logNow(logString, 3);
    
    tls = smfi_getsymval(ctx, "{tls_version}");
    if (tls == NULL) {
	tls = "No TLS";
    };
    if (helohost == NULL) {
	helohost = "???";
    }
    len = strlen(tls) + strlen(helohost) + 3;
    if ((buf = (char*) malloc(len)) == NULL) {
	snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	logNow(logString, 0);
	return SMFIS_TEMPFAIL;
    }
    snprintf(buf, len, "%s, %s", helohost, tls);
    if (priv->mcfi_helofrom != NULL) {
	free(priv->mcfi_helofrom);
    }
    priv->mcfi_helofrom = buf;
    /* continue processing */
    return SMFIS_CONTINUE;
}

void
mc_setreply(SMFICTX *ctx, char *rCode, char *rText) {
    char *replyCode, *replyText;
    
    replyCode = malloc(strlen(rCode) + 1);
    replyText = malloc(strlen(rText) + 1);
    if (replyCode == NULL || replyText == NULL) {
        snprintf(logString, MAXLOGLINE, "Cannot allocate memory");
        logNow(logString, 0);
    } else {
        bzero(replyCode, strlen(rCode) + 1);
        bzero(replyText, strlen(rText) + 1);
        strcpy(replyCode, rCode);
        if (strlen(replyURL) > 0) {
	    strcpy(replyText, replyURL);
	} else {
	    strcpy(replyText, rText);
	}
	if (smfi_setreply(ctx, replyCode, NULL, replyText) != MI_SUCCESS) {
	    snprintf(logString, MAXLOGLINE, "Failed to set code %s: %s", replyCode, replyText);
	    logNow(logString, 0);
	} else {
	    snprintf(logString, MAXLOGLINE, "Successfully set code %s: %s", replyCode, replyText);
	    logNow(logString, 2);
	}
	if (replyCode != NULL) {
	    snprintf(logString, MAXLOGLINE, "Trying to free replyCode in mc_setreply().");
	    logNow(logString, 3);
	    free(replyCode);
	}
        if (replyText != NULL) {
    	    snprintf(logString, MAXLOGLINE, "Trying to free replyText in mc_setreply().");
    	    logNow(logString, 3);
    	    free(replyText);
        }
    }
}

sfsistat
mcfi_envfrom(SMFICTX *ctx, char **argv) {

    struct mlfiPriv *priv;
    
    char *mailHost;
    char *formattedEmail;
    char *emailDelim = "@";
    char emailBuf[MAXEMAIL];
    char *emailBP;
    int returnCode, returnSubCode;
    
    int mxPassKey, directPassKey, fakePassKey;
    /*
	0 - pass
	1 - reject
	2 - tempfail
    */
    char dTranscription[256];
    char fTranscription[256];
    
    char
    *transcriptAnswerCode(int answerCode, char *transcript) {
	switch(answerCode) {
	    case CB_PASS:
		sprintf(transcript, "accepts");
		break;
	    case CB_REJECT:
		sprintf(transcript, "rejects");
		break;
	    case CB_TEMPFAIL:
		sprintf(transcript, "tempfails");
		break;
	    case CB_SKIP:
		sprintf(transcript, "was skipped (sending relay is one of the MXes)");
		break;
	    case CB_GRACEFUL:
		sprintf(transcript, "was skipped (sending relay IP reverse name contains e-mail hostpart)");
		break;
	}
	return transcript;
    }
    
#ifdef WITH_PGSQL
    int
    pushHistory(struct mlfiPriv *privData, char *action){
	char *pgStatFormat = "insert into history (connectdate, connectfrom, helofrom, relaypresip, mxnum, mxesthatreject, mxesthataccept, mxesoutoforder, mxesthattempfail, gracefuldns, whitelisted, resolvedrelayip, receivinghost, envfrom, action) values('%s','%s','%s','%s',%d,%d,%d,%d,%d,%s,%s,'%s','%s','%s', '%s')";
	char *pgStat = NULL;
	int wantedSize = 0;
	int wantedTimeSize = 0;
	char *curTimePres = NULL;
	char *curTimePresFormat = "%.2d/%.2d/%d %.2d:%.2d:%.2d";
	int curYear, curMonth;
	time_t now;
	char whitelistedBool[6];
	char gracefulDNSBool[6];
	int pgConnId;
	
	snprintf(logString, MAXLOGLINE, "Entered pushHistory().");
	logNow(logString, 3);
	
	pgConnId = getPgConn();
	
	now = time(NULL);
	curTime = localtime(&now);
	curYear = curTime->tm_year + 1900;
	curMonth = curTime->tm_mon + 1;
	wantedTimeSize = snprintf(curTimePres, 0, curTimePresFormat, 
			curMonth, curTime->tm_mday, curYear, 
			curTime->tm_hour, curTime->tm_min, curTime->tm_sec);
	wantedTimeSize++;
	curTimePres = malloc(wantedTimeSize);
	if (curTimePres == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	bzero(curTimePres, wantedTimeSize);
	snprintf(curTimePres, wantedTimeSize, curTimePresFormat, 
			curMonth, curTime->tm_mday, curYear, 
			curTime->tm_hour, curTime->tm_min, curTime->tm_sec);
	snprintf(logString, MAXLOGLINE, "Aquired current date, %s.", curTimePres);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Trying to get the size needed for DML statement.");
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Sample of milter data [connectfrom]: \"%s\".", priv->mcfi_connectfrom);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Sample of milter data [helofrom]: \"%s\".", priv->mcfi_helofrom);
	logNow(logString, 3);
	bzero(whitelistedBool, sizeof(whitelistedBool));
	if (privData->whitelisted == 1) {
	    strncpy(whitelistedBool, "true", sizeof(whitelistedBool));
	} else {
	    strncpy(whitelistedBool, "false", sizeof(whitelistedBool));
	}
	if (privData->gracefulDNS == 1) {
	    strncpy(gracefulDNSBool, "true", sizeof(gracefulDNSBool));
	} else {
	    strncpy(gracefulDNSBool, "false", sizeof(gracefulDNSBool));
	}
	wantedSize = snprintf(pgStat, 0, pgStatFormat,
				curTimePres, privData->mcfi_connectfrom, 
				privData->mcfi_helofrom, privData->relayPresIP, privData->MXesNum, 
				privData->MXesThatReject, privData->MXesThatAccept, privData->MXesOutOfOrder, 
				privData->MXesThatTempfail, gracefulDNSBool,
				whitelistedBool, privData->resolvedRelayIP, myHostName, formattedEmail, action);
	wantedSize++;
	snprintf(logString, MAXLOGLINE, "Got the size needed for DML statement, it's %d.", wantedSize);
	logNow(logString, 3);
	pgStat = malloc(wantedSize);
	bzero(pgStat, wantedSize);
	if (pgStat == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	snprintf(pgStat, wantedSize, pgStatFormat,
				curTimePres, privData->mcfi_connectfrom, 
				privData->mcfi_helofrom, privData->relayPresIP, privData->MXesNum, 
				privData->MXesThatReject, privData->MXesThatAccept, privData->MXesOutOfOrder, 
				privData->MXesThatTempfail, gracefulDNSBool,
				whitelistedBool, privData->resolvedRelayIP, myHostName, formattedEmail, action);
	snprintf(logString, MAXLOGLINE, "Got the DML statement for insert: \"%s\".", pgStat);
	logNow(logString, 3);
	
	connPool.pgResArr[pgConnId] = PQexec(connPool.pgConnArr[pgConnId], pgStat);
	if (PQresultStatus(connPool.pgResArr[pgConnId]) != PGRES_COMMAND_OK){
	    snprintf(logString, MAXLOGLINE, "INSERT command failed: %s", PQerrorMessage(connPool.pgConnArr[pgConnId]));
	    logNow(logString, 0);
	    snprintf(logString, MAXLOGLINE, "The result status is: %s",PQresStatus(PQresultStatus(connPool.pgResArr[pgConnId])));
	    logNow(logString, 0);
	    PQclear(connPool.pgResArr[pgConnId]);
	    return 1;
	}
	privData->SQLlogged = 1;
	PQclear(connPool.pgResArr[pgConnId]);
	/* freeing lock */
	unlockPgConn(pgConnId);
	
	snprintf(logString, MAXLOGLINE, "Trying to free pgStat in pushHistory().");
	logNow(logString, 3);
	free(pgStat);
	snprintf(logString, MAXLOGLINE, "Trying to free curTimePres in pushHistory().");
	logNow(logString, 3);
	free(curTimePres);
	return 0;
    }
#endif
    
    int
    processDirectPart(char *emailToTest, uint32_t serverIPRaw, struct mlfiPriv *privData) {
    
	struct in_addr serverIP;
	
	serverIP.s_addr = serverIPRaw;
    
	if (privData->skipDirect == 0 && privData->gracefulDNS == 0) {
	    returnCode = checkServerResponse(emailToTest, NULL, &serverIP, 1);
	    snprintf(logString, MAXLOGLINE, "checkServerResponse() returned %d.", returnCode);
	    logNow(logString, 3);
	    switch (returnCode) {
		case 0:
		    /* 0 - email accepped */
		    snprintf(logString, MAXLOGLINE, "Got relay accepting message to %s.", emailToTest);
		    logNow(logString, 2);
		    return(CB_PASS);
		    break;
		case 1:
		    /* 1 - email not accepted */
		    snprintf(logString, MAXLOGLINE, "Got relay rejecting message to %s.", emailToTest);
		    logNow(logString, 2);
		    return(CB_REJECT);
		    break;
		case 2:
	    	    /* 2 - email tempfailed */
		    snprintf(logString, MAXLOGLINE, "Got relay tempfailing message to %s.", emailToTest);
		    logNow(logString, 2);
		    return(CB_TEMPFAIL);
		    break;
		case 3:
		    /* 3 - socket error */
		    snprintf(logString, MAXLOGLINE, "Got a socket error, returning relay tempfailing message from %s.", emailToTest );
		    logNow(logString, 2);
		    return(CB_TEMPFAIL);
		    break;
		case 4:
		    /* 4 - DNS error */
		    snprintf(logString, MAXLOGLINE, "Got a DNS error, returning relay tempfailing message from %s.", emailToTest);
		    logNow(logString, 2);
		    return(CB_TEMPFAIL);
		    break;
		case 5:
		    /* 5 - unknown answer code */
		    snprintf(logString, MAXLOGLINE, "Got an unknown SMTP answer code, returning relay tempfailing message from %s.", emailToTest);
		    logNow(logString, 2);
	    	    return(CB_TEMPFAIL);
		    break;
		case 6:
		default:
		    /* 6 - memory error */
		    snprintf(logString, MAXLOGLINE, "Got a memory or connection error, returning relay tempfailing message from %s.", emailToTest);
		    logNow(logString, 2);
		    return(CB_TEMPFAIL);
		    break;
	    }
	} else {
	    if (privData->skipDirect == 1) {
		snprintf(logString, MAXLOGLINE, "Skipping reverse relaying of e-mail %s to relay %s [%s].", emailToTest, privData->resolvedRelayIP, privData->relayPresIP);
		logNow(logString, 2);
		return(CB_SKIP);
	    } else {
		snprintf(logString, MAXLOGLINE, "Skipping reverse relaying of e-mail %s to relay %s [%s] due to graceful DNS.", emailToTest, privData->resolvedRelayIP, privData->relayPresIP);
		logNow(logString, 2);
		return(CB_GRACEFUL);
	    }
	}
    }

    int
    processMXpart(char *formattedEmail, char *mailHost, struct mlfiPriv *privData) {
	returnCode = dealWithMXes(formattedEmail, mailHost, privData);
	snprintf(logString, MAXLOGLINE, "dealWithMXes() returned %d.", returnCode);
	switch (returnCode) {
	    case 0:
		/* 0 - all of the MXes accepted emailAddress. */
		snprintf(logString, MAXLOGLINE, "all of the MXes accepted mail to %s.", formattedEmail);
		logNow(logString, 2);
		return CB_PASS;
		break;
	    case 1:
		/* 1 - one of the MXes accepted emailAddress. */
		snprintf(logString, MAXLOGLINE, "some of the MXes accepted mail to %s.", formattedEmail);
		logNow(logString, 2);
		return CB_PASS;
		break;
	    case 2:
		/* 2 - no MX for emailAddress found. */
		/* need to check emailAddress on host and/or on sending host */
		snprintf(logString, MAXLOGLINE, "no MX records for the hostpart of %s found, need direct processing on the target host.", formattedEmail);
		logNow(logString, 2);
		returnSubCode = checkServerResponse(formattedEmail, mailHost, NULL, 0);
		snprintf(logString, MAXLOGLINE, "checkServerResponse() returned %d.", returnSubCode);
		logNow(logString, 2);
		switch (returnSubCode) {
		    case 0:
			/* 0 - email accepped */
			snprintf(logString, MAXLOGLINE, "Got relay accepting message to %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_PASS);
			break;
		    case 1:
			/* 1 - email not accepted */
			snprintf(logString, MAXLOGLINE, "Got relay rejecting message to %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_REJECT);
	    		break;
		    case 2:
		        /* 2 - email tempfailed */
			snprintf(logString, MAXLOGLINE, "Got relay tempfailing message to %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_TEMPFAIL);
			break;
		    case 3:
			/* 3 - socket error */
			snprintf(logString, MAXLOGLINE, "Got a socket error, returning relay tempfailing message to %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_TEMPFAIL);
			break;
		    case 4:
			/* 4 - DNS error */
			snprintf(logString, MAXLOGLINE, "Got a DNS error, returning relay tempfailing message from %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_TEMPFAIL);
			break;
		    case 5:
			/* 5 - unknown answer code */
			snprintf(logString, MAXLOGLINE, "Got an unknown SMTP answer code, returning relay tempfailing message from %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_TEMPFAIL);
			break;
		    case 6:
		    default:
			/* 6 - memory error */
			snprintf(logString, MAXLOGLINE, "Got a memory or some unknown error, returning relay tempfailing message from %s.", formattedEmail);
			logNow(logString, 2);
			return(CB_TEMPFAIL);
			break;
		}
		break;
	    case 3:
	    default:
		/* 3 - some software/memory error happened. */
		snprintf(logString, MAXLOGLINE, "Got a memory or connection error while parsing message from %s.", formattedEmail);
		logNow(logString, 2);
		return(CB_TEMPFAIL);
		break;
	    case 4:
		/* 4 - none of the MXes accepted message. */
		snprintf(logString, MAXLOGLINE, "All MXes rejected message from %s.", formattedEmail);
		logNow(logString, 2);
		return(CB_REJECT);
		break;
	    case 5:
		/* 5 - MXes mostly tempfailed message. */
		snprintf(logString, MAXLOGLINE, "None of the MXes accepted message, some tempfailed message from %s.", formattedEmail);
		logNow(logString, 2);
		return(CB_TEMPFAIL);
		break;
	}
    
	/* some unknown code found */
	snprintf(logString, MAXLOGLINE, "Unknown answer code found, while parsing message from %s.", formattedEmail);
	logNow(logString, 0);
	return(CB_TEMPFAIL);
    }
    
    snprintf(logString, MAXLOGLINE, "Entered mcfi_envfrom().");
    logNow(logString, 3);
    
    priv = ((struct mlfiPriv *) smfi_getpriv(ctx));
    
    /* Non-structural check for existance of the host in whitelisted networks */
    if (hostIsWhitelisted(priv) == 0 && priv->whitelisted == 1) {
#ifdef WITH_PGSQL
	strncpy(action, "passed", sizeof(action));
	pushHistory(priv, &action[0]);	
#endif
	return SMFIS_CONTINUE;
    } else {
	/* return reject if some critical error happened */
	if (hostIsWhitelisted(priv) == 1) {
	    return SMFIS_REJECT;
	}
	/* do nothing if host is not whitelisted */
    }
    /* end of it */
    
    snprintf(logString, MAXLOGLINE, "Got formatted sender address, %s.", argv[0]);
    logNow(logString, 2);
    
    if (strcmp(argv[0], "") == 0) {
	snprintf(logString, MAXLOGLINE, "Rejecting message, invalid envelope-from address received, \"%s\", RFC 821 violation.", argv[0]);
	logNow(logString, 2);
	mc_setreply(ctx, "550", logString);
	return SMFIS_REJECT;
    }
    
    if (strcmp(argv[0], "<>") == 0) {
	/* setting a flag to kill this message on data stage, and passing it - this how I see a null-envfrom callback mode */
	snprintf(logString, MAXLOGLINE, "Null-envfrom received from relay %s, passing message, will refuse it on DATA stage.", priv->relayPresIP);
	logNow(logString, 0);
	priv->killMeOnData = 1;
	return SMFIS_CONTINUE;
    }
    
    formattedEmail = strdup(argv[0]);
    if (formattedEmail == NULL) {
	snprintf(logString, MAXLOGLINE, "Cannot allocate memory.");
	logNow(logString, 0);
	return SMFIS_TEMPFAIL;
    }
    
    /* stripping brackets */
    if (formattedEmail[0] == '<') {
	formattedEmail++;
    }
    if (formattedEmail[strlen(formattedEmail) - 1] == '>') {
	formattedEmail[strlen(formattedEmail) - 1] = '\0';
    }
    
    /* Getting host part of the address in case ${mail_host} not available */
    bzero(emailBuf, sizeof(emailBuf));

    strncpy(emailBuf, formattedEmail, sizeof(emailBuf));
    emailBP = &emailBuf[0];
    strsep(&emailBP, emailDelim);

    mailHost = emailBP;
    
    if (formattedEmail == NULL || strlen(formattedEmail) < 3 || mailHost == NULL || strlen(mailHost) == 0) {
	snprintf(logString, MAXLOGLINE, "Rejecting message, invalid from address received, \"%s\".", argv[0]);
	logNow(logString, 2);
	return SMFIS_REJECT;
    }

    snprintf(logString, MAXLOGLINE, "Got sender address, %s.", formattedEmail);
    logNow(logString, 2);
    snprintf(logString, MAXLOGLINE, "Got host part, %s.", mailHost);
    logNow(logString, 2);
    snprintf(logString, MAXLOGLINE, "Got sender host, %s.", priv->mcfi_connectfrom);
    logNow(logString, 2);
    
    /* need to handle direct/rfc/both modes */
    if (strncmp(callbackMode, "rfc", 3) == 0) {
	snprintf(logString, MAXLOGLINE, "Working in \"rfc\" mode.");
	logNow(logString, 2);
	snprintf(logString, MAXLOGLINE, "Calling processMXes with e-mail %s and hostpart %s.", formattedEmail, mailHost);
	logNow(logString, 2);
	mxPassKey = processMXpart(formattedEmail, mailHost, priv);
	switch (mxPassKey) {
	    case CB_PASS:
		snprintf(logString, MAXLOGLINE, "Passing e-mail from %s, [%d/%d] MXes are accepting.", formattedEmail, priv->MXesThatAccept, priv->MXesNum);
		logNow(logString, 2);
#ifdef WITH_PGSQL
		strncpy(action, "passed", sizeof(action));
		pushHistory(priv, &action[0]);
#endif
		return SMFIS_CONTINUE;
		break;
	    case CB_REJECT:
		snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s and relay [%s], [%d/%d] of MXes for the domain %s rejected this e-mail in reverse direction.", formattedEmail, priv->relayPresIP, priv->MXesThatReject, priv->MXesNum, mailHost);
		logNow(logString, 2);
		mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
		strncpy(action, "dropped", sizeof(action));
		pushHistory(priv, &action[0]);
#endif
		return SMFIS_REJECT;
		break;
	    case CB_TEMPFAIL:
		snprintf(logString, MAXLOGLINE, "Tempfailing e-mail from %s and relay [%s], either MX relays for the domain %s are tempfailing, or some other error happened.", formattedEmail,priv->relayPresIP, mailHost);
		logNow(logString, 2);
		mc_setreply(ctx, "451", logString);
#ifdef WITH_PGSQL
		strncpy(action, "tempfailed", sizeof(action));
		pushHistory(priv, &action[0]);
#endif
		return SMFIS_TEMPFAIL;
		break;
	}
    } else {
	if (strncmp(callbackMode, "direct", 6) == 0) {
	    snprintf(logString, MAXLOGLINE, "Working in \"direct\" mode.");
	    logNow(logString, 2);
	    /* checking original email */
	    snprintf(logString, MAXLOGLINE, "Calling processDirectPart with e-mail %s and hostpart %x.", formattedEmail, priv->relayIP);
	    logNow(logString, 2);
	    directPassKey = processDirectPart(formattedEmail, priv->relayIP, priv);
	    /* checking fake email */
	    snprintf(logString, MAXLOGLINE, "Calling processDirectPart with fake e-mail %s and hostpart %x.", formattedEmail, priv->relayIP);
	    logNow(logString, 2);
	    fakePassKey = processDirectPart(fakeEmail, priv->relayIP, priv);
	    if (fakePassKey == CB_PASS) {
		/* relay is accepting fake emails */
		snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s, relay %s that the mail is coming from is accepting fake emails, probably openrelaying.", formattedEmail, priv->mcfi_connectfrom);
		logNow(logString, 0);
		mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
		strncpy(action, "dropped", sizeof(action));
		pushHistory(priv, &action[0]);
#endif
	        return SMFIS_REJECT;
	    } else {
	        if (directPassKey == CB_REJECT) {
		    snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s, relay %s that the mail is coming from isn't accepting email it's trying to pass.", formattedEmail, priv->mcfi_connectfrom);
		    logNow(logString, 0);
		    mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
		    strncpy(action, "dropped", sizeof(action));
		    pushHistory(priv, &action[0]);
#endif
	    	    return SMFIS_REJECT;
	        } else {
		    if (directPassKey == CB_PASS) {
			snprintf(logString, MAXLOGLINE, "Passing e-mail from %s, relay %s isn't accepting fake email and is accepting original email.", formattedEmail, priv->mcfi_connectfrom);
			logNow(logString, 0);
#ifdef WITH_PGSQL
			strncpy(action, "passed", sizeof(action));
			pushHistory(priv, &action[0]);
#endif
	    		return SMFIS_CONTINUE;
		    } else {
			snprintf(logString, MAXLOGLINE, "Tempfailing email from %s, suspicious relay %s - it %s reversing e-mail.", 
			    formattedEmail, priv->mcfi_connectfrom, 
			    transcriptAnswerCode(directPassKey, dTranscription));
			    snprintf(logString, MAXLOGLINE, "Tempfailing e-mail from %s: accepting MXes [%d/%d], rejecting MXes [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d].",
				formattedEmail,
				priv->MXesThatAccept,
				priv->MXesNum,
				priv->MXesThatReject,
				priv->MXesNum,
				priv->MXesThatTempfail,
				priv->MXesNum,
				priv->MXesOutOfOrder,
				priv->MXesNum);
			logNow(logString, 0);
			mc_setreply(ctx, "451", logString);
#ifdef WITH_PGSQL
			strncpy(action, "tempfailed", sizeof(action));
			pushHistory(priv, &action[0]);
#endif
                        return SMFIS_TEMPFAIL;
		    }
		}
	    }
	} else {
	    snprintf(logString, MAXLOGLINE, "Working in \"both\" mode.");
	    logNow(logString, 2);
	    /* then simply both */
	    snprintf(logString, MAXLOGLINE, "Calling processMXes with e-mail %s and hostpart %s.", formattedEmail, mailHost);
	    logNow(logString, 2);
	    mxPassKey = processMXpart(formattedEmail, mailHost, priv);
	    snprintf(logString, MAXLOGLINE, "Calling processDirectPart with e-mail %s and hostpart %x.", formattedEmail, priv->relayIP);
	    logNow(logString, 2);
	    directPassKey = processDirectPart(formattedEmail, priv->relayIP, priv);
	    /* checking fake email */
	    snprintf(logString, MAXLOGLINE, "Calling processDirectPart with fake e-mail %s and hostpart %x.", fakeEmail, priv->relayIP);
	    logNow(logString, 2);
	    fakePassKey = processDirectPart(fakeEmail, priv->relayIP, priv);
	    if (fakePassKey == CB_PASS) {
		/* relay is accepting fake emails */
		snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s, sending relay %s that is sending the mail is accepting fake emails, probably openrelaying.", formattedEmail, priv->mcfi_connectfrom);
		logNow(logString, 0);
		mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
		strncpy(action, "dropped", sizeof(action));
		pushHistory(priv, &action[0]);
#endif
	        return SMFIS_REJECT;
	    } else {
	        if (directPassKey == CB_REJECT) {
		    snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s, relay %s that is sending the mail isn't accepting email it's trying to pass.", formattedEmail, priv->mcfi_connectfrom);
		    logNow(logString, 0);
		    mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
		    strncpy(action, "dropped", sizeof(action));
		    pushHistory(priv, &action[0]);
#endif
	    	    return SMFIS_REJECT;
	        } else {
		    if (directPassKey == CB_PASS || directPassKey == CB_SKIP || directPassKey == CB_GRACEFUL) {
			/* additionally need to check MX answercode before passing */
			if (mxPassKey == CB_PASS) {
			    switch (directPassKey) {
				case CB_SKIP:
				    snprintf(logString, MAXLOGLINE, "Passing email from %s, relay %s is one of the MXes and [%d/%d] MXes are accepting original email existing on MX.", formattedEmail, mailHost, priv->MXesThatAccept, priv->MXesNum);
				    logNow(logString, 0);
				    break;
				case CB_GRACEFUL:
				    snprintf(logString, MAXLOGLINE, "Passing email from %s, graceful DNS in use and [%d/%d] MXes are accepting original email existing on MX.", formattedEmail, priv->MXesThatAccept, priv->MXesNum);
				    logNow(logString, 0);
				    break;
				case CB_PASS:
				    snprintf(logString, MAXLOGLINE, "Passing email from %s, relay MXes %s aren't accepting fake email and [%d/%d] MXes are accepting original email existing on MX.", formattedEmail, mailHost, priv->MXesThatAccept, priv->MXesNum);
				    logNow(logString, 0);
				    break;
			    }
#ifdef WITH_PGSQL
			    strncpy(action, "passed", sizeof(action));
			    pushHistory(priv, &action[0]);
#endif
	    		    return SMFIS_CONTINUE;
			} else {
			    if (mxPassKey == CB_REJECT) {
				/* Original email doesn't exist on the target MX*/
				snprintf(logString, MAXLOGLINE, "Dropping e-mail from %s and relay [%s], [%d/%d] of MXes for the domain %s rejected that mail.", formattedEmail, priv->relayPresIP, priv->MXesThatReject, priv->MXesNum, mailHost);
				logNow(logString, 0);
				mc_setreply(ctx, "550", logString);
#ifdef WITH_PGSQL
				strncpy(action, "dropped", sizeof(action));
				pushHistory(priv, &action[0]);
#endif
	    			return SMFIS_REJECT;
			    } else {
				snprintf(logString, MAXLOGLINE, "Tempfailing e-mail from %s, suspicious relay %s: when tested this mail on the MXes for domain %s got: accepting MXes [%d/%d], rejecting MXes [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d]; reverse relaying %s, fake relaying %s.",
					formattedEmail, 
					priv->mcfi_connectfrom,
					mailHost,
			    		priv->MXesThatAccept,
					priv->MXesNum,
					priv->MXesThatReject,
					priv->MXesNum,
					priv->MXesThatTempfail,
					priv->MXesNum,
					priv->MXesOutOfOrder,
					priv->MXesNum,
					transcriptAnswerCode(directPassKey, dTranscription),
					transcriptAnswerCode(fakePassKey, fTranscription));
				logNow(logString, 0);
				mc_setreply(ctx, "451", logString);
#ifdef WITH_PGSQL
				strncpy(action, "tempfailed", sizeof(action));
				pushHistory(priv, &action[0]);
#endif
                    		return SMFIS_TEMPFAIL;
			    }
			}
		    } else {
			snprintf(logString, MAXLOGLINE, "Tempfailing e-mail from %s, suspicious relay %s: when tested this mail on the MXes for domain %s got: accepting MXes [%d/%d], rejecting MXes [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d]; reverse relaying %s, fake relaying %s.",
				    formattedEmail,
				    priv->mcfi_connectfrom,
				    mailHost, 
				    priv->MXesThatAccept,
				    priv->MXesNum,
				    priv->MXesThatReject,
				    priv->MXesNum,
				    priv->MXesThatTempfail,
				    priv->MXesNum,
				    priv->MXesOutOfOrder,
				    priv->MXesNum,
				    transcriptAnswerCode(directPassKey, dTranscription),
				    transcriptAnswerCode(fakePassKey, fTranscription));
			logNow(logString, 0);
			mc_setreply(ctx, "451", logString);
#ifdef WITH_PGSQL
			strncpy(action, "tempfailed", sizeof(action));
			pushHistory(priv, &action[0]);
#endif
                        return SMFIS_TEMPFAIL;
		    }
		}
	    }
	    snprintf(logString, MAXLOGLINE, "Tempfailing e-mail from %s, unable to parse conditions from relay %s: accepting MXes [%d/%d], rejecting MXes [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d]; reverse relaying %s, fake relaying %s.",
		    formattedEmail, priv->mcfi_connectfrom,
		    priv->MXesThatAccept,
		    priv->MXesNum,
		    priv->MXesThatReject,
		    priv->MXesNum,
		    priv->MXesThatTempfail,
		    priv->MXesNum,
		    priv->MXesOutOfOrder,
		    priv->MXesNum,
		    transcriptAnswerCode(directPassKey, dTranscription),
		    transcriptAnswerCode(fakePassKey, fTranscription));
	    logNow(logString, 0);
	    mc_setreply(ctx, "451", logString);
#ifdef WITH_PGSQL
	    strncpy(action, "tempfailed", sizeof(action));
	    pushHistory(priv, &action[0]);
#endif
	    return SMFIS_TEMPFAIL;
	}
    }
    /* if we are here - then it's something wrong */
    snprintf(logString, MAXLOGLINE, "Tempfailing email from %s, unable to parse conditions.", formattedEmail);
    logNow(logString, 0);
    mc_setreply(ctx, "451", logString);
    if (formattedEmail != NULL) {
	snprintf(logString, MAXLOGLINE, "Trying to free formattedEmail in mcfi_envfrom().");
	logNow(logString, 3);
	free(formattedEmail);
    }
#ifdef WITH_PGSQL
    strncpy(action, "tempfailed", sizeof(action));
    pushHistory(priv, &action[0]);
#endif
    return SMFIS_TEMPFAIL;
}

sfsistat
mcfi_eom(SMFICTX *ctx) {
    char host[512];
    char hbuf[2048];
    int haStatus;
    char *headFlagName = "X-Callback";
    char *headDBGFlagName = "X-Callback-Status";
    
    struct mlfiPriv *priv;
    
    snprintf(logString, MAXLOGLINE, "Entered mcfi_eom().");
    logNow(logString, 3);
    
    priv = ((struct mlfiPriv *) smfi_getpriv(ctx));

    /* add a header to the message announcing our presence */
    if (gethostname(host, sizeof(host)) < 0) {
        snprintf(host, sizeof(host), "localhost");
    }
    bzero(hbuf, sizeof(hbuf));
    snprintf(hbuf, sizeof(hbuf), "Sender verified by milter-callback %s at %s.", mcVersion, host);
    haStatus = smfi_addheader(ctx, headFlagName, hbuf);
    if (haStatus != MI_SUCCESS) {
        /* failed; we have to wait until later */
        snprintf(logString, MAXLOGLINE, "Couldn't add header: %s: %s, got %d from libmilter.", headFlagName, hbuf, haStatus);
	logNow(logString, 0);
        return SMFIS_TEMPFAIL;
    } else {
	snprintf(logString, MAXLOGLINE, "Successfully added header, %s: %s", headFlagName, hbuf);
	logNow(logString, 2);
    }
    if (priv->whitelisted == 1) {
	snprintf(hbuf, sizeof(hbuf), "relay [%s] found in white list.", priv->relayPresIP);
    } else {
	if (priv->skipDirect == 0 && priv->skipDirect == 0) {
	    snprintf(hbuf, sizeof(hbuf), "accepting MXes: [%d/%d], rejecting MXes: [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d], reverse/fake relaying done.",
	    	    priv->MXesThatAccept, priv->MXesNum, priv->MXesThatReject, priv->MXesNum, priv->MXesThatTempfail, priv->MXesNum, priv->MXesOutOfOrder, priv->MXesNum);
	} else {
	    if (priv->skipDirect == 1) {
		snprintf(hbuf, sizeof(hbuf), "accepting MXes: [%d/%d], rejecting MXes: [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d], reverse/fake relaying skipped.",
	    		priv->MXesThatAccept, priv->MXesNum, priv->MXesThatReject, priv->MXesNum, priv->MXesThatTempfail, priv->MXesNum, priv->MXesOutOfOrder, priv->MXesNum);
	    } else {
		snprintf(hbuf, sizeof(hbuf), "accepting MXes: [%d/%d], rejecting MXes: [%d/%d], tempfailing MXes [%d/%d], MXes out of order [%d/%d], graceful DNS used.",
	    		priv->MXesThatAccept, priv->MXesNum, priv->MXesThatReject, priv->MXesNum, priv->MXesThatTempfail, priv->MXesNum, priv->MXesOutOfOrder, priv->MXesNum);
	    }	
	}	
    }
    haStatus = smfi_addheader(ctx, headDBGFlagName, hbuf);
    if (haStatus != MI_SUCCESS) {
        /* failed; we have to wait until later */
        snprintf(logString, MAXLOGLINE, "Couldn't add header: %s: %s, got %d from libmilter.", headDBGFlagName, hbuf, haStatus);
	logNow(logString, 0);
        return SMFIS_TEMPFAIL;
    } else {
	snprintf(logString, MAXLOGLINE, "Successfully added header, %s: %s", headDBGFlagName, hbuf);
	logNow(logString, 2);
    }
    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mcfi_close(SMFICTX *ctx) {
    struct mlfiPriv *priv;
    
    priv = ((struct mlfiPriv *) smfi_getpriv(ctx));
    
    snprintf(logString, MAXLOGLINE, "Entered mcfi_close().");
    logNow(logString, 3);
    
    if (priv != NULL) {
	snprintf(logString, MAXLOGLINE, "Trying to release private context pointer.");
	logNow(logString, 3);
	if (priv->mcfi_connectfrom != NULL) {
	    free(priv->mcfi_connectfrom);
	}
	if (priv->mcfi_helofrom != NULL) {
	    free(priv->mcfi_helofrom);
	}
	free(priv);
    }
    smfi_setpriv(ctx, NULL);
    return SMFIS_CONTINUE;
}

sfsistat
mcfi_negotiate(SMFICTX *ctx, unsigned long f0, unsigned long f1, unsigned long f2, unsigned long f3, unsigned long *pf0, unsigned long *pf1, unsigned long *pf2, unsigned long *pf3) {
    return SMFIS_ALL_OPTS;
}

sfsistat
mcfi_data(SMFICTX *ctx) {
    struct mlfiPriv *priv;
    
    priv = ((struct mlfiPriv *) smfi_getpriv(ctx));
    
    if (priv->killMeOnData == 1) {
	/* rejecting a message with null-envfrom, if this is a callback - then it's already done */
	/* MAIL-DAEMON notifications will be added later, at this point they are sacrificed in the name of the Holy Mail */
	snprintf(logString, MAXLOGLINE, "An attempt to pass a mail with null-envelope-from from a relay [%s] is made, dropping it.", priv->relayPresIP);
	logNow(logString, 0);
	mc_setreply(ctx, "550", logString);
	return SMFIS_REJECT;
    } else {
	return SMFIS_CONTINUE;
    }
}	

struct smfiDesc smfilter =
{
    "Milter-Callback",	/* filter name */
    SMFI_VERSION,	/* version code -- do not change */
    SMFIF_ADDHDRS,	/* flags */
    mcfi_connect,	/* connection info filter */
    mcfi_helo,		/* SMTP HELO command filter */
    mcfi_envfrom,	/* envelope sender filter */
    NULL,
    NULL,
    NULL,
    NULL,
    mcfi_eom,		/* end of message */
    NULL,		/* message aborted */
    mcfi_close,		/* connection closing */
    NULL,
    mcfi_data,
    mcfi_negotiate	/* Once, at the start of each SMTP connection */
};

static void
usage(char *prog) {
    fprintf(stderr, "Usage: %s [-s socket-address] [-t timeout] [-m milter-email-address] [-u milter-user] [-f fake-email-address] [-c callback-mode] [-l loglevel] [-D] \n", prog);
}

int
main(int argc, char **argv) {

    bool setconn = FALSE;
    int c;
    char *cfToken;
    const char *args = "s:t:f:u:m:c:h:l:vDi:";
    extern char *optarg;
    const char *scPrefix = "unix:";
    FILE *configFile;
    char configFilePath[MAXPATH];
    char pidFilePath[MAXPATH];
    const char *configOptDelimiters = " ,";
    const char *cidrDelimiters = "/";
    
    char *whiteNetworks;
    char whitePresNetworks[4096];
    char *whiteNetwork;
    char *cidrMask;
    struct in_addr *bufNet, *bufMask, *newWhiteNet, *newWhiteMask;
    
    char fmSocketPath[2048];
    struct stat *socketFile;
    int genErr;
    
    char *configBuffer;
    char *configDelimiter = "=";
    
    char *debugBuf;
    
    pid_t   pid, sid;
    
    struct mlfiPriv *privEmu;
    struct in_addr ipEmu;
#ifdef WITH_PGSQL
    int i, wantedInfoSize, minit;
#endif
    
    void
    createPid(int pid) {
	FILE *pidFile;
	
	pidFile = fopen(pidFilePath, "w");
	if (pidFile == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot create pidfile at path %s, %s.", pidFilePath, strerror(errno));
	    logNow(logString, 3);
	    exit(EX_SOFTWARE);
	} else {
	    snprintf(logString, MAXLOGLINE, "Writing the pid %d to pidfile at %s.", pid, pidFilePath);
	    logNow(logString, 3);
	    fprintf(pidFile, "%d", pid);
	    fclose(pidFile);
	}
    }
    
    /* allocate memory for logging */
    logString = malloc(MAXLOGLINE);
    if (logString == NULL) {
	fprintf(stderr, "Cannot allocate memory, aborting.\n");
    }
    
    /* init some paths */
    bzero(configFilePath, sizeof(configFilePath));
    bzero(pidFilePath, sizeof(pidFilePath));
    strncpy(configFilePath, "/usr/local/etc/mail/milter-callback.conf", sizeof(configFilePath));
    strncpy(pidFilePath, "/var/run/milter-callback.pid", sizeof(pidFilePath));
    
    /* initing counter to accept multiple lines */
    whiteNets.netCount = 0;
    
    /* initing replyURL string */
    bzero(replyURL, sizeof(replyURL));
    
    /* Process the config file */
    configFile = fopen(configFilePath, "r");
    if (configFile == NULL) {
	fprintf(stderr, "Cannot open config file at path %s, %s.\n", configFilePath, strerror(errno));
	logNow(logString, 0);
	exit(EX_SOFTWARE);
    } else {
	snprintf(logString, MAXLOGLINE, "%s %s started.", mcName, mcVersion);
	logNow(logString, 0);
    }
    
    int
    showWhiteList (){
	int i;
	char net[INET_ADDRSTRLEN], mask[INET_ADDRSTRLEN];
    
	snprintf(logString, MAXLOGLINE, "Got those networks in white list:");
	logNow(logString, 3);
	for (i = 0; i < whiteNets.netCount; i++) {
	    snprintf(logString, MAXLOGLINE, "Hex: net 0x%x mask 0x%x, network byteorder.",  whiteNets.whiteNet[i].s_addr, whiteNets.whiteMask[i].s_addr);
	    logNow(logString, 3);
	    if (inet_ntop(AF_INET, &whiteNets.whiteNet[i], net, INET_ADDRSTRLEN) == NULL) {
		fprintf(stderr, "Cannot convert %x to presentation format, aborting.\n", whiteNets.whiteNet[i].s_addr);
		exit(1);
	    }
	    if (inet_ntop(AF_INET, &whiteNets.whiteMask[i], mask, INET_ADDRSTRLEN) == NULL){
	        fprintf(stderr, "Cannot convert %x to presentation format, aborting.\n", whiteNets.whiteMask[i].s_addr);
		exit(1);
	    }
	    snprintf(logString, MAXLOGLINE, "Presentation: net %s mask %s.", net, mask);
	    logNow(logString, 3);
	}
	return(0);
    }
        
    if (configFile != NULL) {
	configBuffer = malloc(2048);
	if (configBuffer == NULL) {
	    fprintf(stderr, "Cannot allocate memory, aborting.\n");
	    exit(1);
	}
	while (!feof(configFile)) {
	    debugBuf = fgets(configBuffer, 1024, configFile);
	    if (debugBuf != NULL) {
		if (configBuffer[strlen(configBuffer) - 1] == '\n'){
			configBuffer[strlen(configBuffer) - 1] = '\0';
		}
		snprintf(logString, MAXLOGLINE, "Current line: \"%s\".", configBuffer);
		logNow(logString, 3);
		if (configBuffer[0] != '\0' && configBuffer[0] != '\n' && configBuffer[0] != '#') {
		    cfToken = strsep(&configBuffer, configDelimiter);
		    snprintf(logString, MAXLOGLINE, "cfToken is \"%s\".", cfToken);
		    logNow(logString, 3);
		    snprintf(logString, MAXLOGLINE, "Rest of the line is \"%s\".", configBuffer);
		    logNow(logString, 3);
		    if (cfToken[strlen(cfToken) - 1] == ' '){
			cfToken[strlen(cfToken) - 1] = '\0';
		    }
		    if (configBuffer[0] == ' '){
			configBuffer++;
		    }
		    snprintf(logString, MAXLOGLINE, "After trimming the line is \"%s\".", configBuffer);
		    logNow(logString, 3);
    		    if (strcmp(cfToken, "socket path") == 0){
			strncpy(socketPath, configBuffer, sizeof(socketPath));
		    }
		    if (strcmp(cfToken, "pidfile path") == 0){
			strncpy(pidFilePath, configBuffer, sizeof(pidFilePath));
		    }
		    if (strcmp(cfToken, "loglevel") == 0){
			logLevel = atol(configBuffer);
		    }
		    if (strcmp(cfToken, "verification mode") == 0){
			strncpy(callbackMode, configBuffer, sizeof(callbackMode));
		    }
		    if (strcmp(cfToken, "graceful dns") == 0){
			if (strcmp(configBuffer, "yes") == 0) {
			    gracefulDNS = 1;
			}
		    }
		    if (strcmp(cfToken, "milter user") == 0){
			strncpy(milterUser, configBuffer, sizeof(milterUser));
		    }
		    if (strcmp(cfToken, "fake e-mail") == 0){
			if (configBuffer[0] == '<' || configBuffer[strlen(configBuffer) - 1] == '>') {
			    fprintf(stderr, "Wrong symbols in e-mail address, aborting.\n");
			    exit(1);
			}
			strncpy(fakeEmail, configBuffer, sizeof(fakeEmail));
		    }
		    if (strcmp(cfToken, "milter e-mail") == 0){
			if (configBuffer[0] == '<' || configBuffer[strlen(configBuffer) - 1] == '>') {
			    fprintf(stderr, "Wrong symbols in e-mail address, aborting.\n");
			    exit(1);
			}
		        strncpy(milterEmail, configBuffer, sizeof(milterEmail));
		    }
		    if (strcmp(cfToken, "reply url") == 0){
		        strncpy(replyURL, configBuffer, sizeof(replyURL));
		    }
#ifdef WITH_PGSQL
		    /* database parameters part */
		    if (strcmp(cfToken, "dbhostname") == 0){
		        strncpy(milterDBHost, configBuffer, sizeof(milterDBHost));
		    }
		    if (strcmp(cfToken, "dbname") == 0){
		        strncpy(milterDBName, configBuffer, sizeof(milterDBName));
		    }
		    if (strcmp(cfToken, "dbuser") == 0){
		        strncpy(milterDBUser, configBuffer, sizeof(milterDBUser));
		    }
		    if (strcmp(cfToken, "dbpassword") == 0){
		        strncpy(milterDBPass, configBuffer, sizeof(milterDBPass));
		    }
		    if (strcmp(cfToken, "connections") == 0){
		        connPoolLen = atol(configBuffer);
			if (connPoolLen < 1) {
			    fprintf(stderr, "Wrong connection parameter, aborting.\n");
			    exit(1);
			} else {
			    if (connPoolLen == 0) {
				connPoolLen = 5;
			    }
			}
		    }
		    /* database parameters part end */
#endif		    
		    if (strcmp(cfToken, "whitelisted networks") == 0){
			whiteNetworks = malloc(strlen(configBuffer) + 1);
			if (whiteNetworks == NULL) {
			    fprintf(stderr, "Memory allocation failed.\n");
			    exit(1);
			}
			bzero(whiteNetworks, strlen(configBuffer) + 1);
			/* we just reserved enough memory, so it's not a mistake */
			strncpy(whiteNetworks, configBuffer, strlen(configBuffer));
			strncpy(whitePresNetworks, whiteNetworks, sizeof(whitePresNetworks));
			while (whiteNetworks != NULL) {
			    snprintf(logString, MAXLOGLINE, "Working with line %s.", whiteNetworks);
			    logNow(logString, 3);
			    whiteNetwork = strsep(&whiteNetworks, configOptDelimiters);
			    snprintf(logString, MAXLOGLINE, "After shifting the rest of the config line is  %s.", whiteNetworks);
			    logNow(logString, 3);
			    if (strlen(whiteNetwork) > 0) {
				snprintf(logString, MAXLOGLINE, "Got white network %s.", whiteNetwork);
				logNow(logString, 3);
				/* striping cidr mask */
				cidrMask = strdup(whiteNetwork);
				whiteNetwork = strsep(&whiteNetwork, cidrDelimiters);
				strsep(&cidrMask, cidrDelimiters);
				snprintf(logString, MAXLOGLINE, "Got white network mask /%s.", cidrMask);
				logNow(logString, 3);
				/* getting new cidr mask to proper format */
				if (cidrMask == NULL) {
				    whiteNetMask = 32;
				} else {
				    whiteNetMask = strtol(cidrMask, NULL, 0);
				}
				if (whiteNetMask == 0) {
				    fprintf(stderr, "Cannot convert %s to netmask.\n", cidrMask);
				    exit(1);
				}
				if (whiteNetMask < 0 || whiteNetMask > 32) {
				    fprintf(stderr, "%d isnt't a valid netmask.\n", whiteNetMask);
				    exit(1);
				}
				whiteNetMask = 0xffffffff << (32 - whiteNetMask);
				whiteNetMask = htonl(whiteNetMask);
				snprintf(logString, MAXLOGLINE, "After conversion of mask /%s to network format got 0x%x in host order, 0x%x natively.", cidrMask, ntohl(whiteNetMask), whiteNetMask);
				logNow(logString, 3);
				newWhiteNet = malloc(sizeof(struct in_addr));
				if (newWhiteNet == NULL) {
				    fprintf(stderr, "Cannot allocate memory, aborting.\n");
				    exit(1);
				}
				newWhiteMask = malloc(sizeof(struct in_addr));
				if (newWhiteMask == NULL) {
				    fprintf(stderr, "Cannot allocate memory, aborting.\n");
				    exit(1);
				}
				newWhiteMask->s_addr = whiteNetMask;
				if (inet_pton(AF_INET, whiteNetwork, newWhiteNet) == 1) {
				    snprintf(logString, MAXLOGLINE, "After conversion of net %s to network format got 0x%x in host order, 0x%x natively.", 
					    whiteNetwork, ntohl(newWhiteNet->s_addr), newWhiteNet->s_addr);
				    logNow(logString, 3);
				    if (whiteNets.netCount == 0) {
					/* kinda we got first time here */
					snprintf(logString, MAXLOGLINE, "Initializing network whitelist.");
					logNow(logString, 3);
					snprintf(logString, MAXLOGLINE, "Pushing network %x to new position %d.", newWhiteNet->s_addr, whiteNets.netCount);
					logNow(logString, 3);
					snprintf(logString, MAXLOGLINE, "Pushing mask %x to new position %d.", newWhiteMask->s_addr, whiteNets.netCount);
					logNow(logString, 3);
					whiteNets.whiteNet = newWhiteNet;
					whiteNets.whiteMask = newWhiteMask;
				    } else {
					/* kinda we need to extend our array */
					snprintf(logString, MAXLOGLINE, "Pushing network %x to new position %d.", newWhiteNet->s_addr, whiteNets.netCount + 1);
					logNow(logString, 3);
					bufNet = realloc(whiteNets.whiteNet, (whiteNets.netCount + 1) * sizeof(struct in_addr));
					if (bufNet == NULL) {
					    fprintf(stderr, "Cannot allocate memory, aborting.\n");
					    exit(1);
					} else {
					    whiteNets.whiteNet = bufNet;
					    /* copying contents of one pointer to another */
					    whiteNets.whiteNet[whiteNets.netCount] = *newWhiteNet;
					}
					snprintf(logString, MAXLOGLINE, "Pushing mask %x to new position %d.", newWhiteMask->s_addr, whiteNets.netCount + 1);;
					logNow(logString, 3);
					bufMask = realloc(whiteNets.whiteMask, (whiteNets.netCount + 1) * sizeof(struct in_addr));
					if (bufMask == NULL) {
					    fprintf(stderr, "Cannot allocate memory, aborting.");
					    exit(1);
					} else {
					    whiteNets.whiteMask = bufMask;
					    /* copying contents of one pointer to another */
					    whiteNets.whiteMask[whiteNets.netCount] = *newWhiteMask;
					}
				    }
				    if (whiteNetworks != NULL) {
					whiteNets.netCount++;
				    }
				} else {
				    printf("Cannot convert %s to network format, aborting.\n", whiteNetwork);
				    exit(1);
				}
			    }
			}
			snprintf(logString, MAXLOGLINE, "Got %d networks in whitelist.", whiteNets.netCount++);
			logNow(logString, 3);
			showWhiteList();
		    }
		}
	    }
	}
	snprintf(logString, MAXLOGLINE, "After config file processing I got following attributes:");
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Socket path, %s.", socketPath);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Pid file path, %s.", pidFilePath);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Milter mode, %s.", callbackMode);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Milter user, %s.", milterUser);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Fake e-mail, %s.", fakeEmail);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Milter e-mail, %s.", milterEmail);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Whitelisted networks, %s.", whitePresNetworks);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Log level, %d.", logLevel);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Graceful DNS, %d.", gracefulDNS);
	logNow(logString, 3);
#ifdef WITH_PGSQL
	snprintf(logString, MAXLOGLINE, "Database host, %s.", milterDBHost);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Database name, %s.", milterDBName);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Database user, %s.", milterDBUser);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Database password, %s.", milterDBPass);
	logNow(logString, 3);
	snprintf(logString, MAXLOGLINE, "Database connections, %d.", connPoolLen);
	logNow(logString, 3);
#endif
	/* releasing white networks string, not needed anymore */
	if (whiteNetworks != NULL) {
    	    free(whiteNetworks);
	}
	fclose(configFile);
    }  else {
	fprintf(stderr, "Cannot open config file %s, aborting.\n", configFilePath);
    }
    
    /* Process command line options */
    while ((c = getopt(argc, argv, args)) != -1) {
        switch (c) {
            case 's':
		if (optarg != NULL && *optarg != '\0') {
		    strncpy(socketPath, optarg, sizeof(socketPath));
                }
		break;
	    case 'm':
		if (optarg != NULL && *optarg != '\0') {
		    strncpy(milterEmail, optarg, sizeof(milterEmail));
                }
		break;
	    case 'u':
		if (optarg != NULL && *optarg != '\0') {
		    strncpy(milterUser, optarg, sizeof(milterUser));
                }
		break;
	    case 'f':
		if (optarg != NULL && *optarg != '\0') {
		    strncpy(fakeEmail, optarg, sizeof(fakeEmail));
                }
		break;
	    case 'v':
		fprintf(stdout, "%s %s %s.\n", mcName, mcVersion, mcCR);
		exit(EXIT_SUCCESS);
		break;
	    case 'c':
		if (optarg != NULL && *optarg != '\0' && (strncmp(optarg, "rfc", 3) || strncmp(optarg, "direct", 6) || strncmp(optarg, "both", 4))) {
		    strncpy(callbackMode, optarg, sizeof(callbackMode));
                }
		break;
	    case 'l':
		if (optarg != NULL && *optarg != '\0') {
		    logLevel = atol(optarg);
                }
		break;
	    case 'D':
		mcDebug = 1;
		break;
            case 't':
                if (optarg == NULL || *optarg == '\0') {
                    fprintf(stderr, "Illegal timeout: %s\n", optarg);
		    exit(EX_USAGE);
		}
		if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) {
		    (void) fprintf(stderr, "smfi_settimeout failed\n");
		    exit(EX_SOFTWARE);
		}
		break;
	    case 'i':
		/* address testing mode */
		privEmu = malloc(sizeof(struct mlfiPriv));
		if (privEmu == NULL) {
		    fprintf(stderr, "Cannot allocate memory, aborting.\n");
		    exit(EX_SOFTWARE);
		} else {
		    privEmu->whitelisted = 0;
		    mcDebug = 1;
		    if (optarg == NULL || *optarg == '\0') {
			exit(EX_USAGE);
		    } else {
			if (inet_pton(AF_INET, optarg, &ipEmu) != 1) {
			    fprintf(stderr, "Cannot convert %s to network format, invalid IP.\n", optarg);
			} else {
			    /* all was good, need some inits */
			    privEmu->relayIP = ipEmu.s_addr;
			    strncpy(privEmu->relayPresIP, optarg, INET_ADDRSTRLEN);
			    if (hostIsWhitelisted(privEmu) == 1) {
				fprintf(stderr, "Testing error.\n");
			    }
			}
		    } 
		    exit(EXIT_SUCCESS);
		    if (privEmu != NULL) {
			free(privEmu);
		    }
		}
		break;
	    case 'h':
	    default:
		usage(argv[0]);
		exit(EX_USAGE);
	}
    }
    snprintf(logString, MAXLOGLINE, "After comandline swithes processing I got following attributes:");
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Socket path, %s.", socketPath);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Pid file path, %s.", pidFilePath);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Milter mode, %s.", callbackMode);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Milter user, %s.", milterUser);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Fake e-mail, %s.", fakeEmail);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Milter e-mail, %s.", milterEmail);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Log level, %d.", logLevel);
    logNow(logString, 3);
    snprintf(logString, MAXLOGLINE, "Graceful DNS, %d.", gracefulDNS);
    logNow(logString, 3);

    bzero(fmSocketPath, sizeof(fmSocketPath));
    strncat(fmSocketPath, scPrefix, strlen(scPrefix));
    strncat(fmSocketPath, socketPath, strlen(socketPath));
    
    /* clearing dead socket, if exists*/
    socketFile = malloc(sizeof(struct stat));
    genErr = lstat(socketPath, socketFile);
    if (genErr != 0) {
	/* some error happened */
	if (errno != ENOENT) {
	    /* some other error than just a nonexistent file happened */
	    fprintf(stderr, "Cannot query the socket at path %s, aborting.\n", socketPath);
	    exit(EX_SOFTWARE);
	} else {
	    /* just a nonexistent file */
	    snprintf(logString, MAXLOGLINE, "File at path %s doesn't seem to exist.", socketPath);
	    logNow(logString, 3);
	}
    } else {
	/* file exists */
	if (S_ISSOCK(socketFile->st_mode) == 0) {
	    /* file exists but it isn't a socket */
	    fprintf(stderr, "File exist at path %s, but it's not a socket, refusing to delete, aborting.\n", socketPath);
	} else {
	    /* file exists and it's a socket */
	    snprintf(logString, MAXLOGLINE, "Flushing dead(is he really ?) socket at path %s.", socketPath);
	    logNow(logString, 3);
	    genErr = unlink(socketPath);
	    if (genErr != 0) {
		fprintf(stderr, "Cannot delete socket at path  %s, aborting.\n", socketPath);
		exit(EX_SOFTWARE);
	    }
	}
    }
    
    if (smfi_setconn(fmSocketPath) == MI_FAILURE) {
        fprintf(stderr, "smfi_setconn failed.\n");
	exit(EX_SOFTWARE);
    } else {
	setconn = TRUE;
    }
    if (!setconn) {
	fprintf(stderr, "Cannot live with socket you specified. Think twice about it.\n");
	exit(EX_USAGE);
    }
    if (smfi_register(smfilter) == MI_FAILURE) {
	fprintf(stderr, "smfi_register failed.\n");
	exit(EX_UNAVAILABLE);
    }
    /* now we can daemonize */
    /* free - at last ! */
    if (mcDebug == 0) {
	snprintf(logString, MAXLOGLINE, "Attempting to go to the land of the daemons...");
	logNow(logString, 3);
    
	pid = fork();
    
	if (pid < 0) {
	    fprintf(stderr, "Cannot fork, aborting.\n");
	    exit(EXIT_FAILURE);
	} else if (pid > 0) {
	    /* creating a pidfile */
	    createPid(pid);
    	    exit(EXIT_SUCCESS);
	}

	umask(0);
     
	sid = setsid();
	if (sid < 0) {
	    snprintf(logString, MAXLOGLINE, "Detaching failed, aborting.");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
    
	if ((chdir("/")) < 0) {
	    snprintf(logString, MAXLOGLINE, "Chrooting failed, aborting.");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
#ifdef WITH_PGSQL
	/* determine the name of this host */
	bzero(&myHostName[0], sizeof(myHostName));
	hostNameVal = gethostname(&myHostName[0], sizeof(myHostName));
	if (hostNameVal != 0) {
	    snprintf(logString, MAXLOGLINE, "Cannot determine hostname, aborting.");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	
	/* now we need to allocate memory for connection pool */
	/* connections */
	connPool.pgConnArr = malloc(sizeof(PGconn *) * connPoolLen);
	if (connPool.pgConnArr == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory for connections pool, aborting.\n");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	/* results */
	connPool.pgResArr = malloc(sizeof(PGresult *) * connPoolLen);
	if (connPool.pgResArr == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory for results pool, aborting.\n");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	/* mutexes */
	connPool.pgMutexArr = malloc(sizeof(pthread_mutex_t *) * connPoolLen);
	if (connPool.pgMutexArr == NULL) {
	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory for mutex pool, aborting.\n");
	    logNow(logString, 0);
	    exit(EXIT_FAILURE);
	}
	/* initializing connection info */
	wantedInfoSize = snprintf(pgConnInfo, 0, "host = %s dbname = %s user = %s password = %s", &milterDBHost[0], &milterDBName[0], &milterDBUser[0], &milterDBPass[0]);
	wantedInfoSize++;
	pgConnInfo = malloc(wantedInfoSize);
	if (pgConnInfo == NULL) {
    	    snprintf(logString, MAXLOGLINE, "Cannot allocate memory for connection, aborting.");
    	    logNow(logString, 0);
    	    exit(EXIT_FAILURE);
	}
	snprintf(pgConnInfo, wantedInfoSize, "host = %s dbname = %s user = %s password = %s", &milterDBHost[0], &milterDBName[0], &milterDBUser[0], &milterDBPass[0]);	
	/* initializing connection pool */
	for (i = 0; i < connPoolLen; i++) {
	    snprintf(logString, MAXLOGLINE, "Trying to make a connection to database.");
	    logNow(logString, 0);
	    /* allocating memory */
	    connPool.pgMutexArr[i] = malloc(sizeof(pthread_mutex_t));
	    
	    if (connPool.pgMutexArr[i] == 0) {
		snprintf(logString, MAXLOGLINE, "Cannot alocate memory for mutex pool element %d, aborting.", i);
		logNow(logString, 0);
		exit(EXIT_FAILURE);
	    }
	    
	    connPool.pgConnArr[i] = PQconnectdb(pgConnInfo);
	    /* verifying that we connected successfully */
	    if (PQstatus(connPool.pgConnArr[i]) != CONNECTION_OK) {
		snprintf(logString, MAXLOGLINE, "Connection %d failed, aborting.", i);
		logNow(logString, 0);
		exit(EXIT_FAILURE);
	    } else {
		snprintf(logString, MAXLOGLINE, "Connection %d successfully established.", i);
		logNow(logString, 0);
	    }
	    /* initializing mutex */
	    minit = pthread_mutex_init(connPool.pgMutexArr[i], NULL);
	    if (minit != 0) {
		snprintf(logString, MAXLOGLINE, "Mutex %d initialization failed, aborting.", i);
		logNow(logString, 0);
		exit(EXIT_FAILURE);
	    } else {
		snprintf(logString, MAXLOGLINE, "Mutex %d successfully initializated.", i);
		logNow(logString, 0);
	    }
	}
	
#endif
    } else {
	/* creating a pidfile */
	createPid(getpid());
	snprintf(logString, MAXLOGLINE, "Running in debug mode, not daemonizing.");
	logNow(logString, 3);
    }
    return smfi_main();
}
/* eof */
