/* * httpsvc.cxx * * Classes for service applications using HTTP as the user interface. * * Portable Windows Library * * Copyright (c) 1993-2002 Equivalence Pty. Ltd. * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Portable Windows Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Contributor(s): ______________________________________. * * $Log: httpsvc.cxx,v $ * Revision 1.96 2005/11/30 12:47:41 csoutheren * Removed tabs, reformatted some code, and changed tags for Doxygen * * Revision 1.95 2004/04/03 08:22:20 csoutheren * Remove pseudo-RTTI and replaced with real RTTI * * Revision 1.94 2004/04/03 06:54:24 rjongbloed * Many and various changes to support new Visual C++ 2003 * * Revision 1.93 2004/03/23 04:41:05 csoutheren * Fixed compile problem on Linux * * Revision 1.92 2004/03/23 03:40:57 csoutheren * Change service process default to be more useful in some environments * * Revision 1.91 2004/01/17 17:44:54 csoutheren * Changed to use PString::MakeEmpty * * Revision 1.90 2003/09/17 09:02:13 csoutheren * Removed memory leak detection code * * Revision 1.89 2003/02/19 07:23:45 robertj * Changes to allow for single threaded HTTP service processes. * * Revision 1.88 2002/11/06 22:47:25 robertj * Fixed header comment (copyright etc) * * Revision 1.87 2002/10/10 04:43:44 robertj * VxWorks port, thanks Martijn Roest * * Revision 1.86 2002/08/14 00:43:40 robertj * Added ability to have fixed maximum length PStringStream's so does not do * unwanted malloc()'s while outputing data. * * Revision 1.85 2002/08/13 05:39:17 robertj * Fixed GNU compatibility * * Revision 1.84 2002/08/13 01:57:15 robertj * Fixed "last dump object" position in Memory Dump macro. * * Revision 1.83 2002/08/13 01:30:27 robertj * Added UpTime macro for time service has been running. * Added IfQuery macro blcok to add chunks of HTML depending on the value * of query parameters in the URL. * Added memory statistics dump and memory object dump macros to help in * leak finding. * * Revision 1.82 2002/07/30 08:37:34 robertj * Removed peer host as bad DNS makes it useless due to huge timeout. * * Revision 1.81 2002/07/30 04:51:26 craigs * Added MonitorInfo macro * * Revision 1.80 2002/07/30 03:16:57 craigs * Added StartTime macro * * Revision 1.79 2002/07/17 09:18:00 robertj * made detection of gif file more intelligent for debug version. * * Revision 1.78 2002/07/17 08:03:45 robertj * Allowed for adjustable copyright holder. * Allowed for not having gif file for product name in default header. * * Revision 1.77 2001/10/10 08:06:49 robertj * Fixed problem with not shutting down threads when closing listener. * * Revision 1.76 2001/09/11 02:37:41 robertj * Fixed thread name for HTTP service connection handler. * * Revision 1.75 2001/08/28 06:44:45 craigs * Added ability to override PHTTPServer creation * * Revision 1.74 2001/06/30 06:59:06 yurik * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov * * Revision 1.73 2001/06/27 04:14:48 robertj * Added logging for listener thread open/close. * * Revision 1.72 2001/06/23 00:32:15 robertj * Added parameter to be able to set REUSEADDR on listener socket. * * Revision 1.71 2001/05/07 23:27:06 robertj * Added SO_LINGER setting to HTTP sockets to help with clearing up sockets * when the application exits, which prevents new run of app as "port in use". * * Revision 1.70 2001/03/26 04:55:26 robertj * Made sure OnConfigChanged() is called from OnStart() function. * * Revision 1.69 2001/03/21 06:29:31 robertj * Fixed bug in calling OnConfigChanged after service macros are loaded, * should be before so state can be changed before the macros translated. * * Revision 1.68 2001/03/19 02:41:53 robertj * Made sure HTTP listener thread is shut down in OnStop(). * * Revision 1.67 2001/03/16 03:33:21 robertj * Fixed HTML signature code due to changes in encryption code. * * Revision 1.66 2001/03/04 02:24:44 robertj * Removed default OnControl() from http service as cannot get port number. * * Revision 1.65 2001/02/21 04:33:46 robertj * Fixed GNU warning. * * Revision 1.64 2001/02/20 02:32:41 robertj * Added PServiceMacro version that can do substitutions on blocks of HTML. * * Revision 1.63 2001/02/15 01:12:15 robertj * Moved some often repeated HTTP service code into PHTTPServiceProcess. * * Revision 1.62 2001/02/14 06:52:26 robertj * Fixed GNU compatibility with last change to PServiceMacro. * * Revision 1.61 2001/02/14 02:30:59 robertj * Moved HTTP Service Macro facility to public API so can be used by apps. * Added ability to specify the service macro keyword, defaults to "macro". * Added machine macro to get the OS version and hardware. * * Revision 1.60 2001/01/15 06:17:56 robertj * Set HTTP resource members to private to assure are not modified by * dscendents in non-threadsafe manner. * * Revision 1.59 2001/01/08 22:53:34 craigs * Changed OnPOST to allow subtle usage of embedded commands * * Revision 1.58 2000/12/14 08:09:41 robertj * Fixed missing immediate expiry date on string and file service HTTP resourcer. * * Revision 1.57 2000/12/11 13:15:17 robertj * Added macro to include signed or unsigned chunks of HTML. * Added flag to globally ignore HTML signatures (useful for develeopment). * * Revision 1.56 2000/10/23 09:17:26 robertj * Fixed bug un Linux version where HTML macros didn't work correctly. * * Revision 1.55 2000/08/04 12:48:25 robertj * Added mechanism by which a service can get at new HTTP connections, eg to add SSL. * * Revision 1.54 2000/05/02 02:58:49 robertj * Fixed MSVC warning about unused parameters. * * Revision 1.53 2000/05/02 02:01:18 craigs * Changed stricmp and added implementation of PServiceMacro::Translate * * Revision 1.52 2000/05/02 01:50:37 robertj * Rewrite of PServiceMacro so does not use malloc (indirectly). * * Revision 1.51 2000/01/27 00:35:52 robertj * Fixed benign warning about uninitialised variables in MSVC optimised compile. * * Revision 1.50 1999/08/07 06:50:52 robertj * Removed silly (and incorrect) warning. * * Revision 1.49 1999/04/24 05:16:26 robertj * Fixed incorrect date in copyright notice. * * Revision 1.48 1998/11/30 05:37:46 robertj * New directory structure * * Revision 1.47 1998/11/24 23:05:14 robertj * Fixed extra *** in demo message * * Revision 1.46 1998/11/16 07:23:15 robertj * More PPC GNU compatibility. * * Revision 1.45 1998/11/16 06:50:40 robertj * Fixed PPC GNU compiler compatibility. * * Revision 1.44 1998/10/31 12:49:25 robertj * Added read/write mutex to the HTTP space variable to avoid thread crashes. * * Revision 1.43 1998/10/29 11:58:52 robertj * Added ability to configure the HTTP threads stack size. * * Revision 1.42 1998/10/29 11:31:57 robertj * Fixed default URL to have lower case and spaceless product name. * Increased HTTP stack size. * * Revision 1.41 1998/10/15 01:53:35 robertj * GNU compatibility. * * Revision 1.40 1998/10/13 14:06:24 robertj * Complete rewrite of memory leak detection code. * * Revision 1.39 1998/09/23 06:22:15 robertj * Added open source copyright license. * * Revision 1.38 1998/09/18 01:47:23 robertj * Fixed bug that made files with signature on first line fail on unix systems. * * Revision 1.37 1998/08/20 06:01:02 robertj * Improved internationalisation, registrationpage override. * * Revision 1.36 1998/04/21 02:43:40 robertj * Fixed conditional around wrong way for requiring signature on HTML files. * * Revision 1.35 1998/04/01 01:55:41 robertj * Fixed bug for automatically including GIF file in HTTP name space. * * Revision 1.34 1998/03/23 03:21:40 robertj * Fixed missing invalid case in register page. * * Revision 1.33 1998/03/20 03:18:15 robertj * Added special classes for specific sepahores, PMutex and PSyncPoint. * * Revision 1.32 1998/03/17 10:14:39 robertj * Rewrite of registration page to allow for HTML file override. * * Revision 1.31 1998/03/09 07:17:48 robertj * Added IP peer/local number macros. * Set GetPageGraphic reference to GIF file to be at lop level directory. * * Revision 1.30 1998/02/16 00:14:09 robertj * Added ProductName and BuildDate macros. * Major rewrite of application info passed in PHTTPServiceProcess constructor. * * Revision 1.29 1998/02/03 06:22:45 robertj * Allowed PHTTPServiceString to be overridden by html file after ';'. * * Revision 1.28 1998/01/26 02:49:19 robertj * GNU support. * * Revision 1.27 1998/01/26 02:12:14 robertj * GNU warnings. * * Revision 1.26 1998/01/26 00:45:44 robertj * Added option flags to ProcessMacros to automatically load from file etc. * Assured that all service HTTP resources are overidable with file, using ; URL field. * Added a number of extra #equival macros. * Added "Pty. Ltd." to company name. * * Revision 1.25 1997/11/10 12:40:05 robertj * Changed SustituteEquivalSequence so can override standard macros. * * Revision 1.24 1997/11/04 06:02:46 robertj * Allowed help gif file name to overridable in PServiceHTML, so can be in subdirectory. * * Revision 1.23 1997/10/30 10:21:26 robertj * Added ability to customise regisration text by application. * * Revision 1.22 1997/08/28 14:19:40 robertj * Fixed bug where HTTP directory was not processed for macros. * * Revision 1.20 1997/08/20 08:59:58 craigs * Changed macro handling to commonise #equival sequence * * Revision 1.19 1997/07/26 11:38:22 robertj * Support for overridable pages in HTTP service applications. * * Revision 1.18 1997/07/08 13:11:44 robertj * Added standard header and copyright macros to service HTML. * * Revision 1.17 1997/06/16 13:20:15 robertj * Fixed bug where PHTTPThread crashes on exit. * * Revision 1.16 1997/05/16 12:07:21 robertj * Added operating system and version to hidden fields on registration form. * * Revision 1.15 1997/03/02 03:40:59 robertj * Added error logging to standard HTTP Service HTTP Server. * * Revision 1.14 1997/02/05 11:54:54 robertj * Added support for order form page overridiing. * * Revision 1.13 1997/01/28 11:45:19 robertj * . * * Revision 1.13 1997/01/27 10:22:37 robertj * Numerous changes to support OEM versions of products. * * Revision 1.12 1997/01/03 06:33:23 robertj * Removed slash from operating system version string, so says Windows NT rather than Windows/NT * * Revision 1.11 1996/11/16 10:50:26 robertj * ?? * * Revision 1.10 1996/11/04 03:58:23 robertj * Changed to accept separate copyright and manufacturer strings. * * Revision 1.8 1996/10/08 13:08:29 robertj * Changed standard graphic to use PHTML class. * * Revision 1.7 1996/09/14 13:09:33 robertj * Major upgrade: * rearranged sockets to help support IPX. * added indirect channel class and moved all protocols to descend from it, * separating the protocol from the low level byte transport. * * Revision 1.6 1996/08/25 09:39:00 robertj * Prevented registration if no user etc entered. * * Revision 1.5 1996/08/19 13:39:55 robertj * Fixed race condition in system restart logic. * * Revision 1.4 1996/08/08 13:36:39 robertj * Fixed Registation page so no longer has static link, ie can be DLLed. * * Revision 1.3 1996/07/15 10:36:48 robertj * Added registration info to bottom of order form so can be faxed to us. * * Revision 1.2 1996/06/28 13:21:30 robertj * Fixed nesting problem in tables. * Fixed PConfig page always restarting. * * Revision 1.1 1996/06/13 13:33:34 robertj * Initial revision * */ #ifdef __GNUC__ #pragma implementation "httpsvc.h" #endif #include #include #include PSORTED_LIST(PServiceMacros_base, PServiceMacro); class PServiceMacros_list : public PServiceMacros_base { public: PServiceMacros_list(); }; #define new PNEW #define HOME_PAGE "http://www.equival.com" #define EMAIL "equival@equival.com.au" #define EQUIVALENCE "Equivalence Pty. Ltd." static const PTime ImmediateExpiryTime(0, 0, 0, 1, 1, 1980); /////////////////////////////////////////////////////////////////////////////// PHTTPServiceProcess::PHTTPServiceProcess(const Info & inf) : PServiceProcess(inf.manufacturerName, inf.productName, inf.majorVersion, inf.minorVersion, inf.buildStatus, inf.buildNumber), macroKeyword("macro"), productKey(inf.productKey), securedKeys(inf.securedKeyCount, inf.securedKeys), signatureKey(inf.signatureKey), compilationDate(inf.compilationDate), manufacturersHomePage(inf.manufHomePage != NULL ? inf.manufHomePage : HOME_PAGE), manufacturersEmail(inf.email != NULL ? inf.email : EMAIL), productNameHTML(inf.productHTML != NULL ? inf.productHTML : inf.productName), gifHTML(inf.gifHTML), copyrightHolder(inf.copyrightHolder != NULL ? inf.copyrightHolder : inf.manufacturerName), copyrightHomePage(inf.copyrightHomePage != NULL ? inf.copyrightHomePage : (const char *)manufacturersHomePage), copyrightEmail(inf.copyrightEmail != NULL ? inf.copyrightEmail : (const char *)manufacturersEmail) { ignoreSignatures = FALSE; if (inf.gifFilename != NULL) { PDirectory exeDir = GetFile().GetDirectory(); #if defined(_WIN32) && defined(_DEBUG) // Special check to aid in using DevStudio for debugging. if (exeDir.Find("\\Debug\\") != P_MAX_INDEX) exeDir = exeDir.GetParent(); #endif httpNameSpace.AddResource(new PServiceHTTPFile(inf.gifFilename, exeDir+inf.gifFilename)); if (gifHTML.IsEmpty()) { gifHTML = psprintf("\"%s!\"",GetPort() == port && httpListeningSocket->IsOpen()) return TRUE; return ListenForHTTP(new PTCPSocket(port), reuse, stackSize); } BOOL PHTTPServiceProcess::ListenForHTTP(PSocket * listener, PSocket::Reusability reuse, PINDEX stackSize) { if (httpListeningSocket != NULL) ShutdownListener(); httpListeningSocket = PAssertNULL(listener); if (!httpListeningSocket->Listen(5, 0, reuse)) { PSYSTEMLOG(Debug, "HTTPSVC\tListen on port " << httpListeningSocket->GetPort() << " failed: " << httpListeningSocket->GetErrorText()); return FALSE; } if (stackSize > 1000) new PHTTPServiceThread(stackSize, *this); return TRUE; } void PHTTPServiceProcess::ShutdownListener() { if (httpListeningSocket == NULL) return; if (!httpListeningSocket->IsOpen()) return; PSYSTEMLOG(Debug, "HTTPSVC\tClosing listener socket on port " << httpListeningSocket->GetPort()); httpListeningSocket->Close(); httpThreadsMutex.Wait(); for (PINDEX i = 0; i < httpThreads.GetSize(); i++) httpThreads[i].Close(); while (httpThreads.GetSize() > 0) { httpThreadsMutex.Signal(); Sleep(1); httpThreadsMutex.Wait(); } httpThreadsMutex.Signal(); delete httpListeningSocket; httpListeningSocket = NULL; } PString PHTTPServiceProcess::GetCopyrightText() { PHTML html(PHTML::InBody); html << "Copyright ©" << compilationDate.AsString("yyyy") << " by " << PHTML::HotLink(copyrightHomePage) << copyrightHolder << PHTML::HotLink() << ", " << PHTML::HotLink("mailto:" + copyrightEmail) << copyrightEmail << PHTML::HotLink(); return html; } PString PHTTPServiceProcess::GetPageGraphic() { PFile header; if (header.Open("header.html", PFile::ReadOnly)) return header.ReadString(header.GetLength()); PHTML html(PHTML::InBody); html << PHTML::TableStart() << PHTML::TableRow() << PHTML::TableData(); if (gifHTML.IsEmpty()) html << PHTML::Heading(1) << productNameHTML << " " << PHTML::Heading(1); else html << gifHTML; html << PHTML::TableData() << GetOSClass() << ' ' << GetOSName() << " Version " << GetVersion(TRUE) << PHTML::BreakLine() << ' ' << GetCompilationDate().AsString("d MMMM yyyy") << PHTML::BreakLine() << "By " << PHTML::HotLink(manufacturersHomePage) << GetManufacturer() << PHTML::HotLink() << ", " << PHTML::HotLink("mailto:" + manufacturersEmail) << manufacturersEmail << PHTML::HotLink() << PHTML::TableEnd() << PHTML::HRule(); return html; } void PHTTPServiceProcess::GetPageHeader(PHTML & html) { GetPageHeader(html, GetName()); } void PHTTPServiceProcess::GetPageHeader(PHTML & html, const PString & title) { html << PHTML::Title(title) << PHTML::Body() << GetPageGraphic(); } PTCPSocket * PHTTPServiceProcess::AcceptHTTP() { if (httpListeningSocket == NULL) return NULL; if (!httpListeningSocket->IsOpen()) return NULL; // get a socket when a client connects PTCPSocket * socket = new PTCPSocket; if (socket->Accept(*httpListeningSocket)) return socket; if (socket->GetErrorCode() != PChannel::Interrupted) PSYSTEMLOG(Error, "Accept failed for HTTP: " << socket->GetErrorText()); if (httpListeningSocket != NULL && httpListeningSocket->IsOpen()) return socket; delete socket; return NULL; } BOOL PHTTPServiceProcess::ProcessHTTP(PTCPSocket & socket) { if (!socket.IsOpen()) return TRUE; PHTTPServer * server = CreateHTTPServer(socket); if (server == NULL) { PSYSTEMLOG(Error, "HTTP server creation/open failed."); return TRUE; } // process requests while (server->ProcessCommand()) ; // always close after the response has been sent delete server; // if a restart was requested, then do it, but only if we are not shutting down if (httpListeningSocket->IsOpen()) CompleteRestartSystem(); return TRUE; } void PHTTPServiceProcess::BeginRestartSystem() { if (restartThread == NULL) { restartThread = PThread::Current(); OnConfigChanged(); } } void PHTTPServiceProcess::CompleteRestartSystem() { if (restartThread == NULL) return; if (restartThread != PThread::Current()) return; httpNameSpace.StartWrite(); if (Initialise("Restart\tInitialisation")) restartThread = NULL; httpNameSpace.EndWrite(); if (restartThread != NULL) Terminate(); } void PHTTPServiceProcess::AddRegisteredText(PHTML &) { } void PHTTPServiceProcess::AddUnregisteredText(PHTML &) { } BOOL PHTTPServiceProcess::SubstituteEquivalSequence(PHTTPRequest &, const PString &, PString &) { return FALSE; } PHTTPServer * PHTTPServiceProcess::CreateHTTPServer(PTCPSocket & socket) { #ifdef SO_LINGER const linger ling = { 1, 5 }; socket.SetOption(SO_LINGER, &ling, sizeof(ling)); #endif PHTTPServer * server = OnCreateHTTPServer(httpNameSpace); if (server->Open(socket)) return server; delete server; return NULL; } PHTTPServer * PHTTPServiceProcess::OnCreateHTTPServer(const PHTTPSpace & httpNameSpace) { return new PHTTPServer(httpNameSpace); } ////////////////////////////////////////////////////////////// PHTTPServiceThread::PHTTPServiceThread(PINDEX stackSize, PHTTPServiceProcess & app) : PThread(stackSize, AutoDeleteThread, NormalPriority, "HTTP Service:%x"), process(app) { process.httpThreadsMutex.Wait(); process.httpThreads.Append(this); process.httpThreadsMutex.Signal(); myStackSize = stackSize; socket = NULL; Resume(); } PHTTPServiceThread::~PHTTPServiceThread() { process.httpThreadsMutex.Wait(); process.httpThreads.Remove(this); process.httpThreadsMutex.Signal(); delete socket; } void PHTTPServiceThread::Close() { if (socket != NULL) socket->Close(); } void PHTTPServiceThread::Main() { PTCPSocket * socket = process.AcceptHTTP(); if (socket != NULL) { new PHTTPServiceThread(myStackSize, process); process.ProcessHTTP(*socket); } } ////////////////////////////////////////////////////////////// PConfigPage::PConfigPage(PHTTPServiceProcess & app, const PString & title, const PString & section, const PHTTPAuthority & auth) : PHTTPConfig(title, section, auth), process(app) { } PConfigPage::PConfigPage(PHTTPServiceProcess & app, const PString & section, const PHTTPAuthority & auth) : PHTTPConfig(section.ToLower() + ".html", section, auth), process(app) { } void PConfigPage::OnLoadedText(PHTTPRequest & request, PString & text) { PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly).Mid(1), PServiceHTML::LoadFromFile); PHTTPConfig::OnLoadedText(request, text); PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions); } BOOL PConfigPage::OnPOST(PHTTPServer & server, const PURL & url, const PMIMEInfo & info, const PStringToString & data, const PHTTPConnectionInfo & connectInfo) { PHTTPConfig::OnPOST(server, url, info, data, connectInfo); return FALSE; // Make sure we break any persistent connections } BOOL PConfigPage::Post(PHTTPRequest & request, const PStringToString & data, PHTML & reply) { PSYSTEMLOG(Debug3, "Post to " << request.url << '\n' << data); BOOL retval = PHTTPConfig::Post(request, data, reply); if (request.code == PHTTP::RequestOK) process.BeginRestartSystem(); PServiceHTML::ProcessMacros(request, reply, GetURL().AsString(PURL::PathOnly).Mid(1), PServiceHTML::LoadFromFile); OnLoadedText(request, reply); return retval; } BOOL PConfigPage::GetExpirationDate(PTime & when) { // Well and truly before now.... when = ImmediateExpiryTime; return TRUE; } ////////////////////////////////////////////////////////////// PConfigSectionsPage::PConfigSectionsPage(PHTTPServiceProcess & app, const PURL & url, const PHTTPAuthority & auth, const PString & prefix, const PString & valueName, const PURL & editSection, const PURL & newSection, const PString & newTitle, PHTML & heading) : PHTTPConfigSectionList(url, auth, prefix, valueName, editSection, newSection, newTitle, heading), process(app) { } void PConfigSectionsPage::OnLoadedText(PHTTPRequest & request, PString & text) { PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly).Mid(1), PServiceHTML::LoadFromFile); PHTTPConfigSectionList::OnLoadedText(request, text); } BOOL PConfigSectionsPage::OnPOST(PHTTPServer & server, const PURL & url, const PMIMEInfo & info, const PStringToString & data, const PHTTPConnectionInfo & connectInfo) { PHTTPConfigSectionList::OnPOST(server, url, info, data, connectInfo); return FALSE; // Make sure we break any persistent connections } BOOL PConfigSectionsPage::Post(PHTTPRequest & request, const PStringToString & data, PHTML & reply) { BOOL retval = PHTTPConfigSectionList::Post(request, data, reply); if (request.code == PHTTP::RequestOK) process.BeginRestartSystem(); return retval; } BOOL PConfigSectionsPage::GetExpirationDate(PTime & when) { // Well and truly before now.... when = ImmediateExpiryTime; return TRUE; } ////////////////////////////////////////////////////////////// PRegisterPage::PRegisterPage(PHTTPServiceProcess & app, const PHTTPAuthority & auth) : PConfigPage(app, "register.html", "Secured Options", auth), process(app) { } PString PRegisterPage::LoadText(PHTTPRequest & request) { if (fields.GetSize() > 0) return PConfigPage::LoadText(request); PString mailURL = "mailto:" + process.GetEMailAddress(); PString orderURL = mailURL; PString tempURL = mailURL; if (process.GetHomePage() == HOME_PAGE) { orderURL = "https://home.equival.com.au/purchase.html"; tempURL = "http://www.equival.com/" + process.GetName().ToLower() + "/register.html"; tempURL.Replace(" ", "", TRUE); } PServiceHTML regPage(process.GetName() & "Registration", NULL); regPage << "" "Your registration key is permanent.

