/*****************************************************************************
 * 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: fcommon.c,v 1.161.2.4 2004/01/11 22:42:01 d_sergienko Exp $
 */
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>


#include <smapi/compiler.h>

#ifdef HAS_PROCESS_H
#  include <process.h>
#endif

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

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

#ifdef HAS_SYS_SYSEXITS_H
#include <sys/sysexits.h>
#endif
#ifdef HAS_SYSEXITS_H
#include <sysexits.h>
#endif

#ifdef HAS_DOS_H
#include <dos.h>
#endif

#if defined(__TURBOC__) || defined(__IBMC__)

#if !defined(S_ISDIR)
#define S_ISDIR(a) (((a) & S_IFDIR) != 0)
#endif

#endif

#include <smapi/typedefs.h>
#include <smapi/stamp.h>
#include <smapi/progprot.h>

#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <fidoconf/dirlayer.h>
#include <fidoconf/recode.h>
#include <fidoconf/crc.h>

#include <global.h>
#include <fcommon.h>
#include <dupe.h>

void writeDupeFiles(void)
{
	unsigned i;

	/*  write dupeFiles */
	for (i = 0 ; i < config->echoAreaCount; i++) {
		writeToDupeFile(&(config->echoAreas[i]));
		freeDupeMemory(&(config->echoAreas[i]));
	}
	for (i = 0 ; i < config->netMailAreaCount; i++) {
		writeToDupeFile(&(config->netMailAreas[i]));
		freeDupeMemory(&(config->netMailAreas[i]));
	}
}

void exit_hpt(char *logstr, int print) {

    w_log(LL_FUNC,"exit_hpt()");
    w_log(LL_CRIT, logstr);
    if (!config->logEchoToScreen && print) fprintf(stderr, "%s\n", logstr);

    writeDupeFiles();
    doneCharsets();
    w_log(LL_STOP, "Exit");
    closeLog();
    if (config->lockfile) {
       FreelockFile(config->lockfile ,lock_fd);
    }
    disposeConfig(config);
    
    exit(EX_SOFTWARE);
}

/* this function has no calls
int createLockFile(char *lockfile) {
        int fd;
        char *pidstr = NULL;

        w_log(LL_FUNC,"createLockFile()");
        if ( (fd=open(lockfile, O_CREAT | O_RDWR | O_EXCL, S_IREAD | S_IWRITE)) < 0 )
           {
                   fprintf(stderr,"createLockFile: cannot create lock file\"%s\"\n",lockfile);
                   w_log(LL_ERR, "createLockFile: cannot create lock file \"%s\"m", lockfile);
                   return 1;
           }

        xscatprintf(&pidstr, "%u\n", (unsigned)getpid());
        write (fd, pidstr, strlen(pidstr));

        close(fd);
	nfree(pidstr);
        w_log(LL_FUNC,"createLockFile() OK");
        return 0;
}
*/
/*
e_prio cvtFlavour2Prio(e_flavour flavour)
{
   switch (flavour) {
      case hold:      return HOLD;
      case normal:    return NORMAL;
      case direct:    return DIRECT;
      case crash:     return CRASH;
      case immediate: return IMMEDIATE;
      default:        return NORMAL;
   }
   return NORMAL;
}
*/
#if 1
/* This old code will be removed once the new one proves to be reliable */

int fileNameAlreadyUsed(char *pktName, char *packName) {
   UINT i;

   for (i=0; i < config->linkCount; i++) {
      if ((config->links[i].pktFile != NULL) && (pktName != NULL))
         if ((stricmp(pktName, config->links[i].pktFile)==0)) return 1;
      if ((config->links[i].packFile != NULL) && (packName != NULL))
         if ((stricmp(packName, config->links[i].packFile)==0)) return 1;
   }

   return 0;
}


static char *wdays[7]={ "su", "mo", "tu", "we", "th", "fr", "sa" };

