/* $Id: bsoutil.c,v 1.19.2.3.2.2 2002/12/30 09:13:39 stas_degteff Exp $ */

#include <stdio.h>
#if !defined(__FreeBSD__)
  #include <malloc.h>
#endif
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <smapi/compiler.h>

#if defined (UNIX)
  #include <dirent.h>
  #include <unistd.h>
#endif

#if defined (__WATCOMC__) || defined(__NT__) || defined(__DOS__) || defined(__OS2__)
  #include <process.h>
  #include <direct.h>
  #include <io.h>
#endif

#if defined (__EMX__)
  #include <io.h>
  #include <process.h>
  #include <sys/fcntl.h>
#endif

#include "log.h"
#include "config.h"
#include "bsoutil.h"

/* ANSI C knows nothing about this constant */

#ifndef F_OK
#define F_OK 0
#endif

time_t t;
unsigned long serial;
time_t lastt;

const char outext[5]={'i', 'c', 'd', 'o', 'h'};
const char flowext[5]={'i', 'c', 'd', 'f', 'h'};
const char daynames[7][5]={"su","mo","tu","we","th","fr","sa"};


char *createPktName()
{
    unsigned long num;
    static char name[9];

    t=time(NULL);
    if (serial==MAXPATH) {
        if (lastt==t)
            return 0;
        else
            serial=0;
    }
    if(t == lastt)
        serial++;
    else
        serial=0;
    lastt=t;
    num = (t<<8) + serial;
    sprintf(name,"%08lx",num);
    Debug("generated pkt name: %s\n", name);
    return name;
}

void createDirIfNEx(char *dir)
{
    register unsigned dirlast;

    dirlast = strlen(dir)-1;
    if (dir[dirlast] == PATH_DELIM)
        dir[dirlast] = '\0';        /* we can't create "c:\dir\", only "c:\dir" */

    if (access(dir, F_OK))
    {
        Debug("creating directory %s...\n", dir);
        if (mymkdir(dir))
        {
            Log('9', "Can't create directory %s, errno=%d\n",
                dir, errno);
            Debug("can't create, errno=%d. exiting!", errno);
            exit(-1);
        }
    }
}

char *addr2str(s_link *link){
    static char node[24];
    if (link->hisAka.point!=0)
        sprintf(node, "%u:%u/%u.%u", link->hisAka.zone, link->hisAka.net,
                link->hisAka.node, link->hisAka.point);
    else
        sprintf(node, "%u:%u/%u", link->hisAka.zone, link->hisAka.net,
                link->hisAka.node);

    return node;
}

unsigned long getNMSizeForLink(s_link *link, char *outb)
{
    unsigned long NMSize=0;
    struct stat fInfo;
    char *fname=NULL;
    char *p_flavour=NULL;
    int i;

    Debug("guessing how much netmail this link has...\n");
    // /outb/12345678.hut+'\0' = strlen(outb)+4
    fname=(char *)smalloc(strlen(outb)+4);
    strcpy(fname, outb);
    p_flavour=fname+strlen(outb);
    strcpy(p_flavour, "?ut");

    for (i=0;i<=4;i++)
    {
        *p_flavour=outext[i];
        if (!stat(fname, &fInfo))
        {
            NMSize+=fInfo.st_size;
            Debug("found file %s, size=%lu\n", fname, fInfo.st_size);
        }
    }
    Debug("found %lu bytes of netmail.\n", NMSize);
    nfree(fname);
    return NMSize;
}


