jWebServer : a simple C++ web server

Download jwebserver.zip

Synopsis:

App.cpp
App.h
Buffer.cpp
Buffer.h
cgi.cpp
common.h
config.cpp
ConfigInfo.h
DirLister.cpp
DirLister.h
Html.h
HtmlBuffer.h
HtmlDirGen.h
HtmlPageGen.h
HtmlStream.h
HttpOutStream.cpp
HttpOutStream.h
HttpRequest.cpp
HttpRequest.h
HttpServer.cpp
HttpServer.h
install.cpp
Install.h
jwebserver.cpp
NtHandle.h
request.cpp
Request.h
Socket.cpp
Socket.h


App.cpp

Synopsis
#include <windows.h>

#include "common.h"
#include "App.h"
#include "resource.h"
#include "Request.h"
#include "HttpRequest.h"
#include "Socket.h"
#include "ConfigInfo.h"
#include "Install.h"

const char* szClassName  ="jWebServerClass";// The main window's class

LRESULT WINAPI App::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
  {
  switch(msg)
    {
    case WM_CREATE: web.OnWinCreate(); return 0;
    case WM_ACTIVATE: web.OnWinActivate(wparam); return 0;
    case WM_CLOSE: web.OnWinClose(); return 0;
    case WM_PAINT: web.OnPaint(); return 0;
    case WM_USER:
      switch(WSAGETSELECTEVENT(lparam))
        {
        case FD_CLOSE: web.OnWinSocketClose(wparam); break;
        case FD_ACCEPT: web.OnWinSocketAccept(wparam); break;
        }
      break;
    }
  
  return DefWindowProc(hwnd,msg,wparam,lparam); 
  }

LRESULT WINAPI App::MainDlgProc(HWND hd, UINT msg, WPARAM wparam, LPARAM lparam )
  {
  switch(msg)
    {
    case WM_INITDIALOG: web.OnInitDialog(hd);  return FALSE;
    case WM_CLOSE:      web.OnCloseDialog(); return FALSE;
    case WM_COMMAND:
      {
      switch( LOWORD(wparam) )
        {
        case IDOK:       web.OnClickOk();     return TRUE;
        case IDCANCEL:   web.OnClickCancel(); return TRUE;
        case IDC_CONFIG: web.OnClickConfig(); return TRUE;
        case IDC_ABOUT:  web.OnClickAbout();  return TRUE;
        case IDC_START:  web.OnClickStart();  return TRUE;
        case IDC_STOP:   web.OnClickStop();   return TRUE;
        }
      }
      break;
    case WM_USER:
      {
      switch(lparam)
        {
        case WM_LBUTTONUP: web.ShowDialogWindow(); break;
        }
      }
      break;
    }
  return FALSE;
  }

void App::Create(HINSTANCE hInst)
  {
  RegisterWindowClass(hInst);
  CreateMainWindow();
  CreateMainDialog();
  }

void App::OnWinCreate()
  {
  *mText=0;
  if (gConfigInfo.m_error)
    {
    Install inst(mInstance, mDlg);
    inst.Run();
    return;
    }

  if (!Socket::InitEnvironment())
    SetWinTitle("WinSock(TCP/IP) is not installed on this machine.");
  }

void App::SetNonBlockingMode(Socket& sock)
  {
  sock.SetNonBlockingMode(mHwnd);
  }

void App::OnWinSocketAccept(WPARAM wparam)
  {
  Socket s(wparam);
  if (mWebKnown.Compare(s) )
    mWebKnown.Accept();
  else if( mWebServer.Compare(s) )
    mWebServer.Accept();
  }

void App::OnWinSocketClose(WPARAM wparam)
  {
  Socket s(wparam);
  s.Close();
  }

void App::OnWinActivate(WPARAM wparam)
  {
  if(LOWORD(wparam)==WA_INACTIVE )
    ShowWindow(mHwnd,SW_HIDE);
  }

void App::OnWinClose()
  {
  PostQuitMessage(1);
  }

void App::OnInitDialog(HWND hwnd)
  {
  mDlg = hwnd;
  ServerEvent(0, 2);
  
  for(int i=0;i<HTTP_CONNECTIONS;i++)
    {
    mWebServer.m_connections[i]=new Request();
    mWebKnown.m_connections[i]=new Request();
    }
  
  mWebServer.StartServer(gConfigInfo.m_port);
  }

void App::OnCloseDialog()
  {
  Socket::TermEnvironment();
  }

void App::OnClickOk()
  {
  HideDialogWindow();
  }

void App::OnClickCancel()
  {
  if( MessageBox(NULL,"Are you sure you want to stop jWebServer and quit?",szAppName,MB_YESNO) == IDYES )
    PostQuitMessage(1);
  }

void App::OnClickConfig()
  {
  mWebKnown.m_autoFindPort=true;
  mWebKnown.StartServer(30001);
  if (mWebKnown.IsRunning())
    {
    char str[1024];
    wsprintf(str,"http://127.0.0.1:%d/admin/index.html", GetPort());
    LaunchBrowser(str);
    }
  }

void App::OnClickAbout()
  {
  mWebKnown.m_autoFindPort=true;
  mWebKnown.StartServer(30001);
  if (mWebKnown.IsRunning())
    {
    char str[1024];
    wsprintf(str,"http://127.0.0.1:%d/admin/about.html", GetPort());
    LaunchBrowser(str);
    }
  }

void App::OnClickStart()
  {
  mWebServer.StartServer(gConfigInfo.m_port);
  }

void App::OnClickStop()
  {
  mWebServer.StopServer();
  }

int App::GetPort()
  {
  return mWebKnown.GetPort();
  }

void App::TaskNotify(int action,int icon,char *tip)
  {
  NOTIFYICONDATA nid;
  
  memset(&nid,0,sizeof(NOTIFYICONDATA));
  
  nid.cbSize=sizeof(NOTIFYICONDATA);
  nid.hWnd=mDlg; 
  nid.uID=0;
  nid.uFlags=NIF_ICON|NIF_MESSAGE |NIF_TIP;
  nid.uCallbackMessage=WM_USER;
  nid.hIcon=LoadIcon(mInstance,MAKEINTRESOURCE(icon));
  strncpy(nid.szTip,tip,sizeof(nid.szTip));
  Shell_NotifyIcon(action,&nid);
  }


void App::LaunchBrowser(char *url)
  {
  char str[1024],str2[1024],*ptr;
  long dw;
  
  dw=1024;
  
  if( RegQueryValue( HKEY_CLASSES_ROOT,".html",str,&dw)!=ERROR_SUCCESS )
    {
    if( RegQueryValue( HKEY_CLASSES_ROOT,".htm",str,&dw)!=ERROR_SUCCESS )
      {
      MessageBox(NULL,"Error: Can't locate a browser.","Error",MB_OK);
      return;
      }
    }
  
  strcat(str,"\\shell\\open\\command");
  dw=1024;
  if( RegQueryValue( HKEY_CLASSES_ROOT,str,str2,&dw)!=ERROR_SUCCESS )
    {
    MessageBox(NULL,"Error: Can't locate a browser.","Error",MB_OK);
    return;
    }
  
  
  ptr=strstr(str2,"%1");
  if(ptr!=NULL)
    {
    strncpy(str,ptr+2,sizeof(str));
    strcpy(ptr,url);
    strcat(str2,str);
    
    }
  else
    {
    strcat(str2," ");
    strcat(str2,url);
    }
  
  WinExec(str2,SW_MAXIMIZE);
  }

void App::SetWinTitle(const char *s)
  {
  SIZE sz;
  HDC hdc;
  int x,y,maxx,maxy;
  
  strncpy(mText,s,sizeof(mText));
  hdc = GetDC(mHwnd);
  
  GetTextExtentPoint32( 
    hdc, 
    mText, 
    strlen(mText), 
    &sz); 
  
  maxx = sz.cx+20;
  if(maxx<300)
    maxx=300;
  maxy = sz.cy*3;
  x = (GetSystemMetrics(SM_CXSCREEN)/2) - (maxx/2);
  y = (GetSystemMetrics(SM_CYSCREEN)/2) - (maxy/2);
  
  SetWindowPos(mHwnd,HWND_TOP,x,y,maxx,maxy,0);
  mYLoc=sz.cy;
  mXLoc=(maxx/2)-(sz.cx/2);
  
  ReleaseDC(mHwnd,hdc);
  InvalidateRect(mHwnd,NULL,TRUE);
  ShowWindow(mHwnd,SW_SHOW);
  SetFocus(mHwnd);
  }

void App::OnPaint()
  {
  PAINTSTRUCT ps;
  
  HDC hdc = BeginPaint(mHwnd, &ps);
  SetBkMode(hdc, TRANSPARENT);
  TextOut(hdc, mXLoc, mYLoc, mText, strlen(mText));
  EndPaint(mHwnd, &ps);
  }

void App::RegisterWindowClass(HINSTANCE hInst)
  {
  mInstance=hInst;
  
  WNDCLASS wndclass;
  wndclass.style=CS_HREDRAW|CS_VREDRAW;
  wndclass.lpfnWndProc = (WNDPROC) App::WndProc;
  wndclass.cbClsExtra=0;
  wndclass.cbWndExtra=0;
  wndclass.hInstance=hInst;
  wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
  wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
  wndclass.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
  wndclass.lpszMenuName=NULL;
  wndclass.lpszClassName=szClassName;
  RegisterClass(&wndclass);
  }

void App::CreateMainWindow()
  {
  mHwnd=CreateWindow(
    szClassName,
    szAppName,
    WS_POPUP|WS_DLGFRAME,
    10,
    10,
    100,
    100,
    NULL,
    NULL,
    mInstance,
    NULL);
  ShowWindow(mHwnd,SW_HIDE);
  }

void App::GetResource(char* name, DWORD& contentLength, BYTE*& byt, char* location)
  {
  HRSRC hrc = FindResource(mInstance, name, "WEBDAT");
  HGLOBAL hglb = LoadResource(mInstance, hrc);
  byt = (BYTE*)LockResource(hglb);
  contentLength = SizeofResource(mInstance, hrc);
  }

void App::CreateMainDialog()
  {
  char dlgTitle[80];
  mDlg = ::CreateDialog(mInstance, "MAIN", NULL, (DLGPROC) App::MainDlgProc);
  strncpy(dlgTitle, szAppName, 80);
  strcat(dlgTitle," Control Panel");
  ::SetWindowText(mDlg, dlgTitle);
  ShowDialogWindow();
  }

void App::HideDialogWindow()
  {
  ::ShowWindow(mDlg, SW_HIDE);
  }
void App::ShowDialogWindow()
  {
  ::ShowWindow(mDlg, SW_SHOW);
  ::SetForegroundWindow(mDlg);
  }

void App::CleanUp(void)
  {
  int i;
  
  TaskNotify(NIM_DELETE,IDI_DOWN,"");
  
  mWebKnown.StopServer();
  mWebServer.StopServer();
  
  DestroyWindow(mDlg);
  
  for(i=0;i<HTTP_CONNECTIONS;i++)
    {
    delete mWebServer.m_connections[i];
    delete mWebKnown.m_connections[i];
    }
  
  }


void App::AddSlash(char *f)
  {
  if(*f == 0)
    return;
  
  if( f[strlen(f)-1]!='\\')
    strcat(f,"\\");
  }

// 0=up,1=down,2=add
void App::ServerEvent(HttpServer *source,int type)
  {
  char *t;
  
  if(source==&mWebKnown)
    return;
  
  switch(type)
    {
    case 0:
      t="jWebServer Running";
      TaskNotify(NIM_MODIFY,IDI_UP,t);
      SetDlgItemText(mDlg,IDC_STATUS,t);
      EnableWindow(GetDlgItem(mDlg,IDC_START),FALSE);
      EnableWindow(GetDlgItem(mDlg,IDC_STOP),TRUE);
      break;
      
    case 1:
      t = "jWebServer Not Running";
      TaskNotify(NIM_MODIFY,IDI_DOWN,t);
      SetDlgItemText(mDlg,IDC_STATUS,t);
      EnableWindow(GetDlgItem(mDlg,IDC_START),TRUE);
      EnableWindow(GetDlgItem(mDlg,IDC_STOP),FALSE);
      break;

    case 2:
      t = "jWebServer Not Running";
      TaskNotify(NIM_ADD,IDI_DOWN, t);
      SetDlgItemText(mDlg,IDC_STATUS,t);
      EnableWindow(GetDlgItem(mDlg,IDC_START),FALSE);
      EnableWindow(GetDlgItem(mDlg,IDC_STOP),TRUE);
      break;
    }
  }


App.h

Synopsis
#pragma once

#include "HttpServer.h"
class Socket;

class App
  {
  public:
    void Create(HINSTANCE hInst);
    void CleanUp(void);
    static void AddSlash(char *str);
    int GetPort();
    void GetResource(char* name, DWORD& contentLength, BYTE*& byt, char* location);
    void ServerEvent(HttpServer* source, int type);
    void SetWinTitle(const char *s);
    void SetNonBlockingMode(Socket& sock);
    HWND GetHwnd() {return mHwnd; }

  private:
    void OnPaint();
    void OnInitDialog(HWND hwnd);
    void OnCloseDialog();
    void OnClickOk();
    void OnClickCancel();
    void OnClickConfig();
    void OnClickAbout();
    void OnClickStart();
    void OnClickStop();
    void OnWinCreate();
    void OnWinClose();
    void OnWinSocketAccept(WPARAM wparam);
    void OnWinSocketClose(WPARAM wparam);
    void OnWinActivate(WPARAM wparam);

    void LaunchBrowser(char *url);
    void TaskNotify(int action, int icon, char *tip);
    void CreateMainWindow();
    void RegisterWindowClass(HINSTANCE hInst);
    void CreateMainDialog();
    void HideDialogWindow();
    void ShowDialogWindow();

    static LRESULT WINAPI MainDlgProc(HWND hd, UINT msg, WPARAM wparam, LPARAM lparam);
    static LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );

  private:
    HWND mHwnd;
    char mText[80];
    HttpServer mWebKnown;
    HttpServer mWebServer;

    HWND mDlg;
    HINSTANCE mInstance;

    int mXLoc;
    int mYLoc;
  };