void cleanEmptyBundles(char *pathName, size_t npos, char *wday)
/*  Removing old empty bundles when bundleNameStyle == addDiff */
{
   char           *ptr, *tmpfile, *pattern, savech;
   DIR            *dir;
   struct dirent  *file;
   struct stat stbuf;
   unsigned pathlen;

   if( npos >= strlen(pathName) )
   { w_log( LL_CRIT, "fcommon.c:cleanEmptyBundles(): 'npos' too big! Can't work." );
     return;
   }
   pathlen = strlen(pathName) + 4;
   tmpfile = safe_malloc(pathlen);

   strcpy(tmpfile, pathName);
   savech = tmpfile[npos-1]; /*  there must be path delimiter */
   tmpfile[npos-1] = '\0';

   if(!(dir = opendir(tmpfile))) { /*  nothing to clean */
      nfree(tmpfile);
      return;
   }

   tmpfile[npos-1] = savech;

   pattern = safe_malloc(strlen(tmpfile+npos) + 4);
   strcpy(pattern, tmpfile+npos);

   for (ptr=pattern; *ptr; ptr++);
   ptr[0]='*';
   ptr[1]='\0';

   while ((file = readdir(dir)) != NULL) {

	   if ( patimat(file->d_name, pattern) == 1 &&
	        strncasecmp(file->d_name+(ptr-pattern), wday, 2) != 0 ) {

		   strcpy(tmpfile+npos, file->d_name);

		   if (stat(tmpfile, &stbuf) == 0 && stbuf.st_size == 0) {

				   remove (tmpfile); /*  old empty bundle */
		   }
	   }
   }

   closedir(dir);
   nfree(pattern);
   nfree(tmpfile);
}

/* old algorythm, use it if used didn't set SeqDir */
int createTempPktFileName_legasy(s_link *link)
{
    char *fileName=NULL; /*  pkt file in tempOutbound */
    time_t aTime;
    int counter;

    /* dmitry: prevent generation of the same name if hpt is being run
               several times in the same second */
    if (pkt_aTime == 0) {
        sleep(1);
    }

    aTime = time(NULL);  /* get actual time */
    counter = pkt_count;

    aTime %= 0xffffff;   /* only last 24 bit count */

    /* Making pkt name */
    while(1) {
		do {
			nfree(fileName);
			xscatprintf(&fileName, "%s%06lx%02x.pkt",
						config->tempOutbound, (long)aTime, counter);
			counter++;
			
		} while ((fexist(fileName) || fileNameAlreadyUsed(fileName, NULL)) &&
				 (counter<=255));

		if (counter<=256) break;
		else {
			counter=0;
			if (pkt_aTime==aTime) {
				sleep(1);
				aTime = time(NULL);
				aTime %= 0xffffff;
			}
		}
    }
    pkt_count = counter;
    pkt_aTime = aTime;

    nfree(link->pktFile);
    link->pktFile = fileName;
    w_log(LL_CREAT,"pktFile %s created for [%s]",
          link->pktFile,
          aka2str(link->hisAka));
    return 0;
}

int createTempPktFileName(s_link *link)
{
    char *fileName=NULL; /*  pkt file in tempOutbound */

    if (config->seqDir == NULL)
        return createTempPktFileName_legasy(link);

    /* Making pkt name */
    do {
        nfree(fileName);
        xscatprintf(&fileName, "%s%08x.pkt",
                    config->tempOutbound,
                    GenMsgId(config->seqDir, config->seqOutrun)
                   );
    } while (fexist(fileName) || fileNameAlreadyUsed(fileName, NULL));

    nfree(link->pktFile);
    link->pktFile = fileName;
    w_log(LL_CREAT,"pktFile %s created for [%s]",
          link->pktFile,
          aka2str(link->hisAka));
    return 0;
}

