
/*
 *
 * server.c - part of Danovitsch Webcam
 *
 * Copyright (C) 2001 by Daan Vreeken
 *
 * Published under the terms of the GNU Public License 2.0
 * (or any later version)
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>

#include <string.h>


#include "webcam.h"
#include "server.h"
#include "http.h"



int			Clients = 0;
int			AcceptSock = -1;

long			ImagesServed = 0;
long			ImagesSinceRestart = 0;


const char		*StateName[] = {"Waiting for request",
				"Getting header",
				"Getting POST data",
				"Sending response",
				"Zombie"};


struct Connection	*Server_CurrentConnection = NULL;




struct Connection	 Client[MaxClients];




int SockSend(struct Connection *C, void *Buf, size_t Len)
{
	long		Error;
	socklen_t	OptLen = sizeof(Error);
	int		Bytes;

	if (!C->Close)
	{
		//fcntl(C->FD,F_SETFD,O_NONBLOCK);
		Server_CurrentConnection=C;
		Bytes=send(C->FD,Buf,Len,0);
		if (Bytes==-1)
		{
			Debug(50,"fd:%d b:%d l:%d",C->FD,Buf,Len);
			Debug(50,"(send) Socket just died...");
			C->Close=1;
		}
		Server_CurrentConnection=NULL;
		//fcntl(C->FD,F_SETFD,0);
	}
	else
		return 1000;

	return Bytes;
}



size_t SockRecv(struct Connection *C, void *Buf, size_t Len)
{
	return recv(C->FD,Buf,Len,0);
}



void Print(struct Connection *C, char *Txt)
{
	SockSend(C,Txt,strlen(Txt));
}



void PrintF(struct Connection *C, char *Txt, ...)
{
	char		Temp[200];
	va_list		List;

	va_start(List,Txt);
	vsprintf(Temp,Txt,List);
	Print(C,Temp);
	va_end(List);
}



void Server_CloseConnection(int Nr)
{
	struct Connection	*C = &Client[Nr];

	if (C->Line!=NULL)
		free(C->Line);

	//shutdown(C->FD,SHUT_RDWR);
	close(C->FD);

	if (Nr<Clients-1)
		*C=Client[Clients-1];
	Clients--;
}



void Server_HandleBytes(struct Connection *C, char *Data, int Size)
{
	char		*Ptr;


	switch (C->State)
	{
	case GetData:
		if (Size+C->PostLength>C->ContentLength)
		{
			Error("Got more data than expected!");
		}
		
		if (C->PostData==NULL)
			C->PostData=(char *)malloc(Size);
		else
			C->PostData=(char *)realloc(C->PostData,C->PostLength+Size);

		if (C->PostData==NULL)
		{
			Error("Could not allocate PostData buffer! (dropping connection)");
			C->Close=1;
			return;
		}

		Ptr=C->PostData + C->PostLength;
		memcpy(Ptr,Data,Size);
		C->PostLength+=Size;

		if (C->PostLength>=C->ContentLength)
		{
			Debug(60,"Received POST data");

			//Let's start answering the thing...
			C->State=Response;
			shutdown(C->FD,SHUT_RD);
		}
		
		break;

	default:
		Error("Should not have gotten here!!");
	}
}



void Server_HandleLine(struct Connection *C)
{
	char		**Ptr;
	char		*Temp;


	switch (C->State)
	{
	case GetRequest:
		C->Request=(char *)malloc(C->Length+1);
		if (C->Request==NULL)
		{
			Error("Could not allocate memory for Request! (client will be dropped)");
			C->Close=1;
			break;
		}
		else
		{
			memcpy(C->Request,C->Line,C->Length+1);
		}

		//split request in 3 parts
		Temp=C->Request;
		for (Ptr=C->RequestArg; (*Ptr=strsep(&Temp," "))!=NULL;)
			if (**Ptr!=0)
				if (++Ptr>=&(C->RequestArg[3]))
					break;

		//Check action
		if (((C->RequestArg[0]==NULL) || (C->RequestArg[1]==NULL) || (C->RequestArg[2]==NULL)) ||
			((strcmp(C->RequestArg[0],"GET")) && (strcmp(C->RequestArg[0],"POST"))))
		{
			HTTP_Header(C,501,NULL);
			C->Close=1;
			return;
		}

		Debug(90,"Request: %s",C->RequestArg[1]);
		C->State=GetHeader;
		break;

	case GetHeader:
		if (C->Length==0)
		{
			if (C->Request==NULL)
			{
				HTTP_Header(C,501,NULL);

				//Dump connection now...
				C->Close=1;
				C->State=Zombie;
				Debug(50,"Closing connection...");
				return;
			}

			//GET or POST ?
			if (!strcmp(C->RequestArg[0],"GET"))
			{
				C->State=Response;
				shutdown(C->FD,SHUT_RD);
			}
			else
			{
				C->State=GetData;
				C->Mode=ByteMode;
			}
		}
		else
		{
			//Get 'Content-length'
			if (!strncasecmp(C->Line,"Content-length: ",16))
			{
				sscanf(&C->Line[16],"%d",&C->ContentLength);
				if (C->ContentLength>HTTP_MaxContentLength)
				{
					HTTP_Header(C,501,NULL);
					C->Close=1;
					return;
				}
			}

			Debug(70,"Header: %s",C->Line);
		}
		break;

	default:
		Debug(10,"Dumped line...");
	}
}



void Server_HandleData(int Conn)
{
	struct Connection	*C = &Client[Conn];
	int			Readed;
	char			Temp[1000];
	int			Cnt;
	int			WasInLineMode = 0;
	int			LastLineByte = 0;

	if (C->Line==NULL)
	{
		C->Line=(char *)malloc(MaxLineSize+1);
		if (C->Line==NULL)
		{
			Error("Could not allocate Line buffer! - dumping client");
			C->Close=1;
			return;
		}
	}


	Readed=SockRecv(C,&Temp,sizeof(Temp)-1);
	Temp[Readed]=0;
	if (Readed==0)
	{
		Debug(50,"(read) Connection lost...");
		C->Close=1;
	}

	if (C->Mode==LineMode)
	{
		for (Cnt=0; Cnt<Readed; Cnt++)
			switch (Temp[Cnt])
			{
			case '\r':
				break;

			case '\n':
				*(C->Line+C->Length)=0;
	
				Server_HandleLine(C);
				if (C->Mode==ByteMode)
				{
					LastLineByte=Cnt;
					WasInLineMode=1;
					Cnt=Readed+1;
				}
	
				C->Length=0;
				break;
	
			default:
				if (C->Length==MaxLineSize)
					break;
	
				*(C->Line+C->Length++)=Temp[Cnt];
			}
	}

	if (C->Mode==ByteMode)
	{
		if (WasInLineMode)
			Server_HandleBytes(C,&Temp[LastLineByte+1],(Readed-LastLineByte)-1);
		else
			Server_HandleBytes(C,&Temp[0],Readed);
	}
}



void Server_Init(void)
{
	FILE		*CountFile = fopen("webcam.count","r");
	
	if (CountFile!=NULL)
	{
		fread(&ImagesServed,sizeof(ImagesServed),1,CountFile);
		fclose(CountFile);
		Output("Restarting ImagesServed counter at %ld",ImagesServed);
	}
	else
	{
		CountFile=fopen("webcam.count","w");
		
		if (CountFile!=NULL)
		{
			ImagesServed=0;
			fwrite(&ImagesServed,sizeof(ImagesServed),1,CountFile);
			fclose(CountFile);
			Output("webcam.count did not exits. -has been created-");
		}
		else
			Output("Failed to create webcam.count. (do we have permission?)");
	}
	
	ImagesSinceRestart=0;
}



void Server_Shutdown(void)
{
	int		Cnt;
	FILE		*CountFile = fopen("webcam.count","w");

	//drop accept socket
	if (AcceptSock!=-1)
		close(AcceptSock);

	//drop all clients
	if (Clients>0)
	{
		Output("Dropping %d client(s).",Clients);

		while (Clients>0)
			Server_CloseConnection(Clients-1);
	}

	//Write ImagesServer to count file...
	if (CountFile!=NULL)
	{
		fwrite(&ImagesServed,sizeof(ImagesServed),1,CountFile);
		fclose(CountFile);
	}
	else
		Output("Could not write webcam.count! (do we have permission?)");

	Output("Shutdown server");
}



