// webs.cpp : Defines the entry point for the application.
//
// Oscar's webserver supports GET and POST
// Tested with Firefox / Chrome / Internet explorer / Chrome on Android
// Compiler: Visual studio 2008
// Date: 18/06/2020 04/07/2020


#include "stdafx.h"
#include "webs.h"

#pragma comment(lib,"ws2_32.lib") //Winsock Library
#pragma comment(lib, "IPHLPAPI.lib")

#define MAX_SOCKETS		10
#define PORT			8080
// Global Variables:
HINSTANCE					hInst;
HWND						hWnd;
TCHAR						szTitle[] = L"Webserver";					// The title bar text
TCHAR						szWindowClass[] = L"WEB$";			   // the main window class name
WSADATA						wsaData;
SOCKET						listen_socket = INVALID_SOCKET;
SOCKET						client_socket[MAX_SOCKETS];
SOCKADDR_IN					server_addr;
SOCKADDR_IN					client_addr;
SOCKADDR_IN					addr;
int							client_address_len;
int							count,size,len,n;
char						receive_buf[600*1024+1];            //[100*1024+1];
char						header_buf[1024+1];
GLOBALHANDLE				hGMem;
char						*html_buf,*mem;
char						*start,*ptr;
TCHAR						*w_ptr;
TCHAR						w_filename[MAX_PATH+1];
char						filename[MAX_PATH+1];
TCHAR						debug[200+1];
HANDLE						hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA				ffd;
HANDLE						hFile;
DWORD						filesize,bytesWritten,bytesRead;
int 						header_size;
int							multipart_header_size;
int 						use_mime_type;
TCHAR 						ip_addresses[100];
PIP_ADAPTER_ADDRESSES 		pAddresses = NULL;
PIP_ADAPTER_ADDRESSES 		pCurrAddresses = NULL;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
ULONG 						buf_len;
char 						boundary[70+2+1];
SYSTEMTIME					st;
FILETIME					ft;
FD_SET						ReadSet;

const unsigned char fav_icon[] = {	0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x00, 0x01, 0x00, 0x04, 0x00, 0x28, 0x01,
							0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00,
							0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0xFF, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x00, 0x11, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x11, 0x00, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10,
							0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10,
							0x11, 0x10, 0x11, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
							0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x5B, 0xB7,
							0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x5B, 0xB7, 0x00, 0x00, 0x1B, 0xB3,
							0x00, 0x00, 0x5B, 0xB5, 0x00, 0x00, 0x5B, 0xB5, 0x00, 0x00, 0x5B, 0xB5, 0x00, 0x00, 0x5B, 0xB5,
							0x00, 0x00, 0x5B, 0xB5, 0x00, 0x00, 0x51, 0x13, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 };
							
							
const char error_400_page[] = {"<!DOCTYPE html>\r\n<html>\r\n"
							  "<head><title>400 Bad Request</title></head>\r\n"
							  "<body bgcolor=\"white\">\r\n"
							  "<center><h1>400 Bad Request</h1></center>\r\n"
							  "</body>\r\n</html>\r\n"};

const char error_403_page[] = {"<!DOCTYPE html>\r\n<html>\r\n"
							  "<head><title>403 Forbidden</title></head>\r\n"
							  "<body bgcolor=\"white\">\r\n"
							  "<center><h1>403 Forbidden</h1></center>\r\n"
							  "</body>\r\n</html>\r\n"};


const char error_404_page[] = {"<!DOCTYPE html>\r\n<html>\r\n"
							  "<head><title>404 Not Found</title></head>\r\n"
							  "<body bgcolor=\"white\">\r\n"
							  "<center><h1>404 Not Found</h1></center>\r\n"
							  "</body>\r\n</html>\r\n"};
							  
