/*

    File: ftpp/adbmtool.cc

    Copyright (C) 2002  Dmitry Udalov
    Email: uddm@penza.net
  
    This software 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 of the License, or
    (at your option) any later version.
  
    This program 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.
    
 */


#include "adbmtool.h"
#include "ftpproxy.h"

int fADBMNeedTerm=FALSE;
void adbmsighandler(int iSigNo);

//addrlist methods
//check existing of record by addres
int addrlist::exists(int ClientAddr,int ServerAddr){
    struct addr *rec = list;
    if(list==NULL)
        return FALSE;
    do{
        if((rec->ClientAddr==ClientAddr)&&(rec->ServerAddr==ServerAddr))
            return TRUE;
        rec=rec->next;
    }
    while(rec!=NULL);
    return FALSE;
}

//add addres to database - virtual list
int addrlist::addaddr(int ClientAddr,int ServerAddr){
    struct addr *rec = new struct addr;
    rec->ClientAddr=ClientAddr;
    rec->ServerAddr=ServerAddr;
    rec->next=NULL;
    time(&(rec->tm));
    if(list==NULL){
        list=rec;
        return TRUE;
    }
    struct addr *currec = list,*lastrec;
    do{
        lastrec=currec;
        currec=currec->next;
    }
    while(currec!=NULL);
    lastrec->next=rec;
    return TRUE;
}

//find addres at database - virtual list
int addrlist::findaddr(int ClientAddr){
    struct addr *rec = list,*prevrec=NULL;
    if(list==NULL)
        return -1;
    do{
        if(rec->ClientAddr==ClientAddr){
            int iServerAddr=rec->ServerAddr;
            if(prevrec==NULL)
                list=list->next;
            else
                prevrec->next=rec->next;
            delete rec;
            return iServerAddr;
        }
        prevrec=rec;
        if(rec!=NULL)
            rec=rec->next;
        else
            break;
    }
    while(rec!=NULL);
    return -1;
}

//delete old records from virtual list
void addrlist::checktimeout(){
    if(list==NULL)
        return;
    struct addr *rec = list, *prevrec=NULL;
    time_t nowtime;
    time(&nowtime);
    do{
        if((nowtime-rec->tm)>iADBMDelTimeOut){
            if(prevrec==NULL)
                list=list->next;
            else
                prevrec->next=rec->next;
            delete rec;
            if(list==NULL)
                break;
        }
        prevrec=rec;
        if(rec!=NULL)
            rec=rec->next;
        else
            break;
    }
    while(rec!=NULL);
}

//output all records for debug
int addrlist::output(){
    struct addr *rec = list;
    printf("BEGIN-----------------------------------------------------------\n");
    do{
        if(rec==NULL)
            break;
        printf("Server:   %d   Client:   %d\n",rec->ServerAddr,rec->ClientAddr);
        rec=rec->next;
    }
    while(rec!=NULL);
    printf("END-------------------------------------------------------------\n");
    return -1;
}