" "Do not change your registration details or your key will not " "operate correctly.

" "If you need to " << PHTML::HotLink(orderURL) << "upgrade" << PHTML::HotLink() << " or " << PHTML::HotLink(mailURL) << "change" << PHTML::HotLink() << " your registration, then you may enter the new values sent " << " to you from " << process.GetManufacturer() << " into the fields " "below, and then press the Accept button.

" << PHTML::HRule() << "" "" "Your registration key is temporary and will expire on " ".

" "Do not change your registration details or your key will not " "operate correctly.

" "You may " << PHTML::HotLink(orderURL) << "order a permanent key" << PHTML::HotLink() << " and enter the new values sent to you from " << process.GetManufacturer() << " into the fields below, and then press the Accept button.

" << PHTML::HRule() << "" "" "Your temporary registration key has expired.

" "You may " << PHTML::HotLink(orderURL) << "order a permanent key" << PHTML::HotLink() << " and enter the new values sent to you from " << process.GetManufacturer() << " into the fields below, and then press the Accept button.

" << PHTML::HRule() << ""; PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys()); PString prefix; if (securedConf.GetValidation() != PSecureConfig::IsValid) prefix = securedConf.GetPendingPrefix(); AddFields(prefix); Add(new PHTTPStringField("Validation", 40)); BuildHTML(regPage, InsertIntoHTML); regPage << "" "You have entered the values sent to you from " << process.GetManufacturer() << " incorrectly. Please enter them again. Note, " << PHTML::Emphasis() << PHTML::Strong() << "all" << PHTML::Strong() << PHTML::Emphasis() << "the fields must be entered " << PHTML::Emphasis() << PHTML::Strong() << "exactly" << PHTML::Strong() << PHTML::Emphasis() << " as they appear in the e-mail from " << process.GetManufacturer() << ". We strongly recommend using copy and paste of all the fields, and then " "press the Accept button." "" "" "You may " << PHTML::HotLink(orderURL) << "order a permanent key" << PHTML::HotLink() << " or " << PHTML::HotLink(tempURL) << "obtain a temporary key" << PHTML::HotLink() << " and enter the values sent to you from " << process.GetManufacturer() << " into the fields above, and then press the Accept button.