Buffer.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "Buffer.h"

Buffer::Buffer()
  {
  m_size = 0;
  m_bufferSize = 2048;
  m_data = (BYTE*)GlobalAlloc(GPTR,m_bufferSize);
  }

Buffer::~Buffer()
  {
  GlobalFree(m_data);
  }

void Buffer::Grow(DWORD by)
  {
  DWORD sz = m_bufferSize;
  while( sz < (m_size + by + 5) )
    sz *= 2;
  
  if(sz!=m_bufferSize)
    {
    BYTE* temp = (BYTE*)GlobalAlloc(GPTR,sz);
    memcpy(temp,m_data,m_bufferSize);
    GlobalFree(m_data);
    m_data = temp;
    m_bufferSize = sz;
    }
  }

void Buffer::ConCat(const char *buffer,long length)
  {
  if(length==-1)
    {
    Grow(strlen(buffer)+1);
    strcat((char*)m_data+m_size,buffer);
    m_size += strlen(buffer);
    }
  else
    {
    Grow(length);
    memcpy(m_data+m_size,buffer,length);
    m_size += length;
    }	
  }

void Buffer::ConCat(const string& s)
  {
  ConCat(s.c_str());
  }

void Buffer::ConCat(BYTE b)
  {
  Grow(m_size+1);
  *(m_data+m_size)=b;
  m_size++;
  }


Buffer.h

Synopsis
#pragma once
#include <string>
using std::string;

class Buffer
{
public:
	Buffer();
	~Buffer();

	void Clear(void){m_size=0;*m_data=0;};
	BYTE *GetBuffer(void) { return m_data; };
	DWORD GetLength(void) { return m_size; };
	void ConCat(const char *buffer, long length=-1);
	void ConCat(BYTE b);
	void ConCat(const string& s);
  void Write(const string& s)
    {
    ConCat(s);
    }
  void Writeln(const string& s)
    {
    ConCat(s);
    ConCat("\r\n");
    }

private:
	void Grow(DWORD by);

protected:
	DWORD m_size;// How much data has been added to this buffer
	DWORD m_bufferSize;// How big a buffer is currently allocated
	BYTE *m_data;// A pointer to the actual buffer
};

cgi.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <stdio.h>
#include <time.h>
#include "common.h"
#include "Request.h"
#include "App.h"
#include "NtHandle.h"
#include "ConfigInfo.h"

//uncomment to do tracing!
//#define TRACEME

#ifdef TRACEME
#define TRACE(s, p1, p2, p3) \
    { \
    FILE* fp = fopen("out.txt", "a"); \
    fprintf(fp, "%8.8ld: INFO : " s, clock(), p1, p2, p3); \
    fclose(fp); \
    }
#else
#define TRACE(s, p1, p2, p3)
#endif

#define PRINTERR(s, p1, p2, p3) \
    { \
    FILE* fp = fopen("out.txt", "a"); \
    fprintf(fp, "%8.8ld: ERROR: " s, clock(), p1, p2, p3); \
    fclose(fp); \
    }

class Cgi
  {
  public:
  } ;


bool Request::Cgi(char *path)
  {
  char fn[MAX_PATH],fn2[MAX_PATH],cgiroot[MAX_PATH],*p,*org;
  const int cBufSize = 4096;
  char buffer[cBufSize];
  WIN32_FIND_DATA ff;
  NtHandle hChildStdoutWr("hChildStdoutWr");
  NtHandle hSaveStdin("hSaveStdin");
  NtHandle hChildStdinRd("hChildStdinRd");
  NtHandle hChildStdinWr("hChildStdinWr");
  NtHandle hChildStdinWrDup("hChildStdinWrDup");
  NtHandle hChildStdoutRd("hChildStdoutRd");
  BOOL b,needHeader;
  SECURITY_ATTRIBUTES sa;
  int i;
  Buffer post;
  Buffer env;
  BYTE b1,b2,b3,b4;
  char script[MAX_PATH];
  
  if(*path=='/')
    path++;
  if(!*path)
    return false;
  
  strncpy(fn, gConfigInfo.mWwwRoot.c_str(),sizeof(fn));
  App::AddSlash(fn);
  strcat(fn, szCgiBinName);
  strcat(fn, "\\");
  strncpy(cgiroot,fn,sizeof(cgiroot));
  strcat(fn,path);
  
  // Prevent the use of  '..' to get to previous directory
  p=strchr(fn,'?');
  if(p!=NULL)
    *p=0;
  
  if( (strstr(fn,"..")!=NULL) ||
    (strstr(m_fileRequest,"*")!=NULL) )
    {
    Error(403);
    return false;
    }
  
  
  NtFindHandle h("h");
  h.mHandle = FindFirstFile(fn, &ff);
  if(h.mHandle == INVALID_HANDLE_VALUE)
    return false;
  
  // Setup pipes for output capture
  
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.bInheritHandle = TRUE;
  sa.lpSecurityDescriptor = NULL;
  
  if(!CreatePipe(&hChildStdoutRd.mHandle,&hChildStdoutWr.mHandle,&sa,0))
    return false;
  //hChildStdoutWr closed below
  
  hSaveStdin.mHandle = GetStdHandle(STD_INPUT_HANDLE);
  
  if(!CreatePipe(&hChildStdinRd.mHandle,&hChildStdinWr.mHandle,&sa,0))
    return false;
  
  b = DuplicateHandle(GetCurrentProcess(),hChildStdinWr.mHandle,GetCurrentProcess(),&hChildStdinWrDup.mHandle,0,
    FALSE,DUPLICATE_SAME_ACCESS);
  if(!b)
    return false;
  
  
  // Read the entire post into memory
  m_soc.Skip(1); //skip an initial cr
  for(;;)
    {
    i = m_soc.Recv(buffer, cBufSize);
    if( (i!=-1) && i)
      post.ConCat(buffer,i);
    else
      break;
    }
  
  // Setup server environmental variables
  
  org = p = GetEnvironmentStrings();
  
  while( *p )
    {
    while(*p)
      {
      i++;
      p++;
      }
    p++;
    i++;
    }
  env.ConCat(org,i);
  env.ConCat((BYTE)0);
  
  // Add specific environmental variables
  
  if(m_post)
    env.ConCat("REQUEST_METHOD=POST");
  else
    env.ConCat("REQUEST_METHOD=GET");
  env.ConCat((BYTE)0);
  
  wsprintf(buffer,"CONTENT_LENGTH=%ld",post.GetLength()-1);
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  env.ConCat("QUERY_STRING=");
  if(m_query!=NULL)
    env.ConCat(m_query);
  env.ConCat((BYTE)0);
  env.ConCat("SERVER_SOFTWARE=");
  env.ConCat(szServerVersion);
  env.ConCat((BYTE)0);
  env.ConCat("SERVER_NAME=");
  env.ConCat(gConfigInfo.mSiteName);
  env.ConCat((BYTE)0);
  wsprintf(buffer,"SERVER_PORT=%ld",gConfigInfo.m_port);
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  Socket::Long2IP(m_requestBy,&b1,&b2,&b3,&b4);
  wsprintf(buffer,"REMOTE_HOST=%d.%d.%d.%d",b1,b2,b3,b4);
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  wsprintf(buffer,"REMOTE_ADDR=%d.%d.%d.%d",b1,b2,b3,b4);
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  env.ConCat("DOCUMENT_ROOT=");
  strncpy(buffer, gConfigInfo.mWwwRoot.c_str(), sizeof(buffer));
  SwapChar(buffer,'\\','/');
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  env.ConCat("SCRIPT_FILENAME=");
  strncpy(buffer,fn,sizeof(buffer));
  SwapChar(buffer,'\\','/');
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  wsprintf(buffer,"REMOTE_PORT=%ld",m_port);
  env.ConCat(buffer);
  env.ConCat((BYTE)0);
  env.ConCat("GATEWAY_INTERFACE=CGI/1.1");
  env.ConCat((BYTE)0);
  env.ConCat("SERVER_PROTOCOL=");
  env.ConCat(m_version);
  env.ConCat((BYTE)0);
  env.ConCat("REQUEST_URI=");
  env.ConCat(m_request);
  env.ConCat((BYTE)0);
  env.ConCat("SCRIPT_NAME=");
  p=strchr(m_request,'?');
  if(p!=NULL)
    *p=0;
  env.ConCat(m_request);
  env.ConCat((BYTE)0);
  
  
  // Add the HTTP headers as environmental variables
  
  p = m_headers;
  i = strlen(m_headers);
  b = TRUE;
  needHeader = TRUE;
  
  while(i--)
    {
    if(*p!=13)
      {
      if(b)
        {// before the :
        if(*p==':')
          {
          p++;
          env.ConCat((BYTE)'=');
          b=FALSE;
          while( (*p==' ') && i )
            {
            p++;
            i--;
            }
          p--;
          }
        else
          {
          if(needHeader)
            env.ConCat("HTTP_");
          needHeader=FALSE;
          env.ConCat(toupper((BYTE)*p));
          }
        }
      else
        env.ConCat(toupper((BYTE)*p));
      }
    else
      {// After the :
      env.ConCat((BYTE)0);
      b=TRUE;
      needHeader=TRUE;
      }
    
    p++;
    }
  
  
  env.ConCat((BYTE)0);
  env.ConCat((BYTE)0);
  FreeEnvironmentStrings(org);
  
  // See if this is a script
  p = fn + strlen(fn) - 1;
  i=0;
  while( (*p!='.') && (i<=3) )
    {
    p--;
    i++;
    }
  
  *script=0;
  if(*p=='.')
    gConfigInfo.GetScript(p+1,script);
  
  if(*script)
    {
    strncpy(fn2,script,sizeof(fn2));
    strcat(fn2," ");
    strcat(fn2,fn);
    }
  else
    {
    strcpy(fn2,fn);
    }
		
  PROCESS_INFORMATION pi;
  STARTUPINFO si;
  memset(&si,0,sizeof(si));
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_HIDE;
  si.hStdInput = hChildStdinRd.mHandle;
  si.hStdOutput = hChildStdoutWr.mHandle;
  si.hStdError = hChildStdoutWr.mHandle;
  
  b = CreateProcess(NULL,fn2,NULL,NULL,TRUE,0,env.GetBuffer(),cgiroot,&si,&pi);
  //pi.hProcess closed below
  //pi.hThread closed below
  if(!b)
     return false;
  
  mOut.WriteHeader();
  
  // Write the post data to the process
  // Read in request line and all headers
  DWORD dw;
  WriteFile(hChildStdinWrDup.mHandle,post.GetBuffer(),post.GetLength(),&dw,NULL);
  
  // Non blocking mode
  web.SetNonBlockingMode(m_soc);

  // Now route the incoming bytes to the internet
  // Note: the incoming bytes can arrive in bursts.
  // Note: when the child terminates, it does an implicit close done on the anonymous pipe.
  // Note: there can be bytes in the pipe after the child is dead
  // Note: if there are no bytes in the pipe after the child is dead and Peek returns 0 available bytes, 
  //       then the child did not write anything

  TRACE("about to start main loop\n", 0, 0, 0)

  bool childIsDead = false;
  DWORD bytesRead = 0;
  DWORD avail = 0;
  for(;;)      //main program loop
    {
    //read a byte to see if anything is there
    dw = PeekNamedPipe(hChildStdoutRd.mHandle, buffer, 1, &bytesRead, &avail, NULL);
    TRACE("after peek: dw=%lu avail=%lu bytesread=%lu\n", dw, avail, bytesRead);
    if (!dw)
      {
      PRINTERR("after peek: dw=0x%8.8lX\n", dw, 0, 0)
      break;
      }

    //if there's nothing to read, check if the child is dead
    //otherwise go ahead and read it
    if (bytesRead == 0 && avail == 0)
      {
      //child is dead and there's nothing to read, all done...
      if (childIsDead)
        break;

      //child is still alive and there's nothing to read, wait a bit
      Sleep(100);

      //check if process is still up
      dw = WaitForSingleObject(pi.hProcess, 0);
      childIsDead = (dw == WAIT_OBJECT_0);
      TRACE("wait on process; dw=%ld childIsDead=%d\n", dw, childIsDead, 0)

      continue;
      }

    //there's something to read, so read it all...
    for(;;)
      {
      DWORD bytestoread = avail > cBufSize ? cBufSize : avail;
      TRACE("inner loop: bytestoread=%lu\n", bytestoread, 0, 0);
      if (bytestoread == 0)
        break;
      
      dw = ReadFile(hChildStdoutRd.mHandle,buffer,bytestoread,&bytesRead,NULL);
      if (!dw)
        {
        PRINTERR("after readfile: dw=0x%8.8lX\n", dw, 0, 0);
        break;
        }
      m_soc.Send(buffer, bytesRead);
      avail -= bytesRead;
      TRACE("inner loop: after Read: avail=%lu bytesread=%lu\n", avail, bytesRead, 0);
      }
    }

   TRACE("exit main loop\n", 0, 0, 0);

   m_done = true;
   CloseHandle(pi.hProcess);
   CloseHandle(pi.hThread);
   
   return true;
}

common.h

Synopsis
#pragma once

#define EXE_NAME _pgmptr

// This constant defines how many concurrent connections the http 
// server can handle at one time.  This is NOT how many users can
// use the site at once, but rather how many concurrent requests
// it can process.
const int HTTP_CONNECTIONS = 100;

const int MAX_ERROR_TEXT = 80;
const int MAX_STD_STRING = 255;

