/////////////////////////////////////////////////////////////////////////////
// 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 <wx/wx.h>
#include "baseserv.h"

#include <wx/config.h>
#include <wx/filename.h>
#include <wx/image.h>
#include <wx/mimetype.h>
#include <wx/mstream.h>
#include <wx/sckstrm.h>
#include <wx/tokenzr.h>

#ifndef __WXMSW__
#include <wx/fileconf.h>
#endif

///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////

IMPLEMENT_ABSTRACT_CLASS(TBaseServerApp, wxApp)

TBaseServerApp& GetServerApp()
{	return *wxStaticCast(wxTheApp, TBaseServerApp); }

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, (wxUint32)wxStrlen(str));
	return outf;
}

wxSocketBase& operator<<(wxSocketBase& outf, wxString str)
{
	if (!str.IsEmpty())
    outf.Write(str, (wxUint32)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 wxHAS_TASK_BAR_ICON

///////////////////////////////////////////////////////////
// TTaskbarIcon
///////////////////////////////////////////////////////////

BEGIN_EVENT_TABLE(TTaskBarIcon, wxTaskBarIcon)
  EVT_TASKBAR_LEFT_DOWN(TTaskBarIcon::OnTaskBarClick)
END_EVENT_TABLE()

void TTaskBarIcon::OnTaskBarClick(wxTaskBarIconEvent& WXUNUSED(e))
{
  wxString strURL;
//  wxIPV4address addr; addr.AnyAddress(); 
//  addr.Service(GetServerApp().GetDefaultPort());
//  url << addr.Hostname() << ":" << addr.Service();
  strURL << "http://127.0.0.1:" << GetServerApp().GetDefaultPort();
  wxLaunchDefaultBrowser(strURL);
}

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/*,wxIMAGE_QUALITY_HIGH*/);
    const wxBitmap bmp(img);
    icon.CopyFromBitmap(bmp);
  }
  else
    icon.LoadFile("soap", wxBITMAP_TYPE_ICO_RESOURCE);

  SetIcon(icon, a.GetAppName());
}

#endif

///////////////////////////////////////////////////////////
// TBaseServerApp
///////////////////////////////////////////////////////////

enum 
{
  SERVER_ID = 1001,
  SOCKET_ID = 1002,
  DATAGRAM_ID = 1003,
};


BEGIN_EVENT_TABLE(TBaseServerApp, wxApp)
  EVT_SOCKET(SERVER_ID, TBaseServerApp::OnServerEvent)
  EVT_SOCKET(SOCKET_ID, TBaseServerApp::OnSocketEvent)
  EVT_SOCKET(DATAGRAM_ID, TBaseServerApp::OnDatagramEvent)
  EVT_IDLE(TBaseServerApp::OnIdle)
END_EVENT_TABLE()

void TBaseServerApp::WriteLog(const wxChar* str, int nPriority) const
{
  const bool doit = (m_log != NULL) && (m_bLogVerbose || nPriority > 0);
	if (doit)
  {
    //raccatta data ed ora
    const wxString strNow = wxNow();
	  *m_log << strNow << " " << str << endl;
  }
}

wxString TBaseServerApp::GetTempFilename(const wxChar * ext)
{
	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.%s", GetAppName(), m_nTmpCounter, ext);
  m_nTmpCounter++;
	if (m_nTmpCounter >= 32)
		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 (IsRunning())
  	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 (IsRunning())
    AddLogo(title_tr);
  
	TXmlItem& td1 = title_tr.AddChild("td");
	td1.AddChild("h1").AddChild("center") << header;
	
	if (IsRunning())
    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;
  */

  TXmlItem& b = body.AddChild("button");
	b.SetAttr("type", "button"); 
  wxString strScript;
  strScript += "window.open('";
  strScript += strHref;
  strScript += "', '_self')";
  b.SetAttr("onClick", strScript); 
  b << strCaption;
	return b;
}

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;

  if (m_bRunning) // Aggiungi il bottone di ritorno solo se il server non si sta spegnendo
  {
	  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
		if (outs.IsOk())
			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 %lu bytes", nSize), 0);

	const size_t BUF_TEMP_SIZE = 1024*1024;  // 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, wxUint32(bytes)).LastCount();
		nTotalWritten += nWritten;
  }
	delete buf;

	if (nTotalWritten < nSize)
		WriteLog(wxString::Format("I sent %lu of %lu bytes only.", nTotalWritten, nSize), 1);
}

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::SoapProcessMethod(const TXmlItem& WXUNUSED(xmlMethod), TXmlItem& WXUNUSED(xmlAnswer))
{
}