void getBundleName(s_link *link, int flavour, char *outb)
{
    int i;
    int day=0;
    char *bundleName=NULL;
    time_t t;
    struct stat fInfo;
    FILE *fp;
    char *flowFile=NULL, *flowLine=NULL, *flowLineTmp=NULL;
    char *sepDir=NULL, *outb_end=NULL;
    int foundOtherFlavour=0;
    char Idx='\0';
    char *IdxPtr;
    int outbLen=0;

    Debug("guessing bundle name...\n");
    time(&t);
    day=localtime(&t)->tm_wday;

    outbLen=strlen(outb)+30;
    //  /outb.12b/12345678.pnt/12345678.sep/12345678.su0+'\0';
    bundleName=(char *)smalloc(outbLen);
    //  /outb.12b/12345678.pnt/12345678.flo+'\0';
    flowFile=(char *)smalloc(outbLen-13);
    flowLine=(char *)smalloc(256);
    flowLineTmp=flowLine;

    memset(bundleName, 0, outbLen);
    if (fidoConfig->separateBundles)
    {
        sprintf(bundleName, "%ssep%c", outb, PATH_DELIM);
        outb_end=bundleName+strlen(bundleName);
    }
    else
    {
        strcpy(bundleName, outb);
        outb_end=bundleName+strlen(bundleName)-9;
    }

    if (fidoConfig->addr->point || link->hisAka.point)
    {       // /outb.12b/12345678.pnt/12344321.su0
        sprintf(outb_end, "%04x%04x.%2s0",
                (fidoConfig->addr->node - link->hisAka.node) & 0xffff,
                (fidoConfig->addr->point - link->hisAka.point) & 0xffff,
                daynames[day]);
    } else
    {       // /outb/12344321.su0
        sprintf(outb_end, "%04x%04x.%2s0",
                (fidoConfig->addr->net - link->hisAka.net) & 0xffff,
                (fidoConfig->addr->node - link->hisAka.node) & 0xffff,
                daynames[day]);
    }

    IdxPtr=bundleName+strlen(bundleName)-1;   // points to the last symbol before '\0'

    if (fidoConfig->separateBundles)
    {
        sepDir=(char *)smalloc(strlen(bundleName)-11);
        memset(sepDir, 0, strlen(bundleName)-11);
        strncpy(sepDir, bundleName, strlen(bundleName)-12);
        createDirIfNEx(sepDir);
        nfree(sepDir);
    }

    for (Idx='0';Idx<='z';Idx++)
    {
        if (Idx==('9'+1))
            Idx='a';
        *IdxPtr=Idx;

        if(stat(bundleName, &fInfo)) break; else
            if((fInfo.st_size <= link->arcmailSize*1024) && (fInfo.st_size!=0))
            {
                foundOtherFlavour=0;
                for(i=0;i<5;i++)
                {
                    if(i==flavour) continue;
                    sprintf(flowFile, "%s%clo", outb, flowext[i]);
                    if (access(flowFile, F_OK)) continue;
                    fp=fopen(flowFile, "r");
                    while(fgets(flowLine, 256, fp)!=NULL)
                    {
                        flowLineTmp=flowLine;
                        if(strlen(flowLineTmp)<2) continue;
                        if (flowLineTmp[0]=='#' || flowLineTmp[0]=='^' || flowLineTmp[0]=='~')
                            flowLineTmp++;
                        if(strncmp(flowLineTmp, bundleName, strlen(bundleName))==0)
                        {
                            foundOtherFlavour=1;
                            break;
                        }
                    }
                    fclose(fp);
                    if(foundOtherFlavour) break;
                }
                if (!foundOtherFlavour) break; // this file name is not used for other flavour so we can use it.
            } else continue;
    }

    copyString(bundleName, &(link->packFile));
    Debug("bundle name generated: %s\n", link->packFile);
    nfree(bundleName);
    nfree(flowFile);
    nfree(flowLine);
}