const char error_411_page[] = {"<!DOCTYPE html>\r\n<html>\r\n"
							  "<head><title>411 Length Required</title></head>\r\n"
							  "<body bgcolor=\"white\">\r\n"
							  "<center><h1>411 Length Required</h1></center>\r\n"
							  "</body>\r\n</html>\r\n"};

const char success_201_page[] = {"<!DOCTYPE html>\r\n<html>\r\n"
							  "<head><title>Upload file</title></head>\r\n"
							  "<body bgcolor=\"white\">\r\n"
							  "<center><h1>Your file is saved!</h1></center>\r\n"
							  "</body>\r\n</html>\r\n"};


const char days[][3+1] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const char months[][3+1] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};


struct
{
	TCHAR	*ext;
	char	*filetype;
}
mime_types[] =
{
	{TEXT(".html"),	"text/html"},			// keep this one first!!
	{TEXT(".ico"),	"image/x-icon"}, 		// keep this one second !!
	{TEXT(".css"),	"text/css"},
	{TEXT(".gif"),	"image/gif"},
	{TEXT(".jpg"),	"image/jpeg"},
	{TEXT(".jpeg"),	"image/jpeg"},
	{TEXT(".png"),	"image/png"},
	{TEXT(".zip"),	"image/zip"},
	{TEXT(".gz"),	"image/gz"},
	{TEXT(".tar"),	"image/tar"},
	{TEXT(".htm"),	"text/html"},
	{TEXT(".txt"),	"text/plain"},
	{TEXT(".c"),	"text/plain"},
	{TEXT(".cpp"),	"text/plain"},
	{TEXT(".h"),	"text/plain"},
	{TEXT(".pdf"),	"application/pdf"},
	{TEXT(".svg"),	"image/svg+xml"},
    {TEXT("\0"),	"application/octet-stream"}		// default action: download file
};


BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch(Message)
    {
        case WM_INITDIALOG:

        return TRUE;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDOK:
                    EndDialog(hwnd, IDOK);
                break;

                case IDCANCEL:
                    EndDialog(hwnd, IDCANCEL);
                break;
            }
        break;

        default:
            return FALSE;
    }
    return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		if (WSAStartup(MAKEWORD(2,2), &wsaData ))
		{
			MessageBox(hWnd,TEXT("Error"),TEXT("WSAStartup"),MB_OK);
			break;
		}
		if (wsaData.wVersion != MAKEWORD(2,2))
		{
			MessageBox(hWnd,TEXT("Error"),TEXT("WSAStartup wrong version"),MB_OK);
			break;
		}
//-------------------Get Server IP addresses- (for printing only)--------------------------------------
		buf_len = 0UL;
		GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &buf_len);
		pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, buf_len);
		GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX,NULL,pAddresses,&buf_len);
		pCurrAddresses = pAddresses;
		ZeroMemory(ip_addresses,sizeof(ip_addresses));
		wcscpy(ip_addresses,TEXT("Listen on: "));
		n=wcslen(ip_addresses);
		while(pCurrAddresses != NULL)
		{
			if (pCurrAddresses->OperStatus == IfOperStatusUp)
			{
				pUnicast = pCurrAddresses->FirstUnicastAddress;
				while(pUnicast != NULL)
				{
					memcpy(&server_addr,pUnicast->Address.lpSockaddr,sizeof(SOCKADDR_IN));
					n+=mbstowcs(ip_addresses+n,inet_ntoa(server_addr.sin_addr),16);
					wcscat(ip_addresses,TEXT(" "));
					n++;
					pUnicast=pUnicast->Next;
				}
			}
			pCurrAddresses=pCurrAddresses->Next;
		}
		ip_addresses[n++] = L':';
		_itow(PORT,ip_addresses+n, 10 );
		HeapFree(GetProcessHeap(), 0, pAddresses);
