/*****************************************************************************
 * HPT --- FTN NetMail/EchoMail Tosser
 *****************************************************************************
 * Copyright (C) 1997-1999
 *
 * Matthias Tichy
 *
 * Fido:     2:2433/1245 2:2433/1247 2:2432/605.14
 * Internet: mtt@tichy.de
 *
 * Grimmestr. 12         Buchholzer Weg 4
 * 33098 Paderborn       40472 Duesseldorf
 * Germany               Germany
 *
 * This file is part of HPT.
 *
 * HPT 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, or (at your option) any
 * later version.
 *
 * HPT 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 HPT; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************
 * $Id: hpt.c,v 1.170.2.1 2003/12/27 21:42:43 d_sergienko Exp $
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>

#include <smapi/compiler.h>

#ifdef HAS_IO_H
#include <io.h>
#endif

#ifdef HAS_UNISTD_H
#include <unistd.h>
#endif

#if defined(HAS_SYS_SYSEXITS_H)
#include <sys/sysexits.h>
#endif
#if defined(HAS_SYSEXITS_H)
#include <sysexits.h>
#endif

#include <smapi/progprot.h>
#include <smapi/msgapi.h>
#include <smapi/patmat.h>

#include <fidoconf/fidoconf.h>
#include <fidoconf/xstr.h>
#include <fidoconf/common.h>
#include <fidoconf/dirlayer.h>
#include <fidoconf/afixcmd.h>

#include <fidoconf/log.h>
#include <fidoconf/recode.h>
#include <fidoconf/version.h>

#include "version.h"
#include "cvsdate.h"
#include "pkt.h"
#include "global.h"
#include "hpt.h"
#include "toss.h"
#include "scan.h"
#include "fcommon.h"
#include "post.h"
#include "link.h"
#include "areafix.h"
#include "query.h"
#include "stat.h"

#ifdef DO_PERL
#include "hptperl.h"
#endif
#ifdef _MSC_VER
#ifdef DO_PERL
#include "delayimp.h"
/*  This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc} */
#if defined(__cplusplus)
extern "C"
#endif
extern PfnDliHook
#if (_MSC_VER>=1300)
    __pfnDliFailureHook2;
#else
    __pfnDliFailureHook;
#endif
#endif
#endif

s_log         *hpt_log = NULL;
s_message    **msgToSysop = NULL;
s_query_areas *queryAreasHead = NULL;
char *scanParmA;
char *scanParmF;
char force = 0;

/* kn: I've really tried not to break it.
   FIXME: if there is pack and scan options on cmd line - one set
   of options are lost */
int  processExportOptions(int *i, int argc, char **argv)
{
  int rc = 0;
  while ((*i) < argc-1) {
     if (argv[(*i)+1][0] == '-' && (argv[(*i)+1][1] == 'w' || argv[(*i)+1][1] == 'W')) {
        noHighWaters = 1;
        (*i)++;
        continue;
     } /* endif */
     if (argv[(*i)+1][0] == '-' && (argv[(*i)+1][1] == 'f' || argv[(*i)+1][1] == 'F')) {
        if (stricmp(argv[(*i)+1], "-f") == 0) {
           (*i)++;
           scanParmF = argv[(*i)+1];
        } else {
           scanParmF = argv[(*i)+1]+2;
        } /* endif */
        rc |= 2;
        (*i)++;
        continue;
     } else if (argv[(*i)+1][0] == '-' && (argv[(*i)+1][1] == 'a' || argv[(*i)+1][1] == 'A')) {
        if (stricmp(argv[(*i)+1], "-a") == 0) {
           (*i)++;
           scanParmA = argv[(*i)+1];
        } else {
           scanParmA = argv[(*i)+1]+2;
        } /* endif */
        rc |= 4;
        (*i)++;
        continue;
     } /* endif */
     break;
  } /* endwhile */
  return rc != 0 ? rc : 1;
}

