/*****************************************************************************
 * 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.130 2002/06/17 11:54:02 stas Stab $
 */
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>

#if defined(__BEOS__)
#include <sys/sysexits.h>
#elif defined(UNIX)
#include <sysexits.h>
#endif

#ifdef __EMX__
#include <sys/types.h>
#endif
#include <sys/stat.h>
#if !defined(__TURBOC__) && !(defined (_MSC_VER) && (_MSC_VER >= 1200))
#include <unistd.h>
#endif
#if defined (__TURBOC__)
#include <process.h>
#endif
#ifdef __WATCOMC__
#include <fcntl.h>
#define AW_S_ISDIR(a) (((a) & S_IFDIR) != 0)
#include <process.h>
#include <dos.h>
#endif
#include <fcntl.h>
#include <errno.h>

#if defined(__TURBOC__) || defined(__IBMC__) || (defined(_MSC_VER) && (_MSC_VER >= 1200))

#include <io.h>
#include <fcntl.h>
#include <process.h>

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

#endif

#include <global.h>
#include <fcommon.h>
#include <fidoconf/recode.h>
#include <dupe.h>
#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <fidoconf/dirlayer.h>

#include <smapi/typedefs.h>
#include <smapi/compiler.h>
#include <smapi/stamp.h>
#include <smapi/progprot.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();
    disposeConfig(config);
    doneCharsets();
    w_log(LL_STOP, "Exit");
    closeLog();
    if (_lockfile) {
       close(lock_fd);
       remove(_lockfile);
       nfree(_lockfile);
    }
    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) {
   int 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, int npos)
// Removing old empty bundles when bundleNameStyle == addDiff
{
   char           *ptr, *tmpfile, *pattern, savech;
   DIR            *dir;
   struct dirent  *file;
   struct stat stbuf;
   unsigned pathlen;
   time_t tr;

   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';

   tr = time(NULL);

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

	   if ( patimat(file->d_name, pattern) == 1 ) {

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

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

               if (tr - stbuf.st_mtime >= 60*60*24 && stbuf.st_size == 0 ) {

				   remove (tmpfile); // old empty bundle

               }
		   }
	   }
   }

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


int createTempPktFileName(s_link *link)
{
	char *fileName=NULL; // pkt file in tempOutbound
	char *pfileName=NULL; // name of the arcmail bundle
	char *tmp=NULL; // temp name of the arcmail bundle
    time_t aTime = time(NULL);  /* get actual time */
    int counter, minFreeExt, npos;
    char limiter=PATH_DELIM;
	
    e_bundleFileNameStyle bundleNameStyle = eTimeStamp;

    time_t tr;
    char *wday;
    struct tm *tp;

	int i;
	struct stat stbuf;
	static char *ext3 = "0123456789abcdefghijklmnopqrstuvwxyz";
	int numExt = strlen(ext3);

    tr=time(NULL);
    tp=localtime(&tr);
    counter = pkt_count;

    wday=wdays[tp->tm_wday];

    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;
	
    if (link->linkBundleNameStyle!=eUndef) bundleNameStyle=link->linkBundleNameStyle;
    else if (config->bundleNameStyle!=eUndef) bundleNameStyle=config->bundleNameStyle;
	
    // fileBoxes support
    if (needUseFileBoxForLink(config,link)) {
	if (!link->fileBox) link->fileBox = makeFileBoxName (config,link);
	xstrcat(&tmp, link->fileBox);
	_createDirectoryTree (tmp);
    } else {
	xstrcat(&tmp, config->outbound);

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

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

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

    } // link->fileBox

    npos = strlen(tmp);

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

    case eAddrDiff:
    case eAddrDiffAlways:

		if ( link->hisAka.point == 0 && config->addr[0].point == 0) {
			xscatprintf (&tmp, "%04hx%04hx.",
						 config->addr[0].net - link->hisAka.net,
						 config->addr[0].node - link->hisAka.node);
		} else {
			xscatprintf (&tmp, "%04hx%04hx.",
						 config->addr[0].node - link->hisAka.node,
						 config->addr[0].point- link->hisAka.point);
		}

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

		if (!needUseFileBoxForLink(config,link)) cleanEmptyBundles(tmp,npos);

		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*24) {
					// today's bundle
					counter = i+1;
					if (stbuf.st_size==0 && (counter<numExt ||
											 bundleNameStyle==eAddrDiffAlways ||
											 bundleNameStyle==eAmiga))
						remove (pfileName);
				} else {
					// old bundle
					if (stbuf.st_size == 0)	remove (pfileName);
					else counter = i+1;
				}
			} else {
				if (minFreeExt <0) minFreeExt = i;
			}
			
			nfree(pfileName);
		}

		if (counter >= numExt) {
			if ((bundleNameStyle==eAddrDiffAlways || bundleNameStyle==eAmiga)
				&& minFreeExt>=0) {
				counter = minFreeExt;
			} else {
				w_log(LL_ERR,"Can't use more than %d extensions for bundle names",numExt);
				nfree(fileName);
				nfree(pfileName);
				nfree(tmp);
				// Switch link to TimeStamp style
				link->linkBundleNameStyle = eTimeStamp;
				i = createTempPktFileName(link);
				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(fileName)) && (!fexist(pfileName))) {
        nfree(link->packFile);
        nfree(link->pktFile);
        link->packFile = pfileName;
        link->pktFile = fileName;
        return 0;
    }
    else {
        nfree(fileName);
        nfree(pfileName);
		w_log(LL_ERR,"can't create arcmail bundles any more!");
        return 1;
    }
}/* createTempPktFileName() */
#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 createOutboundFileName(s_link *link, e_flavour prio, e_pollType typ)
{
   int nRet = NCreateOutboundFileName(config,link,prio,typ);
   if(nRet == -1) 
      exit_hpt("cannot create *.bsy file!",0);
   return nRet;
}


void *safe_malloc(size_t size)
{
    void *ptr = malloc (size);
    if (ptr == NULL) exit_hpt("out of memory", 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", 1);
    return newptr;
}

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

int isValidConference(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;
}



syntax highlighted by Code2HTML, v. 0.9.1