void TBaseServerApp::ProcessSoapCommand(wxString cmd, wxSocketBase& sock)
{
	TXmlItem xmlEnvelope;
	xmlEnvelope.SetTag("SOAP-ENV:Envelope");
	TXmlItem& xmlBody = xmlEnvelope.AddChild("SOAP-ENV:Body");

	const int soapstart = cmd.Find("<SOAP-ENV:");
	if (soapstart > 0)
	{
		const size_t soaplen = cmd.length() - soapstart;
		const char* buff = (const char*)cmd;
		buff += soapstart;
		wxMemoryInputStream input(buff, soaplen);
		TXmlItem query; 
		if (query.Read(input))
		{
			const TXmlItem* pxmlBody = query.FindFirst("SOAP-ENV:Body");
			if (pxmlBody != NULL) for (int m = 0; ; m++)
			{
				const TXmlItem* pxmlMethod = pxmlBody->GetChild(m);
				if (pxmlMethod == NULL)
					break;
				if (pxmlMethod->GetTag().StartsWith("m:"))
				{
					wxString str = pxmlMethod->GetTag(); str += "Result";
					TXmlItem& xmlAnswer = xmlBody.AddChild(str);
					SoapProcessMethod(*pxmlMethod, xmlAnswer);
				}
			}
		}
	}
	const wxString strResult = xmlEnvelope.AsString();
  
	sock << "HTTP/1.1 200 OK" << endl;
	sock << "Connection: keep-alive" << endl;
	sock << "Content-Length: " << strResult.Length() << endl;
	sock << "Content-Type: text/xml; charset=utf-8" << endl;
	sock << "Date: " << wxDateTime::Now().Format("%#c") << endl;
	sock << "Server: " << GetAppName() << endl;
  sock << "Host: " << wxGetFullHostName() << endl;
	sock << endl;
	sock << strResult;
}

void TBaseServerApp::ProcessHttpGet(wxString cmd, wxSocketBase& sock)
{
  const int stop = cmd.Find(" HTTP");
	wxString str = cmd.Mid(4, stop-4).Trim();

  if (str == "/")
	  str += "index.htm";
  wxString strFilename = GetDocumentRoot() + str;
  SendFile(strFilename, sock);
}