/* moved to fidoconfig
void fillCmdStatement(char *cmd, const char *call, const char *archiv, const char *file, const char *path) {
   const char *start, *tmp, *add;

   *cmd = '\0';  start = NULL;
   for (tmp = call; (start = (char *) strchr(tmp, '$')) != NULL; tmp = start + 2) {
      switch(*(start + 1)) {
         case 'a': add = archiv; break;
         case 'p': add = path; break;
         case 'f': add = file; break;
         default:
            strncat(cmd, tmp, (size_t) (start - tmp + 1));
            start--; continue;
      };
      strncat(cmd, tmp, (size_t) (start - tmp));
      strcat(cmd, add);
   };
   strcat(cmd, tmp);
}
*/
int addToFlow(s_link *link, int flavour, char *outb)
{
    FILE *fp;
    char *line=NULL;
    int foundOldBundle=0;
    unsigned char *buff=NULL;
    struct stat fInfo;

    sprintf(link->floFile,"%s%clo", outb, flowext[flavour]);

    Debug("adding bundle to flow file %s\n", link->floFile);

    if(stat(link->floFile, &fInfo)!=-1)
    {
        Debug("flow file exists.\n");

        fp=fopen(link->floFile, "r+");
        if (fp==NULL)
        {
            Log('9', "Can't open flow file for %2, errno=%d\n",
                addr2str(link), errno);
            Debug("can't open flow file, errno=%d\n", errno);
            return 0;
        }

        line=(char *)smalloc(MAXPATH);
        buff=(unsigned char *)smalloc(fInfo.st_size);
        memset(buff, 0, fInfo.st_size);

        while(fgets(line, MAXPATH, fp)!=NULL)
        {
            strcat((char*)buff, line);
            if (strlen(line)<2) continue;
            if (strncmp(line+1, link->packFile, strlen(link->packFile))==0)
            {
                foundOldBundle=1;
                Debug("found old bundle, can't add it again.\n");
                break;
            }
        }

        if (!foundOldBundle)
        {
            Debug("now let's add our bundle.\n");
            rewind(fp);
            fprintf(fp, "^%s\n%s", link->packFile, buff);
            Debug("added our bundle.\n");
        }
        nfree(buff);
    }
    else
    {
        Debug("creating new flow file.\n");
        fp=fopen(link->floFile, "w");
        if (fp==NULL)
        {
            Log('9', "Can't open flow file for %s, errno=%d\n",
                addr2str(link), errno);
            Debug("can't create flow file, errno=%d. returning,\n", errno);
            return 0;
        }
        fprintf(fp, "^%s\n", link->packFile);
        Debug("added our bundle.\n");
    }
    fclose(fp);
    return 1;
}

char *initLink(s_link *link)
{
    char *outb;
    Debug("allocating memory for this link...\n");

    /*    /outbound.12b/01234567.pnt/12345678.  +1 = strlen(outb)+5+13+9+1
     and that's enough for nodes too. */

    outb=(char *)scalloc(strlen(fidoConfig->outbound)+28, 1);
    sprintf(outb, "%s", fidoConfig->outbound);
    if (fidoConfig->addr->zone!=link->hisAka.zone)
        sprintf(outb+strlen(outb)-1,".%03x%c",
                link->hisAka.zone, PATH_DELIM);

    if (link->hisAka.point==0)
        sprintf(outb+strlen(outb), "%04x%04x.", link->hisAka.net, link->hisAka.node);
    else
        sprintf(outb+strlen(outb), "%04x%04x.pnt%c%08x.", link->hisAka.net,
                link->hisAka.node, PATH_DELIM, link->hisAka.point);

    link->bsyFile=(char *)smalloc(strlen(outb)+4); // *outb+"bsy"+'\0'
    sprintf(link->bsyFile, "%sbsy", outb);
    link->floFile=(char *)scalloc(strlen(outb)+4, 1);

    if (link->pktFile==NULL)
        link->pktFile=(char *)smalloc(strlen(fidoConfig->tempOutbound)+13);
    else
        link->pktFile=(char *)srealloc(link->pktFile,
                                       strlen(fidoConfig->tempOutbound)+13);
    if (!link->arcmailSize)
    {
        if (!fidoConfig->defarcmailSize)
        {
            link->arcmailSize=100;
        } else
            link->arcmailSize=fidoConfig->defarcmailSize;
    }
    Debug("arcmailSize for this link is %d kb\n", link->arcmailSize);
    return outb;
}


void releaseLink(s_link *link, char **outb)
{
    Debug("freeing allocated memory for link.\n");
    if (link==NULL) return;
    nfree(link->bsyFile);
    nfree(link->floFile);
    nfree(link->packFile);
    nfree(link->pktFile);
    nfree(*outb);
}

int createBsy(s_link *link, char *outb)
{
    if (!access(link->bsyFile, F_OK))
    {
        Log('6', "Link %s is busy now.\n", addr2str(link));
        return 0;
    } else
    {
        FILE *fp;
        Debug("trying to create bsy flag for this link...\n");
        fp=fopen(link->bsyFile, "w");
        if (fp == NULL)
        {
            Log('8', "Can't create bsyFile for %s, errno=%d\n",
                addr2str(link), errno);
            Debug("couldn't create, errno=%d", errno);
            return 0;
        }
        fprintf(fp, "%d", getpid());
        fclose(fp);
        Debug("bsy flag %s created successfully.\n", link->bsyFile);
    }
    return 1;
}

