/* LREADjav.c
   Program to read Linux extended 2 filesystems under DOS

   TCP/IP-like Remote file server for LTOOLS's java graphical user interface LREADgui

   When you have this program running, you can use any web-browser as a user interface for ldir/lread.

   Please note:
   Under DOS/Windows this program must be compiled as a 32bit program, edit 'makefile' appropriately
   and do a 'make'.
   Under UNIX edit 'makefile.unx' and do a 'make -fmakefile.unx'.


   Copyright (C) 1999-2006 Werner Zimmermann(Werner.Zimmermann@fht-esslingen.de)

   This program 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.

   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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   For further information please see file README.TXT.

---History, Changes:-----------------------------------------------------------
   V1.0: First release for public alpha test.
   W.Zimmermann, 15 April 1999
   V1.1: Comment updates
   W.Zimmermann, 28 May 1999
   V1.2: Make it compatible to Microsoft Visual C++
   W.Zimmermann, 26 Mar 2000
   V1.3: Prepare lreadsrv to run as a Windows NT service, started by 
   	loaddrv -instService lreadsrv.exe lreadjav
   	loaddrv -start lreadjav
   W. Zimmermann, 20 Dec 2003
   V1.4: Modified to accept .255. as wildcard in IP addresses
   W. Zimmermann, 12 May 2004
 */
#ifdef _MSC_VER							//for Microsoft Visual C++
#pragma warning(disable: 4047 4068)
#endif

#define VERSION  "V1.4"

#include <stdio.h>
#include <string.h>
#ifndef UNIX
  #include <windows.h>
  #include <winsock.h>
  #include <conio.h>
#ifndef _MSC_VER
  #include <dir.h>
#endif
  #include <dos.h>
  #include <io.h>
#else
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <dirent.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>

//Redefinitions to make LREADjav work under UNIX operating systems---------------------------------------
#ifdef UNIX
  #define SOCKET                int
  #define WORD                  unsigned short
  #define DWORD                 unsigned int
  #define BOOL                  char
  #define SOCKADDR_IN           struct sockaddr_in
  #define FALSE                 0
  #define TRUE                  1
  #define SOCKET_ERROR          -1
  #define INVALID_SOCKET        -1
  #define closesocket           close
  int WSACleanup(void)          { return 0; }
  int WSAGetLastError(void)     { return 0; }

  #define min(a,b)    (a<b ? a : b)

  void ltoa(long input, char * output, int base)
  { char temp[128];
    if (base==10)
	sprintf(temp,"%ld",input);
    else if (base==16)
	sprintf(temp,"%lX",input);
    strcpy(output,temp);
  }

  int filelength(int handle)
  { struct stat statBuf;

    fstat(handle,&statBuf);
    return statBuf.st_size;
  }

#endif
/// End of redefinitions to make LREADjav work under UNIX operating systems------------------------

	
#define BUFSIZE         32768					//Buffer size
#define NIP		16					//Number of registered IPs

/*--------------------------------------------------------------------------------------------------*/
#if 0                                                           /*set to 1, if you do want all these debugging messages */
#define DEBUG         printf
#else
#define DEBUG         dummy
#pragma argsused
int dummy(const char *format,...)
{
    return 0;
}
#endif
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//Macro to send ASCII data (zero terminated strings)
//--------------------------------------------------------------------------------------------------
#define SEND(daten)     memset(&sendBuffer,0,sizeof(sendBuffer));                                        \
			strcpy(sendBuffer,daten);                                                        \
			if (send(sd_current, (char *) &sendBuffer, strlen(sendBuffer),0) == SOCKET_ERROR)\
			{ WSAGetLastError(); perror("srv:send");                                         \
			}

//Macro to send binary data
#define SENDDATA(data,length)                                                                            \
			memset(&sendBuffer,0,length);                                                    \
			memcpy(sendBuffer,data,length);                                                  \
			if (send(sd_current, (char *) &sendBuffer,length,0) == SOCKET_ERROR)             \
			{ WSAGetLastError(); perror("srv:send");                                         \
			}

