#include "policyd.h"


/*
 *
 *
 *                           Policy Daemon
 *
 *  policy daemon is used in conjuction with postfix to combat spam.
 *
 *  Copyright (C) 2004 Cami Sardinha (cami@mweb.co.za)
 *
 *
 *  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 2 of the License, or (at your
 *  option) any later version.
 *
 *  This program  is  distributed  in the hope that  it will be useful, but
 *  WITHOUT  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, write to the Free  Software Foundation Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *
 */


/*
 * function: greylist_check
 *  purpose: check if triplet exists in mysql
 *   return: 0 for new record, 1 for update
 */
int
greylist_check(unsigned int fd)
{

  if(DEBUG > 0)
    logmessage("DEBUG: fd: %d checking greylist\n", fd);
  
  /* set sane defaults */
  mysql_array[fd][0] = -2;
  mysql_optarray[fd][0] = OPTINOUTALL;
  
  /* opt-in/opt-out? */
  if(OPTINOUT == 1)
  {

    /* build up & execute query */
    snprintf(mysqlquery_array[fd], 512,
      "SELECT _optin FROM policy WHERE _rcpt='%s' OR _rcpt='@%s' ORDER BY _priority DESC LIMIT 1",
      triplet_array[fd][2], host_array[fd][9]);
    if(db_optquery(fd) == -1) return(db_failure(fd, "greylist"));

    /* user is opted out */
    if(mysql_optarray[fd][0] == 0)
    {
      logmessage("rcpt=%lu, greylist=optout, host=%s (%s), from=%s, to=%s, size=%s\n",
        rcpt_count,                       /* recipient count      */
        host_array[fd][2],                /* host address         */
        host_array[fd][0],                /* hostname             */
        triplet_array[fd][1],             /* sender               */
        triplet_array[fd][2],             /* recipient            */
        triplet_array[fd][3]);            /* size                 */

      return (0);
    }
  }


  /* domain or mail address is in training mode? */
  if(TRAINING_POLICY_TIMEOUT != 0)
  {
    /* build up & execute query */
    snprintf(mysqlquery_array[fd], 512,
      "SELECT COUNT(*) FROM policy_training WHERE _rcpt='%s' OR _rcpt='@%s'",
      triplet_array[fd][2], host_array[fd][9]);
    if(db_optquery(fd) == -1) return(db_failure(fd, "greylist"));

    /* training policy is activated for domain or email address */
    if(mysql_optarray[fd][0] >= 1)
      mysql_optarray[fd][0] = 2;
  }


  /* build up & execute query */
  snprintf(mysqlquery_array[fd], 512,
    "SELECT _count,_datenew,_datelast FROM triplet WHERE _host='%s' AND _from='%s' AND _rcpt='%s'",
      triplet_array[fd][0], triplet_array[fd][1], triplet_array[fd][2]);
  if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

  /* update the greylist xheader */
  if(GREYLIST_X_HEADER==1)
    snprintf(xgreylist_array[fd], 128, "%s host: %s count: %d size: %s\n\n",
      POSTFIX_X_HEADER, host_array[fd][2], mysql_array[fd][0], triplet_array[fd][3]);

  
  /* triplet not found in greylist database */
  if(mysql_array[fd][0]==-2)
  {
    /* build up & execute query */
    snprintf(mysqlquery_array[fd], 512,
      "INSERT DELAYED INTO triplet (_datenew,_datelast,_host,_from,_rcpt) VALUES (%d,%d,'%s','%s','%s')",
      timenow, timenow, triplet_array[fd][0], triplet_array[fd][1], triplet_array[fd][2]);
    if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

    /* auto black listing is enabled */
    if(AUTO_BLACK_LISTING == 1)
    {
      /* perform query to see how many triplets there are for a host/network */
      /* build up & execute query */
      snprintf(mysqlquery_array[fd], 512,
        "SELECT COUNT(*) FROM triplet WHERE _host='%s' AND _count = 0",
          triplet_array[fd][0]);
      if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

      if(DEBUG > 0)
        logmessage("DEBUG: fd: %d unauth triplet: %d\n", fd, mysql_array[fd][0]);

      /* host has more than allowed number of unauthenticated triplets */
      if(mysql_array[fd][0] >= AUTO_BLACKLIST_NUMBER)
      {
        int expire=0;
      
        /* never auto expire blacklist? */
        if(AUTO_BLACKLIST_EXPIRE > 0)
          expire=timenow+AUTO_BLACKLIST_EXPIRE;

        /* blacklist netblock /24 */
        if(BLACKLIST_NETBLOCK==1)
        { /* blacklist netblock */
          snprintf(mysqlquery_array[fd], 512,
            "INSERT DELAYED INTO blacklist (_blacklist,_description,_expire) VALUES ('%s.%%','# autoblacklisted', %d)",
             triplet_array[fd][0], expire);
        } else { /* blacklist host ip */
          snprintf(mysqlquery_array[fd], 512,
            "INSERT DELAYED INTO blacklist (_blacklist,_description,_expire) VALUES ('%s','# autoblacklisted',%d)",
             host_array[fd][2], expire);
        }
        /* execute query */
        if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));
      
        logmessage("rcpt=%lu, greylist=abl, host=%s (%s), from=%s, to=%s, size=%s, expire=%d\n",
          rcpt_count,                       /* recipient count      */
          host_array[fd][2],                /* host address         */
          host_array[fd][0],                /* hostname             */
          triplet_array[fd][1],             /* sender               */
          triplet_array[fd][2],             /* recipient            */
          triplet_array[fd][3],             /* size                 */
          expire);                          /* expiry date          */

        /* build up & execute query */
        snprintf(mysqlquery_array[fd], 512,
          "DELETE QUICK from triplet WHERE _host='%s'", triplet_array[fd][0]);
        if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

        /* reject */
        return (-1);
      }
    }
      

    /* not in training mode, reject if this is the first attempt */
    if((TRAINING_MODE == 0) && (mysql_optarray[fd][0] != 2))
    {
      logmessage("rcpt=%lu, greylist=new, host=%s (%s), from=%s, to=%s, size=%s\n",
        rcpt_count,                       /* recipient count      */
        host_array[fd][2],                /* host address         */
        host_array[fd][0],                /* hostname             */
        triplet_array[fd][1],             /* sender               */
        triplet_array[fd][2],             /* recipient            */
        triplet_array[fd][3]);            /* size                 */
      
      /* reject */
      return (-1);
    }
    
    /* in training mode, always accept */
    if((TRAINING_MODE == 1) || (mysql_optarray[fd][0] == 2))
    {
      logmessage("rcpt=%lu, greylist=new_train, host=%s (%s), from=%s, to=%s, size=%s\n",
        rcpt_count,
        host_array[fd][2],      /* host */
        host_array[fd][0],      /* hostname */
        triplet_array[fd][1],   /* from */
        triplet_array[fd][2],   /* rcpt */
        triplet_array[fd][3]    /* size */
      );

      /* accept */
      return (0);
    }
    
  } else { /* triplet exists in database */
    
    /* has TRIPLET_TIME expired since triplet creation? */
    if(timenow < (unsigned int)(mysql_array[fd][1]+TRIPLET_TIME))
    {
      /* not in training mode */
      if((TRAINING_MODE == 0) && (mysql_optarray[fd][0] != 2))
      {
        logmessage("rcpt=%lu, greylist=abuse, host=%s (%s), from=%s, to=%s, size=%s\n", 
          rcpt_count,                       /* recipient count      */
          host_array[fd][2],                /* host address         */
          host_array[fd][0],                /* hostname             */
          triplet_array[fd][1],             /* sender               */
          triplet_array[fd][2],             /* recipient            */
          triplet_array[fd][3]);            /* size                 */

        return (-1);
      }
    }
    
    
    /* implement autowhitelisting */
    if(AUTO_WHITE_LISTING==1) 
    {
      /* expire auto-whitelisted hosts if enabled */
      int expire=0;
      if(AUTO_WHITELIST_EXPIRE > 0)
        expire=timenow+AUTO_WHITELIST_EXPIRE;

      /* save an sql lookup if awl == 1 */
      if(AUTO_WHITELIST_NUMBER == 1)
        goto awl;

      /* check how many auth triplets there are for a host/network */
      /* build up & execute query */
      snprintf(mysqlquery_array[fd], 512,
        "SELECT COUNT(*) FROM triplet WHERE _host='%s' AND _count > 0",
          triplet_array[fd][0]);
      if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

      if(DEBUG > 0)
        logmessage("DEBUG: fd: %d whitelist result: %d\n", fd, mysql_array[fd][0]);

      if(mysql_array[fd][0] >= AUTO_WHITELIST_NUMBER)
        goto awl;       /* auto whitelist network/host */
      else
        goto nawl;      /* dont auto white list network */

awl:

      if(AUTO_WHITELIST_NETBLOCK==1)
      {
        /* build up & execute query */
        snprintf(mysqlquery_array[fd], 512,
          "INSERT DELAYED INTO whitelist (_whitelist,_description,_expire) VALUES ('%s.%%','# autowhitelisted host',%d)",
          triplet_array[fd][0], expire);
      } else {
        /* build up & execute query */
        snprintf(mysqlquery_array[fd], 512,
          "INSERT DELAYED INTO whitelist (_whitelist,_description,_expire) VALUES ('%s','# autowhitelisted host',%d)",
          host_array[fd][2], expire);
      }
      if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

      /* build up & execute query */
      snprintf(mysqlquery_array[fd], 512,
        "DELETE QUICK from triplet WHERE _host='%s'", triplet_array[fd][0]);
      if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));

      logmessage("rcpt=%lu, greylist=awl, host=%s (%s), from=%s, to=%s, size=%s, expire=%d\n",
        rcpt_count,                       /* recipient count      */
        host_array[fd][2],                /* host address         */
        host_array[fd][0],                /* hostname             */
        triplet_array[fd][1],             /* sender               */
        triplet_array[fd][2],             /* recipient            */
        triplet_array[fd][3],             /* size                 */
        expire);                          /* expiry date          */

      return(0);
    }
    