" "" << PHTML::HRule() << PHTML::Heading(3) << "Disclaimer" << PHTML::Heading(3) << PHTML::Paragraph() << PHTML::Bold() << "The information and code herein is provided \"as is\" " "without warranty of any kind, either expressed or implied, " "including but not limited to the implied warrenties of " "merchantability and fitness for a particular purpose. In " "no event shall " << process.GetManufacturer() << " be liable " "for any damages whatsoever including direct, indirect, " "incidental, consequential, loss of business profits or special " "damages, even if " << process.GetManufacturer() << " has been " "advised of the possibility of such damages." << PHTML::Bold() << PHTML::Paragraph() << process.GetCopyrightText() << PHTML::Body(); SetString(regPage); return PConfigPage::LoadText(request); } static BOOL FindSpliceBlock(const PRegularExpression & regex, const PString & text, PINDEX & pos, PINDEX & len, PINDEX & start, PINDEX & finish) { if (!text.FindRegEx(regex, pos, len, 0)) return FALSE; PINDEX endpos, endlen; static PRegularExpression EndBlock("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); if (text.FindRegEx(EndBlock, endpos, endlen, pos)) { start = pos+len; finish = endpos-1; len = endpos - pos + endlen; } return TRUE; } void PRegisterPage::OnLoadedText(PHTTPRequest & request, PString & text) { PString block; PINDEX pos, len, start = 0, finish = 0; PSecureConfig securedConf(process.GetProductKey(), process.GetSecuredKeys()); PTime expiry = securedConf.GetTime(securedConf.GetExpiryDateKey()); static PRegularExpression Default("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); static PRegularExpression Permanent("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); static PRegularExpression Temporary("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); static PRegularExpression Expired("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); static PRegularExpression Invalid("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); static PRegularExpression Pending("name[ \t\n]*=[ \t\n]*\"" + securedConf.GetPendingPrefix() + "[^\"]+\"", PRegularExpression::Extended|PRegularExpression::IgnoreCase); PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly).Mid(1), PServiceHTML::LoadFromFile); switch (securedConf.GetValidation()) { case PSecureConfig::Defaults : while (FindSpliceBlock(Default, text, pos, len, start, finish)) text.Splice(text(start, finish), pos, len); while (FindSpliceBlock(Permanent, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Temporary, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Expired, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Invalid, text, pos, len, start, finish)) text.Delete(pos, len); break; case PSecureConfig::Invalid : case PSecureConfig::Pending : while (FindSpliceBlock(Default, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Permanent, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Temporary, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Expired, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Invalid, text, pos, len, start, finish)) text.Splice(text(start, finish), pos, len); break; case PSecureConfig::Expired : while (FindSpliceBlock(Default, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Permanent, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Temporary, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Expired, text, pos, len, start, finish)) text.Splice(text(start, finish), pos, len); while (FindSpliceBlock(Invalid, text, pos, len, start, finish)) text.Delete(pos, len); break; case PSecureConfig::IsValid : while (text.FindRegEx(Pending, pos, len)) { static PINDEX pendingLength = securedConf.GetPendingPrefix().GetLength(); text.Delete(text.Find('"', pos)+1, pendingLength); start = pos + len - pendingLength; } if (expiry.GetYear() < 2011) { while (FindSpliceBlock(Default, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Permanent, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Temporary, text, pos, len, start, finish)) text.Splice(text(start, finish), pos, len); while (FindSpliceBlock(Expired, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Invalid, text, pos, len, start, finish)) text.Delete(pos, len); } else { while (FindSpliceBlock(Default, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Permanent, text, pos, len, start, finish)) text.Splice(text(start, finish), pos, len); while (FindSpliceBlock(Temporary, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Expired, text, pos, len, start, finish)) text.Delete(pos, len); while (FindSpliceBlock(Invalid, text, pos, len, start, finish)) text.Delete(pos, len); } } static PRegularExpression ExpiryDate("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); while (text.FindRegEx(ExpiryDate, pos, len, 0)) text.Splice(expiry.AsString(PTime::LongDate), pos, len); PHTTPConfig::OnLoadedText(request, text); PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::NoOptions); } BOOL PRegisterPage::Post(PHTTPRequest & request, const PStringToString & data, PHTML & reply) { if (fields.GetSize() == 0) LoadText(request); BOOL retval = PHTTPConfig::Post(request, data, reply); if (request.code != PHTTP::RequestOK) return FALSE; PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys()); switch (sconf.GetValidation()) { case PSecureConfig::Defaults : sconf.ResetPending(); break; case PSecureConfig::IsValid : break; case PSecureConfig::Pending : sconf.ValidatePending(); break; default : sconf.ResetPending(); } RemoveAllFields(); LoadText(request); OnLoadedText(request, reply); return retval; } /////////////////////////////////////////////////////////////////// static void DigestSecuredKeys(PHTTPServiceProcess & process, PString & reginfo, PHTML * html) { const PStringArray & securedKeys = process.GetSecuredKeys(); PSecureConfig sconf(process.GetProductKey(), securedKeys); PString prefix; if (sconf.GetValidation() != PSecureConfig::IsValid) prefix = sconf.GetPendingPrefix(); PMessageDigest5 digestor; PStringStream info; info << '"' << process.GetName() << "\" ==="; PINDEX i; for (i = 0; i < securedKeys.GetSize(); i++) { PString val = sconf.GetString(prefix + securedKeys[i]).Trim(); info << " \"" << val << '"'; if (html != NULL) *html << PHTML::HiddenField(securedKeys[i], val); digestor.Process(val); } PString digest = digestor.Complete(); if (html != NULL) *html << PHTML::HiddenField("digest", digest); info.Replace("===", digest); reginfo = info; } /////////////////////////////////////////////////////////////////// PServiceHTML::PServiceHTML(const char * title, const char * help, const char * helpGif) { PHTTPServiceProcess::Current().GetPageHeader(*this, title); ostream & this_stream = *this; this_stream << PHTML::Heading(1) << title; if (help != NULL) this_stream << " " << PHTML::HotLink(help) << PHTML::Image(helpGif, "Help", 48, 23, "align=absmiddle") << PHTML::HotLink(); this_stream << PHTML::Heading(1) << PHTML::Paragraph(); } PString PServiceHTML::ExtractSignature(PString & out) { return ExtractSignature(*this, out); } PString PServiceHTML::ExtractSignature(const PString & html, PString & out, const char * keyword) { out = html; PRegularExpression SignatureRegEx("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); PINDEX pos, len; if (out.FindRegEx(SignatureRegEx, pos, len)) { PString tag = out.Mid(pos, len); out.Delete(pos, len); return tag(tag.Find("signature")+10, tag.FindLast('-')-2).Trim(); } return PString::Empty(); } PString PServiceHTML::CalculateSignature() { return CalculateSignature(*this); } PString PServiceHTML::CalculateSignature(const PString & out) { return CalculateSignature(out, PHTTPServiceProcess::Current().GetSignatureKey()); } PString PServiceHTML::CalculateSignature(const PString & out, const PTEACypher::Key & sig) { // calculate the MD5 digest of the HTML data PMessageDigest5 digestor; PINDEX p1 = 0; PINDEX p2; while ((p2 = out.FindOneOf("\r\n", p1)) != P_MAX_INDEX) { if (p2 > p1) digestor.Process(out(p1, p2-1)); digestor.Process("\r\n", 2); p1 = p2 + 1; if (out[p2] == '\r' && out[p1] == '\n') // CR LF pair p1++; } digestor.Process(out(p1, P_MAX_INDEX)); PMessageDigest5::Code md5; digestor.Complete(md5); // encode it PTEACypher cypher(sig); BYTE buf[sizeof(md5)+7]; memcpy(buf, &md5, sizeof(md5)); memset(&buf[sizeof(md5)], 0, sizeof(buf)-sizeof(md5)); return cypher.Encode(buf, sizeof(buf)); } BOOL PServiceHTML::CheckSignature() { return CheckSignature(*this); } BOOL PServiceHTML::CheckSignature(const PString & html) { if (PHTTPServiceProcess::Current().ShouldIgnoreSignatures()) return TRUE; // extract the signature from the file PString out; PString signature = ExtractSignature(html, out); // calculate the signature on the data PString checkSignature = CalculateSignature(out); // return TRUE or FALSE return checkSignature == signature; } static BOOL FindBrackets(const PString & args, PINDEX & open, PINDEX & close) { open = args.FindOneOf("[{(", close); if (open == P_MAX_INDEX) return FALSE; switch (args[open]) { case '[' : close = args.Find(']', open+1); break; case '{' : close = args.Find('}', open+1); break; case '(' : close = args.Find(')', open+1); break; } return close != P_MAX_INDEX; } static BOOL ExtractVariables(const PString & args, PString & variable, PString & value) { PINDEX open; PINDEX close = 0; if (FindBrackets(args, open, close)) variable = args(open+1, close-1); else { variable = args.Trim(); close = P_MAX_INDEX-1; } if (variable.IsEmpty()) return FALSE; if (FindBrackets(args, open, close)) value = args(open+1, close-1); return TRUE; } /////////////////////////////////////////////////////////////////////////////// PServiceMacro * PServiceMacro::list; PServiceMacro::PServiceMacro(const char * name, BOOL isBlock) { macroName = name; isMacroBlock = isBlock; link = list; list = this; } PServiceMacro::PServiceMacro(const PCaselessString & name, BOOL isBlock) { macroName = name; isMacroBlock = isBlock; } PObject::Comparison PServiceMacro::Compare(const PObject & obj) const { PAssert(PIsDescendant(&obj, PServiceMacro), PInvalidCast); const PServiceMacro & other = (const PServiceMacro &)obj; if (isMacroBlock != other.isMacroBlock) return isMacroBlock ? GreaterThan : LessThan; int cmp = strcasecmp(macroName, other.macroName); if (cmp < 0) return LessThan; if (cmp > 0) return GreaterThan; return EqualTo; } PString PServiceMacro::Translate(PHTTPRequest &, const PString &, const PString &) const { return PString::Empty(); }; PServiceMacros_list::PServiceMacros_list() { DisallowDeleteObjects(); PServiceMacro * macro = PServiceMacro::list; while (macro != NULL) { Append(macro); macro = macro->link; } } PCREATE_SERVICE_MACRO(Header,request,P_EMPTY) { PString hdr = PHTTPServiceProcess::Current().GetPageGraphic(); PServiceHTML::ProcessMacros(request, hdr, "header.html", PServiceHTML::LoadFromFile|PServiceHTML::NoURLOverride); return hdr; } PCREATE_SERVICE_MACRO(Copyright,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetCopyrightText(); } PCREATE_SERVICE_MACRO(ProductName,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetProductName(); } PCREATE_SERVICE_MACRO(Manufacturer,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetManufacturer(); } PCREATE_SERVICE_MACRO(Version,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetVersion(TRUE); } PCREATE_SERVICE_MACRO(BuildDate,P_EMPTY,args) { const PTime & date = PHTTPServiceProcess::Current().GetCompilationDate(); if (args.IsEmpty()) return date.AsString("d MMMM yyyy"); return date.AsString(args); } PCREATE_SERVICE_MACRO(OS,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetOSClass() & PHTTPServiceProcess::Current().GetOSName(); } PCREATE_SERVICE_MACRO(Machine,P_EMPTY,P_EMPTY) { return PHTTPServiceProcess::Current().GetOSVersion() + '-' + PHTTPServiceProcess::Current().GetOSHardware(); } PCREATE_SERVICE_MACRO(LongDateTime,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::LongDateTime); } PCREATE_SERVICE_MACRO(LongDate,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::LongDate); } PCREATE_SERVICE_MACRO(LongTime,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::LongTime); } PCREATE_SERVICE_MACRO(MediumDateTime,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::MediumDateTime); } PCREATE_SERVICE_MACRO(MediumDate,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::MediumDate); } PCREATE_SERVICE_MACRO(ShortDateTime,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::ShortDateTime); } PCREATE_SERVICE_MACRO(ShortDate,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::ShortDate); } PCREATE_SERVICE_MACRO(ShortTime,P_EMPTY,P_EMPTY) { return PTime().AsString(PTime::ShortTime); } PCREATE_SERVICE_MACRO(Time,P_EMPTY,args) { PTime now; if (args.IsEmpty()) return now.AsString(); return now.AsString(args); } PCREATE_SERVICE_MACRO(StartTime,P_EMPTY,P_EMPTY) { return PProcess::Current().GetStartTime().AsString(PTime::MediumDateTime); } PCREATE_SERVICE_MACRO(UpTime,P_EMPTY,P_EMPTY) { PTimeInterval upTime = PTime() - PProcess::Current().GetStartTime(); return upTime.AsString(0, PTimeInterval::IncludeDays); } PCREATE_SERVICE_MACRO(LocalHost,request,P_EMPTY) { if (request.localAddr != 0) return PIPSocket::GetHostName(request.localAddr); else return PIPSocket::GetHostName(); } PCREATE_SERVICE_MACRO(LocalIP,request,P_EMPTY) { if (request.localAddr != 0) return request.localAddr; else return "127.0.0.1"; } PCREATE_SERVICE_MACRO(LocalPort,request,P_EMPTY) { if (request.localPort != 0) return psprintf("%u", request.localPort); else return "80"; } PCREATE_SERVICE_MACRO(PeerHost,request,P_EMPTY) { if (request.origin != 0) return PIPSocket::GetHostName(request.origin); else return "N/A"; } PCREATE_SERVICE_MACRO(PeerIP,request,P_EMPTY) { if (request.origin != 0) return request.origin; else return "N/A"; } PCREATE_SERVICE_MACRO(MonitorInfo,request,P_EMPTY) { const PTime & compilationDate = PHTTPServiceProcess::Current().GetCompilationDate(); PString peerAddr = "N/A"; if (request.origin != 0) peerAddr = request.origin.AsString(); PString localAddr = "127.0.0.1"; if (request.localAddr != 0) localAddr = request.localAddr.AsString(); WORD localPort = 80; if (request.localPort != 0) localPort = request.localPort; PString timeFormat = "yyyyMMdd hhmmss z"; PTime now; PTimeInterval upTime = now - PProcess::Current().GetStartTime(); PStringStream monitorText; monitorText << "Program: " << PHTTPServiceProcess::Current().GetProductName() << "\n" << "Version: " << PHTTPServiceProcess::Current().GetVersion(TRUE) << "\n" << "Manufacturer: " << PHTTPServiceProcess::Current().GetManufacturer() << "\n" << "OS: " << PHTTPServiceProcess::Current().GetOSClass() << " " << PHTTPServiceProcess::Current().GetOSName() << "\n" << "OS Version: " << PHTTPServiceProcess::Current().GetOSVersion() << "\n" << "Hardware: " << PHTTPServiceProcess::Current().GetOSHardware() << "\n" << "Compilation date: " << compilationDate.AsString(timeFormat, PTime::GMT) << "\n" << "Start Date: " << PProcess::Current().GetStartTime().AsString(timeFormat, PTime::GMT) << "\n" << "Current Date: " << now.AsString(timeFormat, PTime::GMT) << "\n" << "Up time: " << upTime << "\n" << "Peer Addr: " << peerAddr << "\n" << "Local Host: " << PIPSocket::GetHostName() << "\n" << "Local Addr: " << localAddr << "\n" << "Local Port: " << localPort << "\n" ; return monitorText; } PCREATE_SERVICE_MACRO(RegInfo,P_EMPTY,P_EMPTY) { PString subs; DigestSecuredKeys(PHTTPServiceProcess::Current(), subs, NULL); return subs; } static PString GetRegInfo(const char * info) { PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys()); PString pending = sconf.GetPendingPrefix(); return sconf.GetString(info, sconf.GetString(pending+info)); } PCREATE_SERVICE_MACRO(RegUser,P_EMPTY,P_EMPTY) { return GetRegInfo("Name"); } PCREATE_SERVICE_MACRO(RegCompany,P_EMPTY,P_EMPTY) { return GetRegInfo("Company"); } PCREATE_SERVICE_MACRO(RegEmail,P_EMPTY,P_EMPTY) { return GetRegInfo("EMail"); } PCREATE_SERVICE_MACRO(Registration,P_EMPTY,args) { PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PSecureConfig sconf(process.GetProductKey(), process.GetSecuredKeys()); PString pending = sconf.GetPendingPrefix(); PString regNow = "Register Now!"; PString viewReg = "View Registration"; PString demoCopy = "Unregistered Demonstration Copy"; PINDEX open; PINDEX close = 0; if (FindBrackets(args, open, close)) { regNow = args(open+1, close-1); if (FindBrackets(args, open, close)) { viewReg = args(open+1, close-1); if (FindBrackets(args, open, close)) demoCopy = args(open+1, close-1); } } PHTML out(PHTML::InBody); out << "" << sconf.GetString("Name", sconf.GetString(pending+"Name", "*** "+demoCopy+" ***")) << PHTML::BreakLine() << "" << sconf.GetString("Company", sconf.GetString(pending+"Company")) << PHTML::BreakLine() << PHTML::BreakLine() << ""; if (sconf.GetString("Name").IsEmpty()) process.AddUnregisteredText(out); else process.AddRegisteredText(out); out << PHTML::HotLink("/register.html") << (sconf.GetString("Name").IsEmpty() ? regNow : viewReg) << PHTML::HotLink(); return out; } PCREATE_SERVICE_MACRO(InputsFromQuery,request,P_EMPTY) { PStringToString vars = request.url.GetQueryVars(); PStringStream subs; for (PINDEX i = 0; i < vars.GetSize(); i++) subs << "\r\n"; return subs; } PCREATE_SERVICE_MACRO(Query,request,args) { if (args.IsEmpty()) return request.url.GetQuery(); PString variable, value; if (ExtractVariables(args, variable, value)) { value = request.url.GetQueryVars()(variable, value); if (!value) return value; } return PString::Empty(); } PCREATE_SERVICE_MACRO(Get,request,args) { PString variable, value; if (ExtractVariables(args, variable, value)) { PString section = request.url.GetQueryVars()("section"); PINDEX slash = variable.FindLast('\\'); if (slash != P_MAX_INDEX) { section += variable.Left(slash); variable = variable.Mid(slash+1); } if (!section && !variable) { PConfig config(section); return config.GetString(variable, value); } } return PString::Empty(); } PCREATE_SERVICE_MACRO(URL,request,P_EMPTY) { return request.url.AsString(); } PCREATE_SERVICE_MACRO(Include,P_EMPTY,args) { PString text; if (!args) { PFile file; if (file.Open(args, PFile::ReadOnly)) text = file.ReadString(file.GetLength()); } return text; } PCREATE_SERVICE_MACRO(SignedInclude,P_EMPTY,args) { PString text; if (!args) { PFile file; if (file.Open(args, PFile::ReadOnly)) { text = file.ReadString(file.GetLength()); if (!PServiceHTML::CheckSignature(text)) { PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PHTML html("Invalid OEM Signature"); html << "The HTML file \"" << args << "\" contains an invalid signature for \"" << process.GetName() << "\" by \"" << process.GetManufacturer() << '"' << PHTML::Body(); text = html; } } } return text; } PCREATE_SERVICE_MACRO_BLOCK(IfQuery,request,args,block) { PStringToString vars = request.url.GetQueryVars(); PINDEX space = args.FindOneOf(" \t\r\n"); PString var = args.Left(space); PString value = args.Mid(space).LeftTrim(); BOOL ok; if (value.IsEmpty()) ok = vars.Contains(var); else { PString operation; space = value.FindOneOf(" \t\r\n"); if (space != P_MAX_INDEX) { operation = value.Left(space); value = value.Mid(space).LeftTrim(); } PString query = vars(var); if (operation == "!=") ok = query != value; else if (operation == "<") ok = query < value; else if (operation == ">") ok = query > value; else if (operation == "<=") ok = query <= value; else if (operation == ">=") ok = query >= value; else if (operation == "*=") ok = (query *= value); else ok = query == value; } return ok ? block : PString::Empty(); } PCREATE_SERVICE_MACRO_BLOCK(IfInURL,request,args,block) { if (request.url.AsString().Find(args) != P_MAX_INDEX) return block; return PString::Empty(); } PCREATE_SERVICE_MACRO_BLOCK(IfNotInURL,request,args,block) { if (request.url.AsString().Find(args) == P_MAX_INDEX) return block; return PString::Empty(); } static void SplitCmdAndArgs(const PString & text, PINDEX pos, PCaselessString & cmd, PString & args) { static const char whitespace[] = " \t\r\n"; PString macro = text(text.FindOneOf(whitespace, pos)+1, text.Find("--", pos+3)-1).Trim(); PINDEX endCmd = macro.FindOneOf(whitespace); if (endCmd == P_MAX_INDEX) { cmd = macro; args.MakeEmpty(); } else { cmd = macro.Left(endCmd); args = macro.Mid(endCmd+1).LeftTrim(); } } BOOL PServiceHTML::ProcessMacros(PHTTPRequest & request, PString & text, const PString & defaultFile, unsigned options) { PINDEX alreadyLoadedPrefixLength = 0; PString filename = defaultFile; if ((options&LoadFromFile) != 0) { if ((options&NoURLOverride) == 0) { filename = request.url.GetParameters(); if (filename.IsEmpty()) filename = defaultFile; } if (!filename) { PString alreadyLoaded = "\r\n"; alreadyLoadedPrefixLength = alreadyLoaded.GetLength(); if (text.Find(alreadyLoaded) != 0) { PFile file; if (file.Open(filename, PFile::ReadOnly)) { text = alreadyLoaded + file.ReadString(file.GetLength()); if ((options&NoSignatureForFile) == 0) options |= NeedSignature; } } } } if ((options&NeedSignature) != 0) { if (!CheckSignature(text.Mid(alreadyLoadedPrefixLength))) { PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PHTML html("Invalid OEM Signature"); html << "The HTML file \"" << filename << "\" contains an invalid signature for \"" << process.GetName() << "\" by \"" << process.GetManufacturer() << '"' << PHTML::Body(); text = html; return FALSE; } } static PServiceMacros_list ServiceMacros; PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PRegularExpression StartBlockRegEx("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); PRegularExpression MacroRegEx("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); BOOL substitedMacro; do { substitedMacro = FALSE; PINDEX pos = 0; PINDEX len; while (text.FindRegEx(StartBlockRegEx, pos, len, pos)) { PString substitution; PCaselessString cmd; PString args; SplitCmdAndArgs(text, pos, cmd, args); PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, TRUE)); if (idx != P_MAX_INDEX) { PRegularExpression EndBlockRegEx("?", PRegularExpression::Extended|PRegularExpression::IgnoreCase); PINDEX endpos, endlen; if (text.FindRegEx(EndBlockRegEx, endpos, endlen, pos+len)) { PINDEX startpos = pos+len; len = endpos-pos + endlen; substitution = ServiceMacros[idx].Translate(request, args, text(startpos, endpos-1)); substitedMacro = TRUE; } } text.Splice(substitution, pos, len); } pos = 0; while (text.FindRegEx(MacroRegEx, pos, len, pos)) { PCaselessString cmd; PString args; SplitCmdAndArgs(text, pos, cmd, args); PString substitution; if (!process.SubstituteEquivalSequence(request, cmd & args, substitution)) { PINDEX idx = ServiceMacros.GetValuesIndex(PServiceMacro(cmd, FALSE)); if (idx != P_MAX_INDEX) { substitution = ServiceMacros[idx].Translate(request, args, PString::Empty()); substitedMacro = TRUE; } } text.Splice(substitution, pos, len); } } while (substitedMacro); return TRUE; } /////////////////////////////////////////////////////////////////// static void ServiceOnLoadedText(PString & text) { PHTTPServiceProcess & process = PHTTPServiceProcess::Current(); PString manuf = ""; if (text.Find(manuf) != P_MAX_INDEX) text.Replace(manuf, process.GetPageGraphic(), TRUE); static const char equiv[] = ""; if (text.Find(equiv) != P_MAX_INDEX) text.Replace(equiv, process.GetPageGraphic(), TRUE); static const char copy[] = ""; if (text.Find(copy) != P_MAX_INDEX) text.Replace(copy, process.GetCopyrightText(), TRUE); } PString PServiceHTTPString::LoadText(PHTTPRequest & request) { PString text = PHTTPString::LoadText(request); ServiceOnLoadedText(text); PServiceHTML::ProcessMacros(request, text, "", PServiceHTML::LoadFromFile); return text; } BOOL PServiceHTTPString::GetExpirationDate(PTime & when) { // Well and truly before now.... when = ImmediateExpiryTime; return TRUE; } void PServiceHTTPFile::OnLoadedText(PHTTPRequest & request, PString & text) { ServiceOnLoadedText(text); PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly), needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions); } BOOL PServiceHTTPFile::GetExpirationDate(PTime & when) { // Well and truly before now.... when = ImmediateExpiryTime; return TRUE; } void PServiceHTTPDirectory::OnLoadedText(PHTTPRequest & request, PString & text) { ServiceOnLoadedText(text); PServiceHTML::ProcessMacros(request, text, GetURL().AsString(PURL::PathOnly), needSignature ? PServiceHTML::NeedSignature : PServiceHTML::NoOptions); } BOOL PServiceHTTPDirectory::GetExpirationDate(PTime & when) { // Well and truly before now.... when = ImmediateExpiryTime; return TRUE; } ///////////////////////////////////////////////////////////////////