void start_help(void) {
  fprintf(stdout,"%s",versionStr);
  fprintf(stdout,"\nUsage: hpt [-c config] [options]\n");
  fprintf(stdout,"   hpt toss - tossing mail\n");
  fprintf(stdout,"   hpt toss -b[f] - tossing mail from badarea [force]\n");
  fprintf(stdout,"   hpt scan - scanning echomail\n");
  fprintf(stdout,"   hpt scan -w - scanning echomail without highwaters\n");
  fprintf(stdout,"   hpt scan -a <pattern> - scanning echomail from areas matching <pattern>\n");
  fprintf(stdout,"   hpt scan -f [filename] - scan only listed areas in this file.\n");
  fprintf(stdout,"   hpt post [options] file - posting a mail (for details run \"hpt post -h\")\n");
  fprintf(stdout,"   hpt pack - packing netmail\n");
  fprintf(stdout,"   hpt pack -a <pattern> - packing netmail from areas matching <pattern>\n");
  fprintf(stdout,"   hpt pack -f [filename] - pack netmail only from areas listed in this file\n");
  fprintf(stdout,"   hpt link [areamask] - links messages\n");
  fprintf(stdout,"   hpt link -j [areamask] - link jam areas using CRC (more quickly)\n");
  fprintf(stdout,"   hpt afix [<addr> command] - process areafix\n");
  fprintf(stdout,"   hpt qupd - update queue file and do some areafix jobs\n");
  fprintf(stdout,"   hpt qrep - make report based on information from queue file\n");
  fprintf(stdout,"   hpt relink <addr> - refresh area subscription\n");
  fprintf(stdout,"   hpt pause - set pause for links who don't poll our system\n");
  fprintf(stdout,"   hpt -q [options] - quiet mode (no screen output)\n");
}

int processCommandLine(int argc, char **argv)
{
   int i = 0;

   if (argc == 1) start_help();

   while (i < argc-1) {
      i++;
      if (0 == stricmp(argv[i], "toss")) {
         cmToss = 1;
         if (i < argc-1) {
	     if (stricmp(argv[i+1], "-b") == 0) {
                cmToss = 2;
                break;
	     } else
	     if (stricmp(argv[i+1], "-bf") == 0) {
                cmToss = 2;
		force = 1;
                break;
	     }
         }
         continue;
      } else if (stricmp(argv[i], "scan") == 0) {
 	 cmScan = processExportOptions(&i, argc, argv);
         continue;
      } else if (stricmp(argv[i], "pack") == 0) {
         cmPack = processExportOptions(&i, argc, argv);
         continue;
      } else if (stricmp(argv[i], "link") == 0) {
		  if (i < argc-1 && stricmp(argv[i+1], "-j") == 0) {
			  i++;
			  linkJamByCRC = 1;
		  }
		  if (i < argc-1) {
			  i++;
			  xstrcat(&linkName,argv[i]);
		  }
		  cmLink = 1;
		  continue;
      } else if (stricmp(argv[i], "afix") == 0) {
		  if (i < argc-1) {
			  i++;
			  string2addr(argv[i], &afixAddr);
			  if (i < argc-1) {
				  i++;
				  xstrcat(&afixCmd,argv[i]);
			  } else printf("parameter missing after \"%s\"!\n", argv[i]);
		  }
		  cmAfix = 1;
		  continue;
      } else if (stricmp(argv[i], "afix!") == 0) {
		  if (i < argc-1) {
			  i++;
			  string2addr(argv[i], &afixAddr);
			  if (i < argc-1) {
				  i++;
				  xstrcat(&afixCmd,argv[i]);
			  } else printf("parameter missing after \"%s\"!\n", argv[i]);
		  }
		  cmAfix = 1;
          cmNotifyLink = 1;
		  continue;
      } else if (stricmp(argv[i], "post") == 0) {
         ++i; post(argc, (unsigned*)&i, argv);
      } else if (stricmp(argv[i], "relink") == 0) {
         i++; relink(argv[i]);
      } else if (stricmp(argv[i], "qupd") == 0) {
          cmQueue |= 2;
      } else if (stricmp(argv[i], "qrep") == 0) {
          cmQueue |= 4;
      } else if (stricmp(argv[i], "-c") == 0) {
		  i++;
		  if (argv[i]!=NULL) xstrcat(&cfgFile, argv[i]);
		  else printf("parameter missing after \"%s\"!\n", argv[i-1]);
		  continue;
      } else if (stricmp(argv[i], "-q") == 0) {
		  quiet = 1;
		  continue;
      } else if (stricmp(argv[i], "-h") == 0) {
		  start_help();
		  return 1;
      } else if (stricmp(argv[i], "pause") == 0) {
		  cmPause = 1;
		  continue;
      } else {
		  printf("Unrecognized commandline option \"%s\"!\n", argv[i]);
		  return EX_USAGE;
	  }

   } /* endwhile */

   return argc;
}