//Macro to receive ASCII data
#define RECEIVE         memset(&readBuffer,0,sizeof(readBuffer));                                        \
			if ((i=recv(sd_current, (char *) &readBuffer, sizeof(readBuffer),0))==SOCKET_ERROR)\
			{ fprintf(stderr,"Error=%u\n",WSAGetLastError());perror("srv:recv"); break;      \
			}                                                                                \
			DEBUG("received command: >>>%s",readBuffer);

//Macro to receive binary data
#define RECEIVEDATA     memset(&readBuffer,0,sizeof(readBuffer));                                        \
			if ((i=recv(sd_current, (char *) &readBuffer, sizeof(readBuffer),0)) == SOCKET_ERROR)    \
			{ fprintf(stderr,"Error=%u\n",WSAGetLastError());perror("srv:recv"); break;      \
			}

//--------------------------------------------------------------------------------------------------
//Winsock global variables
//--------------------------------------------------------------------------------------------------
SOCKET sd = 0, sd_current = 0;
SOCKADDR_IN Sin, Pin;
int addrlen;
WORD wVersionRequested;
#ifndef UNIX
  WSADATA wsaData;
#endif
unsigned short serverPort = 1605;
char strServerPort[32]="0x1234";
unsigned short clientDataPort;
unsigned long registeredClientIP[NIP] =
{0x7F000001L};
BOOL connectionAllowed = FALSE;
struct solinger
{
    int l_onoff;
    int l_linger;
}
linger;
unsigned char cClient, cRegistered;


//--------------------------------------------------------------------------------------------------
//Other global variables
//--------------------------------------------------------------------------------------------------
FILE *fd = NULL;

char sendBuffer[BUFSIZE], readBuffer[BUFSIZE], currentLINdrive[32]="/dev/fd0",
     tempBuffer[BUFSIZE], line[BUFSIZE], tempLine[BUFSIZE], *p, *q, *pArg, *pDat, *pCmd;
char value[4] = "1";
BOOLEAN hide=FALSE;

#ifndef UNIX
//##################################################################################################
//Control-Break-Handler
//As the server runs in an infinite loop, it can only be stopped by pressing the Control-Break keys.
//This routine allows a 'graceful' shutdown.
//--------------------------------------------------------------------------------------------------
BOOL CtrlBreakHandler(DWORD dwCtrlType)
{
    switch (dwCtrlType)
    {
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_SHUTDOWN_EVENT:
	    if (fd != NULL)
		fclose(fd);
	    if (sd != NULL)
		closesocket(sd);
	    if (sd_current != NULL)
		closesocket(sd_current);
	    WSACleanup();
            if (hide==FALSE)
            	printf("\n####### Exit LTOOLS Java Client Remote Server ########\n");
	    exit(0);
	default:
	    return FALSE;
    }
}
#endif


//#################################################################################################
//Convert IP string, e.g. 127.0.0.1 to
//-------------------------------------------------------------------------------------------------
unsigned long convertIPtoLong(char *string)
{
    unsigned char *q, IPstring[32];
    unsigned long dwIP;

    strcpy(IPstring, string);

    if ((q = strtok(IPstring, ".")) == NULL)
    {
	printf("Error: Malformed IP = %s - Exiting\n", IPstring);
	exit(-1);
    }
    dwIP = atol(q) << 24;
    if ((q = strtok(NULL, ".")) == NULL)
    {
	printf("Error: Malformed IP = %s - Exiting\n", IPstring);
	exit(-1);
    }
    dwIP = dwIP + (atol(q) << 16);
    if ((q = strtok(NULL, ".")) == NULL)
    {
	printf("Error: Malformed IP = %s - Exiting\n", IPstring);
	exit(-1);
    }
    dwIP = dwIP + (atol(q) << 8);
    if ((q = strtok(NULL, ".")) == NULL)
    {
	printf("Error: Malformed IP = %s - Exiting\n", IPstring);
	exit(-1);
    }
    dwIP = dwIP + atol(q);

    if (hide==FALSE)
        printf("Registered IP: %lx  for incoming connections\n", dwIP);
    return dwIP;
}


