From befd91205eff729a182f66de15374a577a8718f7 Mon Sep 17 00:00:00 2001 From: SomeDude Date: Sun, 2 Mar 2008 14:47:00 +0100 Subject: [PATCH] version 0.1.15 --- include/db/sqlite3db/sqlite3recordset.h | 1 + include/freenet/identityrequester.h | 28 +------- include/freenet/iindexrequester.h | 29 +++++++-- include/freenet/trustlistrequester.h | 28 +------- include/global.h | 2 +- src/db/sqlite3recordset.cpp | 12 ++++ src/freenet/boardlistxml.cpp | 6 +- src/freenet/identityrequester.cpp | 111 ++------------------------------ src/freenet/messagerequester.cpp | 4 ++ src/freenet/messagexml.cpp | 4 +- src/freenet/periodicdbmaintenance.cpp | 42 +++++++++++- src/freenet/trustlistrequester.cpp | 110 ++----------------------------- src/http/pages/announceidentitypage.cpp | 2 +- src/http/pages/boardspage.cpp | 5 +- src/http/pages/execquerypage.cpp | 14 ++++ src/http/pages/localidentitiespage.cpp | 3 +- src/http/pages/peermaintenancepage.cpp | 18 +++++- 17 files changed, 141 insertions(+), 278 deletions(-) diff --git a/include/db/sqlite3db/sqlite3recordset.h b/include/db/sqlite3db/sqlite3recordset.h index 33d56e2..06c7919 100644 --- a/include/db/sqlite3db/sqlite3recordset.h +++ b/include/db/sqlite3db/sqlite3recordset.h @@ -29,6 +29,7 @@ public: virtual const char *GetField(const int field); virtual const int GetInt(const int field); virtual const double GetDouble(const int field); + virtual const char *GetColumnName(const int column); virtual void Open(const std::string &sql, DB *db); diff --git a/include/freenet/identityrequester.h b/include/freenet/identityrequester.h index 2ded93a..bed219d 100644 --- a/include/freenet/identityrequester.h +++ b/include/freenet/identityrequester.h @@ -1,42 +1,20 @@ #ifndef _identity_requester_ #define _identity_requester_ -#include "../idatabase.h" -#include "../ilogger.h" -#include "../datetime.h" -#include "ifreenetregistrable.h" -#include "ifcpconnected.h" -#include "ifcpmessagehandler.h" -#include "iperiodicprocessor.h" +#include "iindexrequester.h" -class IdentityRequester:public IFreenetRegistrable,public IFCPConnected,public IFCPMessageHandler,public IPeriodicProcessor,public IDatabase,public ILogger +class IdentityRequester:public IIndexRequester { public: IdentityRequester(); IdentityRequester(FCPv2 *fcp); - void FCPConnected(); - void FCPDisconnected(); - - const bool HandleMessage(FCPMessage &message); - - void Process(); - - void RegisterWithThread(FreenetMasterThread *thread); - private: void Initialize(); void PopulateIDList(); // clear and re-populate m_ids with identities we want to query - void StartRequest(const long identityid); + void StartRequest(const long &identityid); const bool HandleAllData(FCPMessage &message); const bool HandleGetFailed(FCPMessage &message); - void RemoveFromRequestList(const long identityid); - - DateTime m_tempdate; - long m_maxrequests; - std::string m_messagebase; - std::vector m_requesting; // list of ids we are currently requesting from - std::map m_ids; // map of all ids we know and whether we have requested file from them yet }; diff --git a/include/freenet/iindexrequester.h b/include/freenet/iindexrequester.h index 35c16c1..e3845e3 100644 --- a/include/freenet/iindexrequester.h +++ b/include/freenet/iindexrequester.h @@ -41,6 +41,8 @@ protected: virtual void RemoveFromRequestList(const IDTYPE id); DateTime m_tempdate; + DateTime m_lastreceived; + DateTime m_lastpopulated; std::string m_messagebase; std::map m_ids; // map of all ids we know and whether we have requested file from them yet std::vector m_requesting; // list of ids we are currently requesting from @@ -82,6 +84,8 @@ void IIndexRequester::FCPConnected() m_requesting.clear(); PopulateIDList(); + m_lastreceived.SetToGMTime(); + m_lastpopulated.SetToGMTime(); } template @@ -96,6 +100,9 @@ const bool IIndexRequester::HandleMessage(FCPMessage &message) if(message["Identifier"].find(m_fcpuniquename)==0) { + + m_lastreceived.SetToGMTime(); + if(message.GetName()=="DataFound") { return true; @@ -134,11 +141,17 @@ void IIndexRequester::InitializeIIndexRequester() Option::Instance()->Get("MessageBase",m_messagebase); m_tempdate.SetToGMTime(); + m_lastreceived.SetToGMTime(); + m_lastpopulated.SetToGMTime(); + m_lastpopulated.Add(0,-10); } template void IIndexRequester::Process() { + DateTime now; + now.SetToGMTime(); + // max is the smaller of the config value or the total number of ids we will request from typename std::map::size_type max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests; @@ -158,19 +171,27 @@ void IIndexRequester::Process() } else { - // we requested from all ids in the list, repopulate the list - PopulateIDList(); + // we requested from all ids in the list, repopulate the list (only every 10 minutes) + if(m_lastpopulated<(now-1.0/144.0)) + { + PopulateIDList(); + m_lastpopulated.SetToGMTime(); + } } } // special case - if there were 0 ids on the list when we started then we will never get a chance to repopulate the list // this will recheck for ids every minute - DateTime now; - now.SetToGMTime(); if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0))) { PopulateIDList(); m_tempdate=now; } + // if we haven't received any messages to this object in 10 minutes, clear the requests and repopulate id list + if(m_ids.size()>0 && m_lastreceived<(now-(1.0/144.0))) + { + m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"IIndexRequester::Process() Object has not received any messages in 10 minutes. Restarting requests."); + FCPConnected(); + } } diff --git a/include/freenet/trustlistrequester.h b/include/freenet/trustlistrequester.h index 721b9cb..516f3f5 100644 --- a/include/freenet/trustlistrequester.h +++ b/include/freenet/trustlistrequester.h @@ -1,43 +1,21 @@ #ifndef _trustlistrequester_ #define _trustlistrequester_ -#include "../idatabase.h" -#include "../ilogger.h" -#include "../datetime.h" -#include "ifreenetregistrable.h" -#include "ifcpconnected.h" -#include "ifcpmessagehandler.h" -#include "iperiodicprocessor.h" +#include "iindexrequester.h" -class TrustListRequester:public IFreenetRegistrable,public IFCPConnected,public IFCPMessageHandler,public IPeriodicProcessor,public IDatabase,public ILogger +class TrustListRequester:public IIndexRequester//public IFreenetRegistrable,public IFCPConnected,public IFCPMessageHandler,public IPeriodicProcessor,public IDatabase,public ILogger { public: TrustListRequester(); TrustListRequester(FCPv2 *fcp); - void FCPDisconnected(); - void FCPConnected(); - - const bool HandleMessage(FCPMessage &message); - - void Process(); - - void RegisterWithThread(FreenetMasterThread *thread); - private: void Initialize(); void PopulateIDList(); // clear and re-populate m_ids with identities we want to query - void StartRequest(const long identityid); + void StartRequest(const long &identityid); const bool HandleAllData(FCPMessage &message); const bool HandleGetFailed(FCPMessage &message); - void RemoveFromRequestList(const long identityid); - DateTime m_tempdate; - std::string m_messagebase; - long m_maxrequests; - std::vector m_requesting; // list of ids we are currently requesting from - std::map m_ids; // map of all ids we know and whether we have requested file from them yet - }; #endif // _trustlistrequester_ diff --git a/include/global.h b/include/global.h index 55a1e63..3e26f37 100644 --- a/include/global.h +++ b/include/global.h @@ -5,7 +5,7 @@ //#include #include "pthreadwrapper/thread.h" -#define FMS_VERSION "0.1.14" +#define FMS_VERSION "0.1.15" // opens database and creates tables and initial inserts if necessary void SetupDB(); diff --git a/src/db/sqlite3recordset.cpp b/src/db/sqlite3recordset.cpp index 9ddfb23..137f7b3 100644 --- a/src/db/sqlite3recordset.cpp +++ b/src/db/sqlite3recordset.cpp @@ -40,6 +40,18 @@ const char *Recordset::Get(const int row, const int field) } } +const char *Recordset::GetColumnName(const int column) +{ + if(column>=0 && column::iterator i=m_boards.begin(); i!=m_boards.end(); i++) { + std::string boardname=(*i).m_name; + StringFunctions::Convert(boardname,boardname); TiXmlElement *tr=new TiXmlElement("Board"); tid->LinkEndChild(tr); - tr->LinkEndChild(XMLCreateCDATAElement("Name",(*i).m_name)); + tr->LinkEndChild(XMLCreateCDATAElement("Name",boardname)); tr->LinkEndChild(XMLCreateCDATAElement("Description",(*i).m_description)); } diff --git a/src/freenet/identityrequester.cpp b/src/freenet/identityrequester.cpp index 2009962..93d65a8 100644 --- a/src/freenet/identityrequester.cpp +++ b/src/freenet/identityrequester.cpp @@ -13,22 +13,11 @@ IdentityRequester::IdentityRequester() Initialize(); } -IdentityRequester::IdentityRequester(FCPv2 *fcp):IFCPConnected(fcp) +IdentityRequester::IdentityRequester(FCPv2 *fcp):IIndexRequester(fcp) { Initialize(); } -void IdentityRequester::FCPConnected() -{ - m_requesting.clear(); - PopulateIDList(); -} - -void IdentityRequester::FCPDisconnected() -{ - -} - const bool IdentityRequester::HandleAllData(FCPMessage &message) { DateTime now; @@ -159,44 +148,10 @@ const bool IdentityRequester::HandleGetFailed(FCPMessage &message) } -const bool IdentityRequester::HandleMessage(FCPMessage &message) -{ - - if(message["Identifier"].find("IdentityRequester")==0) - { - if(message.GetName()=="DataFound") - { - return true; - } - - if(message.GetName()=="AllData") - { - return HandleAllData(message); - } - - if(message.GetName()=="GetFailed") - { - return HandleGetFailed(message); - } - - if(message.GetName()=="IdentifierCollision") - { - // remove one of the ids from the requesting list - long identityid=0; - std::vector idparts; - StringFunctions::Split(message["Identifier"],"|",idparts); - StringFunctions::Convert(idparts[1],identityid); - RemoveFromRequestList(identityid); - return true; - } - } - - return false; -} - void IdentityRequester::Initialize() { std::string tempval=""; + m_fcpuniquename="IdentityRequester"; Option::Instance()->Get("MaxIdentityRequests",tempval); StringFunctions::Convert(tempval,m_maxrequests); if(m_maxrequests<1) @@ -208,8 +163,6 @@ void IdentityRequester::Initialize() { m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxIdentityRequests is currently set at "+tempval+". This value might be incorrectly configured."); } - Option::Instance()->Get("MessageBase",m_messagebase); - m_tempdate.SetToGMTime(); } void IdentityRequester::PopulateIDList() @@ -234,63 +187,7 @@ void IdentityRequester::PopulateIDList() } } -void IdentityRequester::Process() -{ - // max is the smaller of the config value or the total number of identities we will request from - long max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests; - - // try to keep up to max requests going - if(m_requesting.size()::iterator i=m_ids.begin(); - while(i!=m_ids.end() && (*i).second==true) - { - i++; - } - - if(i!=m_ids.end()) - { - StartRequest((*i).first); - } - else - { - // we requested from all ids in the list, repopulate the list - PopulateIDList(); - } - } - // special case - if there were 0 identities on the list when we started then we will never get a chance to repopulate the list - // this will recheck for ids every minute - DateTime now; - now.SetToGMTime(); - if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0))) - { - PopulateIDList(); - m_tempdate=now; - } - -} - -void IdentityRequester::RemoveFromRequestList(const long identityid) -{ - std::vector::iterator i=m_requesting.begin(); - while(i!=m_requesting.end() && (*i)!=identityid) - { - i++; - } - if(i!=m_requesting.end()) - { - m_requesting.erase(i); - } -} - -void IdentityRequester::RegisterWithThread(FreenetMasterThread *thread) -{ - thread->RegisterFCPConnected(this); - thread->RegisterFCPMessageHandler(this); - thread->RegisterPeriodicProcessor(this); -} - -void IdentityRequester::StartRequest(const long identityid) +void IdentityRequester::StartRequest(const long &identityid) { DateTime now; FCPMessage message; @@ -330,7 +227,7 @@ void IdentityRequester::StartRequest(const long identityid) message.SetName("ClientGet"); message["URI"]=publickey+m_messagebase+"|"+now.Format("%Y-%m-%d")+"|Identity|"+indexstr+".xml"; - message["Identifier"]="IdentityRequester|"+identityidstr+"|"+indexstr+"|"+message["URI"]; + message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|"+message["URI"]; message["ReturnType"]="direct"; message["MaxSize"]="10000"; // 10 KB diff --git a/src/freenet/messagerequester.cpp b/src/freenet/messagerequester.cpp index adf8168..6680b8b 100644 --- a/src/freenet/messagerequester.cpp +++ b/src/freenet/messagerequester.cpp @@ -127,11 +127,15 @@ const bool MessageRequester::HandleAllData(FCPMessage &message) if(boards.size()<=0) { m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain any boards! "+message["Identifier"]); + // remove this identityid from request list + RemoveFromRequestList(idparts[1]); return true; } if(xml.GetReplyBoard()=="") { m_log->WriteLog(LogFile::LOGLEVEL_ERROR,"MessageRequester::HandleAllData Message XML did not contain a reply board! "+message["Identifier"]); + // remove this identityid from request list + RemoveFromRequestList(idparts[1]); return true; } diff --git a/src/freenet/messagexml.cpp b/src/freenet/messagexml.cpp index 4b3252a..994ebd4 100644 --- a/src/freenet/messagexml.cpp +++ b/src/freenet/messagexml.cpp @@ -33,7 +33,9 @@ std::string MessageXML::GetXML() tid->LinkEndChild(brds); for(std::vector::iterator i=m_boards.begin(); i!=m_boards.end(); i++) { - brds->LinkEndChild(XMLCreateCDATAElement("Board",(*i))); + std::string boardname=(*i); + StringFunctions::Convert(boardname,boardname); + brds->LinkEndChild(XMLCreateCDATAElement("Board",boardname)); } if(m_inreplyto.size()>0) diff --git a/src/freenet/periodicdbmaintenance.cpp b/src/freenet/periodicdbmaintenance.cpp index 63863f8..dad043e 100644 --- a/src/freenet/periodicdbmaintenance.cpp +++ b/src/freenet/periodicdbmaintenance.cpp @@ -36,8 +36,46 @@ void PeriodicDBMaintenance::Do30MinuteMaintenance() void PeriodicDBMaintenance::Do1HourMaintenance() { // recalculate all trust levels - this is CPU instensive - // TODO - will probably have to change this to do 1 identity at a time as this locks that database for the duration - m_db->Execute("UPDATE tblIdentity SET PeerMessageTrust=(SELECT PeerMessageTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID), PeerTrustListTrust=(SELECT PeerTrustListTrust FROM vwCalculatedPeerTrust WHERE TargetIdentityID=IdentityID);"); + // do 1 identity at a time as doing it with 1 UPDATE statement locks that database for the duration + SQLite3DB::Statement st=m_db->Prepare("SELECT TargetIdentityID,PeerMessageTrust,PeerTrustListTrust FROM vwCalculatedPeerTrust;"); + SQLite3DB::Statement upd=m_db->Prepare("UPDATE tblIdentity SET PeerMessageTrust=?, PeerTrustListTrust=? WHERE IdentityID=?"); + st.Step(); + while(st.RowReturned()) + { + int identityid=0; + int trust=0; + + st.ResultInt(0,identityid); + + upd.Bind(0,identityid); + if(st.ResultNull(1)==false) + { + trust=0; + st.ResultInt(1,trust); + upd.Bind(0,trust); + } + else + { + upd.Bind(0); + } + if(st.ResultNull(2)==false) + { + trust=0; + st.ResultInt(2,trust); + upd.Bind(1,trust); + } + else + { + upd.Bind(1); + } + upd.Bind(2,identityid); + upd.Step(); + upd.Reset(); + + st.Step(); + } + + m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"PeriodicDBMaintenance::Do1HourMaintenance"); } diff --git a/src/freenet/trustlistrequester.cpp b/src/freenet/trustlistrequester.cpp index 77b03b1..90606dd 100644 --- a/src/freenet/trustlistrequester.cpp +++ b/src/freenet/trustlistrequester.cpp @@ -12,22 +12,11 @@ TrustListRequester::TrustListRequester() Initialize(); } -TrustListRequester::TrustListRequester(FCPv2 *fcp):IFCPConnected(fcp) +TrustListRequester::TrustListRequester(FCPv2 *fcp):IIndexRequester(fcp) { Initialize(); } -void TrustListRequester::FCPConnected() -{ - m_requesting.clear(); - PopulateIDList(); -} - -void TrustListRequester::FCPDisconnected() -{ - -} - const bool TrustListRequester::HandleAllData(FCPMessage &message) { DateTime now; @@ -181,44 +170,10 @@ const bool TrustListRequester::HandleGetFailed(FCPMessage &message) } -const bool TrustListRequester::HandleMessage(FCPMessage &message) -{ - - if(message["Identifier"].find("TrustListRequester")==0) - { - if(message.GetName()=="DataFound") - { - return true; - } - - if(message.GetName()=="AllData") - { - return HandleAllData(message); - } - - if(message.GetName()=="GetFailed") - { - return HandleGetFailed(message); - } - - if(message.GetName()=="IdentifierCollision") - { - // remove one of the ids from the requesting list - long identityid=0; - std::vector idparts; - StringFunctions::Split(message["Identifier"],"|",idparts); - StringFunctions::Convert(idparts[1],identityid); - RemoveFromRequestList(identityid); - return true; - } - } - - return false; -} - void TrustListRequester::Initialize() { std::string tempval=""; + m_fcpuniquename="TrustListRequester"; Option::Instance()->Get("MaxIdentityRequests",tempval); StringFunctions::Convert(tempval,m_maxrequests); if(m_maxrequests<1) @@ -230,7 +185,6 @@ void TrustListRequester::Initialize() { m_log->WriteLog(LogFile::LOGLEVEL_WARNING,"Option MaxTrustListRequests is currently set at "+tempval+". This value might be incorrectly configured."); } - Option::Instance()->Get("MessageBase",m_messagebase); m_tempdate.SetToGMTime(); } @@ -260,63 +214,7 @@ void TrustListRequester::PopulateIDList() } } -void TrustListRequester::Process() -{ - // max is the smaller of the config value or the total number of identities we will request from - long max=m_maxrequests>m_ids.size() ? m_ids.size() : m_maxrequests; - - // try to keep up to max requests going - if(m_requesting.size()::iterator i=m_ids.begin(); - while(i!=m_ids.end() && (*i).second==true) - { - i++; - } - - if(i!=m_ids.end()) - { - StartRequest((*i).first); - } - else - { - // we requested from all ids in the list, repopulate the list - PopulateIDList(); - } - } - // special case - if there were 0 identities on the list when we started then we will never get a chance to repopulate the list - // this will recheck for ids every minute - DateTime now; - now.SetToGMTime(); - if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0))) - { - PopulateIDList(); - m_tempdate=now; - } - -} - -void TrustListRequester::RegisterWithThread(FreenetMasterThread *thread) -{ - thread->RegisterFCPConnected(this); - thread->RegisterFCPMessageHandler(this); - thread->RegisterPeriodicProcessor(this); -} - -void TrustListRequester::RemoveFromRequestList(const long identityid) -{ - std::vector::iterator i=m_requesting.begin(); - while(i!=m_requesting.end() && (*i)!=identityid) - { - i++; - } - if(i!=m_requesting.end()) - { - m_requesting.erase(i); - } -} - -void TrustListRequester::StartRequest(const long identityid) +void TrustListRequester::StartRequest(const long &identityid) { DateTime now; FCPMessage message; @@ -356,7 +254,7 @@ void TrustListRequester::StartRequest(const long identityid) message.SetName("ClientGet"); message["URI"]=publickey+m_messagebase+"|"+now.Format("%Y-%m-%d")+"|TrustList|"+indexstr+".xml"; - message["Identifier"]="TrustListRequester|"+identityidstr+"|"+indexstr+"|"+message["URI"]; + message["Identifier"]=m_fcpuniquename+"|"+identityidstr+"|"+indexstr+"|"+message["URI"]; message["ReturnType"]="direct"; message["MaxSize"]="1000000"; // 1 MB diff --git a/src/http/pages/announceidentitypage.cpp b/src/http/pages/announceidentitypage.cpp index 81fad96..32a13cc 100644 --- a/src/http/pages/announceidentitypage.cpp +++ b/src/http/pages/announceidentitypage.cpp @@ -89,7 +89,7 @@ const std::string AnnounceIdentityPage::GeneratePage(const std::string &method, content+="
Select Identity : "; content+=CreateLocalIdentityDropDown("localidentityid",""); content+=""; - content+="
Type the answers of a few puzzles. The puzzles are case sensitive."; + content+="
Type the answers of a few puzzles. The puzzles are case sensitive. Getting announced will take some time. DO NOT continuously solve captchas. Solve 30 at most, wait a day, and if your identity has not been announced, repeat until it is."; content+=""; diff --git a/src/http/pages/boardspage.cpp b/src/http/pages/boardspage.cpp index f143d32..af3baad 100644 --- a/src/http/pages/boardspage.cpp +++ b/src/http/pages/boardspage.cpp @@ -52,6 +52,7 @@ const std::string BoardsPage::GeneratePage(const std::string &method, const std: std::string boarddescription=""; boardname=(*queryvars.find("boardname")).second; + StringFunctions::LowerCase(boardname,boardname); boarddescription=(*queryvars.find("boarddescription")).second; SQLite3DB::Statement addst=m_db->Prepare("INSERT INTO tblBoard(BoardName,BoardDescription,DateAdded) VALUES(?,?,?);"); @@ -172,7 +173,7 @@ const std::string BoardsPage::GeneratePage(const std::string &method, const std: content+=""; content+=""; - content+="
"; + content+="
"; content+=""; content+="
"; @@ -198,7 +199,7 @@ const std::string BoardsPage::GeneratePage(const std::string &method, const std: content+=""+SanitizeOutput(boardname)+""; content+=""; content+=""; - content+=""; + content+=""; content+=""; content+=""; content+="Query((*queryvars.find("query")).second); content+=""; + if(rs.Count()>0) + { + content+=""; + for(int i=0; i
"; content+="
"; - content+="

* An identity is considered successfully announced when you have downloaded a trust list from someone that contains the identity.

"; + content+="

* An identity is considered successfully announced when you have downloaded a trust list from someone that contains the identity.

"; + content+="

Single Use Identities will automatically be deleted 7 days after creation.

"; return "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"+StringFunctions::Replace(m_template,"[CONTENT]",content); } diff --git a/src/http/pages/peermaintenancepage.cpp b/src/http/pages/peermaintenancepage.cpp index f6cf386..3ded4eb 100644 --- a/src/http/pages/peermaintenancepage.cpp +++ b/src/http/pages/peermaintenancepage.cpp @@ -45,7 +45,17 @@ const std::string PeerMaintenancePage::GeneratePage(const std::string &method, c StringFunctions::Convert((*queryvars.find("daysago")).second,tempint); date.SetToGMTime(); date.Add(0,0,0,-tempint); - st=m_db->Prepare("DELETE FROM tblIdentity WHERE IdentityID NOT IN (SELECT IdentityID FROM tblMessage GROUP BY IdentityID) AND LastSeenPrepare("DELETE FROM tblIdentity WHERE LastSeenPrepare("DELETE FROM tblIdentity WHERE LastSeen
"; content+=""; + content+=""; + content+="
"; + content+="last seen days ago, and have null local trust"; + content+="
"; + content+=""; + content+=""; return "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"+StringFunctions::Replace(m_template,"[CONTENT]",content); -- 2.7.4