void TBaseServerApp::ProcessFormCommand(wxString WXUNUSED(cmd), 
                                        wxSocketBase& WXUNUSED(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";
}

bool TBaseServerApp::ProcessCommand(wxString cmd, wxSocketBase& outs)
{
  WriteLog(cmd, 0);

  bool bProcessed = true;
  if (cmd.StartsWith("POST "))
	{
    if (cmd.Find("SOAPAction") > 0)
			ProcessSoapCommand(cmd, outs);
		else
      ProcessFormCommand(cmd, outs);
	} else
  if (cmd.StartsWith("GET "))
		ProcessHttpGet(cmd, outs); else
  if (cmd.StartsWith("PING"))
    outs << "PONG\n";
  else
    bProcessed = false;
  return bProcessed;
}

void TBaseServerApp::OnServerEvent(wxSocketEvent& e)
{
  // 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).

  switch(e.GetSocketEvent())
  {
  case wxSOCKET_CONNECTION:
    {
      wxSocketBase* sock = m_server->Accept(false);
      if (sock != NULL)
      {
        sock->SetEventHandler(*this, SOCKET_ID);
        sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
        sock->Notify(true);
        WriteLog(_("Connection accepted."));
      }
      else
        WriteLog(_("Connection REFUSED!"), 1);
    }
    break;
  default:
    WriteLog(_("Unhandled server event!"), 1);
    break;
  }
}

struct TCommand : public wxObject
{
	wxSocketBase* m_Sock;
	wxString m_Command;
};

void TBaseServerApp::OnSocketEvent(wxSocketEvent& e)
{
  wxSocketBase* sock = e.GetSocket();
  switch(e.GetSocketEvent())
  {
  case wxSOCKET_INPUT:
    if (sock != NULL && sock->IsOk())
    {
      // We disable input events, so that the test doesn't trigger wxSocketEvent again.
      sock->SetNotify(wxSOCKET_LOST_FLAG);
			TCommand* message = new TCommand;
			message->m_Sock = sock;

		  // Read the data
			const size_t BUFSIZE = 1024;
			wxChar  buf[BUFSIZE + 16];
			memset(buf, 0, sizeof(buf));
			while (sock->Read(buf, BUFSIZE).LastCount() == BUFSIZE)
			{
				message->m_Command << buf;
				memset(buf, 0, sizeof(buf));
			}
			message->m_Command << buf;
			m_Sockets.Add(message);

      // Enable input events again.
      sock->SetNotify(wxSOCKET_LOST_FLAG | wxSOCKET_INPUT_FLAG);
    }
    break;
  case wxSOCKET_LOST:
    WriteLog(_("--- Socket lost."), 0);
    if (sock != NULL)
    {
      for (int i = m_Sockets.GetCount()-1; i >= 0; i--)
      {
        TCommand& cmd = *(TCommand*)m_Sockets[i];
        if (cmd.m_Sock == sock)
          m_Sockets.RemoveAt(i);
      }
      if (sock->IsOk())
        sock->Destroy();
    }
    break;
  default: 
    break;
  }
}

void TBaseServerApp::OnDatagramEvent(wxSocketEvent& WXUNUSED(e))
{
}

void TBaseServerApp::OnIdle(wxIdleEvent& evt)
{
	if (m_Sockets.GetCount() > 0)
	{
    TCommand& cmd = *(TCommand*)m_Sockets[0];
	  wxSocketBase& sock = *cmd.m_Sock;
		const bool valid_socket = sock.IsOk();
		if (valid_socket)
			sock.SetNotify(wxSOCKET_LOST_FLAG);

		wxString& str = cmd.m_Command;

		if (CanProcessCommand(str, sock))
		{
			if (valid_socket)
      {
  			const wxSocketFlags flags = sock.GetFlags();
				sock.SetFlags(wxSOCKET_WAITALL);
	  		ProcessCommand(str, sock);
				sock.SetFlags(flags);
      }
      else
        ProcessCommand(str, sock); // Comandi come "UserLogout" vanno processati anche per socket non validi
		}

		// Enable input events again.

		if (valid_socket)
		{
			sock.SetNotify(wxSOCKET_LOST_FLAG | wxSOCKET_INPUT_FLAG);
		  sock.Notify(true);
		}

    if (m_Sockets.GetCount() > 0)
		  m_Sockets.RemoveAt(0, 1);

    evt.RequestMore(); // FONDAMENTALE per gestire richieste multiple!
	}
	wxApp::OnIdle(evt);
}

const wxString& TBaseServerApp::GetConfigName() const
{
  if (m_strIni.IsEmpty())
	{
	  wxFileName name(argv[0]); // Prendo il persorso completo del server in esecuzione
	  name.SetName("servers");  // Trasformo il nome in servers ...
	  name.SetExt("ini");       // ... e l'esetensione in .ini
    name.MakeAbsolute();
	  (wxString&)m_strIni = name.GetFullPath();
	}
	return m_strIni;
}

wxString TBaseServerApp::GetConfigFileString(const wxChar* file, const wxChar* paragraph, const wxChar* key, const wxChar* def) const
{
  wxString str;

  if (file == NULL || *file <= ' ')
    file = GetConfigName();
  if (paragraph == NULL || *paragraph <= ' ')
    paragraph = GetAppName();

#ifdef __WXMSW__
  wxChar value[512]; memset(value, 0, sizeof(value));
  const int len = ::GetPrivateProfileString(paragraph, key, def, value, sizeof(value)-1, file);
  str = len <= 0 ? def : value;
#else
  wxFileConfig ini("", "", file, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);
	str << '/' << paragraph;
	ini.SetPath(str);
	ini.Read(key, &str, def);
#endif

  return str;
}

long TBaseServerApp::GetConfigFileInt(const wxChar* file, const wxChar* paragraph, const wxChar* key, long val) const
{
  const wxString str = GetConfigFileString(file, paragraph, key, "*");
  if (str != "*")
    str.ToLong(&val);
  return val;
}

void TBaseServerApp::SetConfigFileString(const wxChar* file, const wxChar* paragraph, const wxChar* key, const wxChar* val) const
{
  if (file == NULL || *file <= ' ')
		file = GetConfigName();
  if (paragraph == NULL || *paragraph <= ' ')
		paragraph = GetAppName();

#ifdef __WXMSW__
  ::WritePrivateProfileString(paragraph, key, val, file);
#else
  wxFileConfig ini("", "", file, "", wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_RELATIVE_PATH);
	wxString str;
	str << '/' << paragraph;
	ini.SetPath(str);
	ini.Write(key, val);
#endif
}

void TBaseServerApp::SetConfigString(const wxChar* key, const wxChar* val, const wxChar* app) const
{
  return SetConfigFileString(NULL, app, key, val);
}

void TBaseServerApp::SetConfigInt(const wxChar* key, int val, const wxChar* app) const
{
  wxString str; str.Printf("%d", val);
  SetConfigString(key, str, app);
}

wxString TBaseServerApp::GetConfigString(const wxChar* key, const wxChar* def, const wxChar* app) const
{
  return GetConfigFileString(NULL, app, key, def);
}

int TBaseServerApp::GetConfigInt(const wxChar* key, int def, const wxChar* app) const
{
	return GetConfigFileInt(NULL, app, key, def);
}

bool TBaseServerApp::GetConfigBool(const wxChar* key, bool def, const wxChar* app) const
{
	bool val = def;
	const wxString str = GetConfigString(key, "*", app);
	if (str != "*")
		val = (str[0u] == '1') || (str[0u] == 'T') || (str[0u] == 'X') || (str[0u] == 'Y') || (str.CmpNoCase("On") == 0);
	return val;
}

int TBaseServerApp::GetDefaultPort() const
{	return GetConfigInt("Port", 3883); }

int TBaseServerApp::GetUDPPort() const
{ return GetConfigInt("PortUDP", 0); }

#define OEM_INI wxT("../setup/oem.ini")

int TBaseServerApp::GetOemInt(const wxChar* key, int def) const
{
  static int _oem = GetConfigFileInt(OEM_INI, "MAIN", "OEM", -1);
  if (_oem >= 0)
  {
    if (wxStrcmp(key, wxT("OEM")) != 0)
    {
      wxString str; str.Printf(wxT("OEM_%d"), _oem);
      def = GetConfigFileInt(OEM_INI, str, key, def);
    }
    else
      def = _oem;
  }

  return def;
}

wxString TBaseServerApp::GetOemString(const wxChar* key, const wxChar* def) const
{
  const int oem = GetOemInt("OEM", -1);
  wxString str = def;
  if (oem >= 0)
  {
    if (wxStrcmp(key, "OEM") != 0)
    {
      str.Printf("OEM_%d", oem);
      str = GetConfigFileString(OEM_INI, str, key, def);
    }
    else
      str.Printf("%d", oem);
  }
  return str;
}

wxString TBaseServerApp::GetInstallString(const wxChar* key, const wxChar* module) const 
{ 
  if (module == NULL || *module <= ' ')
    module = wxT("sr");
  return GetConfigFileString(wxT("../install.ini"), module, key, wxEmptyString); 
}

bool TBaseServerApp::IsAdvanced() const 
{ 
  const int nOEM = GetOemInt(wxT("OEM"), -1);
  return nOEM==0;
}

wxString TBaseServerApp::GetLogFileName() const
{ return GetConfigString("LogFile"); }

wxString TBaseServerApp::GetDocumentRoot() const
{ return GetConfigString("DocumentRoot", m_strPath); }

bool TBaseServerApp::IsRunning() const 
{ return m_bRunning && IsMainLoopRunning() && Ok(); }

bool TBaseServerApp::OnInit()
{
  m_server = NULL;
	m_datagram = NULL;
	m_log = NULL;
  m_bRunning = false;

  m_SingleInstance = new wxSingleInstanceChecker(GetAppName());
  if (m_SingleInstance->IsAnotherRunning())
	{
    wxMessageBox(_("Another instance is already running"), GetAppName(), wxICON_HAND);
		delete m_SingleInstance;
		m_SingleInstance = NULL;
		return false;
	}

	wxFileName::SplitPath(argv[0], &m_strPath, NULL, NULL);
	if (!m_strPath.IsEmpty())
  {
		wxSetWorkingDirectory(m_strPath);
    if (!wxEndsWithPathSeparator(m_strPath))
      m_strPath += wxFILE_SEP_PATH;
  }

  // Create the address - defaults to localhost:0 initially
  wxIPV4address addr;
  addr.AnyAddress(); // I docs affermano che AnyAddress sia meglio di LocalHost
  const int nTCPPort = GetDefaultPort();
  addr.Service(nTCPPort);
  // Create the TCP socket
  m_server = new wxSocketServer(addr);

  // Create the Log file
	//Prima di creare il nuovo log file (vuoto) salva quello vecchio aggiungendo al nome data e ora di ripartenza..
  //..Serve per monitorare i defungimenti dei server
	const wxString strLog = GetLogFileName();
  if (!strLog.IsEmpty())
  {
    wxFileName fnLogFileName(strLog);
    if (fnLogFileName.FileExists())
    {
      const wxDateTime dtNow = wxDateTime::Now();
      wxString strWorkString;
      strWorkString.Printf("%04d%02d%02d_%02d%02d_", 
                        dtNow.GetYear(), dtNow.GetMonth() + 1, dtNow.GetDay(), 
                        dtNow.GetHour(), dtNow.GetMinute());
      strWorkString << fnLogFileName.GetName();
      fnLogFileName.SetName(strWorkString);
      wxCopyFile(strLog, fnLogFileName.GetFullPath());
    }

  	m_log = new wxFileOutputStream(strLog);
    m_bLogVerbose = GetConfigBool("LogVerbose");
  }
	else
  {
		m_log = NULL;
    m_bLogVerbose = false;
  }

	m_nTmpCounter = 0;

	// We use Ok() here to see if the server is really listening
  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);

    wxString str;
    str << GetAppName() << _(" listening at ") << addr.Hostname() << wxT(":") << addr.Service();
  	WriteLog(str);

		m_bRunning = Initialization();
	}
  else
  {
    wxString str;
    str << GetAppName() << _(" could not listen to port ") << addr.Service();
  	WriteLog(str);
  }

