/*-
 * Copyright (c) 2002  Granch Ltd. 	All rigts reserved.
 *
 * All or some portions of this file are derived from material licensed
 * to the Grahcn Ltd. and are reproduced herein with the permission of
 * Granch Ltd.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE GRANCH LTD. AND THEIR CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Author : Rashid N. Achilov		E-Mail shelton@granch.ru
 *
 *	@(#)kavmilter.c			0.91.3 (Granch Ltd.) 	23/01/03
 */
/*------------------------------------------------------------------------

        Kaspersky Anti-Virus mail filter, based on libmilter API

 	UNIX FreeBSD 4.6 version

        Main program.

 	This program based on:
 	LibMilter API sample program (sample.c)
        KAV Sample program (AvpDaemonClient.cpp)

  	Also in general develompent were used sources of:

 	avpmilter.c by Vadim Zotov (general idea about milter API)
  	avp-smf.c by Andrey Shidenko (mailing to postmaster)

  	This program REQUIRED a GNU getopt package. You can install it from
  	/usr/ports/devel/libgnugetopt.

--------------------------------------------------------------------------*/
#include "kavmilter.h"
#include "externals.h"
#include "functions.h"

// Main program section

int main(int argc, char *argv[])
{
  extern char *optarg;

  char c,*p;
  int retcode;
  struct stat st,sb;			// Stat file struct
  FILE *pidf;				// PID file to save

  const char *options = "hvdc:s:D:p:t:m:T:P:C:";
  const char *mustbe = "must be specified";

  static const struct option long_options[] =
   {
     {"help",                   0, 0, 'h' },
     {"version",                0, 0, 'v' },
     {"daemon",                 0, 0, 'd' },
     {"sendmail",               1, 0, 'c' },
     {"KAV-socket",             1, 0, 's' },
     {"debug",                  1, 0, 'D' },
     {"tmpdir",                 1, 0, 'p' },
     {"sendmail-timeout",       1, 0, 't' },
     {"mode",	           	1, 0, 'm' },
     {"KAV-timeout",            1, 0, 'T' },
     {"pidfile",		1, 0, 'P' },
     {"config-file",		1, 0, 'C' },
     { NULL,                    0, 0,  0  }
   };

// Catch-up signal handling

  signal(SIGINT,_KAV_milter_close);
  signal(SIGTERM,_KAV_milter_close);
  signal(SIGHUP,SIG_IGN);

// Store pointer to program name

  p = strrchr(argv[0],'/' );

  if (p == NULL)
    progname = argv[0];
   else
     progname = p + 1;

// Set up default values to flags and parameters

  *((int *) (&_KAV_milter_config._idflags)) = 0;

// Process short or long options

  while ((c = getopt_long(argc, argv, options, long_options, NULL)) != EOF)
   {
     switch (c)
      {
        case 'c': // Sendmail communication socket
         if (*optarg == '\0')
           {
             fprintf(stderr,"sendmail communication socket %s\n",mustbe);
             exit(EX_USAGE);
           }

         mailsocket = optarg;
         break;

        case 's': // KAV control socket
         if (*optarg == '\0')
           {
             fprintf(stderr,"KAV control socket %s\n",mustbe);
             exit(EX_USAGE);
           }

         kavconnect = optarg;
         break;

        case 't': // Sendmail connect timeout
         if (*optarg == '\0')
           {
             fprintf(stderr,"sendmail connect timeout %s\n",mustbe);
             exit(EX_USAGE);
           }

         _timeout = atoi(optarg);
         break;

        case 'T': // KAV connect timeout
         if (*optarg == '\0')
           {
             fprintf(stderr,"KAV connect timeout %s\n",mustbe);
               exit(EX_USAGE);
           }

         _kavtimeout = atoi(optarg);
         break;

        case 'p': // Temp directory
         if (*optarg == '\0')
           {
             fprintf(stderr,"temp directory %s\n",mustbe);
             exit(EX_USAGE);
           }

         tmpdir = optarg;
         break;

        case 'P': // PID file specification
         if (*optarg == '\0')
           {
             fprintf(stderr,"PID file directory and name %s\n",mustbe);
             exit(EX_USAGE);
           }

         pidfile = optarg;
         break;

        case 'C': // Config file specification
         if (*optarg == '\0')
           {
             fprintf(stderr,"Config file name %s\n",mustbe);
             exit(EX_USAGE);
           }

         config = optarg;
         break;

        case 'd': // Daemon mode
         _flags._isdaemon = OK;
         break;

        case 'D': // Debug level
         _debug_level = atoi(optarg);
         break;

        case 'm': // Reject/discard mode
         if (*optarg == '\0' )
           {
             fprintf(stderr,"mode must be specified as \"reject\" or \"discard\"\n");
             exit(EX_USAGE);
           }
            else
              if (!(strcmp(optarg,"reject")))
                {
                  _flags._mode_reject = OK;
                }
               else
                  _flags._mode_discard = OK;
         break;

        case 'v': // Version info requested
         fprintf(stderr,"version: %s\n%s, version %1d.%2d.%1d\n",
             KAV_MILTER,KAV_DATE,VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH);
         exit(EX_USAGE);

        case 'h': // Help requested or unknown parameter was given
        default:
         _KAV_milter_usage();
         exit(EX_USAGE);
      }
   }

// At first, process configuration file. Options from command-line should
// overlap configfile options

  if (!config)
    config = CONFIG;

  _KAV_milter_parse_config(config);

// Check command-line specified parameters and values. When checknig
// sendmail timeout, call appropriate procedure to set it. When timeout in command-line
// greather than zero, store it in parameters structure, else use config value

  if (_timeout)
    _KAV_milter_config.sendmail_timeout = _timeout;

  if (_KAV_milter_config.sendmail_timeout < 0)
    {
     fputs("Sendmail connect timeout must be greather than zero",stderr);
      _KAV_milter_close(EX_USAGE);
    }

  if (smfi_settimeout(_KAV_milter_config.sendmail_timeout) == MI_FAILURE)
    {
      fputs("Set timeout to sendmail connect (smfi_settimeout) failed",stderr);
      _KAV_milter_close(EX_USAGE);
    }

// Check KAV timeout, when set, store it, else use config value

  if (_kavtimeout)
    _KAV_milter_config.KAV_timeout = _kavtimeout;

  if (_KAV_milter_config.KAV_timeout < 0)
    {
      fputs("KAV connect timeout must be greather than zero",stderr);
      _KAV_milter_close(EX_USAGE);
    }

// Check specified temporary directory against exist, directory and
// permissions.

  if (tmpdir)
    _KAV_milter_config.temp_directory = tmpdir;

  if (stat(_KAV_milter_config.temp_directory,&st) == ERR)
    {
      fprintf(stderr,"cannot stat tempopary directory: %s\n",strerror(errno));
      _KAV_milter_close(EX_TEMPFAIL);
   }

  if (!S_ISDIR(st.st_mode))
    {
      fprintf(stderr,"%s is not a directory\n",_KAV_milter_config.temp_directory);
      _KAV_milter_close(EX_TEMPFAIL);
    }

  if (access(_KAV_milter_config.temp_directory, R_OK | W_OK | X_OK) == ERR)
    {
      fprintf(stderr,"wrong permissions on the %s : %s\n",
      			_KAV_milter_config.temp_directory,strerror(errno));
      _KAV_milter_close(EX_TEMPFAIL);
    }

// Store PID file pathname, when requested

  if (pidfile)
    _KAV_milter_config.pidfile = pidfile;

// Check and store debug level value in config structure

  if (_debug_level < 0)
    {
      fprintf(stderr,
      		"Debug level %d incorrect, fallback to config value\n",_debug_level);
      _debug_level = 0;
    }

  if (_debug_level)
    _KAV_milter_config.debug_level = _debug_level;

// Check on presence -P (--pidfile) parameter

  if (pidfile)
    _KAV_milter_config.pidfile = pidfile;

// Check on presence -m (--mode) parameter and store it in config structure

  if (_flags._mode_reject)
    {
      _KAV_milter_config._idflags._mode_reject = OK;
      _KAV_milter_config._idflags._mode_discard = NO;
    }
   else
     if (_flags._mode_discard)
       {
         _KAV_milter_config._idflags._mode_reject = NO;
         _KAV_milter_config._idflags._mode_discard = OK;
       }

// Check on presence -d (--daemon) parameter and store it in config structure

  if (_flags._isdaemon)
    _KAV_milter_config._idflags._isdaemon = OK;

// Check on presence -c (--sendmail) parameter and store it in config structure

  if (mailsocket)
    _KAV_milter_config.sendmail_pipe = mailsocket;

// Check on presence -s (--KAV-socket) paramater and store it in config  structure

  if (kavconnect)
    _KAV_milter_config.KAV_pipe = kavconnect;

// When requested, go into daemon mode

  if (_KAV_milter_config._idflags._isdaemon)
    _KAV_milter_daemon_mode();

// Open system log and send start identification line

  if (!_KAV_milter_config._idflags._isdaemon)
    openlog(progname, LOG_PID | LOG_PERROR, LOG_MAIL);
   else
     openlog(progname, LOG_PID, LOG_MAIL);

  _flags._openlog = OK;
  syslog(LOG_INFO,"starting sendmail filter '%s' version %s",
  	   _KAV_milter_config.progname,_KAV_milter_config.progversion);

// Store PID number of daemonized process

  if (stat(_KAV_milter_config.pidfile,&sb) > NULL)
    {
      unlink(_KAV_milter_config.pidfile);
      syslog(LOG_INFO,"Old PID file was detected - unclean shutdown?");
    }

  if ((pidf = (fopen(_KAV_milter_config.pidfile,"w"))) == NULL)
    {
      syslog(LOG_ERR,"PID file %s cannot open : %m",_KAV_milter_config.pidfile);
      _KAV_milter_close(EX_TEMPFAIL);
    }
   else
     {
       _flags._pidfile = OK;

       if (_KAV_milter_config.debug_level > 30)
         syslog(LOG_ERR,"PID file open, name %s, flag %d",
         			_KAV_milter_config.pidfile,_flags._pidfile);

       fprintf(pidf,"%d",(int) getpid());
       fclose(pidf);
     }

// Set debug level and print startup parameters

  smfi_setdbg(_KAV_milter_config.debug_level);

  if (_KAV_milter_config._idflags._mode_reject)
    syslog(LOG_INFO,"action with infected - REJECT");
   else
     syslog(LOG_INFO,"action with infected - DISCARD");

  if (_KAV_milter_config.debug_level > 10)
    syslog(LOG_INFO,"debug level: %d, sendmail timeout: %d, KAV timeout: %d",
      	    _KAV_milter_config.debug_level, _KAV_milter_config.sendmail_timeout,
      	    					    _KAV_milter_config.KAV_timeout);

// Set reasonable umask

  umask(0027);

// Check and remove, when need old local communication socket file

  if (stat(_KAV_milter_config.sendmail_pipe,&st) != ERR)
    unlink(_KAV_milter_config.sendmail_pipe);

// Set connection port

  if (smfi_setconn(_KAV_milter_config.sendmail_pipe) == MI_FAILURE)
    {
      syslog(LOG_ERR,"Set connection port (smfi_setconn) failed, port %s",_KAV_milter_config.sendmail_pipe);
      _KAV_milter_close(EX_SOFTWARE);
    }

// Register this filter in sendmail space

  if (smfi_register(kavfilter) == MI_FAILURE)
    {
      syslog(LOG_ERR,"kavfilter registration (smfi_register) failed");
      _KAV_milter_close(EX_UNAVAILABLE);
    }

// Main SMFI_ call

  if ((retcode = smfi_main()) != MI_SUCCESS)
    syslog(LOG_ERR,"Main SMFI_ call (smfi_main) failed, return code %d",retcode);

  return retcode;
}


syntax highlighted by Code2HTML, v. 0.9.1