void removeBsy(s_link *link)
{
    Debug("removing bsy file for this link...\n");
    if (access(link->bsyFile, F_OK)) {
        Debug("can't acces bsy file %s, errno=%d. returning.\n", link->bsyFile, errno);
        return;
    }
    if(remove(link->bsyFile))
    {
       Log('9', "Can't remove bsyFile for %s, errno=%d\n",
           addr2str(link), errno);
       Debug("can't remove, errno=%d.\n", errno);
    }
}

void packNetMailForLink(s_link *link)
{
    int flavour=0;
    int retval;
    char *pktname=NULL;
    char *execstr=NULL;
    char *bsoNetMail=NULL;   // /outb.12b/12345678.pnt/12345678.hut
    char *outbForLink=NULL; // /outb.12b/12345678.pnt/12345678.
    char *dir=NULL;          // /outb.12b/12345678.pnt
    unsigned long nmSize=0;


    Debug("packNetmail for this link is on, trying to pack...\n");
    if (link->packerDef == NULL)
    {
        Log('9', "Packer for link %s undefined but 'packNetmail' is on, skipping...\n",
            addr2str(link));
        Debug("packer for this link is undefined, returning.\n");
        return;
    }

    outbForLink=initLink(link);
    dir=(char *)smalloc(strlen(outbForLink)-9); // strlen(outb)-10 + '\0';
    memset(dir, 0, strlen(outbForLink)-9);
    strncpy(dir, outbForLink, strlen(outbForLink)-10);
    if (access(dir, F_OK))
    {
        Debug("directory %s doesn't exist, skipping...\n", dir);
        nfree(dir);
        releaseLink(link, &outbForLink);
        return;
    }
    nfree(dir);

    if (!createBsy(link, outbForLink)) {
        releaseLink(link, &outbForLink);
        return;
    }

    execstr=(char *)smalloc(MAXPATH);
    bsoNetMail=(char *)smalloc(strlen(outbForLink)+4);

    nmSize=getNMSizeForLink(link, outbForLink);

    if ((nmSize!=0) && (nmSize >= (link->maxUnpackedNetmail*1024)))
    {
        Log('5', "Found %lu bytes of netmail for %s\n", nmSize, addr2str(link));

        for (flavour=0;flavour<5;flavour++)
        {
            sprintf(bsoNetMail, "%s%cut", outbForLink, outext[flavour]);

            if (!access(bsoNetMail, F_OK))
                if ((pktname=(char *)createPktName())!=NULL)
                {
                    sprintf(link->pktFile, "%s%s.pkt", 
		    fidoConfig->tempOutbound, pktname);

                    Debug("found netmail packet %s, moving to %s\n",
                          bsoNetMail, link->pktFile);

                    if (rename(bsoNetMail, link->pktFile))
                    {
                        Log('9', "Error renaming %s to %s\n", bsoNetMail, link->pktFile);
                        Debug("can't move, errno=%d. exiting!\n", errno);
                        releaseLink(link, &outbForLink);
                        exit(-1);
                    }

                    getBundleName(link, flavour, outbForLink); // calculating bundleName;
                    fillCmdStatement(execstr, link->packerDef->call,
                                     link->packFile, link->pktFile, "");
                    Log('6', "Packing %s -> %s\n", bsoNetMail, link->packFile);
                    Debug("executing packer: %s\n", execstr);
                    if ((retval=system(execstr))!=0)
                    {
                        Log('9', "Error executing packer, errorlevel %d\n", retval);
                        Debug("packer returned errorlevel %d, errno=%d\n", retval, errno);
                        releaseLink(link, &outbForLink);
                        exit(-1);
                    }

                    if (addToFlow(link, flavour, outbForLink))
                        if(remove(link->pktFile)==-1)
                            Log('9', "Can't remove pktFile %s, errno=%d\n",
                                link->pktFile, errno);
                } // if (createPktName(pktname))
        } // for()
    } // if (nmSize...)
    else
    {
        if (nmSize)
            Log('5', "Found %lu bytes of netmail for %s\n", nmSize, addr2str(link));
    }


    removeBsy(link);
    releaseLink(link, &outbForLink);
    nfree(execstr);
    nfree(bsoNetMail);
}