nawl:

    /* build up & execute query */
    snprintf(mysqlquery_array[fd], 512,
      "UPDATE triplet SET _datelast='%d',_count=_count+1 WHERE _host='%s' AND _from='%s' AND _rcpt='%s'",
      timenow, triplet_array[fd][0], triplet_array[fd][1], triplet_array[fd][2]);
    if(db_doquery(fd) == -1) return(db_failure(fd, "greylist"));
    
    /* training mode */
    if((TRAINING_MODE == 0) && (mysql_optarray[fd][0] != 2))
    {
      /* yes, it has.. update */
      logmessage("rcpt=%lu, greylist=update, host=%s (%s), from=%s, to=%s, size=%s\n",
        rcpt_count,                       /* recipient count      */
        host_array[fd][2],                /* host address         */
        host_array[fd][0],                /* hostname             */
        triplet_array[fd][1],             /* sender               */
        triplet_array[fd][2],             /* recipient            */
        triplet_array[fd][3]);            /* size                 */
    }
    
    /* training mode */
    if((TRAINING_MODE == 1) || (mysql_optarray[fd][0] == 2))
    {
      /* yes, it has.. update */
      logmessage("rcpt=%lu, greylist=update_train, host=%s (%s), from=%s, to=%s, size=%s\n",
        rcpt_count,                       /* recipient count      */
        host_array[fd][2],                /* host address         */
        host_array[fd][0],                /* hostname             */
        triplet_array[fd][1],             /* sender               */
        triplet_array[fd][2],             /* recipient            */
        triplet_array[fd][3]);            /* size                 */
    }

    return (0);
  }

  return (0); /* never reached */
}
 
/* EOF */
