Navigation
Home
gpl
danovitschwebcam
1.3
server.c








































server.c
   
   /*
    *
    * 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");
   }
   
   
   
   
   

syntax highlighted by Code2HTML, v. 0.9.1


Email me with questions/comments : Daan <Daan @ pa4dan . nl>