extern const char* szExeName; //jwebserver
extern const char* szAppName; //jwebserver v9.9
extern const char* szCgiBinName; //cgi-bin
extern const char* errors[];
extern const char* szServerVersion;

class App;
extern App& web;
class ConfigInfo;
extern ConfigInfo& gConfigInfo;

config.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>

#include "common.h"
#include "NtHandle.h"
#include "ConfigInfo.h"

ConfigInfo::ConfigInfo()
  {
  m_error=false;
  LoadConfig();
  }


ConfigInfo::~ConfigInfo()
  {
  SaveConfig();
  }

void ConfigInfo::LoadConfig(void)
  {
  char defwww[MAX_PATH];
  char buffer[80];
  WIN32_FIND_DATA ff;
  char *ptr;
  
  m_error=false;
  
  strncpy(configPath,EXE_NAME,sizeof(configPath));
  ptr = configPath + strlen(configPath) - 1;
  while( (*ptr!='\\') && (ptr>configPath) )
    ptr--;
  if(*ptr=='\\')
    {
    ptr++;
    *ptr=0;
    }
  strncpy(defwww,configPath,sizeof(defwww));
  strcat(defwww,"www\\");
  
  GetFileName(configPath);
  
    {
    NtFindHandle h("h");
    h.mHandle = FindFirstFile(configPath,&ff);
    if( h.mHandle == INVALID_HANDLE_VALUE )
      {
      m_error=true;
      return;
      }
    }
    
    char temp[512];
    GetPrivateProfileString("Config","servername","127.0.0.1", temp, MAX_PATH, configPath); 
    mSiteName = temp;
    GetPrivateProfileString("Config","wwwroot", defwww, temp, MAX_PATH, configPath); 
    mWwwRoot = temp;
    GetPrivateProfileString("Config","adminid","admin", temp,80,configPath); 
    mAdminId = temp;
    GetPrivateProfileString("Config","adminpw","admin", temp, 80,configPath); 
    mAdminPwd = temp;
    GetPrivateProfileString("Config","default","index.html", temp,80,configPath);
    mDefaultDoc = temp;
    GetPrivateProfileString("Config","logs","N",buffer,80,configPath);
    if(toupper(*buffer)=='Y')
      m_logs=true;
    else
      m_logs=false;
    GetPrivateProfileString("Config","list","N",buffer,80,configPath);
    if(toupper(*buffer)=='Y')
      m_list=true;
    else
      m_list=false;
    
    GetPrivateProfileString("Config","cgi","Y",buffer,80,configPath);
    if(toupper(*buffer)=='Y')
      m_cgi=true;
    else
      m_cgi=false;
    
    
    m_port=GetPrivateProfileInt("Config","port",80,configPath); 
    
    GetPrivateProfileString("Mime","html","DEFAULT",buffer,80,configPath); 
    if(stricmp(buffer,"DEFAULT") == 0)
      GenerateDefaultMime();
    
    GetPrivateProfileString("Script","pl","DEFAULT",buffer,80,configPath); 
    if(!stricmp(buffer,"DEFAULT"))
      GenerateDefaultScript();
  }

void ConfigInfo::SaveConfig(void)
  {
  char buffer[80];
  
  WritePrivateProfileString("Config","servername", (char*) mSiteName.c_str(), configPath);
  WritePrivateProfileString("Config","wwwroot", (char*) mWwwRoot.c_str(), configPath);
  WritePrivateProfileString("Config","adminid", (char*) mAdminId.c_str(), configPath);
  WritePrivateProfileString("Config","adminpw", (char*) mAdminPwd.c_str(), configPath);
  WritePrivateProfileString("Config","default", (char*) mDefaultDoc.c_str(), configPath);
  *buffer=m_logs?'Y':'N';
  buffer[1]=0;
  WritePrivateProfileString("Config","logs",buffer,configPath);
  *buffer=m_list?'Y':'N';
  WritePrivateProfileString("Config","list",buffer,configPath);
  *buffer=m_cgi?'Y':'N';
  WritePrivateProfileString("Config","cgi",buffer,configPath);
  
  wsprintf(buffer,"%ld",m_port);
  WritePrivateProfileString("Config","port",buffer,configPath);
  }

void ConfigInfo::GetFileName(char *dir)
  {
  char *ptr;
  char tryit[MAX_PATH];
  WIN32_FIND_DATA ff;
  
  strncpy(dir,EXE_NAME,MAX_PATH);
  ptr = dir + strlen(dir)-1;
  while( (*ptr!='\\') && (ptr>dir) )
    ptr--;
  if(*ptr=='\\')
    {
    ptr++;
    *ptr=0;
    }
  strcpy(tryit,dir);
  strcat(tryit,"JWeb.ini");
  
    {
    NtFindHandle h("h");
    h.mHandle = FindFirstFile(tryit,&ff);
    if( h.mHandle != INVALID_HANDLE_VALUE )
      {
      strcpy(dir,tryit);
      }
    else
      strcat(dir,"jwebserver.ini");
    }
  }

void ConfigInfo::GenerateDefaultMime(void)
  {
  char dir[MAX_PATH];
  
  GetFileName(dir);
  WritePrivateProfileString("Mime","gif","image/gif",dir);
  WritePrivateProfileString("Mime","jpeg","image/jpeg",dir);
  WritePrivateProfileString("Mime","jpg","image/jpeg",dir);
  WritePrivateProfileString("Mime","html","text/html",dir);
  WritePrivateProfileString("Mime","htm","text/html",dir);
  WritePrivateProfileString("Mime","pps","application/pps",dir);
  WritePrivateProfileString("Mime","pot","application/pot",dir);
  WritePrivateProfileString("Mime","ppt","application/ppt",dir);
  WritePrivateProfileString("Mime","pps","application/pps",dir);
  WritePrivateProfileString("Mime","doc","application/msword",dir);
  }

void ConfigInfo::AddMime(const string& e, const string& t)
  {
  char dir[MAX_PATH];
  const char* ext = e.c_str();
  const char* tag = t.c_str();
  if (t.empty())
    tag = 0;
  char ext2[256];
  char tag2[256];
  
  if(*ext == '.')
    ext++;
  
  GetFileName(dir);
  
  if(tag == NULL)
    {
    WritePrivateProfileString("Mime",ext,NULL,dir);
    return;
    }
  
  strncpy(ext2,ext, sizeof(ext2));
  CharLowerBuff(ext2, strlen(ext2));
  strncpy(tag2,tag, sizeof(tag2));
  CharLowerBuff(tag2, strlen(tag2));
  WritePrivateProfileString("Mime", ext2, tag2, dir);
  }


void ConfigInfo::GetMime(char *ext,char *tag)
  {
  char dir[MAX_PATH];
  
  if(*ext=='.')
    ext++;
  
  GetFileName(dir);
  GetPrivateProfileString("Mime",ext,"application/octet-stream",tag,80,dir); 
  }


void ConfigInfo::DelMime(const string& ext)
  {
  AddMime(ext, "");
  }


void ConfigInfo::GetMimeList(char *buffer)
  {
  char dir[MAX_PATH];
  
  GetFileName(dir);
  GetPrivateProfileString("Mime",NULL,"",buffer,4096,dir); 
  }

void ConfigInfo::AddScript(const string& e, const string& t)
  {
  char dir[MAX_PATH];
  const char* ext = e.c_str();
  const char* tag = t.c_str();
  if (t.empty())
    tag = 0;
  char ext2[256];
  char tag2[256];
  
  if(*ext=='.')
    ext++;
  
  GetFileName(dir);
  
  if(tag==NULL)
    {
    WritePrivateProfileString("Script",ext,NULL,dir);
    return;
    }
  
  strncpy(ext2,ext,sizeof(ext2));
  CharLowerBuff(ext2, strlen(ext2));
  strncpy(tag2,tag, sizeof(ext2));
  CharLowerBuff(tag2, strlen(tag2));
  WritePrivateProfileString("Script", ext2, tag2, dir);
  }


void ConfigInfo::GetScript(char *ext,char *tag)
  {
  char dir[MAX_PATH];
  
  if(*ext=='.')
    ext++;
  
  GetFileName(dir);
  GetPrivateProfileString("Script",ext,"",tag,80,dir); 
  }


void ConfigInfo::DelScript(const string& ext)
  {
  AddScript(ext, "");
  }


void ConfigInfo::GetScriptList(char *buffer)
  {
  char dir[MAX_PATH];
  
  GetFileName(dir);
  GetPrivateProfileString("Script",NULL,"",buffer,4096,dir); 
  }

void ConfigInfo::GenerateDefaultScript(void)
  {
  char dir[MAX_PATH];
  
  GetFileName(dir);
  WritePrivateProfileString("Script","pl","perl.exe",dir);
  WritePrivateProfileString("Script","cgi","perl.exe",dir);
  }

ConfigInfo.h

Synopsis
#pragma once

#include <string>
using std::string;

class ConfigInfo
{
public:
	ConfigInfo();
	~ConfigInfo();

	void LoadConfig(void);
	void SaveConfig(void);
	void GenerateDefaultMime(void);
	void GetFileName(char *dir);

	// Mime tags
	void AddMime(const string& e, const string& t);
	void GetMime(char *ext,char *tag);
	void DelMime(const string& ext);
	void GetMimeList(char *buffer);

	// Scripts
	void AddScript(const string& ext, const string& path);
	void GetScript(char *ext,char *path);
	void DelScript(const string& ext);
	void GetScriptList(char *buffer);
	void GenerateDefaultScript(void);


	string mSiteName;
	string mWwwRoot;
	string mDefaultDoc;
	string mAdminId;
	string mAdminPwd;
	int m_port;
	bool m_logs;
	bool m_error;
	bool m_list;
	bool m_cgi;

	char configPath[MAX_PATH];
};

DirLister.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>
#include "NtHandle.h"

#include "DirLister.h"

class DirLister::Private
  {
  public:
    Private()
      : h("DirLister")
      {
      }

    string mPath;
    NtFindHandle h;
    WIN32_FIND_DATA mFData;
  } ;


DirLister::DirLister()
: impl(* new DirLister::Private)
  {
  }
DirLister::~DirLister()
  {
  delete &impl;
  }
void DirLister::First(const string& path)
  {
  impl.mPath = path;
  string fspec = path + "\\*.*";
  impl.h.mHandle = FindFirstFile(fspec.c_str(), &impl.mFData);
  }
bool DirLister::Next()
  {
  return FindNextFile(impl.h.mHandle, &impl.mFData) == TRUE;
  }
bool DirLister::IsValid()
  {
  return impl.h.mHandle != INVALID_HANDLE_VALUE;
  }