void allDiff(char *nam, char *var, ...)
{
   va_list	ap;
   char	*ptr;

   struct diffCmp {
      char *name;
      char *var;
   } *diffData;

   int	ncmp, i, j;

   for (va_start(ap, var), ncmp = 1; va_arg(ap, char *) != NULL; ) {
      ptr = va_arg(ap, char *); /*  variable may be set ti NULL */
      ncmp++;
   }

   diffData = safe_malloc(ncmp * sizeof(struct diffCmp));

   diffData[0].name = nam;
   diffData[0].var  = var;

   for (va_start(ap, var), i=1; i<ncmp; i++) {
      diffData[i].name = va_arg(ap, char *);
      diffData[i].var  = va_arg(ap, char *);
   }
   ptr = NULL;

   for (i=0; i < ncmp-1; i++) {
      if (diffData[i].var) {
         for (j=i+1; j < ncmp; j++) {
            if (diffData[j].var && strcmp (diffData[i].var, diffData[j].var) == 0) {
               xscatprintf(&ptr, "%s & %s must be different", diffData[i].name, diffData[j].name);
               nfree(diffData);
               exit_hpt(ptr, 1);
            }
         }
      }
   }

   nfree(diffData);
}


void processConfig()
{
   char *buff = NULL;
/* #if !defined(__OS2__) && !defined(UNIX) */
/*    time_t   time_cur, locklife = 0; */
/*    struct   stat stat_file; */
/* #endif */
/*    unsigned long pid; */
/*    FILE *f; */

   setvar("module", "hpt");
   xscatprintf(&buff, "%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
   setvar("version", buff);
   nfree(buff);
   SetAppModule(M_HPT);
   config = readConfig(cfgFile);
   if (NULL == config) {
       nfree(cfgFile);
       fprintf(stderr,"Config not found\n");
       exit(EX_UNAVAILABLE);
   }

#if 0
   /*  lock... */
   if (config->lockfile!=NULL && fexist(config->lockfile)) {
	   if ((f = fopen(config->lockfile, "rt"))==NULL) {
		   fprintf(stderr,"Can't open file: \"%s\"\n",config->lockfile);
		   exit_hpt("Can't open lock-file",0);
	   }
	   fscanf(f, "%lu\n", &pid);
	   fclose(f);
	   /*  Checking process PID */
#ifdef __OS2__
      if (DosKillProcess(DKP_PROCESSTREE, pid) == ERROR_NOT_DESCENDANT) {
#elif UNIX
      if (kill(pid, 0) == 0) {
#else
      if (stat(config->lockfile, &stat_file) != -1) {
          time_cur = time(NULL);
	  locklife = (time_cur - stat_file.st_mtime)/60;
      }
      if (locklife < 180) {
#endif
           fprintf(stderr,"lock file found! exit...\n");
           disposeConfig(config);
           exit(1);
      } else {
         remove(config->lockfile);
         createLockFile(config->lockfile);
      }
   }
   else if (config->lockfile!=NULL) createLockFile(config->lockfile);
#endif

   if (config->lockfile) {
       lock_fd = lockFile(config->lockfile, config->advisoryLock);
       if( lock_fd < 0 )
       {
           disposeConfig(config);
           exit(EX_CANTCREAT);
       }
   }

   /*  open Logfile */
   if (config->logFileDir) {
	xstrscat(&buff, config->logFileDir, LogFileName, NULL);
	hpt_log = openLog(buff, versionStr, config);
	if (hpt_log && quiet) hpt_log->logEcho=0; /* Don't display messages */
	nfree(buff);
   } else
     fprintf(stderr,"logFileDir not defined, there will be no log created!\n");

   if (config->addrCount == 0) exit_hpt("at least one addr must be defined",1);
   if (config->linkCount == 0) exit_hpt("at least one link must be specified",1);
   if (config->outbound == NULL) exit_hpt("you must set outbound in fidoconfig first",1);
   if (config->tempOutbound == NULL) exit_hpt("you must set tempOutbound in fidoconfig first",1);
   if (config->inbound == NULL && config->protInbound == NULL)
	   exit_hpt("you must set Inbound or protInbound in fidoconfig first",1);
   if (config->tempInbound == NULL) exit_hpt("you must set tempInbound in fidoconfig first",1);
   if (config->msgBaseDir==NULL) exit_hpt("No msgBaseDir specified in config file!",1);
   if (config->dupeHistoryDir==NULL) exit_hpt("No dupeHistoryDir specified in config file!",1);
   if (config->dupeArea.areaName==NULL) exit_hpt("you must define DupeArea!",1);
   if (config->dupeArea.fileName==NULL) exit_hpt("DupeArea can not be passthrough!",1);
   if (config->badArea.areaName==NULL) exit_hpt("you must define BadArea!",1);
   if (config->badArea.fileName==NULL) exit_hpt("BadArea can not be passthrough!",1);

   if (config->netMailAreaCount>0) {
     if (config->netMailAreas[0].fileName==NULL) exit_hpt("First NetmailArea can not be passthrough!",1);
   }
   else exit_hpt("You must define NetmailArea!",1);

   allDiff ( "Inbound",      config->inbound,
			 "tempInbound",  config->tempInbound,
			 "protInbound",  config->protInbound,
			 "localInbound", config->localInbound,
			 "outbound",     config->outbound,
			 "tempOutbound", config->tempOutbound,
			 NULL);

   /*  load recoding tables */
   initCharsets();
   if (config->outtab) getctab(outtab, (unsigned char*) config->outtab);
   if (config->intab) getctab(intab, (unsigned char*) config->intab);
   w_log(LL_START, "Start");
}

int isFreeSpace(char *path) {
	unsigned long sp;

	sp = fc_GetDiskFreeSpace(path);	
	if (sp < config->minDiskFreeSpace*1024) {
		fprintf(stderr, "no free space in %s! (needed %d mb, available %d mb).\n",
				path, config->minDiskFreeSpace, (unsigned)(sp/1024));
		exit_hpt("no free disk space!",0);
	}

	return 0;
}


#ifdef _MSC_VER
#ifdef DO_PERL
FARPROC WINAPI ourhook(unsigned dliNotify,PDelayLoadInfo pdli)
{
  /* print error message and exit */
  char msg[128];
  memset(msg,0,sizeof(msg));
  sprintf(msg,"Loading of %s failed - exiting ",pdli->szDll);
  w_log(LL_CRIT,msg);
  /* standart deinit sequence */
  /*  deinit SMAPI */
  MsgCloseApi();
  w_log(LL_STOP, "End");

  closeLog(hpt_log);
  doneCharsets();
  nfree(versionStr);
  if (config->lockfile) {
	   close(lock_fd);
	   remove(config->lockfile);
  }

  disposeConfig(config);
  nfree(cfgFile);
  exit(EX_UNAVAILABLE);
   return 0;
}
#endif
#endif

int main(int argc, char **argv)
{
   struct _minf m;
   unsigned int i, rc;
#if defined ( __NT__ )
   #define TITLESIZE 256
   char *title=NULL, oldtitle[ TITLESIZE ];
#endif

   versionStr = GenVersionStr( "hpt", VER_MAJOR, VER_MINOR, VER_PATCH,
                               VER_BRANCH, cvs_date );

   rc = processCommandLine(argc, argv);
   if ((rc==1 || rc==EX_USAGE) && config!=NULL)
      if (config->lockfile) {
          close(lock_fd);
          remove(config->lockfile);
          disposeConfig(config);
          doneCharsets();
      }
   if (rc==1){ nfree(versionStr); exit(EX_OK); }
   if (rc==EX_USAGE){ nfree(versionStr); exit(EX_USAGE); }

   if (config==NULL) processConfig();

#if defined ( __NT__ )
   if (config->setConsoleTitle) {
     GetConsoleTitleA( oldtitle, TITLESIZE );
     if( versionStr ){
       xstrscat( &title, "Highly Portable Tosser ", versionStr+4, NULL); /* versionStr is "hpt ..." */
       SetConsoleTitleA( title );
       nfree(title);
     }else
       SetConsoleTitleA( "Highly Portable Tosser" );
   }
#endif

   /*  check for free space */
   if (config->minDiskFreeSpace) {
	   isFreeSpace(config->tempInbound);
	   if (stricmp(config->msgBaseDir,"passthrough")!=0)
		   isFreeSpace(config->msgBaseDir);
	   for (i=0; i<config->linkCount; i++) {
		   if (config->links[i].msgBaseDir &&
			   stricmp(config->links[i].msgBaseDir,"passthrough")!=0)
			   isFreeSpace(config->links[i].msgBaseDir);
	   }
   }

   if ( initSMAPI == -1 ) {
       /*  init SMAPI */
       initSMAPI = 0;
       m.req_version = 2;
       m.def_zone = (UINT16) config->addr[0].zone;
       if (MsgOpenApi(&m) != 0) {
	   exit_hpt("MsgApiOpen Error",1);
       }
   }
#ifdef _MSC_VER
#ifdef DO_PERL
#if _MSC_VER>=1300
   __pfnDliFailureHook2=ourhook;
#else
   __pfnDliFailureHook=ourhook;
#endif
   /* attempt to start Perl */
   PerlStart();
#endif
#endif
   msgToSysop = (s_message**) safe_malloc(config->addrCount * sizeof(s_message*));
   for (i = 0; i < config->addrCount; i++) {
	
       /* Some results of wrong patching ? A memleak anyway
	* msgToSysop[i] = (s_message*)malloc(sizeof(s_message));
	*/
       msgToSysop[i] = NULL;
   }

   tossTempOutbound(config->tempOutbound);

   if (1 == cmToss) toss();
   if (cmToss == 2) tossFromBadArea(force);

   if (cmScan == 1) scanExport(SCN_ALL  | SCN_ECHOMAIL, NULL);
   if (cmScan &  2) scanExport(SCN_FILE | SCN_ECHOMAIL, scanParmF);
   if ((cmScan &  4) && scanParmA) scanExport(SCN_NAME | SCN_ECHOMAIL, scanParmA);

   if (cmAfix == 1) afix(afixAddr, afixCmd);
   nfree(afixCmd);

   if (cmPack == 1) scanExport(SCN_ALL  | SCN_NETMAIL, NULL);
   if (cmPack &  2) scanExport(SCN_FILE | SCN_NETMAIL, scanParmF);
   if ((cmPack &  4) && scanParmA) scanExport(SCN_NAME | SCN_NETMAIL, scanParmA);

   if (cmLink == 1) {
	   if (linkName && (strstr(linkName,"*")||strstr(linkName,"?"))) {
		   for (i=0; i < config->echoAreaCount; i++)
			   if (patimat(config->echoAreas[i].areaName, linkName))
				   linkAreas(config->echoAreas[i].areaName);
		   for (i=0; i < config->localAreaCount; i++)
			   if (patimat(config->localAreas[i].areaName, linkName))
				   linkAreas(config->localAreas[i].areaName);
		   for (i=0; i < config->netMailAreaCount; i++)
			   if (patimat(config->netMailAreas[i].areaName, linkName))
				   linkAreas(config->netMailAreas[i].areaName);
	   } else linkAreas(linkName);
   }
   nfree(linkName);
#ifdef DO_PERL
   perldone();
#endif

   writeMsgToSysop();

   for (i = 0; i < config->addrCount; i++) {
       if (msgToSysop[i]) freeMsgBuffers(msgToSysop[i]);
       nfree(msgToSysop[i]);
   }
   if (cmPause || config->autoPassive) autoPassive();

   if (cmQueue &  2) af_QueueUpdate();
   if (cmQueue &  4) af_QueueReport();

   /*  save forward requests info */
   af_CloseQuery();
   nfree(msgToSysop);

#ifdef ADV_STAT
   if (config->advStatisticsFile != NULL) upd_stat(config->advStatisticsFile);
#endif

   /*  deinit SMAPI */
   MsgCloseApi();

   w_log(LL_STOP, "End");
   closeLog();
   doneCharsets();
   nfree(versionStr);

   if (config->lockfile) {
       FreelockFile(config->lockfile ,lock_fd);
   }

#if defined ( __NT__ )
   if (config->setConsoleTitle) SetConsoleTitleA(oldtitle);
#endif
   disposeConfig(config);
   nfree(cfgFile);

   return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1