int createPackFileName(s_link *link)
{
    char *pfileName=NULL; /*  name of the arcmail bundle */
    char *tmp=NULL; /*  temp name of the arcmail bundle */
    char *tmp2=NULL;    /* temp string */
    int  minFreeExt;
    size_t npos;
    char limiter=PATH_DELIM;
    time_t tr, aTime;
    char *wday;
    struct tm *tp;
    int i;
    struct stat stbuf;
    static char ext3[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    int numExt = sizeof(ext3)-1;
    int counter;
    hs_addr *aka;
    char *str_hisAka, *str_Aka;
    e_bundleFileNameStyle bundleNameStyle = eTimeStamp;

    tr=aTime=time(NULL);
    aTime %= 0xffffff;
    tp=localtime(&tr);
    counter = pkt_count;

    wday=wdays[tp->tm_wday];

    aka = SelectPackAka(link);

    if (link->linkBundleNameStyle!=eUndef) bundleNameStyle=link->linkBundleNameStyle;
    else if (config->bundleNameStyle!=eUndef) bundleNameStyle=config->bundleNameStyle;
	
    /*  fileBoxes support */
    if (needUseFileBoxForLinkAka(config,link,aka)) {
	if (!link->fileBox) link->fileBox = makeFileBoxNameAka (config,link,aka);
	xstrcat(&tmp, link->fileBox);
	_createDirectoryTree (tmp);
    } else {
	xstrcat(&tmp, config->outbound);

	/*  add suffix for other zones */
	if (aka->zone != config->addr[0].zone && bundleNameStyle != eAmiga) {
		tmp[strlen(tmp)-1]='\0';
		xscatprintf(&tmp, ".%03x%c", aka->zone, limiter);
	}

	/*  path to bundle */
	if (bundleNameStyle!=eAmiga) {
		if (aka->point)
			xscatprintf(&tmp, "%04x%04x.pnt%c",
						aka->net, aka->node, limiter);

		/*  separate bundles */
		if (config->separateBundles) {
			if (aka->point) xscatprintf(&tmp, "%08x.sep%c",
							aka->point, limiter);
			else xscatprintf(&tmp, "%04x%04x.sep%c", aka->net,
							aka->node, limiter);
		}
	}

    } /*  link->fileBox */

    npos = strlen(tmp);

    /* bundle file name */
    switch ( bundleNameStyle ) {

    case eAddrsCRC32:
    case eAddrsCRC32Always:
    case eAddrDiff:
    case eAddrDiffAlways:
                if( bundleNameStyle == eAddrsCRC32 ||
                    bundleNameStyle == eAddrsCRC32Always ) {
                  xscatprintf( &tmp2, "hpt %s ", aka2str(config->addr[0]) );
  	          xstrcat( &tmp2, aka2str(*aka) );
                  xscatprintf(&tmp,"%08x.", strcrc32(tmp2,0xFFFFFFFFUL) );
                  nfree(tmp2);
                } else {
                  if ( aka->point == 0 && config->addr[0].point == 0) {
                    xscatprintf (&tmp, "%04hx%04hx.",
                                 config->addr[0].net - aka->net,
                                 config->addr[0].node - aka->node);
                  } else {
                    xscatprintf (&tmp, "%04hx%04hx.",
                                 config->addr[0].node - aka->node,
                                 config->addr[0].point- aka->point);
                  }
                }
                w_log(LL_FILENAME, "bundle name generating: %s", tmp);

    case eAmiga:
		if (bundleNameStyle==eAmiga) {
			xscatprintf (&tmp, "%u.%u.%u.%u.",
						 aka->zone, aka->net,
						 aka->node, aka->point);
		}

		if (!needUseFileBoxForLinkAka(config,link,aka)) cleanEmptyBundles(tmp,npos,wday);

		counter = 0;
		minFreeExt = -1;
		for (i=0; i<numExt; i++) {

			xstrscat(&pfileName, tmp, wday, NULL);
			xscatprintf(&pfileName, "%c", ext3[i]);
			
			if (stat(pfileName, &stbuf) == 0) {

				if (tr - stbuf.st_mtime < 60*60*48) {
					/*  today's bundle */
					counter = i+1;
					if (stbuf.st_size==0 && (counter<numExt ||
      					    bundleNameStyle==eAddrDiffAlways ||
      					    bundleNameStyle==eAddrsCRC32Always ||
      					    bundleNameStyle==eAmiga))
					   remove (pfileName);
				} else {
					/*  old bundle */
					if (stbuf.st_size == 0)	remove (pfileName);
					else counter = i+1;
				}
			} else if (errno==ENOENT) {
				if (minFreeExt <0) minFreeExt = i;
			}
			
			nfree(pfileName);
		}

		if (counter >= numExt) {
			if ((bundleNameStyle==eAddrDiffAlways ||
			     bundleNameStyle==eAddrsCRC32Always ||
			     bundleNameStyle==eAmiga)
				&& minFreeExt>=0) {
				counter = minFreeExt;
			} else {
				w_log(LL_ERR,"Can't use more than %d extensions for bundle names",numExt);
				nfree(pfileName);
				nfree(tmp);
				/*  Switch link to TimeStamp style */
				link->linkBundleNameStyle = eTimeStamp;
				i = createPackFileName(link);
				link->linkBundleNameStyle = bundleNameStyle;
				return i;
			}
		}
	
		xstrscat(&pfileName, tmp, wday, NULL);
		xscatprintf(&pfileName, "%c", ext3[counter]);

		break;

    case eTimeStamp:
		counter = 0;
		do {
			nfree(pfileName);
			xscatprintf(&pfileName, "%s%06lx%02x.%s%c", tmp, (long)aTime, counter%256, wday, ext3[counter/256]);
			counter++;
		} while ((fexist(pfileName) || fileNameAlreadyUsed(NULL, pfileName))
				 && (counter < numExt*256));

		if (counter >= numExt*256)
			w_log(LL_STAT,"created %d bundles/sec!", numExt*256);

		break;

    default:
		w_log(LL_ERR, "Unknown bundleNameStyle (non-compatible fidoconfig library?)");
		exit(EX_SOFTWARE);
		break;
    }
    nfree(tmp);

    if (!fexist(pfileName)) {
        nfree(link->packFile);
        link->packFile = pfileName;
        str_hisAka = aka2str5d(link->hisAka);
        str_Aka = aka2str5d(*aka);
        w_log(LL_CREAT,"packFile %s created for [%s via %s]",
            link->packFile,
            str_hisAka, str_Aka);
        nfree(str_hisAka); nfree(str_Aka);
        return 0;
    }
    else {
        nfree(pfileName);
	w_log(LL_ERR,"can't create arcmail bundles any more!");
        return 1;
    }
}/* createPackFileName() */
#endif

#if 0
/* filenames are not FTSC compliant, some links have problems :-( */
int createTempPktFileName(s_link *link)
{
    char  *filename = NULL;     /* pkt file in tempOutbound */
    char  *pfilename;           /* name of the arcmail bundle */
    char   limiter = PATH_DELIM;
    char   ext[4];              /* week-day based extension of the pack file */
    char   zoneSuffix[6]="\0";
    char  *zoneOutbound;        /* this contains the correct outbound directory
                                   including zones */
    char   uniquestring[9];     /* the unique part of filename */

    time_t       tr;
    static char *wdays[7]={ "su", "mo", "tu", "we", "th", "fr", "sa" };
    struct tm   *tp;

    tr=time(NULL);
    tp=localtime(&tr);
    sprintf(ext,"%s0", wdays[tp->tm_wday]);

    pfilename = (char *) malloc(strlen(config->outbound)+13+13+12+1);

    if (link->hisAka.zone != config->addr[0].zone) {
        sprintf(zoneSuffix, ".%03x%c", link->hisAka.zone, PATH_DELIM);
        zoneOutbound = safe_malloc(strlen(config->outbound)-1+strlen(zoneSuffix)+1);
        strcpy(zoneOutbound, config->outbound);
        strcpy(zoneOutbound+strlen(zoneOutbound)-1, zoneSuffix);
    } else
        zoneOutbound = safe_strdup(config->outbound);

    do
    {
        nfree(filename);
        filename = makeUniqueDosFileName(config->tempOutbound, "pkt", config);
        memcpy(uniquestring, filename + strlen(config->tempOutbound), 8);
        uniquestring[8] = '\0';

        if (link->hisAka.point == 0)
        {
            if (config->separateBundles)
            {
                sprintf(pfilename,"%s%04x%04x.sep%c%s.%s",
                        zoneOutbound, link->hisAka.net, link->hisAka.node,
                        limiter, uniquestring, ext);
            } else
            {
                sprintf(pfilename,"%s%s.%s",zoneOutbound,
                        uniquestring, ext);
            }
        } else
        {
            if (config->separateBundles)
            {
                sprintf(pfilename,"%s%04x%04x.pnt%c%08x.sep%c%s.%s",
                        zoneOutbound, link->hisAka.net, link->hisAka.node,
                        limiter, link->hisAka.point, limiter,
                        uniquestring, ext);
            } else
            {
                sprintf(pfilename,"%s%04x%04x.pnt%c%s.%s",
                        zoneOutbound, link->hisAka.net, link->hisAka.node,
                        limiter, uniquestring, ext);
            }
        }
    } while (fexist(filename) || fexist(pfilename));

    nfree(zoneOutbound);

    nfree(link->packFile);
    nfree(link->pktFile);

    link->packFile = pfilename;
    link->pktFile = filename;
    return 0;
}
/*  this function moved to smapi has name _createDirectoryTree */
int createDirectoryTree(const char *pathName) {
   char *start, *slash;

   char limiter=PATH_DELIM;

   int i;

   start = (char *) safe_malloc(strlen(pathName)+2);
   strcpy(start, pathName);
   i = strlen(start)-1;
   if (start[i] != limiter) {
      start[i+1] = limiter;
      start[i+2] = '\0';
   }
   slash = start;

#ifndef __UNIX__
   /*  if there is a drivename, jump over it */
   if (slash[1] == ':') slash += 2;
#endif

   /*  jump over first limiter */
   slash++;

   while ((slash = strchr(slash, limiter)) != NULL) {
      *slash = '\0';

      if (!direxist(start)) {
         if (!fexist(start)) {
            /*  this part of the path does not exist, create it */
            if (mymkdir(start) != 0) {
               w_log(LL_ERR, "Could not create directory %s", start);
               nfree(start);
               return 1;
            }
         } else {
            w_log(LL_ERR, "%s is a file not a directory", start);
            nfree(start);
            return 1;
         }
      }

      *slash++ = limiter;
   }

   nfree(start);

   return 0;
} /* createDirectoryTree() */
#endif

int createOutboundFileNameAka(s_link *link, e_flavour prio, e_pollType typ, hs_addr *aka)
{
   int nRet = NCreateOutboundFileNameAka(config,link,prio,typ,aka);
   if(nRet == -1)
      exit_hpt("cannot create *.bsy file!",0);
   return nRet;
}

int createOutboundFileName(s_link *link, e_flavour prio, e_pollType typ)
{
  return createOutboundFileNameAka(link, prio, typ, &(link->hisAka));
}

void *safe_malloc(size_t size)
{
    void *ptr = malloc (size);
    if (ptr == NULL) exit_hpt("out of memory (safe_malloc())", 1);
    return ptr;
}

void *safe_calloc(size_t nmemb, size_t size)
{
    void *ptr = safe_malloc (size*nmemb);
    memset(ptr,'\0',size*nmemb);
    return ptr;
}

void *safe_realloc(void *ptr, size_t size)
{
    void *newptr = realloc (ptr, size);
    if (newptr == NULL) exit_hpt("out of memory (safe_realloc())", 1);
    return newptr;
}

char *safe_strdup(const char *src)
{
    char *ptr=NULL;
    if(src)
      if ( !( ptr = strdup (src) ) ) /* use sstrdup() from fidoconfig library */
        exit_hpt("out of memory (safe_strdup())", 1);
    return ptr;
}

int isValidConference(const char *s) {
    /*  according to FSC-0074 with lowercase symbols */
    /*  lowercase symbols only for internal use */
    while (*s) {
	if ( !(*s >= 33 && *s <= 126) ) return 0;
	s++;
    }
    return 1;
}

void writeEchoTossLogEntry(char *areaName)
{
    if (config->echotosslog) {
        FILE *f=fopen(config->echotosslog, "a");
        if (f==NULL)
            w_log(LL_ERROR, "Could not open or create EchoTossLogFile.");
        else {
            fprintf(f, "%s\n", areaName);
            fclose(f);
        }
    }
}