bool DirLister::IsHidden()
  {
  return (impl.mFData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
  }
bool DirLister::IsDir()
  {
  return (impl.mFData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  }
bool DirLister::IsDot()
  {
  return strcmp(impl.mFData.cFileName,".") == 0;
  }
string DirLister::SizeAsString()
  {
  char sz[40];
  if(IsDir())
    strcpy(sz, "-");
  else if (impl.mFData.nFileSizeLow <= 1024)
    wsprintf(sz, "%d", impl.mFData.nFileSizeLow);
  else
    wsprintf(sz, "%dK", impl.mFData.nFileSizeLow/1024);
  return sz;
  }
string DirLister::Name()
  {
  return impl.mFData.cFileName;
  }
string DirLister::DateAsString()
  {
  char strMod[40];
  SYSTEMTIME st;
  FileTimeToSystemTime(&impl.mFData.ftLastWriteTime,&st);
  TimeAsString(st, strMod);
  return strMod;
  }
void DirLister::TimeAsString(const SYSTEMTIME& st,char *out)
  {
  char *dow[7]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  char *month[12]={ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
  
  assert(st.wDayOfWeek<7);
  strcpy(out, dow[st.wDayOfWeek]);
  
  assert(st.wMonth && (st.wMonth<=12) );
  wsprintf(out+ strlen(out),". %ld %s %ld %02ld:%02ld:%02ld GMT",
    st.wDay,month[st.wMonth-1], st.wYear, st.wHour, st.wMinute, st.wSecond);
  }

DirLister.h

Synopsis
#pragma once
#include <string>
using std::string;

//#include "NtHandle.h"

class DirLister
  {
  public:
    DirLister();
    ~DirLister();
    void First(const string& path);
    bool Next();
    bool IsValid();
    bool IsHidden();
    bool IsDir();
    bool IsDot();
    string SizeAsString();
    string Name();
    string DateAsString();
    void TimeAsString(const SYSTEMTIME& st,char *out);
    
  private:
    class Private;
    Private& impl;
  } ;


Html.h

Synopsis
#pragma once
#pragma warning(disable: 4786)

#include "HtmlStream.h"
#include "HttpOutStream.h"
#include "HtmlBuffer.h"

typedef HtmlStream<HttpOutStream> HtmlOutStream;
typedef HtmlStream<HtmlBuffer> HtmlStrStream;

HtmlBuffer.h

Synopsis
#pragma once

class HtmlBuffer : public Buffer
  {
  public:
    void WriteOn(HttpOutStream& out)
      {
      out.WriteBytes(GetBuffer(), GetLength());
      }
  } ;

HtmlDirGen.h

Synopsis
#pragma once

class HtmlDirGen
  {
  public:
    HtmlDirGen(HtmlOutStream& os)
      : mOut(os)
      {
      }
    void Gen(const string& path)
      {
      Init(path);
      mLister.First(path);
      if(mLister.IsValid())
        {
        GenHeadingRow();
        GenRows();
        }
      else
        {
        mOut.H1("FindFirst failed. Cannot read directory.");
        }
      
      Term();
      }
    
  private:    
    void GenRows()
      {
      do	
        {
        if (mLister.IsDot()) continue;
        if (mLister.IsHidden()) continue;
        GenRow();
        } while (mLister.Next());
      }
    
    void Init(const string& path)
      {
      mOut.HTML();
      mOut.HEAD();
      mOut.TITLE(path);
      mOut.CloseTag();//head
      
      mOut.BODY();
      mOut.H1("Directory listing of " + path);
      mOut.TABLE(false);
      }
    void GenHeadingRow()
      {
      mOut.TR();
      mOut.TH("Type");
      mOut.TH("File Name");
      mOut.TH("Size");
      mOut.TH("Last Modified");
      mOut.CloseTag(); //TR
      }
    void GenRow()
      {
      mOut.TR();
      GenLabelColumn();
      GenPathColumn();
      GenSizeColumn();
      GenDateColumn();
      mOut.CloseTag(); //TR
      }
    
    void GenSizeColumn()
      {
      mOut.TD(mLister.SizeAsString());
      }
    static void toPlus(char& c)
      {
      if (c == ' ')
        c = '+';
      }
    void GetConvertedPath()
      {
      mConvertedPath = mLister.Name();
      for_each(mConvertedPath.begin(), mConvertedPath.end(), toPlus);
      if(mLister.IsDir())
        mConvertedPath += "/";
      }
    
    void GenPathColumn()
      {
      GetConvertedPath();
      mOut.TD();
      mOut.A(mConvertedPath, strcmp(mConvertedPath.c_str(), "../") == 0 ? "Parent Directory" : mLister.Name() );
      mOut.CloseTag(); //TD
      }
    
    void GenLabelColumn()
      {
      mOut.TD();
      if(mLister.IsDir())
        mOut.Write("[DIR]");
      else
        mOut.Write("[FILE]");
      mOut.CloseTag(); //TD
      }
    void GenDateColumn()
      {
      mOut.TD(mLister.DateAsString());
      }
    
    void Term()
      {
      mOut.CloseAllTags();
      }
    
  private:
    DirLister mLister;
    HtmlOutStream mOut;
    string mConvertedPath;
  } ;

HtmlPageGen.h

Synopsis
#pragma once

#include "Html.h"
#include "ConfigInfo.h"

class HtmlPageGen
  {
  public:
    HtmlPageGen(HtmlStrStream& out)
      : mStrOut(out)
      {
      }
    
    enum eConfigType {Script, Mime};
    
    void ConfirmationPage(const string& title, const string& text, bool docontinue = true)
      {
      mStrOut.Clear();
      mStrOut.HTML();
      mStrOut.HEAD();
      mStrOut.TITLE(title);
      mStrOut.CloseTag();
      mStrOut.BODY();
      mStrOut.H1(title);
      mStrOut.Text(text);
      if (docontinue)
        AdminContinue();
      mStrOut.CloseAllTags();
      }
    
    void ConfigPage(eConfigType type, const string& title, const string& text, 
      const string& changeaction, const string& changetitle, const string& changeitem,
      const string& deleteaction, const string& deletetitle)
      {
      mStrOut.HTML();
      mStrOut.HEAD();
      mStrOut.TITLE(title);
      mStrOut.CloseTag();
      mStrOut.BODY();
      mStrOut.H1(title);
      mStrOut.P();
      mStrOut.Text(text);
      mStrOut.CloseTag(); //P
      
      if (type == Script)
        GenScriptTable();
      else
        GenMimeTable();
      
      mStrOut.HR();
      ChangeForm(changeaction, changetitle, changeitem);
      mStrOut.HR();
      DeleteForm(deleteaction, deletetitle);
      mStrOut.HR();
      
      AdminContinue();
      mStrOut.CloseAllTags();
      }
    
    void MainPage(int port)
      {
      char buffer[1024];
      mStrOut.HTML();
      mStrOut.HEAD();
      wsprintf(buffer,"Welcome Back %s", gConfigInfo.mAdminId.c_str());
      mStrOut.TITLE(buffer);
      mStrOut.CloseTag(); //HEAD  
      mStrOut.BODY();
      mStrOut.H1("jWebServer Configuration");
      mStrOut.FORM_POST("/admin/change.html");
      AddHiddenFields();
      
      mStrOut.TABLE(true);
      InputRow("Root Directory for Web Site:", "ROOT", gConfigInfo.mWwwRoot);
      InputRow("Port to run Web from:", "PORT", gConfigInfo.m_port);
      InputRow("Administrator ID:", "ID", gConfigInfo.mAdminId);
      PasswordRow("Administrator Password:", "PW", gConfigInfo.mAdminPwd);
      InputRow("Default Document:", "DEF",  gConfigInfo.mDefaultDoc);
      InputRow("Website URL(domain eg. www.arrizza.com):", "SITE", gConfigInfo.mSiteName);
      CheckboxRow("Log requests (access.log)?", "LOG", gConfigInfo.m_logs);
      CheckboxRow("Allow directory browsing?", "LIST", gConfigInfo.m_list);
      CheckboxRow("Enable cgi-bin?", "CGI", gConfigInfo.m_cgi);
      
      mStrOut.TR();
      mStrOut.Writeln("<TD COLSPAN=2 ALIGN=\"CENTER\">");
      mStrOut.SUBMIT("ACTION", "SAVE"); 
      mStrOut.SUBMIT("ACTION", "MIME"); 
      mStrOut.SUBMIT("ACTION", "SCRIPT"); 
      mStrOut.Writeln("</TD>");
      mStrOut.CloseTag(); //TR
      mStrOut.CloseTag(); //TABLE
      
      mStrOut.CloseTag(); //FORM
      
      mStrOut.P();
      wsprintf(buffer,"http://127.0.0.1:%d/admin", port);
      mStrOut.A(buffer, "[Cancel Changes]");
      
      mStrOut.CloseAllTags();
      }
    
  private:
    void DeleteForm(const string& action, const string& title)
      {
      DeleteForm(action, title, "File Extension to Delete:", "EXT", "Delete");
      }
    
    void DeleteForm(const string& action, const string& title, const string& item, const string& tag, const string& submitname)
      {
      mStrOut.FORM_POST(action);
      mStrOut.H2(title);
      AddHiddenFields();
      
      mStrOut.TABLE(false);
      InputRow(item, tag, "");
      mStrOut.CloseTag();//TABLE
      
      mStrOut.SUBMIT("", submitname);
      mStrOut.CloseTag();//FORM
      }
    
    void ChangeForm(const string& action, const string& title, const string& item)
      {
      ChangeForm(action, title, item, "TAG", "Add/Change");
      }
    
    void ChangeForm(const string& action, const string& title, const string& item, const string& tag, const string& submitname)
      {
      mStrOut.FORM_POST(action);
      mStrOut.H2(title);
      AddHiddenFields();
      
      mStrOut.TABLE(false);
      InputRow("File Extension", "EXT", "");
      InputRow(item, tag, "");
      mStrOut.CloseTag(); //TABLE
      
      mStrOut.SUBMIT("", submitname);
      mStrOut.CloseTag(); //FORM
      }
    void InputRow(const string& title, const string& name, const string& value)
      {
      mStrOut.TR();
      mStrOut.TD(title);
      mStrOut.TD();
      mStrOut.INPUT(name, value);
      mStrOut.CloseTag(); //TD
      mStrOut.CloseTag(); //TR
      }
    
    void InputRow(const string& title, const string& name, long value)
      {
      mStrOut.TR();
      mStrOut.TD(title);
      mStrOut.TD();
      mStrOut.INPUT(name, value);
      mStrOut.CloseTag(); //TD
      mStrOut.CloseTag(); //TR
      }
    
    void PasswordRow(const string& title, const string& name, const string& value)
      {
      mStrOut.TR();
      mStrOut.TD(title);
      mStrOut.TD();
      mStrOut.PASSWORD(name, value);
      mStrOut.CloseTag(); //TD
      mStrOut.CloseTag(); //TR
      }
    void CheckboxRow(const string& title, const string& name, bool value)
      {
      mStrOut.TR();
      mStrOut.TD(title);
      mStrOut.TD();
      mStrOut.CHECKBOX(name, value);
      mStrOut.CloseTag(); //TD
      mStrOut.CloseTag(); //TR
      }
    void AdminContinue(void)
      {
      mStrOut.FORM_POST("/admin/login.html");
      mStrOut.HIDDEN("ID", gConfigInfo.mAdminId);
      mStrOut.HIDDEN("PW", gConfigInfo.mAdminPwd);
      mStrOut.SUBMIT("", "Continue");
      mStrOut.CloseTag(); //FORM
      }
    void AddHiddenFields()
      {
      mStrOut.HIDDEN("AID", gConfigInfo.mAdminId);
      mStrOut.HIDDEN("APW", gConfigInfo.mAdminPwd);
      }
    
    void GenMimeTable()
      {
      mStrOut.TABLE(true);
      mStrOut.TR();
      mStrOut.TH("File Extension(i.e. .gif, .jpg, .html etc)");
      mStrOut.TH("Mime Tag to Map");
      mStrOut.CloseTag(); //TR
      
      char buffer[4096];
      gConfigInfo.GetMimeList(buffer);
      char str[256];
      char str2[256];
      for(char* sptr = buffer; *sptr; sptr += strlen(sptr) + 1)
        {
        strcpy(str, sptr);
        gConfigInfo.GetMime(str, str2);
        mStrOut.TR();
        mStrOut.TD(str);
        mStrOut.TD(str2);
        mStrOut.CloseTag(); //TR
        }
      mStrOut.CloseTag(); //TABLE
      }
    
    void GenScriptTable()
      {
      mStrOut.TABLE(true);
      mStrOut.TR();
      mStrOut.TH("File Extension(i.e. .cgi, .pl, etc)");
      mStrOut.TH("Script Engine");
      mStrOut.CloseTag(); //TR
      
      char buffer[4096];
      gConfigInfo.GetScriptList(buffer);
      char str[256];
      char str2[256];
      for (char* sptr = buffer; *sptr; sptr += strlen(str) + 1)
        {
        strcpy(str, sptr);
        gConfigInfo.GetScript(str, str2);
        mStrOut.TR();
        mStrOut.TD(str);
        mStrOut.TD(str2);
        mStrOut.CloseTag(); //TR
        }
      mStrOut.CloseTag(); //TABLE
      }
    
  private:
    HtmlStrStream& mStrOut;
  }; 

HtmlStream.h

Synopsis
#pragma once

#include <stack>
using std::stack;

//writes HTML 
// tWriter must implement:
//   Write(const string& s)
//   Writeln(const string& s)

template <class tWriter>
class HtmlStream : public tWriter
  {
  public:
    void HTML()
      {
      Tag("HTML");
      }
    void HEAD()
      {
      Tag("HEAD");
      }
    void BODY()
      {
      Tag("BODY");
      }
    void P()
      {
      Tag("P");
      }
    void BR()
      {
      Writeln("<BR/>");
      }
    void HR()
      {
      Writeln("<HR/>");
      }
    void TITLE(const string& s)
      {
      Write("<TITLE>");
      Write(s);
      Writeln("</TITLE>");
      }
    void H1(const string& s)
      {
      Write("<H1>");
      Write(s);
      Writeln("</H1>");
      }
    void H2(const string& s)
      {
      Write("<H2>");
      Write(s);
      Writeln("</H2>");
      }
    void INPUT(const string& name, const string& value)
      {
      InputField("TEXT", name, value);
      }
    void INPUT(const string& name, long value)
      {
      char buf[20];
      sprintf(buf, "%ld", value);
      InputField("TEXT", name, buf);
      }
    void HIDDEN(const string& name, const string& value)
      {
      InputField("HIDDEN", name, value);
      }
    void SUBMIT(const string& name, const string& value)
      {
      InputField("SUBMIT", name, value);
      }
    void PASSWORD(const string& name, const string& value)
      {
      InputField("PASSWORD", name, value);
      }
    void CHECKBOX(const string& name, bool checked)
      {
      char buffer[2048];
      strcpy(buffer, "<INPUT TYPE='CHECKBOX' ");
      if (!name.empty())
        {
        strcat(buffer, "NAME='");
        strcat(buffer, name.c_str());
        strcat(buffer, "' ");
        }
      if (checked)
        {
        strcat(buffer, "CHECKED");
        }
      strcat(buffer, " />");
      Writeln(buffer);
      }
    void FORM_POST(const string& action)
      {
      Write("<FORM METHOD='POST' ACTION='");
      Write(action);
      Write("'>");
      mCloseTags.push("</FORM>");
      }
    void Text(const string& text)
      {
      Writeln(text);
      }
    void CloseTag()
      {
      if (mCloseTags.empty()) return;
      string tag = mCloseTags.top();
      mCloseTags.pop();
      Write(tag);
      };
    void CloseAllTags()
      {
      while(!mCloseTags.empty())
        CloseTag();
      };
    void TABLE(bool border)
      {
      if (border)
        Write("<TABLE BORDER=1>");
      else
        Write("<TABLE BORDER=0>");
      mCloseTags.push("</TABLE>");
      }
    void TH(const string& s)
      {
      Write("<TH>");
      Write(s);
      Writeln("</TH>");
      }
    void TD()
      {
      Tag("TD");
      }
    void TD(const string& s)
      {
      Write("<TD>");
      Write(s);
      Writeln("</TD>");
      }
    void TR()
      {
      Tag("TR");
      }
    void A(const string& href, const string& s)
      {
      Write("<A HREF=\"");
      Write(href);
      Write("\">");
      Write(s);
      Write("</A>");
      }
    
  private:
    void InputField(const string& type, const string& name, const string& value)
      {
      char buffer[2048];
      strcpy(buffer, "<INPUT ");
      if (!type.empty())
        {
        strcat(buffer, "TYPE='");
        strcat(buffer, type.c_str());
        strcat(buffer, "' ");
        }
      if (!name.empty())
        {
        strcat(buffer, "NAME='");
        strcat(buffer, name.c_str());
        strcat(buffer, "' ");
        }
      if (!value.empty())
        {
        strcat(buffer, "VALUE='");
        strcat(buffer, value.c_str());
        strcat(buffer, "' ");
        }
      strcat(buffer, " />");
      Writeln(buffer);
      }
    void Tag(const string& name)
      {
      Write("<" + name + ">");
      mCloseTags.push("</" + name + ">");
      }
  private:    
    stack<string> mCloseTags;
  };
  

HttpOutStream.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>

#include "common.h"
#include "HttpOutStream.h"

HttpOutStream::HttpOutStream() 
: mBytesSent(0)
  {
  }
void HttpOutStream::Write(const string& s)
  {
  Write(s.c_str());
  }
void HttpOutStream::Write(const char* s)
  {
  mBytesSent += mSocket.Send(s, (unsigned long) strlen(s));
  }
void HttpOutStream::Writeln(const char *s)
  {
  Write(s);
  Write("\r\n");
  }
void HttpOutStream::WriteBytes(const unsigned char* buffer, unsigned long len)
  {
  mBytesSent += mSocket.Send(buffer, len);
  }
void HttpOutStream::SetSocket(Socket& s)
  {
  mSocket = s;
  }
void HttpOutStream::WriteHtmlContentType()
  {
  WriteMimeFor(0);
  }
void HttpOutStream::WriteHeader()
  {
  Writeln("HTTP/1.0 200 OK");
  WriteServerVersion();
  Writeln("Accept-Ranges: bytes");
  }
void HttpOutStream::WriteEndOfHeaders()
  {
  Write("\r\n");
  mBytesSent = 0;
  }
void HttpOutStream::WriteDate()
  {
  SYSTEMTIME st;
  GetSystemTime(&st);
  WriteDate(st);
  }
void HttpOutStream::WriteDate(const SYSTEMTIME& st)
  {
  WriteDate(st, "Date: ");
  }
void HttpOutStream::WriteLastModifiedDate(const SYSTEMTIME& st)
  {
  WriteDate(st, "Last-Modified: ");
  }
void HttpOutStream::WriteLastModifiedDate()
  {
  SYSTEMTIME st;
  GetSystemTime(&st);
  WriteLastModifiedDate(st);
  }
void HttpOutStream::WriteContentLength(long len)
  {
  char buffer[100];
  wsprintf(buffer, "Content-Length: %ld", len);
  Writeln(buffer);
  }
void HttpOutStream::WriteMimeFor(const char* ext)
  {
  char mime[80];
  GenerateMime(ext, mime);
  Writeln(mime);
  }
void HttpOutStream::WriteServerVersion()
  {
  Write("Server: ");
  Writeln(szServerVersion);
  }
void HttpOutStream::WriteMovePermanently()
  {
  Writeln("HTTP/1.0 301 Moved Permanently");
  }
long HttpOutStream::GetBytesSent()
  {
  return mBytesSent;
  }

void HttpOutStream::TimeString(const SYSTEMTIME& st,char *out)
  {
  char *dow[7]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  char *month[12]={ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
  
  assert(st.wDayOfWeek<7);
  strcpy(out, dow[st.wDayOfWeek]);
  
  assert(st.wMonth && (st.wMonth<=12) );
  wsprintf(out+strlen(out),". %ld %s %ld %02ld:%02ld:%02ld GMT", st.wDay,month[st.wMonth-1], st.wYear, st.wHour, st.wMinute, st.wSecond);
  }
void HttpOutStream::WriteDate(const SYSTEMTIME& st, char* header)
  {
  char buffer[100];
  strcpy(buffer,"Date: "); 
  TimeString(st, buffer+strlen(buffer)-1);
  Writeln(buffer);
  }
void HttpOutStream::GenerateMime(const char *e, char *buffer)
  {
  strcpy(buffer,"Content-Type: ");
  
  if (e == 0)
    strcat(buffer, "text/html");
  else if( stricmp(e,"HTML") == 0 || stricmp(e,"HTM") == 0 )
    strcat(buffer,"text/html");
  else if( stricmp(e,"JPG") == 0 || stricmp(e,"JPEG") == 0)
    strcat(buffer,"image/jpeg");
  else if( stricmp(e,"GIF") == 0)
    strcat(buffer,"image/gif");
  else
    strcat(buffer,"application/octet-stream");
  }

HttpOutStream.h

Synopsis
#pragma once
#include <string>
using std::string;

#include "Socket.h"

//sends an HTTP stream to a socket
class HttpOutStream
  {
  public:
    HttpOutStream();
    void Write(const string& s);
    void Write(const char* s);
    void Writeln(const char *s);
    void WriteBytes(const unsigned char* buffer, unsigned long len);
    void SetSocket(Socket& s);
    void WriteHtmlContentType();
    void WriteHeader();
    void WriteEndOfHeaders();
    void WriteDate();
    void WriteDate(const SYSTEMTIME& st);
    void WriteLastModifiedDate(const SYSTEMTIME& st);
    void WriteLastModifiedDate();
    void WriteContentLength(long len);
    void WriteMimeFor(const char* ext);
    void WriteServerVersion();
    void WriteMovePermanently();
    long GetBytesSent();

  private:
    void TimeString(const SYSTEMTIME& st,char *out);
    void WriteDate(const SYSTEMTIME& st, char* header);
    void GenerateMime(const char *e, char *buffer);
    
  private:
    Socket mSocket;
    long mBytesSent;
  };

HttpRequest.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <assert.h>

#include "common.h"
#include "App.h"
#include "HttpRequest.h"
#include "NtHandle.h"


HttpRequest::HttpRequest()
: mPageGen(mStrOut)
  {
  m_started=false;
  m_post = false;
  m_thread = NULL;
  }

const char* errors[] = {
  "400 Bad Request",
    "403 Forbidden",
    "404 Object Not Found",
    "500 Internal Error",
    "501 Not Supported",
    0
  };

void HttpRequest::Error(int n)
  {
  strncpy(m_errorText, "HTTP/1.0 ", MAX_ERROR_TEXT);
  for(int i = 0; errors[i]; ++i)
    {
    if(atoi(errors[i])==n)
      {
   	  strcat(m_errorText, errors[i]);
      return;
      }
    }
  strcat(m_errorText,"500 Internal Error");
  }

void HttpRequest::DoError(void)
  {
  mOut.Writeln(m_errorText);
  mOut.WriteHtmlContentType();
  mOut.WriteEndOfHeaders();
  mOut.BODY();
  mOut.H1(m_errorText);
  mOut.CloseTag(); //body
  }


void HttpRequest::Start(const Socket& s, unsigned long by,unsigned long by_port, HttpServer *svr)
  {
  m_started=true;
  m_soc = s;
  mOut.SetSocket(m_soc);
  m_requestBy = by;
  m_server = svr;
  m_port = by_port;
  if(m_thread != NULL )
    m_thread=NULL;
  
  }

bool HttpRequest::IsRunning()
  {
  return m_started;
  }

void HttpRequest::DoTransaction(void)
  {
  char ch,last;
  int i;
  DWORD t;
  
 	SYSTEMTIME st;
  GetSystemTime(&st);
  
  *m_fileRequest=0;
  *m_errorText=0;
  
  m_done=false;
  m_head=false;
  m_post = false;
  
  try
    {
    
    // Read in request line and all headers
    
    mStrOut.Clear();
    m_headerBuffer.Clear();
    t = GetTickCount()/1000;
    
    for(;;)
      {
      i = m_soc.Recv(&ch, 1);
      if( (i!=-1) && i)
        {
        t = GetTickCount()/1000;
        if(ch!='\n')
          {
          m_headerBuffer.ConCat((BYTE)ch);
          
          if( (ch==13) &&  (last==13) ) // two CR's mark the end of headers
            break;
          last = ch;
          }
        }
      else
        {
        if( ((GetTickCount()/1000)-t)>30)
          break;
        }
      }
    
    
    // Get the request
    
    m_headers=strchr(m_verb=(char*)m_headerBuffer.GetBuffer(),'\r');
    if(m_headers==NULL)
      return;
    *(m_headers++)=0;
    
    m_request=strchr(m_verb,' ');
    if(m_request==NULL)
      return;
    *(m_request++)=0;
    
    if( strlen(m_request) >2048 )
      m_request[2048] = 0;
    
    m_version=strchr(m_request,' ');
    if(m_version==NULL)
      return;
    *(m_version++)=0;
    }
  catch(...)
    {
    return;
    }
  
  
  // now parse the request
  
  try
    {
    m_head=false;
    CharUpperBuff(m_verb, strlen(m_verb));
    if (stricmp(m_verb,"HEAD") == 0)
      {
      m_head=true;
      Get(m_request);
      }
    else if (stricmp(m_verb,"GET") == 0)
      {
      Get(m_request);
      }
    else if (stricmp(m_verb,"POST") == 0)
      {
      Post(m_request);
      }
    else if (stricmp(m_verb,"PUT") == 0 || stricmp(m_verb,"TRACE") == 0 )
      {
      Error(501);
      }
    else
      Error(400);
    }
  catch(...)
    {
    Error(500);
    }
  
  
  try
    {
    // Complete transaction
    if( *m_errorText )
      {
      DoError();
      }
    else if (*m_fileRequest && ( *(m_fileRequest + strlen(m_fileRequest)-1)=='\\' ) )
      {
      FileList();
      }
    else if (!m_done)
      {
      if(*m_fileRequest)
        SendFile();
      else
        {
        // Transmit the response
        mOut.WriteHeader();
        mOut.WriteDate(st);
        mOut.WriteHtmlContentType();
        mOut.WriteLastModifiedDate(st);
        mOut.WriteContentLength(mStrOut.GetLength());
        mOut.WriteEndOfHeaders();
        
        // Transmit data
        if(!m_head)
          mStrOut.WriteOn(mOut);
        }
      }
    
    
    m_soc.AsyncClose(web.GetHwnd());
    m_soc.ShutDown();
    
    m_started=false;
    LogRequest();
    }
  catch(...)
    {
    }
}

void HttpRequest::LogRequest(void)
  {
  }

void HttpRequest::SendFile(void)
  {
  unsigned char buffer[256];
  char* ext;
  NtHandle fp("fp");
  DWORD dw;
  long bytesLeft;
  FILETIME ft;
  SYSTEMTIME st;
  DWORD contentLength;
  
  fp.mHandle = CreateFile(
    m_fileRequest,
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_SEQUENTIAL_SCAN,
    NULL);
  
  if(fp.mHandle ==INVALID_HANDLE_VALUE)
    {
    Error(404);
    return;
    }
  
  contentLength = GetFileSize(fp.mHandle, NULL);
  GetFileTime(fp.mHandle, NULL,NULL,&ft);
  FileTimeToSystemTime(&ft,&st);
  
  ext = strchr(m_fileRequest,'.');
  if (ext != 0)
    ext++;
  
  mOut.WriteHeader();
  mOut.WriteDate();
  mOut.WriteMimeFor(ext);
  mOut.WriteLastModifiedDate(st);
  mOut.WriteContentLength(contentLength);
  mOut.WriteEndOfHeaders();
  
  bytesLeft = contentLength;
  
  // Block mode
  m_soc.SetNonBlockingMode(web.GetHwnd());
  
  // Transmit data
  while(bytesLeft>0)
    {
    if( ReadFile(fp.mHandle, buffer, 256, &dw, NULL) == 0)
      break;
    
    mOut.WriteBytes(buffer, dw);
    bytesLeft -= dw;
    } 
  }

void HttpRequest::SendResource(char *name, char *ext)
  {
  BYTE *byt;
  DWORD contentLength;
  web.GetResource(name, contentLength, byt, "WEBDAT");
  
  mOut.WriteHeader();
  mOut.WriteDate();
  mOut.WriteMimeFor(ext);
  mOut.WriteLastModifiedDate();
  mOut.WriteContentLength(contentLength);
  mOut.WriteEndOfHeaders();
  mOut.WriteBytes(byt, contentLength);
  
  m_done=true;
  }

void HttpRequest::Get(char *)
  {
  Error(404);
  }

void HttpRequest::Post(char *s)
  {
  m_post = true;
  Get(s);
  }

void HttpRequest::FileList(void)
  {
  Error(403);
  }

bool HttpRequest::Parse(string& name, string& value)
  {
  int i;
  char ch,hexcode[3];
  int state;
  int hex;
  DWORD t;
  
  name = "";
  value = "";

  // Read in request line and all headers
  state = 0;
  hex=0;
  t = GetTickCount()/1000;
  
  for(;;)
    {
    if( ((GetTickCount()/1000)-t)>30)
      return false;
    
    i = m_soc.Recv(&ch, 1);
    if( (i!=-1) && i)
      {
      t = GetTickCount()/1000;
      
      if( (ch==13) || (ch==10) )
        continue;
      
      // URL decode first
      
      switch(hex)
        {
        case 0:
          if(ch=='=')
            {
            state = 1;
            continue;
            }
          
          if(ch=='&')
            return true;
          
          if(ch=='+')
            ch=32;
          if(ch=='%')
            {
            hex=1;
            continue;
            }
          break;
          
        case 1:
          hexcode[0]=toupper(ch);
          hexcode[2]=0;
          hex=2;
          continue;
          
        case 2:
          hexcode[1]=toupper(ch);
          hexcode[2]=0;
          hex=0;
          ch = IntFromHex(hexcode);
          break;
        }
      
      // now place it where it goes
      
      switch(state)
        {
        case 0: name += ch; break;
        case 1: value += ch; break;
        }
      }
    else
      return !name.empty();
    }
  
  return true;
  }

int HttpRequest::IntFromHex(char *pChars)
  {
  int hi,lo,result;
  
  hi=pChars[0];
  if( '0'<=hi && hi <='9' ) 
    hi-='0';
  else if( 'a'<=hi && hi<='f' ) 
    hi-=('a'-10);
  else if( 'A'<=hi && hi<='F' )
    hi-=('A'-10);
  
  lo=pChars[1];
  if( '0'<=lo && lo <='9' ) 
    lo-='0';
  else if( 'a'<=lo && lo<='f' ) 
    lo-=('a'-10);
  else   if( 'A'<=lo && lo<='F' )
    lo-=('A'-10);
  
  result = lo + (16* hi);
  return result;
  }

void HttpRequest::SwapChar(char *pOriginal, char cBad,char cGood)
  {
  int i;
  
  i=0;
  while(pOriginal[i])
    {
    if(pOriginal[i])
      {
      if(pOriginal[i]==cBad) pOriginal[i]=cGood;
      i++;
      }
    }
  }


void HttpRequest::URLDecode(char *pEncoded)
  {
  char *pDecoded;
  
  SwapChar(pEncoded,'+',' ');
  
  pDecoded=pEncoded;
  while(*pEncoded)
    {
    if(*pEncoded=='%')
      {
      pEncoded++;
      if(isxdigit(pEncoded[0]) && isxdigit(pEncoded[1]) )
        {
        *pDecoded++=(char)IntFromHex(pEncoded);
        pEncoded+=2;
        }
      }
    else
      {
      *pDecoded++=*pEncoded++;
      }
    }
  *pDecoded=0;
  }

bool HttpRequest::StartsWith(char *s1,char *cmp)
  {
  while(*cmp)
    {
    if( toupper(*s1)!=toupper(*cmp) )
      return false;
    s1++;
    cmp++;
    }
  
  return true;
  }

HttpRequest.h

Synopsis
#pragma once

#include "Buffer.h"
class HttpServer;
#include "Html.h"

#include "Socket.h"
#include "HtmlPageGen.h"

class HttpRequest
{

public:
	HttpRequest();

	void Start(const Socket& s,unsigned long by,unsigned long by_port,HttpServer *svr);
	void DoTransaction();
	bool IsRunning();
	void Error(int n);
	HANDLE m_thread;

	static bool StartsWith(char *s1,char *cmp);
	static int IntFromHex(char *pChars);
	static void SwapChar(char *pOriginal, char cBad,char cGood);
	static void URLDecode(char *pEncoded);

protected:
	virtual void Get(char *path);
	virtual void Post(char *path);
	virtual void LogRequest(void);
	virtual void FileList(void);
	void SendFile(void);
	void SendResource(char *name,char *mime);
	void DoError(void);
  bool Parse(string& name, string& value);
	
	bool	m_started;
  Socket m_soc;
	unsigned long m_port;
	unsigned long m_requestBy;
	HttpServer		*m_server;
	char m_fileRequest[MAX_PATH];
	char m_errorText[MAX_ERROR_TEXT];
	bool m_head;
  bool m_done;
	bool m_post;
	Buffer m_headerBuffer;
  HtmlStrStream mStrOut;
  HtmlOutStream mOut;
  HtmlPageGen mPageGen; //must come after mStrOut!
	char* m_verb;
  char* m_request;
  char* m_headers;
  char* m_version;
};

HttpServer.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>

#include "common.h"
#include "App.h"
#include "HttpServer.h"
#include "HttpRequest.h"

void HttpServer::HandleRequest(void *p)
  {
  HttpRequest *http=(HttpRequest *)p;
  http->DoTransaction();
  _endthread();
  }

HttpServer::HttpServer()
  {
  int i;
  m_autoFindPort=false;
  m_running=false;
  
  for(i=0;i<HTTP_CONNECTIONS;i++)
    m_connections[i]=NULL;
  }

HttpServer::~HttpServer()
  {
  StopServer();
  }

bool HttpServer::IsRunning(void)
  {
  return m_running;
  }

void HttpServer::Accept(void)
  {
  unsigned long addr;
  unsigned long port;
  
  Socket newConn = m_listener.Accept(addr, port);
  HttpRequest *req = FindConnection();
  req->Start(newConn, addr, port, this);
  req->m_thread = (HANDLE)_beginthread(HandleRequest, 32767, (LPVOID)req);
  }

void HttpServer::StartServer(int p)
  {
  if(m_running)
    return;
  
  for(int i=0; i < HTTP_CONNECTIONS; i++)
    if(m_connections[i]==NULL)
      m_connections[i]=new HttpRequest;
    
    web.ServerEvent(this, 1);
    m_portno = p;
    
    // Setup the listner socket
    
    m_listener.CreateListener();
    if (m_listener.IsInvalid())
      {
      web.SetWinTitle(m_listener.GetLastErrorDesc());
      return;
      }
    
    if(m_autoFindPort)
      {
      bool done=false;
      while(!done)
        {
        int err = m_listener.Bind(m_portno);
        if(m_listener.IsError(err)) 
          {
          if(m_listener.IsAddressInUse()) 
            {
            char out[80];
            wsprintf(out, "Can't bind to port %i.",m_portno);
            web.SetWinTitle(out);
            return;
            }
          else
            m_portno++;
          }
        else
          done=true;
        }
      }
    else
      {
      int err = m_listener.Bind(m_portno);
      if (m_listener.IsError(err))
        {
        char out[80];
        if (m_listener.IsAddressInUse())
          wsprintf(out, "Can't use port %d. Another web server may be running.",m_portno);
        else
          wsprintf(out, "Can't bind to port %d.",m_portno);
        web.SetWinTitle(out);
        return;
        }
      }
    
    int err = m_listener.Listen(5);
    if (m_listener.IsError(err))
      {
      web.SetWinTitle("Socket Listen Failed");
      return;
      }
    
    m_listener.AsyncCloseOrAccept(web.GetHwnd());
    m_running=true;
    web.ServerEvent(this, 0);
    
  }

void HttpServer::StopServer(void)
  {
  if(!m_running)
    return;
  
  m_listener.Close();
  m_running=false;
  web.ServerEvent(this, 1);
  }

int HttpServer::GetPort(void)
  {
  return m_portno;
  }

HttpRequest *HttpServer::FindConnection(void)
  {
  for(;;)
    {
    for(int i=0;i<HTTP_CONNECTIONS;i++)
      if( !m_connections[i]->IsRunning() )
        return m_connections[i];
      Sleep(1000);
    }
  }

bool HttpServer::Compare(const Socket& s)
  {
  return m_listener.IsEqual(s);
  }

HttpServer.h

Synopsis
#pragma once

#include "Socket.h"

class HttpRequest;

class HttpServer
{
public:
	HttpServer();
	~HttpServer();

	void StartServer(int p=80);
	void StopServer(void);
	bool IsRunning(void);
	int GetPort(void);
	bool Compare(const Socket& s);
	static void HandleRequest(void *p);
	HttpRequest *FindConnection(void);
	void Accept(void);

	bool m_autoFindPort;
	HttpRequest *m_connections[HTTP_CONNECTIONS];
protected:

	Socket m_listener;
	int m_portno;
	bool m_running;
};


install.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "common.h"
#include "App.h"
#include "resource.h"
#include <shlobj.h>
#include "NtHandle.h"
#include "ConfigInfo.h"
#include "Install.h"

static Install* This = 0;

Install::Install()
: mInstance(0), mDlg(0)
  {
  }
Install::Install(HINSTANCE inst, HWND dlg)
: mInstance(inst), mDlg(dlg)
  {
  This = this;
  }

void Install::Run(void)
  {
  
  if( DialogBox(mInstance,"INSTALL",NULL,(DLGPROC) Proc1) == IDCANCEL )
    return;
  
  if( DialogBox(mInstance,"INSTALL2",NULL,(DLGPROC) Proc2) == IDCANCEL )
    return;
  
  MakeDir(mBinDir);
  MakeDir(mWwwrootDir);
  
  char cgi[MAX_PATH];
  strncpy(cgi, mWwwrootDir, MAX_PATH);
  App::AddSlash(cgi);
  strcat(cgi,"\\");
  strcat(cgi, szCgiBinName);
  strcat(cgi, "\\");
  MakeDir(cgi);

  char exeName[MAX_PATH];
  strncpy(exeName,mBinDir,MAX_PATH);
  App::AddSlash(exeName);
  strcat(exeName,"jWebServer.exe");
  
  if( stricmp(EXE_NAME, exeName) != 0)
    {
    if( CopyFile(EXE_NAME,exeName,FALSE) == 0)
      {
      char str[MAX_PATH];
      wsprintf(str,"Could not copy %s to %s.",EXE_NAME,exeName);
      MessageBox(NULL, str, szAppName,MB_OK);
      return;
      }
    }
  
  CoInitialize(NULL);
  CreateStartMenuLink(exeName);
  CreateAutoStartLink(exeName);
  CoUninitialize();
  
  char s1[MAX_PATH];
  strncpy(gConfigInfo.configPath, mBinDir, sizeof(gConfigInfo.configPath));
  App::AddSlash(gConfigInfo.configPath);
  strncpy(s1, gConfigInfo.configPath, sizeof(s1));

  strcat(gConfigInfo.configPath, "jWebServer.ini");
  
  char s2[MAX_PATH];
  strncpy(s2, s1, sizeof(s2));
  strcat(s2,"fileid.diz");
  ResourceFileCopy(IBIN_FILEID_DIZ,s2);
  
  gConfigInfo.mWwwRoot = mWwwrootDir;
  gConfigInfo.mDefaultDoc = "index.html";
  gConfigInfo.m_port = 80;
  gConfigInfo.mAdminId = "admin";
  gConfigInfo.mAdminPwd = "admin";
  gConfigInfo.m_logs = false;
  gConfigInfo.SaveConfig();
  MessageBox(NULL,"jWebServer was successfully installed.",szAppName, MB_OK);
}

void Install::CreateAutoStartLink(const char* exeName)
  {
  if(!mAutoStart ) return;
  
  char str[MAX_PATH];
  ITEMIDLIST* lst;
  SHGetSpecialFolderLocation(mDlg,CSIDL_COMMON_STARTUP,&lst);
  if(lst == 0)
    SHGetSpecialFolderLocation(mDlg,CSIDL_STARTUP,&lst);
  
  if(lst == 0)
    return;
  
  SHGetPathFromIDList(lst,str);
  LPMALLOC ma;
  SHGetMalloc( &ma );
  ma->Free(lst);
  
  App::AddSlash(str);
  strcat(str,"jWebServer.lnk");
  
  if(CreateLink(exeName, str, szAppName) )
    MessageBox(NULL,"Warning, could not create a link to autostart.", szAppName, MB_OK);
  }

void Install::CreateStartMenuLink(const char* exeName)
  {
  if (!mStartMenu ) return;

  char str[MAX_PATH];
  ITEMIDLIST *lst;
  SHGetSpecialFolderLocation(mDlg,CSIDL_PROGRAMS,&lst);

  if(lst == 0)
    return;

  SHGetPathFromIDList(lst,str);
  LPMALLOC ma;
  SHGetMalloc( &ma );
  ma->Free(lst);
  
  App::AddSlash(str);
  strcat(str,"jWebServer");
  
  MakeDir(str);
  strcat(str,"\\jWebServer.lnk");
  
  if( CreateLink(exeName,str,szAppName) )
    MessageBox(NULL,"Warning, could not create a link on the start menu.",szAppName,MB_OK);
  }

void Install::ResourceFileCopy(unsigned long res,char *dest)
  {
  BYTE *byt;
  unsigned long length;
  web.GetResource(MAKEINTRESOURCE(res), length, byt, "BIN");
  
  NtHandle fp("fp");
  fp.mHandle = CreateFile(
    dest,
    GENERIC_WRITE,
    0,
    NULL,
    CREATE_NEW,
    FILE_FLAG_SEQUENTIAL_SCAN,
    NULL);
  
  DWORD dw;
  WriteFile(fp.mHandle, byt,length, &dw, NULL);
  }

void Install::MakeDir(char *str)
  {
  for (char* p = strchr(str,'\\'); p; p = strchr(p+1,'\\'))
    {
    *p=0;
    CreateDirectory(str, NULL);
    *p='\\';
    }
  
  CreateDirectory(str, NULL);
  }

LRESULT WINAPI Install::Proc1(HWND hdlg, UINT msg, WPARAM wparam, LPARAM )
  {
  switch(msg)
    {
    case WM_INITDIALOG: This->SetHwnd(hdlg); break;
    case WM_COMMAND:
      switch( LOWORD(wparam) )
        {
        case IDCANCEL: This->OnCancel(wparam); return TRUE;
        case IDOK: This->CloseDialog(wparam); return TRUE;
        }
      break;
    }
  
  return FALSE;
  }
LRESULT WINAPI Install::Proc2(HWND hdlg, UINT msg, WPARAM wparam, LPARAM)
  {
  switch(msg)
    {
    case WM_INITDIALOG: This->OnInitDialog(hdlg); break;
    case WM_COMMAND:
      switch( LOWORD(wparam) )
        {
        case IDCANCEL: This->OnCancel(wparam); return TRUE;
        case IDOK: This->OnOk(wparam); return TRUE;
        case IDC_BROWSE1: This->OnBrowse1(); return TRUE;
        case IDC_BROWSE2: This->OnBrowse2(); return TRUE;
        }
      break;
    }
  
  return FALSE;
  }

void Install::SetHwnd(HWND hdlg)
  {
  mHwnd = hdlg;
  }

void Install::OnInitDialog(HWND hdlg)
  {
  char str[MAX_PATH];
  DWORD dw;
  HKEY key;

  SetHwnd(hdlg);
  key = NULL;
  RegOpenKey( HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion",&key);
  dw=MAX_PATH;
  RegQueryValueEx( key,"ProgramFilesDir",NULL,NULL,(BYTE*)str,&dw);
  RegCloseKey(key);
  App::AddSlash(str);
  strcat(str,"jWebServer");
  SetDlgItemText(mHwnd,IDC_JWEBDIR,str);
  strcat(str,"\\wwwroot");
  SetDlgItemText(mHwnd, IDC_JWEBHTML,str);
  
  CheckDlgButton(mHwnd, IDC_STARTMENU,BST_CHECKED);
  }
void Install::OnCancel(WPARAM wParam)
  {
  PostQuitMessage(1);
  CloseDialog(wParam);
  }
void Install::OnOk(WPARAM wParam)
  {
  GetDlgItemText(mHwnd,IDC_JWEBHTML, mWwwrootDir, MAX_PATH);
  GetDlgItemText(mHwnd,IDC_JWEBDIR, mBinDir, MAX_PATH);
  mStartMenu = !!IsDlgButtonChecked(mHwnd,IDC_STARTMENU);
  mAutoStart = !!IsDlgButtonChecked(mHwnd,IDC_AUTORUN);
  CloseDialog(wParam);
  }
void Install::CloseDialog(WPARAM wParam)
  {
  EndDialog(mHwnd, LOWORD(wParam));
  }
void Install::OnBrowse1()
  {
  OnBrowse("Select where to install jWebServer", IDC_JWEBDIR);
  }
void Install::OnBrowse2()
  {
  OnBrowse("Select where the HTML files will be kept", IDC_JWEBHTML);
  }
void Install::OnBrowse(const char* title, int resourceid)
  {
  ITEMIDLIST *lst;
  LPMALLOC ma;
  BROWSEINFO bi;
  char str[MAX_PATH];

  bi.hwndOwner = mHwnd;
  bi.pidlRoot=NULL;
  bi.pszDisplayName=str;
  bi.lpszTitle="Select where to install jWebServer";
  bi.ulFlags=0;
  bi.lpfn=NULL;
  bi.lParam=0;
  bi.iImage=0;
  lst = SHBrowseForFolder( &bi);
  if(lst!=NULL)
    {
    SHGetPathFromIDList(lst,str);
    SHGetMalloc( &ma );
    ma->Free(lst);
    SetDlgItemText(mHwnd, resourceid, str);
    }
  }


// CreateLink - uses the shell's IShellLink and IPersistFile interfaces 
//   to create and store a shortcut to the specified object. 
// Returns the result of calling the member functions of the interfaces. 
// lpszPathObj - address of a buffer containing the path of the object. 
// lpszPathLink - address of a buffer containing the path where the 
//   shell link is to be stored. 
// lpszDesc - address of a buffer containing the description of the 
//   shell link. 
HRESULT Install::CreateLink(LPCSTR lpszPathObj, LPSTR lpszPathLink, LPCSTR lpszDesc) 
  { 
  // Get a pointer to the IShellLink interface. 
  IShellLink* psl; 
  HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&psl); 
  
  if (FAILED(hres)) 
    return hres;

  // Set the path to the shortcut target and add the 
  // description. 
  psl->SetPath(lpszPathObj); 
  psl->SetDescription(lpszDesc); 
  
  // Query IShellLink for the IPersistFile interface for saving the 
  // shortcut in persistent storage. 
  IPersistFile* ppf; 
  hres = psl->QueryInterface( IID_IPersistFile, (void**)&ppf); 
  
  if (SUCCEEDED(hres)) 
    { 
    // Ensure that the string is ANSI. 
    WORD wsz[MAX_PATH]; 
    MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, (LPWSTR)wsz, MAX_PATH);
    
    // Save the link by calling IPersistFile::Save. 
    hres = ppf->Save( (LPWSTR)wsz, TRUE); 
    ppf->Release(); 
    } 

  psl->Release(); 
  return hres; 
  } 

Install.h

Synopsis
#pragma once

class Install
  {
  public:
    Install(HINSTANCE inst, HWND dlg);
    void Run(void);
    Install();
  private:
    void MakeDir(char *str);
    void ResourceFileCopy(unsigned long res,char *dest);
    void CreateStartMenuLink(const char* exeName);
    void CreateAutoStartLink(const char* exeName);
    HRESULT CreateLink(LPCSTR lpszPathObj, LPSTR lpszPathLink, LPCSTR lpszDesc);

    void OnInitDialog(HWND hdlg);
    void OnCancel(WPARAM wParam);
    void OnOk(WPARAM wParam);
    void OnBrowse1();
    void OnBrowse2();
    void OnBrowse(const char* title, int resourceid);
    void CloseDialog(WPARAM wParam);
    void SetHwnd(HWND hdlg);
    
    static LRESULT WINAPI Proc1(HWND hdlg, UINT msg, WPARAM wparam, LPARAM );
    static LRESULT WINAPI Proc2(HWND hdlg, UINT msg, WPARAM wparam, LPARAM);
    
    char mWwwrootDir[MAX_PATH];
    char mBinDir[MAX_PATH];
    bool mStartMenu;
    bool mAutoStart;
    HWND mHwnd;
    HWND mDlg;
    HINSTANCE mInstance;
  } ;

jwebserver.cpp

Synopsis
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "common.h"
#include "App.h"
#include "ConfigInfo.h"

//globals
const char* szAppName ="jWebServer V0.1";// The name of the application
const char* szServerVersion ="jWebServer/0.1";// The version string used in responses
const char* szCgiBinName = "cgi-bin";

static App localWeb;
App& web = localWeb;
static ConfigInfo localConfigInfo;
ConfigInfo& gConfigInfo = localConfigInfo;

int PASCAL    WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int)
  {
  //jaa: allow multiple instances 
  //	h = CreateMutex(NULL,TRUE,szAppName);
  //	if(GetLastError()==ERROR_ALREADY_EXISTS)
  //		return 0;
  
  web.Create(hInst);
  
  MSG	msg;
  while (GetMessage( &msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
  
  web.CleanUp();
  //jaa: allow multiple instances
  //	CloseHandle(h);
  return 0;
  
  }

NtHandle.h

Synopsis
#pragma once

class NtHandle
  {
  public:
    HANDLE mHandle;
    char mId[50];
    NtHandle(char* id)
      : mHandle(INVALID_HANDLE_VALUE)
      {
      strcpy(mId, id);
      }
    ~NtHandle()
      {
      if (mHandle != INVALID_HANDLE_VALUE)
        CloseHandle(mHandle);
      mHandle = INVALID_HANDLE_VALUE;
      }
  private:
    // NtHandle();
  } ;


class NtFindHandle
  {
  public:
    HANDLE mHandle;
    char mId[50];
    NtFindHandle(char* id)
      : mHandle(INVALID_HANDLE_VALUE)
      {
      strcpy(mId, id);
      }
    ~NtFindHandle()
      {
      if (mHandle != INVALID_HANDLE_VALUE)
        FindClose(mHandle);
      mHandle = INVALID_HANDLE_VALUE;
      }
  private:
    //NtHandle();
  } ;

request.cpp

Synopsis
#pragma warning(disable: 4786)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <algorithm>
using std::for_each;

#include "common.h"
#include "App.h"
#include "resource.h"
#include "Request.h"
#include "NtHandle.h"
#include "ConfigInfo.h"
#include "DirLister.h"
#include "HtmlDirGen.h"

Request::Request()
  {
  m_query=NULL;
  }
void Request::Get(char *path)
  {
  WIN32_FIND_DATA ff;
  char *back;
  
  URLDecode(path);
  if(*path=='/')
    path++;
  
  if( StartsWith(path,"admin") )
    {
    Admin(path+5);
    return;
    }
  
  strncpy(m_fileRequest,gConfigInfo.mWwwRoot.c_str(),MAX_PATH);
  App::AddSlash( m_fileRequest );
  strcat(m_fileRequest,path);
  SwapChar(m_fileRequest,'/','\\');
  
  // Prevent using .. to get to previous directory
  m_query=strchr(m_fileRequest,'?');
  if(m_query != 0)
    *(m_query++) = 0;
  
  if(strstr(m_fileRequest,"..") != 0 || strstr(m_fileRequest,"*") != 0 )
    {
    Error(403);
    return;
    }
  
  if( gConfigInfo.m_cgi && StartsWith(path,"cgi-bin") )
    {
    if( Cgi(path+7) )
      return;
    }
  
  NtFindHandle h("h");
  h.mHandle =FindFirstFile(m_fileRequest,&ff);
  if(h.mHandle !=INVALID_HANDLE_VALUE)
    {
    if(ff.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
      {
      if(m_fileRequest[strlen(m_fileRequest)-1] != '\\')
        {
        strcat(m_fileRequest,"\\");
        GenMovedPermanentlyPage(path);
        return;
        }
      }
    }
  
  if(m_fileRequest[strlen(m_fileRequest)-1]=='\\')
    {
    back = m_fileRequest+strlen(m_fileRequest);
    strcat(m_fileRequest, gConfigInfo.mDefaultDoc.c_str());
    NtHandle h("h2");
    h.mHandle =FindFirstFile(m_fileRequest,&ff);
    if(h.mHandle == INVALID_HANDLE_VALUE)
      {
      *back=0;
      App::AddSlash(m_fileRequest);
      }
    }
  }


void Request::Admin(char *path)
  {
  if(*path=='/')
    path++;
  
  CharUpperBuff(path, strlen(path));
  
  if(StartsWith(path,"LOGIN.HTML") )
    HandleLogin();		
  else if(StartsWith(path,"CHANGE.HTML") )
    HandleChange();		
  else if(StartsWith(path,"DELMIME.HTML") )
    HandleMimeDelete();		
  else if(StartsWith(path,"CHANGEMIME.HTML") )
    HandleMimeChange();		
  else if(StartsWith(path,"DELSCRIPT.HTML") )
    HandleScriptDelete();		
  else if(StartsWith(path,"CHANGESCRIPT.HTML") )
    HandleScriptChange();
  else if (!stricmp(path,"SINFO.HTML") )	
    SendResource(MAKEINTRESOURCE(IWD_SINFO_HTML),"HTML");
  else if(!stricmp(path,"VERSION.HTML") )	
    SendResource(MAKEINTRESOURCE(IWD_VERSION_HTML),"HTML");
  else if(!stricmp(path,"INDEX.HTML") || !*path)
    SendResource(MAKEINTRESOURCE(IWD_INDEX_HTML),"HTML");
  else if(!stricmp(path,"LICENSE.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_LICENSE_HTML),"HTML");
  else if(!stricmp(path,"TECH.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_TECH_HTML),"HTML");
  else if(!stricmp(path,"FAQ.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_FAQ_HTML),"HTML");
  else if(!stricmp(path,"ADDRESS.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_ADDRESS_HTML),"HTML");
  else if(!stricmp(path,"MINFO.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_MINFO_HTML),"HTML");
  else if(!stricmp(path,"FILES.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_FILES_HTML),"HTML");
  else if(!stricmp(path,"CONFIG.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_CONFIG_HTML),"HTML");
  else if(!stricmp(path,"QUICK.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_QUICK_HTML),"HTML");
  else if(!stricmp(path,"ABOUT.HTML") )
    SendResource(MAKEINTRESOURCE(IWD_ABOUT_HTML),"HTML");
  else
    Error(404);
  }

void Request::HandleChange(void)
  {
  
  gConfigInfo.m_logs=false;
  gConfigInfo.m_list=false;
  gConfigInfo.m_cgi=false;
  
  string id;
  string pw;
  string newid;
  string newpw;
  string action;
  string name;
  string value;
  while( Parse(name, value) )
    {
    if(name == "AID")
      id = value;
    else if (name == "APW")
      pw = value;
    else if(name == "ACTION") 
      action = value;
    else  if(name == "ID") 
      newid = value;
    else if(name == "PW") 
      newpw = value;
    else if(name == "SITE") 
      gConfigInfo.mSiteName = value;
    else if(name == "PORT") 
      gConfigInfo.m_port=atoi(value.c_str());
    else if(name == "ROOT") 
      gConfigInfo.mWwwRoot = value;
    else if(name == "DEF") 
      gConfigInfo.mDefaultDoc = value;
    else if(name == "LOG") 
      gConfigInfo.m_logs = value =="on";
    else if(name == "LIST") 
      gConfigInfo.m_list = value =="on";
    else if(name == "CGI") 
      gConfigInfo.m_cgi = value =="on";
    }
  
  if (LoginFailed(id, pw))
    return;
  
  if(action == "MIME")
    {
    HandleMime();
    return;
    }
  
  if(action == "SCRIPT")
    {
    HandleScript();
    return;
    }
  
  gConfigInfo.mAdminId = newid;
  gConfigInfo.mAdminPwd = newpw;
  
  gConfigInfo.SaveConfig();
  mPageGen.ConfirmationPage("Changes Saved", "Restart the server if you changed the port (click stop and then start).", false);
  }

void Request::HandleLogin(void)
  {
  char *ptr=NULL;
  
  string id;
  string pw;
  string name;
  string value;
  while(Parse(name, value))
    {
    if(name == "ID") 
      id = value;
    else  if(name == "PW") 
      pw = value;
    }
  
  if (LoginFailed(id, pw))
    return;
  
  GenMainConfigPage();
  }


void Request::HandleMime(void)
  {
  mPageGen.ConfigPage(HtmlPageGen::Mime, "MIME Type Configuration",
    "Mime types indicate the kind of file being sent to a web browser. "
    "Web browsers recognize standard mime tags and display files depending on the type. "
    "You can associate a MIME tag with a file extension using this page.",
    "/admin/changemime.html", "Add/Change a MIME mapping", "Mime Mapping",
    "/admin/delmime.html", "Delete a MIME Mapping"
    );
  }

void Request::HandleMimeChange(void)
  {
  string id;
  string pw;
  string ext;
  string tag;
  string name;
  string value;
  while(Parse(name, value))
    {
    if(name == "AID")
      id = value;
    else if(name == "APW")
      pw = value;
    else if(name == "EXT") 
      ext = value;
    else if(name == "TAG") 
      tag = value;
    }
  
  if (LoginFailed(id, pw))
    return;
  
  if(!ext.empty() && !tag.empty())
    gConfigInfo.AddMime(ext,tag);
  
  gConfigInfo.SaveConfig();
  mPageGen.ConfirmationPage("Mime Changes Made", "The MIME changes you requested have been made."); 
  }

void Request::HandleMimeDelete(void)
  {
  string id;
  string pw;
  string ext;
  string name;
  string value;
  while( Parse(name, value) )
    {
    if(name == "AID")
      id = value;
    else if(name == "APW")
      pw = value;
    else if(name == "EXT")
      ext = value;
    }
  
  if (LoginFailed(id, pw))
    return;
  
  if (!ext.empty())
    gConfigInfo.DelMime(ext);
  
  gConfigInfo.SaveConfig();
  mPageGen.ConfirmationPage("Mime Tag Deleted", "The MIME tag has been deleted.");
  }

void Request::HandleScript(void)
  {
  mPageGen.ConfigPage(HtmlPageGen::Script, "Script Configuration",
    "Indicate which script engine to run for file extensions in your cgi-bin directory.",
    "/admin/changescript.html", "Add/Change a Script Mapping", "Script Engine (full path)",
    "/admin/delscript.html", "Delete a Script Mapping"
    );
  }

void Request::HandleScriptChange(void)
  {
  string id;
  string pw;
  string ext;
  string tag;
  string value;
  string name;
  while(Parse(name, value))
    {
    if(name == "AID")
      id = value;
    else if(name == "APW")
      pw = value;
    else if(name == "EXT") 
      ext = value;
    else if(name == "TAG") 
      tag = value;
    }
  
  if (LoginFailed(id, pw))
    return;
  
  
  if (!ext.empty() && !tag.empty())
    gConfigInfo.AddScript(ext,tag);
  
  gConfigInfo.SaveConfig();
  mPageGen.ConfirmationPage("Script Changes Made", "The script changes have been made.");
  }

void Request::HandleScriptDelete()
  {
  string id;
  string pw;
  string ext;
  string name;
  string value;
  while( Parse(name, value) )
    {
    if(name == "AID")
      id = value;
    else if(name == "APW")
      pw = value;
    else if(name == "EXT")
      ext = value;		
    }
  
  if (LoginFailed(id, pw))
    return;
  
  if(!ext.empty())
    gConfigInfo.DelScript(ext);
  
  gConfigInfo.SaveConfig();
  mPageGen.ConfirmationPage("Script Tag Deleted", "The script tag has been deleted.");
  }

void Request::GenerateMime(char *e,char *buffer)
  {
  char tag[80];
  
  gConfigInfo.GetMime(e, tag);
  strncpy(buffer,"Content-Type: ",80);
  strcat(buffer,tag);
  }


void Request::LogRequest()
  {
  char buffer[4096];
  char path[256];
  
  if(!gConfigInfo.m_logs)
    return;
  
  GetCurrentDirectory(256,path);
  if( path[strlen(path)-1]!='\\')
    strcat(path,"\\");
  strcat(path,"access.log");
		
  
  NtHandle fp("fp");
  fp.mHandle = CreateFile( 
    path, 
    GENERIC_WRITE, 
    FILE_SHARE_READ, 
    NULL, 
    OPEN_ALWAYS , 
    FILE_FLAG_SEQUENTIAL_SCAN, 
    NULL); 
  if(fp.mHandle ==INVALID_HANDLE_VALUE)
    return;
  
  //format:
  // 000.000.000.000 - - [dd/mmm/yyyy:hh:mm:ss -0400] "GET / HTTP/1.0" 200 361
  
  
  unsigned char a,b,c,d;
  char adj[20];
  DWORD dw;
  SYSTEMTIME st;
  TIME_ZONE_INFORMATION tz;
  int bias;
  char *month[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
  
  GetTimeZoneInformation(&tz);
  GetLocalTime(&st);
  
  Socket::Long2IP(m_requestBy,&a,&b,&c,&d);
  SetFilePointer(fp.mHandle,0,0,FILE_END);
  
  bias=tz.Bias;
  if(bias<0)
    bias=-bias;
  wsprintf(buffer,"%d.%d.%d.%d - - [%02d/%s/%d:%02d:%02d:%02d %c%04d] \"%s %s\" ",a,b,c,d,
    st.wDay,month[st.wMonth-1],st.wYear,st.wHour,st.wMinute,st.wSecond,tz.Bias<0?'-':'+',bias,
    m_verb, m_request);
  
  if(*m_errorText)
    wsprintf(adj,"%d %ld\r\n",atol(m_errorText+9),mStrOut.GetLength());
  else
    wsprintf(adj,"%d %ld\r\n",100, mOut.GetBytesSent());
  strcat(buffer,adj);
  WriteFile(fp.mHandle,buffer,strlen(buffer),&dw,NULL);
  }


void Request::FileList(void)
  {
  if(!gConfigInfo.m_list)
    {
    Error(403);
    return;
    }
  
  mOut.WriteHeader();
  mOut.WriteHtmlContentType();
  mOut.WriteEndOfHeaders();
  
  HtmlDirGen gen(mOut);
  gen.Gen(m_fileRequest);
  
  m_done = true;
  }


void Request::GenMainConfigPage()
  {
  mPageGen.MainPage(web.GetPort());
  }


bool Request::LoginFailed(const string& id, const string& pw)
  {
  if (gConfigInfo.mAdminPwd.empty())
    return false;
  
  int val =0;
  if (pw == gConfigInfo.mAdminPwd)
    val++;
  if(id == gConfigInfo.mAdminId)
    val++;
  if(val ==2)
    return false;
  
  gConfigInfo.LoadConfig();
  mPageGen.ConfirmationPage("Login Failed", "Invalid user name or password", false);
  return true;
  }

void Request::GenMovedPermanentlyPage(const string& newpath)
  {
  mOut.WriteMovePermanently();
  mOut.WriteDate();
  mOut.WriteServerVersion();
  mOut.Write("Location: ");
  mOut.Write(newpath);
  mOut.Writeln("/");
  mOut.Writeln("Keep-Alive: timeout=15, max=100");
  mOut.WriteHtmlContentType();
  mOut.WriteEndOfHeaders();
  
  mOut.HTML();
  mOut.HEAD();
  mOut.TITLE("301 Moved Permanently");
  mOut.CloseTag();//head
  mOut.BODY();
  mOut.H1("Moved Permanently");
  
  mOut.Write("The document has moved ");
  mOut.A(newpath, "here");
  mOut.BR();
  mOut.CloseAllTags();
  }


Request.h

Synopsis
#pragma once

#include "HttpRequest.h"

class Request:public HttpRequest
{
public:
	Request();

protected:
	void Get(char *path);
	void Admin(char *path);
	bool Cgi(char *path);
	void HandleLogin(void);
	void HandleChange(void);
	void HandleMime(void);
  bool LoginFailed(const string& id, const string& pw);
	void GenerateMime(char *e,char *buffer);
	void HandleMimeChange(void);
	void HandleMimeDelete(void);
	void HandleScriptChange(void);
	void HandleScriptDelete(void);

	void LogRequest(void);
	void FileList(void);
	void HandleScript(void);

  void GenMainConfigPage();
  void GenMovedPermanentlyPage(const string& newpath);

  char *m_query;
};

Socket.cpp

Synopsis
#include <winsock2.h>
#include "Socket.h"

class Socket::Private
  {
  public:
    Private()
    : mSocket(0)
      {
      }
    SOCKET mSocket;
    HWND m_hwnd;
  } ; 

Socket::Socket()
: impl(* new Socket::Private())
  {
  }

Socket::Socket(unsigned int s)
: impl(* new Socket::Private())
  {
  impl.mSocket = s;
  }

Socket::~Socket()
  {
  delete &impl;
  }

bool Socket::IsInvalid()
  {
  return impl.mSocket == INVALID_SOCKET;
  }
bool Socket::IsAddressInUse()
  {
  return WSAGetLastError () != WSAEADDRINUSE;
  }
bool Socket::IsError(int err)
  {
  return err == SOCKET_ERROR;
  }
bool Socket::IsEqual(const Socket& other)
  {
  return impl.mSocket == other.impl.mSocket;
  }
void Socket::CreateListener()
  {
  impl.mSocket = socket(AF_INET, SOCK_STREAM, 0);
  }
void Socket::Close()
  {
  if (impl.mSocket != 0)
    {
    closesocket(impl.mSocket);
    impl.mSocket = 0;
    }
  }
void Socket::SetNonBlockingMode(HWND hwnd)
  {
  WSAAsyncSelect(impl.mSocket, hwnd, 0, 0);
  unsigned long ul =0;
  ioctlsocket(impl.mSocket, FIONBIO, &ul);
  }

void Socket::AsyncClose(HWND hwnd)
  {
  WSAAsyncSelect(impl.mSocket, hwnd, WM_USER, FD_CLOSE);
  }

void Socket::AsyncCloseOrAccept(HWND hwnd)
  {
  WSAAsyncSelect(impl.mSocket, hwnd, WM_USER, FD_CLOSE|FD_ACCEPT);
  }

void Socket::Skip(int n)
  {
  char* buffer = new char[n+1];
  recv(impl.mSocket, buffer, n, 0);
  delete[] buffer;
  }

unsigned long Socket::Recv(char* buffer, unsigned long bufsize)
  {
  return recv(impl.mSocket, buffer, bufsize,0);
  }

unsigned long Socket::Send(const char* buffer, unsigned long bytestosend)
  {
  return send(impl.mSocket, buffer, bytestosend, 0);
  }

void Socket::ShutDown()
  {
	shutdown(impl.mSocket, SD_SEND);
  }

Socket Socket::Accept(unsigned long& addr, unsigned long& port)
  {
  struct sockaddr_in remote;

  int dw = sizeof(remote);
  remote.sin_family = AF_INET;
  SOCKET s = accept(impl.mSocket, (struct sockaddr *)&remote, &dw );

  addr = IP2Long(remote.sin_addr.S_un.S_un_b.s_b1,
    remote.sin_addr.S_un.S_un_b.s_b2,
    remote.sin_addr.S_un.S_un_b.s_b3,
    remote.sin_addr.S_un.S_un_b.s_b4);

  port = remote.sin_port;
  return Socket(s);
  }

Socket& Socket::operator=(const Socket& other)
  {
  impl.mSocket = other.impl.mSocket;
  return *this;
  }

int Socket::Bind(unsigned long portno)
  {
  SOCKADDR_IN localAddr;
  memset(&localAddr,0,sizeof(localAddr) );
  localAddr.sin_port = htons ((u_short) portno);
  localAddr.sin_family = AF_INET;

  return bind (impl.mSocket, (PSOCKADDR) &localAddr, sizeof (localAddr));
  }

int Socket::Listen(int n)
  {
  return listen(impl.mSocket, n);
  }
const char* Socket::GetLastErrorDesc()
  {
  switch(WSAGetLastError())
    {
    case WSANOTINITIALISED: return "Could not start server. WSAStartup not called.";
    case WSAENETDOWN: return "Could not start server. The network subsystem failed.";
    case WSAEAFNOSUPPORT: return "Could not start server. Address family not supported.";
    case WSAEINPROGRESS: return "Could not start server. Still processing a call.";
    case WSAEMFILE: return "Could not start server. No more socket descriptors available.";
    case WSAENOBUFS: return "Could not start server. No buffer available.";
    case WSAEPROTONOSUPPORT: return "Could not start server. Protocol is not supported.";
    case WSAEPROTOTYPE: return "Could not start server. The wrong protocol for this socket.";
    case WSAESOCKTNOSUPPORT: return "Could not start server. Unsupported socket type for this address family.";
    default: return "Could not start server.";
    }
  }

void Socket::Long2IP(unsigned long l,unsigned char *b1,unsigned char *b2,unsigned char *b3,unsigned char *b4)
{
	*b4=(unsigned char)(l&0xff);
	*b3=(unsigned char)((l&0xff00)/0x100);
	*b2=(unsigned char)((l&0xff0000)/0x10000);
	*b1=(unsigned char)((l&0xff000000)/0x1000000);
}

unsigned long Socket::IP2Long(unsigned char b1,unsigned char b2,unsigned char b3,unsigned char b4)
{
	unsigned long rtn;

	rtn=b4+(b3*0x100)+(b2*0x10000)+(b1*0x1000000);
	return rtn;
}

void Socket::TermEnvironment()
  {
  WSACleanup();
  }

bool Socket::InitEnvironment()
  {
  WSADATA WsaData;
  int err = WSAStartup (0x0101, &WsaData);
  return err != SOCKET_ERROR;
  }


Socket.h

Synopsis
#pragma once

class Socket
  {
  public:
    Socket();
    explicit Socket(unsigned int s);
    ~Socket();

    Socket Accept(unsigned long& addr, unsigned long& port);
    int Listen(int n);
    int Bind(unsigned long portno);

    bool IsInvalid();
    bool IsEqual(const Socket& other);
    void CreateListener();
    void Close();
    void SetNonBlockingMode(HWND hwnd);
    void AsyncClose(HWND hwnd);
    void AsyncCloseOrAccept(HWND hwnd);
    void Skip(int n);
    unsigned long Recv(char* buffer, unsigned long bufsize);
    unsigned long Send(const char* buffer, unsigned long bytestosend);
    inline unsigned long Send(const unsigned char* buffer, unsigned long bytestosend)
      {
      return Send((const char*) buffer, bytestosend);
      }
    void ShutDown();
    bool IsAddressInUse();
    bool IsError(int err);

    Socket& operator=(const Socket& other);
    const char* GetLastErrorDesc();
    static void Long2IP(unsigned long l,unsigned char *b1,unsigned char *b2,unsigned char *b3,unsigned char *b4);
    static void TermEnvironment();
    static bool InitEnvironment();
 
  private:
    unsigned long IP2Long(unsigned char b1,unsigned char b2,unsigned char b3,unsigned char b4);

    class Private;
    Private& impl;
  } ;






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010