//##################################################################################################
//##################################################################################################
//##################################################################################################
//Main programm
//--------------------------------------------------------------------------------------------------
#pragma argsused
int main(int argc, char **argv)
{
    int i, j;
    char *q;
    long currentIP;
    long bytesRead, bytesToRead;
#ifndef UNIX
    HKEY hKey;
    char buffer[256];
#endif

    for (i=1; i<NIP; i++)
    	registeredClientIP[i]=0;
    	
#ifndef UNIX
    _fmode = O_BINARY;                                          /*we want all files binary */
    SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlBreakHandler, TRUE);   /*install CTRL-Break handler */
#endif

    for (i=1, j=1; i < argc; i++)
    {	if (!strncmp(argv[i],"-p", 2))
        {   serverPort = atoi(argv[i]+2);
        } else if (!strncmp(argv[i],"-i", 2))
        {   if (j<NIP)
               registeredClientIP[j++] = convertIPtoLong(argv[i]+2);
	} else if (!strncmp(argv[i],"-x", 2))
	{   
#ifndef UNIX		
	    FreeConsole();
#endif	    
	    hide=TRUE;
	}
    }

/*
    if (argc > 1)
	serverPort = atoi(argv[1]);                             //if portnumber is specified on the command line
    //the following client IP addresses will be allowed to conntect to our server
    registeredClientIP[0] = 0x7F000001L;                        //default client IP is 127.0.0.1 (localhost)
    if (argc > 2)
	registeredClientIP[1] = convertIPtoLong(argv[2]);
    if (argc > 3)
	registeredClientIP[2] = convertIPtoLong(argv[3]);
    if (argc > 4)
	registeredClientIP[3] = convertIPtoLong(argv[4]);
*/

    //############ Startup message #################################################
    if (hide==FALSE)
    {
    	printf("\n******************************************************************************\n");
    	printf("LTOOLS GUI Server version %s (C) 1999-2006 Werner.Zimmermann@fht-esslingen.de\n", VERSION);
    	printf("******************************************************************************\n");
    	printf("List, read and write files on Linux Extended 2 filesystems\n\n");
    	printf("usage: lreadjav [-p<portnumber> [-i<registered Client IP> ...]]\n");
    	printf("       * Default value for the server's TCP/IP portnumber is %d .\n",serverPort);
    	printf("       * Due to safety reasons, we do only accept connections from 'localhost'\n");
    	printf("         or from one of max. 3 client IPs specified as <registered Client IP>.\n");
    	printf("         IP addresses must be specified as dotted decimal, use 255 as wildcard\n");
    	printf("       * Client access is granted based on IP, there is no password Check.\n");
    	printf("       * Please set environment variable LDRIVE (default Linux drive)\n");
    	printf("\n");
    }
#ifndef UNIX
    //In case this software was installed as a service by loaddrv
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Loaddrive\\lreadjav\\application", 0, KEY_ALL_ACCESS, &hKey)==ERROR_SUCCESS)
    {	i=sizeof(buffer);
    	if (RegQueryValueEx(hKey, "", NULL, &j, buffer, &i)==ERROR_SUCCESS)
    	{   q=strrchr(buffer,'\\');
    	    *q=0;
    	    SetCurrentDirectory(buffer);
    	    if (hide==FALSE)
    	        printf("Service running from directory %s\n\n", buffer);
    	}
    }