//----------------------------------------------------------------------------------------------------
		for(n=0;n < MAX_SOCKETS;n++)
			client_socket[n] = INVALID_SOCKET;
		if ((listen_socket=socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
		{
			MessageBox(hWnd,TEXT("Error"),TEXT("socket"),MB_OK);
			break;
		}
		wsprintf(debug,TEXT("listen_socket = %d\r\n"),(int) listen_socket);
		OutputDebugString(debug);
		ZeroMemory(&server_addr,sizeof(SOCKADDR_IN));
		server_addr.sin_family = AF_INET;
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		server_addr.sin_port = htons(PORT);
		if (bind(listen_socket, (SOCKADDR *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
		{
			MessageBox(hWnd,TEXT("Error"),TEXT("bind"),MB_OK);
			break;
		}
		listen(listen_socket, 10);
		WSAAsyncSelect(listen_socket,hWnd,(WM_USER+1), FD_CLOSE | FD_READ | FD_ACCEPT);
		break;

	case WM_USER+1:
		OutputDebugString(TEXT("!!WM_USER+1!!\r\n"));
		if (WSAGETSELECTERROR(lParam))
        {
			OutputDebugString(TEXT("--WSAGETSELECTERROR--\r\n"));
			for(n=0;n < MAX_SOCKETS;n++)
			{
				if (client_socket[n] == (SOCKET) wParam)
				{
					WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
					closesocket(client_socket[n]);
					client_socket[n] = INVALID_SOCKET;
					wsprintf(debug,TEXT("Error: Socket %d\r\n"),n);
					OutputDebugString(debug);
					break;
				}
			}
			if (listen_socket == (SOCKET) wParam)
			{
				OutputDebugString(TEXT("Error Listen_socket\r\n"));
			}
			break;
		}
		switch(WSAGETSELECTEVENT(lParam))
		{
			case FD_ACCEPT:
				OutputDebugString(TEXT("--FD_ACCEPT--\r\n"));
				ZeroMemory(&client_addr,sizeof(SOCKADDR_IN));
				client_address_len = sizeof(SOCKADDR_IN);
				for(n=0;n < MAX_SOCKETS;n++)
				{
					if (client_socket[n] == INVALID_SOCKET)
					{
						client_socket[n] = accept(listen_socket, (sockaddr*) &client_addr, &client_address_len);
						if (client_socket[n] != INVALID_SOCKET)
						{
							WSAAsyncSelect(client_socket[n],hWnd,(WM_USER+1),(FD_READ | FD_CLOSE));
							wsprintf(debug,TEXT("Accept socket=%d [%d]\r\n"),client_socket[n],n);
							OutputDebugString(debug);
						}
						break;
					}
				}
			break;
			
			case FD_CLOSE:
				wsprintf(debug,TEXT("--FD_CLOSE--%d\r\n"),(int)wParam);
				OutputDebugString(debug);
				for(n=0;n < MAX_SOCKETS;n++)
				{
					if (client_socket[n] == (SOCKET) wParam)
					{
						WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
						closesocket(client_socket[n]);
						wsprintf(debug,TEXT("closed socket=%d [%d]\r\n"),client_socket[n],n);
						OutputDebugString(debug);
						client_socket[n] = INVALID_SOCKET;
						break;
					}
				}
			break;

			case FD_READ:
				wsprintf(debug,TEXT("--FD_READ-- %d\r\n"),(int)wParam);
				OutputDebugString(debug);
				for(n=0;n < MAX_SOCKETS;n++)
				{
					if (client_socket[n] == (SOCKET) wParam)
					{
						count=0;
						multipart_header_size = 0;
						filesize = 0L;
						ZeroMemory(w_filename,sizeof(w_filename));
						ZeroMemory(receive_buf,sizeof(receive_buf));
						ZeroMemory(header_buf,sizeof(header_buf));
						size=recv(client_socket[n],receive_buf,sizeof(receive_buf)-1,0);
						wsprintf(debug,TEXT("recv size=%d\r\n"),size);
						OutputDebugString(debug);
						if ((start=strstr(receive_buf,(const char*)"POST")) != NULL)
						{
//----------------------------------------------------------------- POST HANDLING --------------------------------------------------------------
							OutputDebugString(TEXT("POST\r\n"));
							if ((ptr=strstr(receive_buf,(const char *)"\r\n\r\n")) != NULL)
							{
								ptr+=4;
								header_size = (int) (ptr-receive_buf);
								wsprintf(debug,TEXT("header_size=%d\r\n"),header_size);
								OutputDebugString(debug);
							}
							if ((ptr=strstr(receive_buf,(const char *)"Content-Length: ")) != NULL)
							{
								filesize=atol(ptr+16);
								wsprintf(debug,TEXT("filesize=%ld\r\n"),filesize);
								OutputDebugString(debug);
							}
							else
							{
								WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
								while(recv(client_socket[n],receive_buf,sizeof(receive_buf),0) > 0)
									;;;
								wsprintfA(header_buf,"HTTP/1.0 411 Length Required\r\n"
													"Content-Type: %s\r\nContent-Length: %d\r\n"
													"Connection: close\r\n\r\n",
													mime_types[0].filetype,
													sizeof(error_411_page)-1);
								send(client_socket[n], header_buf, strlen(header_buf),0);
								send(client_socket[n], error_411_page,sizeof(error_411_page)-1,0);
								closesocket(client_socket[n]);
								client_socket[n] = INVALID_SOCKET;
								break;
							}
							hGMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,filesize+1000);
							mem=(char*)hGMem;
							memcpy(mem,receive_buf+header_size,(size-header_size));
							bytesRead = (size-header_size);
							wsprintf(debug,TEXT("header_size = %d filesize = %d size = %d"),header_size,filesize,size);
							OutputDebugString(debug);
							if ((header_size + filesize) > size)
							{
								while(bytesRead < filesize)
								{		
									wsprintf(debug,TEXT("%d < %d\r\n"),bytesRead , filesize);
									OutputDebugString(debug);
									if ((len=recv(client_socket[n],mem+bytesRead,filesize,0)) == -1) // read remaining part of the file
									{
										if (WSAGetLastError() == WSAEWOULDBLOCK)					//in case data in second packet
										{
											WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
											FD_ZERO(&ReadSet);
											FD_SET(client_socket[n], &ReadSet);
											if (select(0, &ReadSet, NULL, NULL, NULL) == SOCKET_ERROR)
											{
												wsprintf(debug,TEXT("select errror%d\r\n"),WSAGetLastError());
												OutputDebugString(debug);
												closesocket(client_socket[n]);
												client_socket[n] = INVALID_SOCKET;
												break;	
											}
											if (FD_ISSET(client_socket[n], &ReadSet))
											{
												continue;
											}
										}
									}
									else
									{
										wsprintf(debug,TEXT("len%d\r\n"),len);
										OutputDebugString(debug);
										bytesRead+=len;
									}
								}
							}
							if ((ptr=strstr(receive_buf,(const char*)" HTTP")) != NULL)
							{
								*ptr='\0';
								mbstowcs(w_filename,start+5,sizeof(w_filename)/sizeof(w_filename[0]));
								*ptr=' ';
								OutputDebugString(w_filename);
								if ((w_filename[0] == L'/') && (w_filename[1] == L'\0'))
								{
									if (strstr(mem,"name=\"upload\";") == NULL)  
									{
											WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
											wsprintfA(header_buf,"HTTP/1.0 400 Bad Request\r\n"
													"Content-Type: %s\r\nContent-Length: %d\r\n"
													"Connection: close\r\n\r\n",
													mime_types[0].filetype,
													sizeof(error_400_page)-1);
											send(client_socket[n], header_buf, strlen(header_buf),0);
											send(client_socket[n], error_400_page, sizeof(error_400_page)-1,0);
											closesocket(client_socket[n]);
											client_socket[n] = INVALID_SOCKET;
											break;
									}
									if ((start=strstr(mem,"filename=\"")) != NULL)
									{
										if ((ptr=strchr(start+10,'\"')) != NULL)
										{
											w_filename[0] = L'/';
											*ptr='\0';
											mbstowcs(w_filename+1,start+10,sizeof(w_filename)/sizeof(w_filename[0]));
											*ptr='\"';
										}
										if ((start=strstr(receive_buf,"boundary=")) != NULL)
										{
											ZeroMemory(boundary,sizeof(boundary));
											start+=9;
											if ((ptr=strchr(start,'\r')) != NULL)
											{
												*ptr='\0';
												boundary[0] = '-';
												boundary[1] = '-';
												strcat(boundary,start);
												*ptr='\r';
											}
											if ((start = strstr(mem,boundary)) != NULL)
											{
												if ((ptr=strstr(start,(const char *)"\r\n\r\n")) != NULL)
												{
													ptr+=4;
													multipart_header_size = (int) (ptr-mem);
													//multipart_header_size -= header_size;
													wsprintf(debug,TEXT("multi_part header_size=%d\r\n"),multipart_header_size);
													OutputDebugString(debug);
													filesize -= multipart_header_size;
													filesize -= (2 + strlen(boundary) + 2 + 2);
												}
											}
										}
									}
								}
								if ((w_ptr=wcsrchr(w_filename,L'\\')) != NULL)								// IE sends the whole filepath
									wmemmove(w_filename+1,w_ptr+1,wcslen(w_filename)+1-(w_ptr-w_filename)); // Get only the filename

								wmemmove(w_filename+1,w_filename,wcslen(w_filename));		// convert / to .\ for windows filesystem
								w_filename[0] = L'.';
								w_filename[1] = L'\\';
							}

							hFile = CreateFile(w_filename,GENERIC_WRITE,FILE_SHARE_READ,0,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,0);  
							if (hFile == INVALID_HANDLE_VALUE)
							{
								WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
								wsprintfA(header_buf,"HTTP/1.0 403 Forbidden\r\n"
													"Content-Type: %s\r\nContent-Length: %d\r\n"
													"Connection: close\r\n\r\n",
													mime_types[0].filetype,
													sizeof(error_403_page)-1);
								send(client_socket[n], header_buf, strlen(header_buf),0);
								send(client_socket[n], error_403_page,sizeof(error_403_page)-1,0);
								closesocket(client_socket[n]);
								client_socket[n] = INVALID_SOCKET;
								break;
							}
							WriteFile(hFile,mem+multipart_header_size,filesize,&bytesWritten,0);
							GlobalUnlock(mem);
							CloseHandle(hFile);
							WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
							GetSystemTime(&st);
							ZeroMemory(filename,sizeof(filename));
							wcstombs(filename, w_filename, sizeof(filename)-1);
							ZeroMemory(&addr,sizeof(SOCKADDR_IN));
							len=sizeof(SOCKADDR_IN);
							getsockname(client_socket[n],(sockaddr*)&addr,&len);
							wsprintfA(header_buf,"HTTP/1.0 201 Created\r\n"
											     "Date: %s,%02d %s %d %02d:%02d:%02d GMT\r\n"
												 "Content-Type: %s\r\nContent-Length: %d\r\n"
												 "Location: http://%s:%d/%s\r\n"
												 "Server: WEBS\r\n"
												 "Connection: close\r\n\r\n",
													days[st.wDayOfWeek],
													st.wDay,
													months[st.wMonth-1],
													st.wYear,
													st.wHour,
													st.wMinute,
													st.wSecond,
													mime_types[0].filetype,
													sizeof(success_201_page)-1,
													inet_ntoa(addr.sin_addr),PORT,filename+2);
							send(client_socket[n], header_buf, strlen(header_buf),0);
							send(client_socket[n], success_201_page,sizeof(success_201_page)-1,0);
							Sleep(1000);						// Needed for Chrome ??
							closesocket(client_socket[n]);
							client_socket[n] = INVALID_SOCKET;
							break;
//-------------------------------------------------------------------------------------------------------------------------------------------------
						}
						if ((start=strstr(receive_buf,(const char*)"GET")) != NULL)
						{
//------------------------------------------------------------------------------------GET Handling -------------------------------------------------							
							ZeroMemory(&ft,sizeof(FILETIME));
							OutputDebugString(TEXT("GET\r\n"));
							if ((ptr=strstr(receive_buf,(const char*)" HTTP")) != NULL)
								*ptr='\0';
							ZeroMemory(w_filename,sizeof(w_filename));
							mbstowcs(w_filename,start+4,sizeof(w_filename)/sizeof(w_filename[0]));
							OutputDebugString(w_filename);
							if (wcscmp(w_filename,TEXT("/favicon.ico")) == 0)
							{
								header_buf[0] = '\0';
								hGMem=GlobalAlloc(GMEM_FIXED,sizeof(fav_icon));
								html_buf=(char*)hGMem;
								len = sizeof(fav_icon)-1;
								memcpy(html_buf,fav_icon,len);
								use_mime_type = 1;						// .ico
							}
							else if (wcscmp(w_filename,TEXT("/upload.html")) == 0)
							{
								hGMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,20*1024);
								html_buf=(char*)hGMem;
								strcpy(html_buf,"<!DOCTYPE html>\r\n"
												"<head>\r\n"
												"<title>upload</title>\r\n"
												"</head>\r\n"
												"<body bgcolor=\"white\">\r\n"
												"<form action=\"http://");
								ZeroMemory(&addr,sizeof(SOCKADDR_IN));
								len=sizeof(SOCKADDR_IN);
								getsockname(client_socket[n],(sockaddr*)&addr,&len);
								strcat(html_buf,inet_ntoa(addr.sin_addr));
								strcat(html_buf,":");
								len=strlen(html_buf);
								_itoa(PORT,html_buf+len,10);
								strcat(html_buf,"\" method=\"post\" enctype=\"multipart/form-data\">\r\n"
								"<p><input type=\"file\" name=\"upload\">\r\n"
								"<p><button type=\"submit\">Upload</button>\r\n"
								"</form>\r\n"
								"</body>\r\n"
								"</html>\r\n");
								len=strlen(html_buf);
								use_mime_type=0;
							}
							else if ((w_filename[0] == L'/') && (w_filename[1] == L'\0'))
							{
								hGMem=GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,100*1024);
								html_buf=(char*)hGMem;
								strcpy(html_buf,"<!DOCTYPE html>\r\n"
												"<HTML>\r\n<HEAD>\r\n"
												"<TITLE>Directory:</TITLE>\r\n"
												"</HEAD>\r\n<BODY bgcolor=\"white\">"
												"<a href=\"upload.html\">Upload a new file</a><hr><br>\r\n");
								hFind = FindFirstFile(L".\\*.*", &ffd);
								if (hFind != INVALID_HANDLE_VALUE)
								{
									do
									{
										if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
										{
											strcat(html_buf,"<a href=\"");
											ptr=html_buf+strlen(html_buf);
											wcstombs(ptr, ffd.cFileName, wcslen(ffd.cFileName));
											strcat(html_buf,"\">");
											ptr=html_buf+strlen(html_buf);
											wcstombs(ptr, ffd.cFileName, wcslen(ffd.cFileName));
											strcat(html_buf,"</a><br>\r\n");
										}
									}
									while (FindNextFile(hFind, &ffd) != 0);
									FindClose(hFind);
								}
								strcat(html_buf,"</body>\r\n</html>\r\n");
								len=strlen(html_buf);
								use_mime_type=0;
							}
							else
							{
								wmemmove(w_filename+1,w_filename,wcslen(w_filename));		// convert / to .\ for windows filesystem
								w_filename[0] = L'.';
								w_filename[1] = L'\\';
								OutputDebugString(TEXT("open file\r\n"));
								hFile = CreateFile(w_filename,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);  
								if (hFile != INVALID_HANDLE_VALUE)
								{
									filesize = GetFileSize(hFile,NULL);
									GetFileTime(hFile, NULL,NULL,&ft);
									hGMem=GlobalAlloc(GMEM_FIXED,filesize+1);
									html_buf=(char*)hGMem;
									ReadFile(hFile,html_buf,filesize,&bytesRead,0);
									len=bytesRead;
									CloseHandle(hFile);
									for(use_mime_type = 0;use_mime_type < (sizeof(mime_types) / sizeof(mime_types[0]))-1;use_mime_type++)
									{
										if (wcsstr(w_filename,mime_types[use_mime_type].ext) != NULL)
											break;
									}
								}
								if ((hFile == INVALID_HANDLE_VALUE) || (filesize != bytesRead))
								{
									GlobalUnlock(html_buf);
									hGMem=GlobalAlloc(GMEM_FIXED,sizeof(error_404_page)+1);
									html_buf=(char*)hGMem;
									strcpy(html_buf,error_404_page);
									wsprintfA(header_buf,"HTTP/1.0 404 Not Found\r\n"
														 "Content-Type: %s\r\nContent-Length: %d\r\n"
														 "Connection: close\r\n\r\n",
														 mime_types[0].filetype,
														 strlen(error_404_page));
									OutputDebugString(TEXT("404 Page\r\n"));
								}
							}
							WSAAsyncSelect(client_socket[n], hWnd, 0, 0);
							FileTimeToSystemTime(&ft, &st);
							if (header_buf[0] == '\0')
								wsprintfA(header_buf,"HTTP/1.0 200 OK\r\n"
													"Date: %s,%02d %s %d %02d:%02d:%02d GMT\r\n"
													"Content-Type: %s\r\nContent-Length: %d\r\n"
													"Server: WEBS\r\n"
													"Connection: close\r\n\r\n",
													days[st.wDayOfWeek],
													st.wDay,
													months[st.wMonth-1],
													st.wYear,
													st.wHour,
													st.wMinute,
													st.wSecond,
													mime_types[use_mime_type].filetype,len); 
							send(client_socket[n], header_buf, strlen(header_buf),0);
							if (send(client_socket[n],html_buf,len,0) != len)
							{
								OutputDebugString(TEXT("SEND not finished\r\n"));
							}
							closesocket(client_socket[n]);
							client_socket[n] = INVALID_SOCKET;
							GlobalUnlock(html_buf);
							wsprintf(debug,TEXT("read done for socket = %d\r\n"),n);
							OutputDebugString(debug);
							break;
						}
					}
				}
			break;		// FD_READ

			default:
				OutputDebugString(TEXT("default!!\r\n"));
				break;

		}
		break;

	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
			case IDM_EXIT:
				DestroyWindow(hWnd);
			break;

			case IDM_ABOUT:
				DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutDlgProc);
			break;
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOutW(hdc,10,20,ip_addresses,wcslen(ip_addresses));
		EndPaint(hWnd, &ps);
		break;


	case WM_DESTROY:
		if (listen_socket != INVALID_SOCKET)
			closesocket(listen_socket);
		WSACleanup();
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}





int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WEBS));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WEBS);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	RegisterClassEx(&wcex);

	hWnd = CreateWindow(szWindowClass, 
						szTitle, 
						WS_OVERLAPPEDWINDOW,
						CW_USEDEFAULT, 
						CW_USEDEFAULT, 
						CW_USEDEFAULT,
						CW_USEDEFAULT,
						NULL,
						NULL,
						hInstance,
						NULL);

	if (!hWnd)
	{
		return(FALSE);
	}
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int) msg.wParam;
}
