campo-sirio/server/baseserv.cpp

661 lines
16 KiB
C++
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////
// Name: BaseServ.cpp
// Purpose: Simple base Server
// Author: Guy
// Modified by:
// Created: 21/08/2002
// Copyright: (c) 2002 Guy
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ==========================================================================
// declarations
// ==========================================================================
// --------------------------------------------------------------------------
// headers
// --------------------------------------------------------------------------
#include "BaseServ.h"
#include <wx/config.h>
#include <wx/image.h>
#include <wx/mimetype.h>
#include <wx/sckstrm.h>
#include <wx/msw/iniconf.h>
#include <wx/app.h>
///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////
TBaseServerApp& GetServerApp()
{
return (TBaseServerApp&)*wxTheApp;
}
wxString Date2String(const wxDateTime& date)
{
return date.Format("%d-%m-%Y");
}
wxDateTime String2Date(const wxString& str)
{
int d, m, y;
sscanf(str, "%d-%d-%d", &d, &m, &y);
wxDateTime date(d, wxDateTime::Month(m-1), y);
return date;
}
wxSocketBase& operator<<(wxSocketBase& outf, const wxChar* str)
{
if (str && *str)
outf.Write(str, wxStrlen(str));
return outf;
}
wxSocketBase& operator<<(wxSocketBase& outf, wxString str)
{
if (!str.IsEmpty())
outf.Write(str, str.Length());
return outf;
}
wxSocketBase& operator<<(wxSocketBase& outf, size_t num)
{
return outf << wxString::Format("%u", num);
}
// --------------------------------------------------------------------------
// classes
// --------------------------------------------------------------------------
///////////////////////////////////////////////////////////
// THashTable
///////////////////////////////////////////////////////////
void THashTable::Put(const wxString& key, const wxString& value)
{
THashString* entry = new THashString(value);
m_Hash.Put(key, entry);
}
wxString THashTable::Get(const wxChar* key) const
{
wxString strValue;
THashString* str = (THashString*)m_Hash.Get(key);
if (str != NULL)
strValue = str->m_str;
return strValue;
}
THashTable::THashTable(size_t size) : m_Hash(wxKEY_STRING, size)
{
m_Hash.DeleteContents(true);
}
#ifdef WIN32
///////////////////////////////////////////////////////////
// TTaskbarIcon
///////////////////////////////////////////////////////////
BEGIN_EVENT_TABLE(TTaskBarIcon, wxTaskBarIcon)
EVT_TASKBAR_LEFT_DOWN(TTaskBarIcon::OnTaskBarClick)
END_EVENT_TABLE()
void TTaskBarIcon::OnTaskBarClick(wxTaskBarIconEvent& e)
{
wxString url;
url << "http://127.0.0.1:" << GetServerApp().GetDefaultPort();
::ShellExecute(0, "open", url, NULL, NULL, SW_SHOWNORMAL);
}
void TTaskBarIcon::Init()
{
const TBaseServerApp& a = GetServerApp();
wxIcon icon;
const wxString strIcon = a.GetConfigString("Icon");
if (wxFileExists(strIcon))
{
wxInitAllImageHandlers();
wxImage img(strIcon);
img.Rescale(32,32);
icon.CopyFromBitmap(img.ConvertToBitmap());
}
else
icon.LoadFile("mondrian", wxBITMAP_TYPE_ICO_RESOURCE);
SetIcon(icon, a.GetAppName());
}
#endif
///////////////////////////////////////////////////////////
// TBaseServerApp
///////////////////////////////////////////////////////////
enum
{
SERVER_ID = 1001,
SOCKET_ID = 1002
};
BEGIN_EVENT_TABLE(TBaseServerApp, wxApp)
EVT_SOCKET(SERVER_ID, TBaseServerApp::OnServerEvent)
EVT_SOCKET(SOCKET_ID, TBaseServerApp::OnSocketEvent)
END_EVENT_TABLE()
void TBaseServerApp::WriteLog(const wxChar* str) const
{
if (m_log != NULL)
*m_log << str << endl;
}
wxString TBaseServerApp::GetTempFilename()
{
wxString strTmp;
if (m_strTempDir.IsEmpty())
{
const wxString strPrefix = "Srv";
::wxGetTempFileName(strPrefix, strTmp);
::wxRemoveFile(strTmp);
(wxString&)m_strTempDir << ::wxPathOnly(strTmp) << "/";
}
strTmp = m_strTempDir;
strTmp += wxString::Format("%s_%d.htm", GetAppName(), m_nTmpCounter);
m_nTmpCounter++;
if (m_nTmpCounter >= 4)
m_nTmpCounter = 0;
return strTmp;
}
TXmlItem& TBaseServerApp::AddLogo(TXmlItem& tr) const
{
TXmlItem& td = tr.AddChild("td");
td.SetAttr("width", "15%");
td.SetAttr("align", "center");
TXmlItem& a = td.AddChild("a");
a.SetAttr("href", "/index.htm");
const wxString gif = GetConfigString("Icon", "euro.gif");
TXmlItem& img = a.AddChild("img");
img.SetAttr("src", gif);
img.SetAttr("border", "0");
img.SetAttr("alt", "Return to Home Page");
return td;
}
TXmlItem& TBaseServerApp::CreatePageBody(TXmlItem& html, wxString header) const
{
if (header.IsEmpty())
header << GetAppName() << " Server";
html.SetTag("html");
TXmlItem& head = html.AddChild("head");
TXmlItem& meta1 = head.AddChild("meta");
meta1.SetAttr("name", "GENERATOR");
meta1.SetAttr("content", GetAppName());
TXmlItem& meta2 = head.AddChild("meta");
meta2.SetAttr("HTTP-EQUIV", "Content-Type");
meta2.SetAttr("content", "text/html; charset=iso-8859-1");
head.AddChild("title") << header;
TXmlItem& body = html.AddChild("body");
body.SetAttr("bgcolor", "#EFCEAD");
if (m_bRunning)
body.SetAttr("background", "back.gif");
TXmlItem& title = body.AddChild("table");
title.SetAttr("border", "5"); title.SetAttr("width", "100%");
TXmlItem& title_tr = title.AddChild("tr");
if (m_bRunning)
AddLogo(title_tr);
TXmlItem& td1 = title_tr.AddChild("td");
td1.AddChild("h1").AddChild("center") << header;
if (m_bRunning)
AddLogo(title_tr);
body.AddChild("br");
return body;
}
TXmlItem& TBaseServerApp::AddLinkButton(TXmlItem& body, const wxChar* strCaption, const wxChar* strHref) const
{
TXmlItem& table = body.AddChild("table");
table.SetAttr("border", "2"); table.SetAttr("bgcolor", "#C0C0C0");
TXmlItem& td = table.AddChild("tr").AddChild("td");
td.SetAttr("align", "center");
td.AddChild("a").SetAttr("href", strHref) << strCaption;
return table;
}
void TBaseServerApp::MessageBox(const wxChar* caption, const wxChar* msg, wxSocketBase& outs)
{
TXmlItem html;
TXmlItem& body = CreatePageBody(html);
TXmlItem& table = body.AddChild("center").AddChild("table");
table.SetAttr("width", "70%"); table.SetAttr("border", "2");
table.AddChild("caption").AddChild("h2") << caption;
TXmlItem& td0 = table.AddChild("tr").AddChild("td");
td0.SetAttr("align", "justify");
td0.AddChild("h3") << msg;
TXmlItem& td1 = table.AddChild("tr").AddChild("td");
td1.SetAttr("align", "center");
AddLinkButton(td1, "Return to main page", "index.htm");
const wxString strTmp = GetTempFilename();
html.Save(strTmp);
SendFile(strTmp, outs);
}
bool TBaseServerApp::CanProcessCommand(wxString& cmd, wxSocketBase& outs)
{
if (cmd.Find("stop.cgi") > 0)
{
m_bRunning = false; // Disable gif on title
MessageBox("Warning!", "Server Stopped", outs);
ExitMainLoop();
return false;
}
return true;
}
void TBaseServerApp::SendContent(wxFileInputStream& inf, wxSocketBase& sock)
{
const size_t nSize = inf.GetSize();
WriteLog(wxString::Format("Sending %u bytes", nSize));
const size_t BUF_TEMP_SIZE = nSize; // was 1024*16
char* buf = new char[BUF_TEMP_SIZE];
size_t bytes = BUF_TEMP_SIZE;
size_t nTotalWritten = 0;
while (bytes == BUF_TEMP_SIZE)
{
bytes = inf.Read(buf, bytes).LastRead();
size_t nWritten = sock.Write(buf, bytes).LastCount();
nTotalWritten += nWritten;
}
delete buf;
if (nTotalWritten < nSize)
WriteLog(wxString::Format("I sent %u on %u bytes only.", nTotalWritten, nSize));
}
void TBaseServerApp::SendFile(wxString strFilename, wxSocketBase& sock)
{
const bool bFound = wxFileExists(strFilename);
if (!bFound)
{
const wxString strTmp = GetTempFilename();
if (!strTmp.IsEmpty())
{
TXmlItem html;
TXmlItem& body = CreatePageBody(html);
body.AddChild("h1") << "Sorry: File not found!";
body.AddChild("br");
body.AddChild("h2") << strFilename;
html.Save(strTmp);
}
strFilename = strTmp;
}
wxString strType;
wxSplitPath(strFilename, NULL, NULL, &strType);
wxFileType* type = wxTheMimeTypesManager->GetFileTypeFromExtension(strType);
if (type != NULL)
type->GetMimeType(&strType);
else
strType = "text/plain";
const wxDateTime date = ::wxFileModificationTime(strFilename);
wxFileInputStream inf(strFilename);
const size_t nSize = inf.GetSize();
if (bFound)
sock << "HTTP/1.1 200 OK\n";
else
sock << "HTTP/1.1 401 Not found\n";
sock << "Server: " << GetAppName() << "\n";
sock << "Host: " << wxGetFullHostName() << "\n";
if (bFound && m_bRunning)
sock << "Connection: keep-alive\n";
else
sock << "Connection: close\n";
sock << "Content-Type: " << strType << "\n";
sock << "Content-Length: " << nSize << "\n";
sock << "Date: " << date.Format("%#c") << "\n";
wxDateTime dtFraUnPo = wxDateTime::Now();
const wxTimeSpan minuto(0, 1, 0); // Zero ore, Un minuto, Zero secondi
dtFraUnPo += minuto;
sock << "Expires: " << dtFraUnPo.Format("%#c") << "\n";
sock << "\n";
SendContent(inf, sock);
}
void TBaseServerApp::SendNotModifiedFile(wxSocketBase& sock)
{
sock << "HTTP/1.1 304 Not Modified\n";
sock << "Date: " << wxDateTime::Now().Format("%#c") << "\n";
sock << "Server: " << GetAppName() << "\n";
sock << "Connection: close\n";
}
const wxChar* TBaseServerApp::GetAppName() const
{
// Pure virtual function
return "Server";
}
void TBaseServerApp::ProcessCommand(wxString cmd, wxSocketBase& outs)
{
// Pure virtual function
WriteLog("Processing...");
SendFile("index.htm", outs);
}
void TBaseServerApp::OnServerEvent(wxSocketEvent& event)
{
wxString s = "--- OnServerEvent: ";
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION : s.Append("wxSOCKET_CONNECTION"); break;
default : s.Append("Unexpected event!"); break;
}
WriteLog(s);
// Accept new connection if there is one in the pending
// connections queue, else exit. We use Accept(FALSE) for
// non-blocking accept (although if we got here, there
// should ALWAYS be a pending connection).
wxSocketBase* sock = m_server->Accept(FALSE);
if (sock)
{
WriteLog("--- New client connection accepted");
}
else
{
WriteLog("### Error: couldn't accept a new connection");
sock->Destroy();
return;
}
sock->SetEventHandler(*this, SOCKET_ID);
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
sock->Notify(TRUE);
}
void TBaseServerApp::OnSocketEvent(wxSocketEvent& event)
{
wxSocketBase& sock = *event.GetSocket();
switch(event.GetSocketEvent())
{
case wxSOCKET_INPUT:
{
// We disable input events, so that the test doesn't trigger
// wxSocketEvent again.
sock.SetNotify(wxSOCKET_LOST_FLAG);
// Read the data
const size_t BUFSIZE = 2048;
wxString str;
wxChar* bufStart = str.GetWriteBuf(BUFSIZE);
memset(bufStart, 0, BUFSIZE);
wxChar* buf = bufStart;
const size_t len = sock.Read(buf, BUFSIZE).LastCount();
buf += len;
// Attendi la fine del comando HTTP!
if (strncmp(bufStart, "GET ", 4) == 0 || strncmp(bufStart, "POST ", 5) == 0)
{
while (strstr(bufStart, "\r\n\r\n") == NULL)
{
const size_t len = sock.Read(buf, BUFSIZE).LastCount();
buf += len;
}
}
str.UngetWriteBuf();
WriteLog(str);
if (CanProcessCommand(str, sock))
{
const wxSocketFlags flags = sock.GetFlags();
sock.SetFlags(wxSOCKET_WAITALL);
ProcessCommand(str, sock);
sock.SetFlags(flags);
}
// Enable input events again.
sock.SetNotify(wxSOCKET_LOST_FLAG | wxSOCKET_INPUT_FLAG);
break;
}
case wxSOCKET_LOST:
{
WriteLog("--- Deleting socket.");
sock.Destroy();
break;
}
default: ;
}
}
const wxChar* TBaseServerApp::GetConfigName() const
{
return "./servers.ini";
}
void TBaseServerApp::SetConfigString(const wxChar* key, const wxChar* val, const wxChar* app) const
{
wxIniConfig ini("", "", GetConfigName());
wxString str;
if (app == NULL || *app == '\0')
app = GetAppName();
str << '/' << app;
ini.SetPath(str);
ini.Write(key, val);
}
void TBaseServerApp::SetConfigInt(const wxChar* key, int val, const wxChar* app) const
{
wxString str = wxString::Format("%d", val);
SetConfigString(key, str, app);
}
wxString TBaseServerApp::GetConfigString(const wxChar* key, const wxChar* def, const wxChar* app) const
{
wxIniConfig ini("", "", GetConfigName());
wxString str;
if (app == NULL || *app == '\0')
app = GetAppName();
str << '/' << app;
ini.SetPath(str);
ini.Read(key, &str, def);
return str;
}
int TBaseServerApp::GetConfigInt(const wxChar* key, int def, const wxChar* app) const
{
wxString str = GetConfigString(key, "*", app);
int val = def;
if (str != "*")
val = atoi(str);
return val;
}
bool TBaseServerApp::GetConfigBool(const wxChar* key, bool def, const wxChar* app) const
{
wxString str = GetConfigString(key, "*", app);
bool val = def;
if (str != "*")
val = (str[0] == '1') || (str[0] == 'X') || (str[0] == 'Y');
return val;
}
int TBaseServerApp::GetDefaultPort() const
{
return GetConfigInt("Port", 3883);
}
wxString TBaseServerApp::GetLogFileName() const
{
return GetConfigString("LogFile");
}
wxString TBaseServerApp::GetDocumentRoot() const
{
return GetConfigString("DocumentRoot", ".");
}
bool TBaseServerApp::OnInit()
{
m_server = NULL;
m_log = NULL;
m_SingleInstance = new wxSingleInstanceChecker(GetAppName());
if (m_SingleInstance->IsAnotherRunning())
{
delete m_SingleInstance;
m_SingleInstance = NULL;
return false;
}
// Create the address - defaults to localhost:0 initially
wxIPV4address addr;
addr.Service(GetDefaultPort());
// Create the socket
m_server = new wxSocketServer(addr);
// Create the Log file
wxString str;
str = GetLogFileName();
if (!str.IsEmpty())
m_log = new wxFileOutputStream(str);
else
m_log = NULL;
m_nTmpCounter = 0;
// We use Ok() here to see if the server is really listening
str.Empty();
if (m_server->Ok())
{
// Setup the event handler and subscribe to connection events
m_server->SetEventHandler(*this, SERVER_ID);
m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);
m_server->Notify(TRUE);
str << GetAppName() << " listening on port " << addr.Service();
m_bRunning = true;
}
else
{
str << GetAppName() << " could not listen to port " << addr.Service();
}
WriteLog(str);
bool ok = m_server->Ok();
if (ok)
ok = Initialization();
#ifdef WIN32
if (ok) m_Tray.Init();
#endif
return ok;
}
int TBaseServerApp::OnExit()
{
if (m_server != NULL)
{
Deinitialization();
delete m_SingleInstance;
delete m_server;
}
if (m_log != NULL)
{
wxString str;
str << GetAppName() << " shutting down.";
WriteLog(str);
delete m_log;
}
return wxApp::OnExit();
}
wxString TBaseServerApp::UnformatString(const wxString& strFormString) const
{
wxString strResult;
for (int i = 0; strFormString[i]; i++)
{
switch(strFormString[i])
{
case '+':
strResult += ' '; break;
case '%':
{
const wxChar c = hex2int(strFormString.Mid(i+1, 2));
strResult += c;
i += 2;
}
break;
default:
strResult += strFormString[i]; break;
}
}
return strResult;
}
int TBaseServerApp::ParseArguments(wxString args, THashTable& hashArgs) const
{
int uguale = args.Find('=');
while (uguale > 0)
{
wxString name = args.Left(uguale);
name.Trim(false); name.Trim(true);
wxString value;
const int acapo = args.Find('&');
if (acapo > uguale)
value = args.Mid(uguale+1, acapo-uguale-1);
else
value = args.Mid(uguale+1);
value.Trim(false); value.Trim(true);
hashArgs.Put(name, UnformatString(value));
if (acapo > 0)
{
args = args.Mid(acapo+1);
uguale = args.Find('=');
}
else
uguale = -1;
}
return hashArgs.GetCount();
}