#endif

    if (hide==FALSE)
    {	printf("*** Accept connections from\n");
    	for (i=0; i < NIP; i++)
        {   if (registeredClientIP[i]!=0)
        	printf("i=%u IP= %u:%u:%u:%u\n", i, 
        		(registeredClientIP[i] >> 24) & 0xFF, 
        		(registeredClientIP[i] >> 16) & 0xFF, 
        		(registeredClientIP[i] >>  8) & 0xFF, 
        		(registeredClientIP[i])       & 0xFF);
        }
    }

    p = (char *) getenv("LDRIVE");                              /*Get default drive from environment variable LDRIVE, if set */
    if (p != NULL)
    {
	strncpy(currentLINdrive, p, sizeof(currentLINdrive));
    }
    //############ Establish connection to host ####################################
#ifndef UNIX
    //Load and start Winsock.DLL
    wVersionRequested = MAKEWORD(1, 1);
    if (WSAStartup(wVersionRequested, &wsaData))
    {
	if (hide==FALSE)
	    perror("srv: WSAStartup");
	exit(1);
    }
    DEBUG("srv: opened Winsock.dll\n Version=%x\n HighVersion=%x\n Description=%s\n SystemStatus=%s\n MaxSockets=%u\n MaxUdpDg=%u\n VendorInfo=%s\n", \
	  wsaData.wVersion, wsaData.wHighVersion, wsaData.szDescription, wsaData.szSystemStatus, \
	  wsaData.iMaxSockets, wsaData.iMaxUdpDg, wsaData.lpVendorInfo);