//Addres Database Manager routines
void proccesdb(){
    char cBuf[BUF_SIZE];
    //get params from config file
    if(getparam("WORKINGPORT",cBuf)==FALSE)
        iWorkingPort=0;
    else
        iWorkingPort=atoi(cBuf);
    if(iWorkingPort==0)
        iWorkingPort=WORKINGPORT;
    if(getparam("WORKINGADDR",szWorkingAddr)==FALSE)
        strcpy(szWorkingAddr,WORKINGADDR);
    if(getipbyhost(szWorkingAddr)==FALSE){
        logex(0,"Can't convert host name to IP %s",szWorkingAddr);
        return;
    }

    addrlist alist;//virtual list
    struct sockaddr_in serv_addr,clnt_addr;
    int hSocket;
    int saddrlen,max_caddrlen, ilen;
    socklen_t caddrlen;
    if((hSocket=socket(AF_INET,SOCK_DGRAM,0))<0){
//        logex(111111,"Can't create socket");
        return;
    }
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(szWorkingAddr);
    serv_addr.sin_port=htons(iWorkingPort);
    saddrlen=sizeof(serv_addr);
    //bind udp port
    if(bind(hSocket,(struct sockaddr*)&serv_addr,saddrlen)<0){
        logex(0,"Can't bind adbm udp. Addres: %s port: %d",szWorkingAddr,iWorkingPort);
        return;
    }
    struct sigaction act,oact;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    act.sa_handler=adbmsighandler;
    sigaction(SIGSEGV,&act,&oact);
    sigaction(SIGKILL,&act,&oact);
    sigaction(SIGTERM,&act,&oact);
    sigaction(SIGPIPE,&act,&oact);
    sigaction(SIGINT,&act,&oact);
    
    max_caddrlen = sizeof(clnt_addr);
    while(TRUE){
       if(fADBMNeedTerm==TRUE)
            break;
        caddrlen = max_caddrlen;
        fd_set rfds;
        struct timeval tv;
        int iRetval=1;
        FD_ZERO(&rfds);
        FD_SET(hSocket,&rfds);
        tv.tv_sec = 0;
        tv.tv_usec = 500;
        alist.checktimeout();
        iRetval = select(hSocket+1, &rfds, NULL, NULL, &tv);
        if(fADBMNeedTerm==TRUE)
            break;
        if (!iRetval)
            continue;
        if(fADBMNeedTerm==TRUE)
            break;
        //get request
        ilen = recvfrom(hSocket, cBuf, BUF_SIZE, 0, (struct sockaddr*)&clnt_addr,&caddrlen);
        cBuf[ilen]='\0';
        char cCmd[STR_SIZE];
        strncpy(cCmd,cBuf,3);
        cCmd[3]='\0';
        //parse and process request
        if(strcasecmp(cCmd,"ADD")==0){
            int iClntAddr,iServAddr;
            sscanf(cBuf,"ADD %d,%d",&iClntAddr,&iServAddr);
            if(alist.exists(iClntAddr,iServAddr)==FALSE){
                alist.addaddr(iClntAddr,iServAddr);
                sendto(hSocket,"0",1,0,(struct sockaddr*)&clnt_addr,caddrlen);
            }
            else
                sendto(hSocket,"1",1,0,(struct sockaddr*)&clnt_addr,caddrlen);
                //0 - succefully added
                //1 - already exists
        }
        else
        if(strcasecmp(cCmd,"FND")==0){
            int iClntAddr=0;
            sscanf(cBuf,"FND %d",&iClntAddr);
            sprintf(cBuf,"%d",alist.findaddr(iClntAddr));
            sendto(hSocket,cBuf,strlen(cBuf),0,(struct sockaddr*)&clnt_addr,caddrlen);
            //send server addres
        }
        else
            sendto(hSocket,"-1",2,0,(struct sockaddr*)&clnt_addr,caddrlen);
            //-1 - not exists
        /*debug ver*/
/*        else
        if(strcasecmp(cCmd,"OUT")==0){
            alist.output();
            sendto(hSocket,"0",1,0,(struct sockaddr*)&clnt_addr,caddrlen);
        }*/
        /*debug ver*/
    }
    close(hSocket);
}

//send request to adbm and get reply
int sendtoadbm(char *lpszBuf){
    char cBuf[BUF_SIZE];
    //get params
    if(getparam("WORKINGPORT",cBuf)==FALSE)
        iWorkingPort=0;
    else
        iWorkingPort=atoi(cBuf);
    if(iWorkingPort==0)
        iWorkingPort=WORKINGPORT;

    if(getparam("WORKINGADDR",szWorkingAddr)==FALSE)
        strcpy(szWorkingAddr,WORKINGADDR);
    if(getipbyhost(szWorkingAddr)==FALSE){
        logex(0,"Can't convert host name to IP %s",szWorkingAddr);
        return FALSE;
    }

    struct sockaddr_in serv_addr;
    int hSocket;
    int iRes,iLen;
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(szWorkingAddr);
    serv_addr.sin_port=htons(iWorkingPort);
    if((hSocket=socket(AF_INET,SOCK_DGRAM,0))<0){
//        logex(1111,"Can't create socket");
          return -1;
    }
    iLen=strlen(lpszBuf);
    //send request
    iRes=sendto(hSocket,lpszBuf,iLen,0,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
    if(iRes!=iLen){
//            logex(1111,"CLNT:Can't send: %s,  %d",lpszBuf,errno);
//          logex(1111,"Can't sendto");
        return -1;
    }
      //read
//    logex(111111,"CLNT Send: %s",lpszBuf);
    //get result
    iLen=recvfrom(hSocket,cBuf,STR_SIZE,0,NULL,0);
    if(iLen<0){
//        logex(1111,"Can't recvfrom\n");
          return -1;
    }
    cBuf[iLen]='\0';
      //output
//    logex(0,"CLNT:Received %s",cBuf);
    sscanf(cBuf,"%d",&iLen);
    close(hSocket);
    return iLen;
}

//signal handler for adbm process
void adbmsighandler(int iSigNo){
    switch(iSigNo){
        case SIGPIPE:
            break;//ignore
        case SIGSEGV:
        case SIGKILL:
        case SIGTERM:
        case SIGINT:
            fADBMNeedTerm=TRUE;//flag for termination process
            break;
        default:
            break;
    }
    return;
}


