/*

    File: ftpp/ftpproxy.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 "ftpproxy.h"
#include "adbmtool.h"

int checkacces(struct ClientParam *Session){
    char cBuf[STR_SIZE];
    strcpy(cBuf,inet_ntoa(Session->ClientAddr.sin_addr));
    return getiprights(cBuf);
}

//handle client request's at different process
void handleclient(int hControlSocket,struct sockaddr_in ClientAddr){
     int pid=-1;
     if((pid=fork())==-1){
         logex(0,"Can't fork.");
         close(hControlSocket);
         return;
     }
     if(pid==0){//if new process
         struct ClientParam Session;
         Session.hCtrlClientSocket=hControlSocket;
         Session.ClientAddr=ClientAddr;
         Session.hCtrlServerSocket=-1;
         if(checkacces(&Session)==FALSE){
             sendreply(&Session,FTPNPROMT);
             close(hControlSocket);
             hControlSocket=-1;
             exit(0);
         }
         char cBuf[BUF_SIZE];
         int iRes=TRUE,iLen=0;
         //if transparent mode then
         if(fTransparentFTPProxy==TRUE){
             //get info from adbm
             usleep(iADBMReadTimeOut);//wait iADBMReadTimeOut milliseconds for read from adbm
             memset(&(Session.ServerAddr),0,sizeof(struct sockaddr_in));
             sprintf(cBuf,"FND %d",ClientAddr.sin_addr.s_addr);
             logex(iClientNum,"FND %d",ClientAddr.sin_addr.s_addr);
             //send request to adbm and get reply, fill server addres structure
             Session.ServerAddr.sin_addr.s_addr=sendtoadbm(cBuf);
//             logex(iClientNum,"Client %s connected to server %s",inet_ntoa(Session.ClientAddr.sin_addr),inet_ntoa(Session.ServerAddr.sin_addr));
             logex(iClientNum,"res FND: %d",Session.ServerAddr.sin_addr.s_addr);
             Session.ServerAddr.sin_port=htons(21);
             Session.ServerAddr.sin_family=AF_INET;

             if(Session.hCtrlServerSocket!=-1){
                 shutdown(Session.hCtrlServerSocket,SHUT_RDWR);
                 close(Session.hCtrlServerSocket);
             }
             Session.hCtrlServerSocket=socket(PF_INET,SOCK_STREAM,0);
             //connect to server
             iRes=connect(Session.hCtrlServerSocket,(struct sockaddr *)&(Session.ServerAddr),sizeof(Session.ServerAddr));
             logex(iClientNum,"Connect result: %d  errno %d",iRes,errno);
             if(iRes==-1){
                 strcpy(cBuf,S421MSG1);
                 sendreply(&Session,cBuf);
                 shutdown(Session.hCtrlServerSocket,SHUT_RDWR);
                 close(Session.hCtrlServerSocket);
                 return;
             }
             else{
                 //read server promt
                 if(waitserver(&Session)==FALSE){
                     strcpy(cBuf,S421MSG1);
                     sendreply(&Session,cBuf);
                     shutdown(Session.hCtrlServerSocket,SHUT_RDWR);
                     close(Session.hCtrlServerSocket);
                     return;
                 }
                 iRes=getreply(&Session,cBuf);
                 logex(iClientNum,"Server promt: %s",cBuf);
                 while(iRes==0){
                     iRes=getreply(&Session,cBuf);
                 }
                 if(iRes==421)
                 {
                     sendreply(&Session,cBuf);
                     shutdown(Session.hCtrlServerSocket,2);
                     close(Session.hCtrlServerSocket);
                     return;
                 }
                 //send server promt to client
                 sendreply(&Session,cBuf);
                 logex(iClientNum,"Client %s connected to server %s",inet_ntoa(Session.ClientAddr.sin_addr),inet_ntoa(Session.ServerAddr.sin_addr));
             }
         }
         else//if not transparent send self promt to client
             sendreply(&Session,FTPPROMT);
         iRes=TRUE;

         //receive and process client commands
         while(iRes==TRUE){
             if(fNeedTerm)
                 break;
             if(waitclient(&Session)==FALSE)
                 break;
             iLen=recv(hControlSocket,cBuf,BUF_SIZE,0);
             if(iLen!=0){
                 cBuf[iLen]='\0';
                 iRes=proccescmd(&Session,cBuf);
             }
             else
                 break;
             if((fServerDisconnected==TRUE)||(fClientDisconnected==TRUE))
                 break;
             
         }
         //if server or client close connection then write to log
         if((fServerDisconnected==TRUE)||(fClientDisconnected==TRUE)){
             if(fServerDisconnected==TRUE)
                 logex(iClientNum,"Server disconnected");
             if(fClientDisconnected==TRUE){
                 sendcmd(&Session,QUITCMD);
                 getreply(&Session,cBuf);
                 logex(iClientNum,"Client disconnected");
             }
         }
         //close all connection
         if(Session.hCtrlServerSocket!=-1){
             shutdown(Session.hCtrlServerSocket,SHUT_RDWR);
             close(Session.hCtrlServerSocket);
         }
         if(Session.hDataServerSocket!=-1){
             shutdown(Session.hDataServerSocket,SHUT_RDWR);
             close(Session.hDataServerSocket);
         }
         if(Session.hDataClientSocket!=-1){
             shutdown(Session.hDataClientSocket,SHUT_RDWR);
             close(Session.hDataClientSocket);
         }
         if(Session.hListeningClientSocket!=-1){
             shutdown(Session.hListeningClientSocket,SHUT_RDWR);
             close(Session.hListeningClientSocket);
         }
         logex(iClientNum,"Disconnected %s(%s).",Session.szUserName,inet_ntoa(Session.ClientAddr.sin_addr));
         Session.hCtrlServerSocket=-1;
         _exit(0);
     }
     else{
         close(hControlSocket);
         hControlSocket=-1;
     }
}

//process all commands
//select command and her parametrs
int proccescmd(struct ClientParam *Session,char *lpszBuf){
    logex(iClientNum,"Cmd %s",lpszBuf); 
    if(strlen(lpszBuf)<4)
        return TRUE;
    char szFTPCmd[STR_SIZE],szParam[STR_SIZE];
    char *lpszFTPCmd;
    if((lpszBuf[strlen(lpszBuf)-2]=='\r') && (lpszBuf[strlen(lpszBuf)-2]=='\r'))
	lpszBuf[strlen(lpszBuf)-2]='\0';
    if(strstr(lpszBuf,ABORCMD)!=NULL){
        strcpy(szFTPCmd,ABORCMD);
        strcpy(szParam,"");
    }
    else{
        strncpy(szFTPCmd,lpszBuf,4);
        szFTPCmd[4]='\0';
        trim(szFTPCmd,szFTPCmd);
        if(strlen(lpszBuf)>4){
            lpszBuf=lpszBuf+4;
            strcpy(szParam,lpszBuf);
            trim(szParam,szParam);
            lpszBuf=lpszBuf-4;
        }
        else
            szParam[0]='\0';
    }
    lpszFTPCmd=strupr(szFTPCmd);
    //parse command and process
    return parsecmd(Session,lpszFTPCmd,szParam);
}

int parsecmd(struct ClientParam *Session,char* lpszFTPCmd,char* lpszParam){
    char cBuf[BUF_SIZE];
    int iRes=TRUE;
    //parse command and call procedure for her process
    if(strcmp(lpszFTPCmd,USERCMD)==0)
        return usercmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,PASSCMD)==0)
      	return passcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,QUITCMD)==0)
      	return quitcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    
    //data transfer init commands
    if(strcmp(lpszFTPCmd,PORTCMD)==0)
      	return portcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,PASVCMD)==0)
      	return pasvcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    //data transfer commands
    if(strcmp(lpszFTPCmd,LISTCMD)==0)
      	return listcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,NLSTCMD)==0)
      	return listcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,RETRCMD)==0)
      	return retrcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,STORCMD)==0)
      	return storcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,APPECMD)==0)
      	return storcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strcmp(lpszFTPCmd,STOUCMD)==0)
      	return storcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
    if(strstr(cBuf,ABORCMD)!=NULL)
        return aborcmdprocces(Session,lpszFTPCmd,lpszParam);
    else
        return servicecmdprocces(Session,lpszFTPCmd,lpszParam);
    sendreply(Session,S500MSG);
    return FALSE;
}

//service command process
//send command to server, get reply, send server reply to client
int servicecmdprocces(struct ClientParam *Session,char *lpszFTPCmd,char* lpszParam){
    char cBuf[BUF_SIZE];
    sprintf(cBuf,"%s %s",lpszFTPCmd,lpszParam);
    trim(cBuf,cBuf);
    sendcmd(Session,cBuf);
    logex(iClientNum,"Logged %s(%s) > %s",Session->szUserName,inet_ntoa(Session->ClientAddr.sin_addr),cBuf);
    int iRetCode=getreply(Session,cBuf);
    if(iRetCode==-1)
        return FALSE;
    sendreply(Session,cBuf);
    logex(iClientNum,"Logged %s(%s) < %s",Session->szUserName,inet_ntoa(Session->ClientAddr.sin_addr),cBuf);
    while(iRetCode==0){
       iRetCode=getreply(Session,cBuf);
       sendreply(Session,cBuf);
       logex(iClientNum,"Logged %s(%s) < %s",Session->szUserName,inet_ntoa(Session->ClientAddr.sin_addr),cBuf);
    }
    return TRUE;
}