#ifdef wxHAS_TASK_BAR_ICON
  m_Tray = NULL;
  if (m_bRunning) 
  {
    m_Tray = new TTaskBarIcon;
    m_Tray->Init();
  }
#endif

/*
  // Create the UDP socket
  const int nUDPPort = GetUDPPort();
  if (m_bRunning && nUDPPort > nTCPPort)
  {
    wxIPV4address udp_addr;
    udp_addr.AnyAddress(); // I docs affermano che AnyAddress sia meglio di LocalHost
    udp_addr.Service(nUDPPort);
    m_datagram = new wxDatagramSocket(udp_addr);

		// Setup the event handler and subscribe to connection events
		m_datagram->SetEventHandler(*this, DATAGRAM_ID);
		m_datagram->Notify(true);
  }
*/
	
  return m_bRunning;
}

int TBaseServerApp::OnExit()
{
	if (m_server != NULL)
	{
    m_bRunning = false;
    Deinitialization();
		delete m_SingleInstance;
    m_SingleInstance = NULL;
  	delete m_server;
    m_server = NULL;
	}
	if (m_log != NULL)
	{
		wxString str;
		str << GetAppName() << " shutting down.";
		WriteLog(str);
	  delete m_log;
    m_log = NULL;
	}

#ifdef wxHAS_TASK_BAR_ICON
  if (m_Tray != NULL) 
  {
    m_Tray->RemoveIcon();
    delete m_Tray;
    m_Tray = NULL;
  }
#endif

	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;
}

size_t 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();
}	

bool TBaseServerApp::GetVersionInfo(int& year, int& release, int& tag, int& patch) const
{                             
  const char* const VERSIONANDPATCH = "Don't cry for me Argentina.2091.10.00.0000.2101";
  wxStringTokenizer tok(VERSIONANDPATCH, wxT("."));

  tok.GetNextToken();             // Ignore string
  year         = wxAtoi(tok.GetNextToken());
  release      = wxAtoi(tok.GetNextToken());
  tag          = wxAtoi(tok.GetNextToken());
  patch        = wxAtoi(tok.GetNextToken());
  int checksum = wxAtoi(tok.GetNextToken());

  bool valid = year >= 2091 && release > 0 && tag >= 0 && patch >= 0 && 
               checksum == (year + release + tag + patch);
  return valid;             
}