#endif
    //Set TCP/IP protocol and port
    memset(&Sin, 0, sizeof(Sin));
    Sin.sin_family = AF_INET;
    Sin.sin_addr.s_addr = INADDR_ANY;
    Sin.sin_port = htons(serverPort);
    //Create a socket, bind and listen
    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
	if (hide==FALSE)
	    perror("srv:socket");
	WSACleanup();
	exit(1);
    }
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
    if (bind(sd, (struct sockaddr *) &Sin, sizeof(Sin)) == SOCKET_ERROR)
    {
	if (hide==FALSE)
	    perror("srv:bind");
	WSACleanup();
	exit(1);
    }
    if (listen(sd, 5) == SOCKET_ERROR)
    {
	if (hide==FALSE)
	    perror("srv:client");
	WSACleanup();
	exit(1);
    }
    addrlen = sizeof(Pin);                                      /*most important ! */

    //############ Wait for connection loop ######################################
    while ((sd_current = accept(sd, (struct sockaddr *) &Pin, &addrlen)) != INVALID_SOCKET)
    {
	//checking, if incoming connection comes from one of the allowed IPs
	connectionAllowed = FALSE;

	currentIP = ntohl(*(unsigned long *) &Pin.sin_addr);
	for (i = 0; i < NIP; i++)
	{
/*	    if (currentIP == registeredClientIP[i])		//compare for equality
	    {	connectionAllowed = TRUE;
	        break;
	    }
*/
	    
	    for (j=3; j>=0; j--)					//compare taking .255. = FFh as wildcard
	    {	

	    	cClient = *((unsigned char*) &currentIP + j);
	    	cRegistered = *((unsigned char*) &registeredClientIP[i] + j);
	    
//	    	printf("i=%u  j=%u  qRegistered=%2X   pClient=%2X\n", i, j, cRegistered, cClient);
	    	
	    	if (cRegistered != 255)
	        {   if (cRegistered != cClient)
	            {	break;
	            }
	        }
	        if (j==0)
	        {   connectionAllowed=TRUE;
	        }
	
	    }
	    
	}
	if (connectionAllowed == FALSE)
	{
	    if (hide==FALSE)
	        printf("Sorry, due to security reasons LREADjav does not accept connections from this site\n");
	    SEND("403 Sorry, due to security reasons LREADjav does not accept connections from your site\n");
	    closesocket(sd_current);
	    sd_current = 0;
	    if (hide==FALSE)
	        printf("srv: refused connection from %3d.%3d.%3d.%3d:%5d --- \n",
		   (currentIP) & 0xFF, (currentIP >> 8) & 0xFF, (currentIP >> 16) & 0xFF, (currentIP >> 24) & 0xFF, Pin.sin_port);
	    continue;
	}
#ifndef UNIX
	if (hide==FALSE)
	    printf("srv: connect from %d.%d.%d.%d:%5d --- \n",
	     (currentIP >> 24) & 0xFF,(currentIP >> 16) & 0xFF, (currentIP >> 8) & 0xFF, (currentIP) & 0xFF,Pin.sin_port);
#else
	if (hide==FALSE)
	    printf("srv: connect from %d.%d.%d.%d:%5d --- \n",
	     (currentIP >> 24) & 0xFF,(currentIP >> 16) & 0xFF, (currentIP >> 8) & 0xFF, (currentIP) & 0xFF,Pin.sin_port);
#endif
	clientDataPort = Pin.sin_port;

	setsockopt(sd_current, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
	linger.l_onoff = 0xFFFFFFFF;
	linger.l_linger = 1;
	setsockopt(sd_current, SO_LINGER, SO_REUSEADDR, (char *) &linger, sizeof(linger));


	// ioctlsocket(sd_current,FIONBIO,(u_long *) "1");     //make socket non blocking

	//### Send/receive loop ####################################################
	do
        {   RECEIVEDATA;
	    //Parse received data to isolate command, argument and data
//	    printf("--->%s<---\n", readBuffer);
	    
scan:       pCmd=readBuffer; pArg=NULL; pDat=NULL;
	    bytesRead=0;
	    for (j=0;j<i;j++)
	    { if ((*(readBuffer+j)==0x0D)||(*(readBuffer+j)==0x0A))	//find CR or LF
	      { *(readBuffer+j)=0;					//if yes, set to 0 (end of string)
	        if (*(readBuffer+j+1)!=0)
	            if (pArg==NULL) pArg=readBuffer+j+1;
	            else            pDat=readBuffer+j+1;
		if (pDat!=NULL)						//break out of loop, if data was found
		{   bytesRead=i-(pDat-pCmd);				//data length
    	    	    if (bytesRead<0) 
    	    	        if (hide==FALSE)
    	    	            printf("Oops, bytesRead<0\n");
		    break;
		}
	      }
	    }
	    if (hide==FALSE)
	    {    printf("#############################################################################\n");
	         printf("received command: %s<<<\n", pCmd);			//display received command
	    
    	         if (pArg!=NULL) {if (hide==FALSE) printf("       arguments: %s<<<\n", pArg);} else { if (hide==FALSE) printf("no arguments\n"); }
	         if (hide==FALSE) printf("            data: %d Byte\n",bytesRead);
	    }

	    if (!strncmp(pCmd, "ESC",3))				//ESC closes connection----
            {    if (hide==FALSE)
                      printf("\n");
            	break;
	    } else if (!strncmp(pCmd,"EXECUTE",7))			//EXECUTE operation system command----
            {   system(pArg);						//execute command

		if ((fd = fopen("zzz.zwz", "r")) == NULL)		//open result file
                {   if (hide==FALSE)
                        perror("srv: could not open result file\n");
		    break;
		} else
		{   i=open("zzz.zwz",O_RDONLY);				//find out file length
		    bytesToRead=filelength(i);
		    ltoa(bytesToRead,tempBuffer,10);
		    close(i);
		    strcat(tempBuffer,"\n");
		    SEND(tempBuffer);					//... and send it
		}

		bytesRead=0;						//now read the file itself
		while (!feof(fd))
                {   memset(&tempBuffer, 0, sizeof(tempBuffer));
		    i = fread(tempBuffer,1,min(sizeof(tempBuffer),bytesToRead),fd);
		    SENDDATA(tempBuffer,i);				//... and send it
		    bytesRead=bytesRead+i;
		    if (bytesRead>=bytesToRead) break;
		};
		fclose(fd); fd=NULL;
	    } else if (!strncmp(pCmd,"SENDFILE",8))			//Send file zzz.xxx
            {

		if ((i=open("zzz.xxx",O_RDONLY))<0)			//find out file length
		{   if (hide==FALSE)
		    	perror("srv: could not open result file\n");
		    break;
		} else
		{   bytesToRead=filelength(i);
		    ltoa(bytesToRead,tempBuffer,10);
		    close(i);
		    strcat(tempBuffer,"\n");
		    SEND(tempBuffer);					//... and send it
		}

		if ((fd = fopen("zzz.xxx", "r+")) == NULL)		//open file
                {   if (hide==FALSE)
                	perror("srv: could not open result file\n");
		    break;
		}

		bytesRead=0;						//now read the file itself
		while (!feof(fd))
                {   memset(&tempBuffer, 0, sizeof(tempBuffer));
		    i = fread(tempBuffer,1,sizeof(tempBuffer),fd);
		    SENDDATA(tempBuffer,i);				//... and send it
		    bytesRead=bytesRead+i;
		    //printf("i=%d     bytesRead=%d    bytesToRead=%d\n",i,bytesRead, bytesToRead);
		    if (bytesRead>=bytesToRead) break;
		};
		fclose(fd); fd=NULL;
	    } else if (!strncmp(pCmd,"RECEIVEFILE",8))			//Receive a file and store as zzz.xxx
            {
            	bytesToRead=atol(pArg);
         	if (bytesToRead<=0) 
         	    if (hide==FALSE)
         	       	printf("Oops, bytesToRead<=0   %s\n",pArg);

		if ((fd = fopen("zzz.xxx", "w+")) == NULL)		//create temporary file
                {   if (hide==FALSE)
                 	perror("srv: could not open result file\n");
		    break;
		}
		if (bytesRead>0)
		{   fwrite(pDat,1,bytesRead,fd);			//write received data to temporary file
		    if (hide==FALSE)
		    	printf("first: i=%d     bytesRead=%d    bytesToRead=%d\n",bytesRead,bytesRead, bytesToRead);
		}
		while (bytesRead<bytesToRead)				//receive and write more data
                {   RECEIVEDATA;
		    bytesRead=bytesRead+i;
		    if (bytesRead>bytesToRead)				//under LINUX sometimes the next command
		    {   j=i-(bytesRead-bytesToRead); 			//is appended to the last data
		        //printf("Warning: Read to much %d\n",(bytesRead-bytesToRead));
		    } else
		        j=i;
		    fwrite(readBuffer,1,j,fd);
		    //printf("loop: i=%d     bytesRead=%d    bytesToRead=%d\n",i,bytesRead, bytesToRead);
		};
		fclose(fd); fd=NULL;
                if (bytesRead>bytesToRead)
		{   memmove(readBuffer,(void*) &readBuffer[j],j);
		    goto scan;
		}
	    }

	}
	while (1);
	//##########################################################################
	closesocket(sd_current);
	sd_current = 0;                                         /*close connection */
#ifndef UNIX
	DEBUG(" srv: closed connection to %d.%d.%d.%d:%5d\n",
	     (currentIP >> 24) & 0xFF,(currentIP >> 16) & 0xFF, (currentIP >> 8) & 0xFF, (currentIP) & 0xFF,Pin.sin_port);
#else
	DEBUG(" srv: closed connection to %d.%d.%d.%d:%5d\n",
	     (currentIP >> 24) & 0xFF,(currentIP >> 16) & 0xFF, (currentIP >> 8) & 0xFF, (currentIP) & 0xFF,Pin.sin_port);
#endif
	DEBUG("------------------------------------------------------------------\n");
    }

//############ Close connection to host ########################################
    //we never should reach this point, as the infinite 'wait for connection' loop can only
    //be left by CTRL-Break.
    closesocket(sd);
    sd = 0;
    WSACleanup();
    if (hide==FALSE)
    	perror("srv:accept failed");
    return (-1);
}

//##################################################################################################
//##################################################################################################
//##################################################################################################
